@ai-sdlc/orchestrator 0.10.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 (156) 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/backlog-adapter.d.ts +45 -0
  10. package/dist/backlog-adapter.js +145 -1
  11. package/dist/calibration.d.ts +81 -0
  12. package/dist/calibration.js +76 -0
  13. package/dist/cli/commands/init-features.d.ts +301 -2
  14. package/dist/cli/commands/init-features.js +634 -7
  15. package/dist/cli/commands/init-templates.d.ts +198 -1
  16. package/dist/cli/commands/init-templates.js +943 -1
  17. package/dist/cli/commands/init.d.ts +45 -0
  18. package/dist/cli/commands/init.js +147 -5
  19. package/dist/cli/commands/run.js +9 -1
  20. package/dist/cli/index.d.ts +11 -0
  21. package/dist/cli/index.js +42 -8
  22. package/dist/compliance/composer.d.ts +79 -0
  23. package/dist/compliance/composer.js +258 -0
  24. package/dist/compliance/errors.d.ts +64 -0
  25. package/dist/compliance/errors.js +85 -0
  26. package/dist/compliance/loader.d.ts +52 -0
  27. package/dist/compliance/loader.js +124 -0
  28. package/dist/compliance/types.d.ts +184 -0
  29. package/dist/compliance/types.js +41 -0
  30. package/dist/compliance-clearance.d.ts +269 -0
  31. package/dist/compliance-clearance.js +269 -0
  32. package/dist/config.js +17 -0
  33. package/dist/cost-tracker.d.ts +22 -0
  34. package/dist/cost-tracker.js +41 -0
  35. package/dist/database/adapters/external.js +5 -1
  36. package/dist/embedding/adapters/openai-text-embedding-3-small.d.ts +71 -0
  37. package/dist/embedding/adapters/openai-text-embedding-3-small.js +190 -0
  38. package/dist/embedding/consumers/tessellation-drift.d.ts +74 -0
  39. package/dist/embedding/consumers/tessellation-drift.js +76 -0
  40. package/dist/embedding/cross-provider.d.ts +78 -0
  41. package/dist/embedding/cross-provider.js +75 -0
  42. package/dist/embedding/deprecation.d.ts +151 -0
  43. package/dist/embedding/deprecation.js +229 -0
  44. package/dist/embedding/errors.d.ts +90 -0
  45. package/dist/embedding/errors.js +150 -0
  46. package/dist/embedding/index.d.ts +29 -0
  47. package/dist/embedding/index.js +24 -0
  48. package/dist/embedding/pipeline-load.d.ts +146 -0
  49. package/dist/embedding/pipeline-load.js +178 -0
  50. package/dist/embedding/registry.d.ts +45 -0
  51. package/dist/embedding/registry.js +61 -0
  52. package/dist/embedding/stale-vector.d.ts +110 -0
  53. package/dist/embedding/stale-vector.js +92 -0
  54. package/dist/embedding/storage/index.d.ts +51 -0
  55. package/dist/embedding/storage/index.js +43 -0
  56. package/dist/embedding/storage/jsonl-backend.d.ts +150 -0
  57. package/dist/embedding/storage/jsonl-backend.js +332 -0
  58. package/dist/embedding/storage/types.d.ts +135 -0
  59. package/dist/embedding/storage/types.js +13 -0
  60. package/dist/embedding/types.d.ts +180 -0
  61. package/dist/embedding/types.js +10 -0
  62. package/dist/execute.d.ts +29 -2
  63. package/dist/execute.js +171 -30
  64. package/dist/index.d.ts +14 -2
  65. package/dist/index.js +19 -2
  66. package/dist/journey/inheritance-validator.d.ts +396 -0
  67. package/dist/journey/inheritance-validator.js +370 -0
  68. package/dist/journey/state-id-drift-rule.d.ts +137 -0
  69. package/dist/journey/state-id-drift-rule.js +245 -0
  70. package/dist/journey-sa2-router.d.ts +395 -0
  71. package/dist/journey-sa2-router.js +308 -0
  72. package/dist/runners/review-agent.js +6 -2
  73. package/dist/runners/runner-registry.d.ts +36 -0
  74. package/dist/runners/runner-registry.js +90 -0
  75. package/dist/runtime/attestations.d.ts +173 -13
  76. package/dist/runtime/attestations.js +252 -40
  77. package/dist/runtime/index.d.ts +1 -1
  78. package/dist/runtime/index.js +1 -1
  79. package/dist/sa-scoring/layer3-llm.js +6 -1
  80. package/dist/sa-scoring/revision-proposal-config.d.ts +178 -0
  81. package/dist/sa-scoring/revision-proposal-config.js +198 -0
  82. package/dist/sa-scoring/revision-proposal.d.ts +285 -0
  83. package/dist/sa-scoring/revision-proposal.js +417 -0
  84. package/dist/signal-ingestion/adapters/community-thread.d.ts +43 -0
  85. package/dist/signal-ingestion/adapters/community-thread.js +55 -0
  86. package/dist/signal-ingestion/adapters/in-app-feedback.d.ts +67 -0
  87. package/dist/signal-ingestion/adapters/in-app-feedback.js +51 -0
  88. package/dist/signal-ingestion/adapters/manual.d.ts +78 -0
  89. package/dist/signal-ingestion/adapters/manual.js +112 -0
  90. package/dist/signal-ingestion/adapters/support-ticket.d.ts +47 -0
  91. package/dist/signal-ingestion/adapters/support-ticket.js +51 -0
  92. package/dist/signal-ingestion/classifier.d.ts +205 -0
  93. package/dist/signal-ingestion/classifier.js +494 -0
  94. package/dist/signal-ingestion/clustering-types.d.ts +36 -0
  95. package/dist/signal-ingestion/clustering-types.js +14 -0
  96. package/dist/signal-ingestion/clustering.d.ts +200 -0
  97. package/dist/signal-ingestion/clustering.js +413 -0
  98. package/dist/signal-ingestion/config.d.ts +351 -0
  99. package/dist/signal-ingestion/config.js +587 -0
  100. package/dist/signal-ingestion/d1.d.ts +252 -0
  101. package/dist/signal-ingestion/d1.js +235 -0
  102. package/dist/signal-ingestion/errors.d.ts +73 -0
  103. package/dist/signal-ingestion/errors.js +108 -0
  104. package/dist/signal-ingestion/governance-events.d.ts +181 -0
  105. package/dist/signal-ingestion/governance-events.js +189 -0
  106. package/dist/signal-ingestion/index.d.ts +35 -0
  107. package/dist/signal-ingestion/index.js +53 -0
  108. package/dist/signal-ingestion/manual-share-metric.d.ts +93 -0
  109. package/dist/signal-ingestion/manual-share-metric.js +106 -0
  110. package/dist/signal-ingestion/registry.d.ts +40 -0
  111. package/dist/signal-ingestion/registry.js +137 -0
  112. package/dist/signal-ingestion/residency.d.ts +227 -0
  113. package/dist/signal-ingestion/residency.js +238 -0
  114. package/dist/signal-ingestion/significance.d.ts +554 -0
  115. package/dist/signal-ingestion/significance.js +555 -0
  116. package/dist/signal-ingestion/types.d.ts +191 -0
  117. package/dist/signal-ingestion/types.js +8 -0
  118. package/dist/substrate/drift-composition.d.ts +270 -0
  119. package/dist/substrate/drift-composition.js +306 -0
  120. package/dist/substrate/drift-tui-surface.d.ts +61 -0
  121. package/dist/substrate/drift-tui-surface.js +102 -0
  122. package/dist/substrate/identity-class.d.ts +176 -0
  123. package/dist/substrate/identity-class.js +201 -0
  124. package/dist/tessellation/cross-soul-provenance-rule.d.ts +133 -0
  125. package/dist/tessellation/cross-soul-provenance-rule.js +171 -0
  126. package/dist/tessellation/inter-soul-embedding-distance-rule.d.ts +61 -0
  127. package/dist/tessellation/inter-soul-embedding-distance-rule.js +67 -0
  128. package/dist/tessellation/rule-registry.d.ts +269 -0
  129. package/dist/tessellation/rule-registry.js +92 -0
  130. package/dist/tessellation/soul-slug-ast-scan-rule.d.ts +90 -0
  131. package/dist/tessellation/soul-slug-ast-scan-rule.js +158 -0
  132. package/dist/tessellation-admission.d.ts +162 -0
  133. package/dist/tessellation-admission.js +146 -0
  134. package/dist/tessellation-drift.d.ts +246 -0
  135. package/dist/tessellation-drift.js +250 -0
  136. package/dist/validate-config.js +13 -0
  137. package/dist/validate-issue.js +2 -2
  138. package/dist/variant/cardinality-activation.d.ts +126 -0
  139. package/dist/variant/cardinality-activation.js +101 -0
  140. package/dist/variant/deprecation-lifecycle.d.ts +184 -0
  141. package/dist/variant/deprecation-lifecycle.js +208 -0
  142. package/dist/variant/drift-extension.d.ts +136 -0
  143. package/dist/variant/drift-extension.js +164 -0
  144. package/dist/variant/engineering-review.d.ts +185 -0
  145. package/dist/variant/engineering-review.js +142 -0
  146. package/dist/variant/index.d.ts +32 -0
  147. package/dist/variant/index.js +32 -0
  148. package/dist/variant/inheritance-validator.d.ts +165 -0
  149. package/dist/variant/inheritance-validator.js +139 -0
  150. package/dist/variant/internal-adopter/index.d.ts +11 -0
  151. package/dist/variant/internal-adopter/index.js +10 -0
  152. package/dist/variant/internal-adopter/products.d.ts +156 -0
  153. package/dist/variant/internal-adopter/products.js +366 -0
  154. package/dist/variant-admission.d.ts +316 -0
  155. package/dist/variant-admission.js +247 -0
  156. package/package.json +10 -8
@@ -0,0 +1,269 @@
1
+ /**
2
+ * RFC-0009 §13 + RFC-0018 OQ-10 — Tessellation§13RuleRegistry.
3
+ *
4
+ * Provides a concrete registration mechanism for §13 drift-detection rules,
5
+ * resolving RFC-0018 OQ-10 (2026-05-28, full rubric):
6
+ *
7
+ * "4th rule in same §13 engine + concrete registration mechanism spec'd:
8
+ * Tessellation§13RuleRegistry.register(rule). Industry research:
9
+ * unified-engine-with-plugin-rules (Sonar, Semgrep, CodeQL, Snyk,
10
+ * Dependabot, Renovate) is the modern de-facto pattern."
11
+ *
12
+ * Standard rule interface: `{ name, description, scan(target): DriftEvent[], severity }`
13
+ * §13 dispatcher fans out all registered rules in parallel; aggregates Decisions
14
+ * for catalog routing.
15
+ *
16
+ * ### Composition with RFC-0028 OQ-7.2
17
+ *
18
+ * RFC-0028 OQ-7.2 (2026-05-27 resolution) establishes the structural-vs-statistical
19
+ * drift detection pairing:
20
+ *
21
+ * - **Structural drift** (this registry, CI authoring-time): BLOCKS PR via
22
+ * `Decision: <rule-specific-name>` when `severity === 'high'`.
23
+ * - **Statistical drift** (runtime PPA `SoulDriftDetected`): SURFACES via
24
+ * RFC-0035 G0 non-blocking pipeline contract.
25
+ *
26
+ * Rules register their `severity` to indicate which path applies:
27
+ * `'high'` = structural-blocking; `'medium'` | `'warning'` = surfaces non-blocking.
28
+ *
29
+ * @see spec/rfcs/RFC-0009-tessellated-design-intent-documents.md §13
30
+ * @see spec/rfcs/RFC-0018-in-soul-journey-pattern.md §10.1 OQ-8 + OQ-10
31
+ * @see spec/rfcs/RFC-0028-engineering-axis-substrate-enforcement.md OQ-7.2
32
+ */
33
+ /**
34
+ * Severity level for a drift event / rule.
35
+ *
36
+ * Per RFC-0028 OQ-7.2:
37
+ * - `'high'` — structural drift; BLOCKS PR at CI authoring-time when configured.
38
+ * - `'medium'` — surfaces non-blocking via RFC-0035 G0 catalog route.
39
+ * - `'warning'` — informational; surfaces for operator batch review.
40
+ */
41
+ export type DriftSeverity = 'high' | 'medium' | 'warning';
42
+ /**
43
+ * A single drift event emitted by a registered §13 rule.
44
+ *
45
+ * All rules emit events of this shape. The `rule` field discriminates
46
+ * the originating rule; consumers narrow on it before reading `details`.
47
+ *
48
+ * Events are aggregated by the §13 dispatcher and forwarded to the
49
+ * RFC-0035 Decision Catalog for routing.
50
+ */
51
+ export interface DriftEvent {
52
+ /**
53
+ * Canonical rule name — matches the `name` field of the `TessellationRule`
54
+ * that emitted this event. Used for Decision Catalog routing and operator
55
+ * attribution.
56
+ */
57
+ rule: string;
58
+ /**
59
+ * RFC-3339 UTC timestamp at detection time.
60
+ */
61
+ timestamp: string;
62
+ /**
63
+ * Human-readable one-line summary; safe for operator surfaces (TUI, Slack).
64
+ */
65
+ message: string;
66
+ /**
67
+ * Effective severity for this event instance. Rules may downgrade from
68
+ * their registered `severity` based on per-org configuration (e.g. default
69
+ * `medium` can be escalated to `high` via journey-config.yaml
70
+ * `driftDetection.severityOverride`).
71
+ */
72
+ severity: DriftSeverity;
73
+ /**
74
+ * Free-form structured payload; rule-specific. Consumers narrow via `rule`.
75
+ */
76
+ details: unknown;
77
+ }
78
+ /**
79
+ * Standard interface for §13 tessellation drift-detection rules.
80
+ *
81
+ * RFC-0018 OQ-10 resolution specifies this shape:
82
+ * `{ name, description, scan(target): DriftEvent[], severity }`
83
+ *
84
+ * Rules MUST be stateless — all context is passed through `target`.
85
+ * The registry fans out `scan(target)` calls in parallel; the order of
86
+ * rule registration does not affect dispatch order (all fire concurrently).
87
+ */
88
+ export interface TessellationRule<TTarget = RuleScanTarget> {
89
+ /**
90
+ * Canonical stable rule name. Used as the `rule` field on emitted events
91
+ * and as the Decision Catalog routing key. SHOULD be kebab-case.
92
+ *
93
+ * Examples: `'soul-slug-ast-scan'`, `'cross-soul-provenance'`,
94
+ * `'journey-state-id-drift'`.
95
+ */
96
+ readonly name: string;
97
+ /**
98
+ * Human-readable description of what this rule detects. Shown in
99
+ * operator TUI and Slack notifications.
100
+ */
101
+ readonly description: string;
102
+ /**
103
+ * Default severity level for events emitted by this rule.
104
+ *
105
+ * Per RFC-0028 OQ-7.2:
106
+ * - `'high'` → structural blocking (BLOCKS PR)
107
+ * - `'medium'` → non-blocking, surfaces via RFC-0035 G0 catalog
108
+ * - `'warning'` → informational batch-review queue
109
+ */
110
+ readonly severity: DriftSeverity;
111
+ /**
112
+ * Scan the supplied target for drift.
113
+ *
114
+ * Called once per tick by the §13 dispatcher. MUST be synchronous or
115
+ * return a Promise. Stateless — all state must be captured in closure
116
+ * at construction time. MUST NOT throw; surface errors as `DriftEvent`
117
+ * with an `error` details payload and `severity: 'warning'`.
118
+ *
119
+ * @param target All inputs available to rules in this dispatch cycle.
120
+ * @returns Zero or more `DriftEvent` objects. Empty array = no drift.
121
+ */
122
+ scan(target: TTarget): DriftEvent[] | Promise<DriftEvent[]>;
123
+ }
124
+ /**
125
+ * The shared scan-target passed to every registered rule during a §13
126
+ * dispatch cycle.
127
+ *
128
+ * Rules destructure only the fields they need. Optional fields are absent
129
+ * when the caller did not provide them (no-op for rules that depend on them).
130
+ *
131
+ * This is the context object that evolves across RFC phases:
132
+ * - Phase 4.2 (AISDLC-317): `substrateFiles`, `tessellation`, `tessellatedDid`
133
+ * - Phase 4.2 (AISDLC-317): `provenance`
134
+ * - Phase 3 (AISDLC-467): `journeysBySoul`, `journeyStatus`
135
+ *
136
+ * New rule families add fields here as phases land. Existing rules
137
+ * destructure only what they need; unknown fields are ignored — forward-
138
+ * compatible with zero changes to existing rule implementations.
139
+ */
140
+ export interface RuleScanTarget {
141
+ /** DID URI of the parent Tessellation being scanned. */
142
+ tessellatedDid: string;
143
+ /** Substrate files to scan (for AST-scan based rules). */
144
+ substrateFiles?: SubstrateFileEntry[];
145
+ /** Provenance entries to audit (for cross-soul provenance rule). */
146
+ provenance?: unknown[];
147
+ /**
148
+ * Journey declarations keyed by soulId — the in-memory representation
149
+ * of all active journeys across the tessellation.
150
+ *
151
+ * Shape matches `JourneyContext.journeysBySoul` from `journey-sa2-router.ts`
152
+ * (RFC-0018 Phase 2). Rules read state IDs from `journey.states[]`.
153
+ */
154
+ journeysBySoul?: Record<string, ActiveJourneyDeclaration[]>;
155
+ /**
156
+ * Journey lifecycle status keyed by soulId+journeyId. Used by the
157
+ * JourneyStateIdDriftRule to detect removed journeys.
158
+ *
159
+ * Key format: `<soul-id>/<journey-id>`. Missing entry = assume active.
160
+ */
161
+ journeyStatus?: Record<string, JourneyLifecycleStatus>;
162
+ }
163
+ /** A substrate file entry for AST-scan based rules. */
164
+ export interface SubstrateFileEntry {
165
+ /** Workspace-relative path (forward slashes). */
166
+ path: string;
167
+ /** Full file contents as UTF-8. */
168
+ contents: string;
169
+ }
170
+ /**
171
+ * Minimal journey declaration shape for rule consumption.
172
+ * Mirrors `JourneyDeclaration` from `journey-sa2-router.ts` but
173
+ * is defined here to avoid a circular import across RFC phase packages.
174
+ * The drift registry ships in AISDLC-467 (Phase 3); the SA2 router
175
+ * ships in AISDLC-466 (Phase 2) — they must not create import cycles.
176
+ */
177
+ export interface ActiveJourneyDeclaration {
178
+ /** Journey identifier (kebab-case, unique within parent scope). */
179
+ id: string;
180
+ /**
181
+ * Declared state identifiers for this journey. Rules scan substrate
182
+ * code for references to these IDs.
183
+ *
184
+ * Each state has an `id` (kebab-case, unique within the journey) plus
185
+ * optional metadata. The drift rule only cares about `id`.
186
+ */
187
+ states: Array<{
188
+ id: string;
189
+ [key: string]: unknown;
190
+ }>;
191
+ }
192
+ /**
193
+ * Journey lifecycle status — whether a journey is currently active or
194
+ * has been removed (deprecated / archived).
195
+ *
196
+ * Per RFC-0018 §6.2 and OQ-8 resolution: application code referencing
197
+ * a state in a removed journey is a drift signal.
198
+ */
199
+ export type JourneyLifecycleStatus = 'active' | 'removed';
200
+ /**
201
+ * Tessellation§13RuleRegistry — the registration + parallel dispatch
202
+ * mechanism for RFC-0009 §13 drift-detection rules.
203
+ *
204
+ * RFC-0018 OQ-10 resolution (2026-05-28) specifies:
205
+ * "concrete registration mechanism: Tessellation§13RuleRegistry.register(rule)
206
+ * … §13 dispatcher fans out all registered rules in parallel; aggregates
207
+ * Decisions for catalog routing."
208
+ *
209
+ * ### Usage
210
+ *
211
+ * ```ts
212
+ * import { createTessellation13Registry } from './tessellation/rule-registry.js';
213
+ * import { SoulSlugAstScanRule } from './tessellation/soul-slug-ast-scan-rule.js';
214
+ *
215
+ * const registry = createTessellation13Registry();
216
+ * registry.register(new SoulSlugAstScanRule());
217
+ * const events = await registry.dispatch(target);
218
+ * ```
219
+ *
220
+ * ### Parallelism
221
+ *
222
+ * `dispatch()` fans out `scan(target)` calls via `Promise.allSettled` so
223
+ * that a slow or failing rule does not block others. Settled rejections
224
+ * are converted to `DriftEvent` with `severity: 'warning'` so they surface
225
+ * as operator-visible noise rather than silently dropping.
226
+ */
227
+ export interface Tessellation13Registry {
228
+ /**
229
+ * Register a rule with the §13 dispatcher.
230
+ *
231
+ * Rules are registered once at startup and remain for the lifetime of
232
+ * the registry. Duplicate names are allowed (the registry does not
233
+ * de-duplicate); the second `register()` call adds a second instance,
234
+ * which is intentional for testing / override scenarios.
235
+ *
236
+ * @param rule A `TessellationRule` implementation.
237
+ */
238
+ register(rule: TessellationRule): void;
239
+ /**
240
+ * Return all currently registered rules, in registration order.
241
+ *
242
+ * Useful for operator-facing diagnostics (TUI rule list, Slack digest)
243
+ * and for test assertions that verify which rules are wired up.
244
+ */
245
+ getRegisteredRules(): ReadonlyArray<TessellationRule>;
246
+ /**
247
+ * Fan out all registered rules in parallel and aggregate the results.
248
+ *
249
+ * Returns the union of all `DriftEvent[]` arrays emitted by registered
250
+ * rules. Rules that throw are caught; their error is surfaced as a
251
+ * `DriftEvent` with `severity: 'warning'` so operators can investigate
252
+ * without silently dropping findings.
253
+ *
254
+ * When no rules are registered, returns an empty array immediately.
255
+ *
256
+ * @param target The shared scan-target for this dispatch cycle.
257
+ * @returns Aggregated drift events from all registered rules.
258
+ */
259
+ dispatch(target: RuleScanTarget): Promise<DriftEvent[]>;
260
+ }
261
+ /**
262
+ * Create a new `Tessellation13Registry` instance.
263
+ *
264
+ * Each call returns an independent registry. The typical pattern is to
265
+ * create ONE registry per orchestrator startup and register all §13
266
+ * rules into it.
267
+ */
268
+ export declare function createTessellation13Registry(): Tessellation13Registry;
269
+ //# sourceMappingURL=rule-registry.d.ts.map
@@ -0,0 +1,92 @@
1
+ /**
2
+ * RFC-0009 §13 + RFC-0018 OQ-10 — Tessellation§13RuleRegistry.
3
+ *
4
+ * Provides a concrete registration mechanism for §13 drift-detection rules,
5
+ * resolving RFC-0018 OQ-10 (2026-05-28, full rubric):
6
+ *
7
+ * "4th rule in same §13 engine + concrete registration mechanism spec'd:
8
+ * Tessellation§13RuleRegistry.register(rule). Industry research:
9
+ * unified-engine-with-plugin-rules (Sonar, Semgrep, CodeQL, Snyk,
10
+ * Dependabot, Renovate) is the modern de-facto pattern."
11
+ *
12
+ * Standard rule interface: `{ name, description, scan(target): DriftEvent[], severity }`
13
+ * §13 dispatcher fans out all registered rules in parallel; aggregates Decisions
14
+ * for catalog routing.
15
+ *
16
+ * ### Composition with RFC-0028 OQ-7.2
17
+ *
18
+ * RFC-0028 OQ-7.2 (2026-05-27 resolution) establishes the structural-vs-statistical
19
+ * drift detection pairing:
20
+ *
21
+ * - **Structural drift** (this registry, CI authoring-time): BLOCKS PR via
22
+ * `Decision: <rule-specific-name>` when `severity === 'high'`.
23
+ * - **Statistical drift** (runtime PPA `SoulDriftDetected`): SURFACES via
24
+ * RFC-0035 G0 non-blocking pipeline contract.
25
+ *
26
+ * Rules register their `severity` to indicate which path applies:
27
+ * `'high'` = structural-blocking; `'medium'` | `'warning'` = surfaces non-blocking.
28
+ *
29
+ * @see spec/rfcs/RFC-0009-tessellated-design-intent-documents.md §13
30
+ * @see spec/rfcs/RFC-0018-in-soul-journey-pattern.md §10.1 OQ-8 + OQ-10
31
+ * @see spec/rfcs/RFC-0028-engineering-axis-substrate-enforcement.md OQ-7.2
32
+ */
33
+ /**
34
+ * Create a new `Tessellation13Registry` instance.
35
+ *
36
+ * Each call returns an independent registry. The typical pattern is to
37
+ * create ONE registry per orchestrator startup and register all §13
38
+ * rules into it.
39
+ */
40
+ export function createTessellation13Registry() {
41
+ const rules = [];
42
+ return {
43
+ register(rule) {
44
+ rules.push(rule);
45
+ },
46
+ getRegisteredRules() {
47
+ return rules;
48
+ },
49
+ async dispatch(target) {
50
+ if (rules.length === 0)
51
+ return [];
52
+ const now = new Date().toISOString();
53
+ // Fan out in parallel — all rules execute concurrently.
54
+ // Wrap each scan() call in a new Promise so synchronous throws are
55
+ // caught by Promise.allSettled just as async rejections are.
56
+ const settled = await Promise.allSettled(rules.map((rule) => new Promise((resolve, reject) => {
57
+ try {
58
+ const result = rule.scan(target);
59
+ if (result instanceof Promise) {
60
+ result.then(resolve, reject);
61
+ }
62
+ else {
63
+ resolve(result);
64
+ }
65
+ }
66
+ catch (err) {
67
+ reject(err);
68
+ }
69
+ })));
70
+ const events = [];
71
+ for (let i = 0; i < settled.length; i++) {
72
+ const result = settled[i];
73
+ const rule = rules[i];
74
+ if (result.status === 'fulfilled') {
75
+ events.push(...result.value);
76
+ }
77
+ else {
78
+ // Rule threw — surface as a warning so operators can investigate.
79
+ events.push({
80
+ rule: rule.name,
81
+ timestamp: now,
82
+ message: `§13 rule '${rule.name}' threw during scan: ${String(result.reason)}`,
83
+ severity: 'warning',
84
+ details: { error: String(result.reason) },
85
+ });
86
+ }
87
+ }
88
+ return events;
89
+ },
90
+ };
91
+ }
92
+ //# sourceMappingURL=rule-registry.js.map
@@ -0,0 +1,90 @@
1
+ /**
2
+ * RFC-0009 §13 Rule #1 — SoulSlugAstScanRule.
3
+ *
4
+ * Implements `TessellationRule` for the Tessellation§13RuleRegistry so Rule #1
5
+ * is dispatchable via `registry.register(rule)` (AISDLC-489, AISDLC-467 AC#3
6
+ * follow-up).
7
+ *
8
+ * Wraps the existing soul-slug AST scan logic from `tessellation-drift.ts` as a
9
+ * first-class `TessellationRule` instance. The implementation is a thin adapter
10
+ * that delegates to the same two-pattern regex scan used by `detectTessellationDrift()`
11
+ * internally, preserving the existing detection semantics unchanged.
12
+ *
13
+ * ### Scan strategy
14
+ *
15
+ * Line-by-line regex over `target.substrateFiles`:
16
+ * 1. `'<slug>'` or `"<slug>"` — bare string-literal match.
17
+ * 2. `soul === '<slug>'` / `soulId === '<slug>'` — soul-discriminating branch.
18
+ *
19
+ * Soul slugs are supplied at construction time via `soulSlugs`. The registry
20
+ * dispatcher passes the shared `RuleScanTarget` to all rules; callers building
21
+ * the target should set `target.soulSlugs` to the tessellation's soul ID list.
22
+ * When `target.soulSlugs` is present it takes precedence; otherwise the rule
23
+ * falls back to the `soulSlugs` supplied at construction time.
24
+ *
25
+ * ### Severity
26
+ *
27
+ * Default `'warning'` (matches the pre-registry `TessellationDriftDetectedEvent`
28
+ * `severity: 'warning'` field in the original `tessellation-drift.ts`).
29
+ *
30
+ * @see spec/rfcs/RFC-0009-tessellated-design-intent-documents.md §7.2 Rule #1
31
+ * @see orchestrator/src/tessellation-drift.ts (original Rule #1 logic)
32
+ */
33
+ import type { TessellationRule, DriftEvent, DriftSeverity, RuleScanTarget } from './rule-registry.js';
34
+ /**
35
+ * A single soul-slug AST scan finding.
36
+ */
37
+ export interface SoulSlugAstScanFinding {
38
+ /** Path of the substrate file that contained the soul-leaking literal. */
39
+ filePath: string;
40
+ /** Soul slug whose name leaked into shared substrate. */
41
+ soulSlug: string;
42
+ /** 1-based line number in `filePath`. */
43
+ line: number;
44
+ /**
45
+ * Discriminator for which pattern triggered the finding:
46
+ * - `'string-literal'` — bare `'<slug>'` (or `"<slug>"`) appeared in substrate
47
+ * - `'soul-conditional'` — `soul === '<slug>'` / similar branched on soul
48
+ */
49
+ pattern: 'string-literal' | 'soul-conditional';
50
+ /** The raw matching substring (trimmed, max 200 chars) for operator inspection. */
51
+ excerpt: string;
52
+ }
53
+ /**
54
+ * Structured details payload for soul-slug-ast-scan drift events.
55
+ */
56
+ export interface SoulSlugAstScanDetails {
57
+ rule: 'soul-slug-ast-scan';
58
+ findings: SoulSlugAstScanFinding[];
59
+ }
60
+ /**
61
+ * SoulSlugAstScanRule — RFC-0009 §13 Rule #1.
62
+ *
63
+ * Scans substrate files for soul-slug string literals and soul-discriminating
64
+ * conditionals. Emits a `DriftEvent` when any soul slug leaks into shared
65
+ * substrate code.
66
+ *
67
+ * ### Registration
68
+ *
69
+ * ```ts
70
+ * const registry = createTessellation13Registry();
71
+ * registry.register(new SoulSlugAstScanRule(['soul-a', 'soul-b']));
72
+ * // With target-side soul slugs (takes precedence):
73
+ * // target.soulSlugs = tessellation.souls.map(s => s.soulId);
74
+ * ```
75
+ */
76
+ export declare class SoulSlugAstScanRule implements TessellationRule {
77
+ readonly name = "soul-slug-ast-scan";
78
+ readonly description = "Scans shared substrate files for soul-slug string literals and soul-discriminating conditionals (RFC-0009 \u00A77.2 Rule #1)";
79
+ readonly severity: DriftSeverity;
80
+ private readonly constructionSlugs;
81
+ /**
82
+ * @param soulSlugs Default set of soul slugs to scan for. When the scan
83
+ * target carries `soulSlugs`, those take precedence.
84
+ * Pass an empty array when you always supply slugs via the target.
85
+ * @param severity Default `'warning'` (matches pre-registry behaviour).
86
+ */
87
+ constructor(soulSlugs?: readonly string[], severity?: DriftSeverity);
88
+ scan(target: RuleScanTarget): DriftEvent[];
89
+ }
90
+ //# sourceMappingURL=soul-slug-ast-scan-rule.d.ts.map
@@ -0,0 +1,158 @@
1
+ /**
2
+ * RFC-0009 §13 Rule #1 — SoulSlugAstScanRule.
3
+ *
4
+ * Implements `TessellationRule` for the Tessellation§13RuleRegistry so Rule #1
5
+ * is dispatchable via `registry.register(rule)` (AISDLC-489, AISDLC-467 AC#3
6
+ * follow-up).
7
+ *
8
+ * Wraps the existing soul-slug AST scan logic from `tessellation-drift.ts` as a
9
+ * first-class `TessellationRule` instance. The implementation is a thin adapter
10
+ * that delegates to the same two-pattern regex scan used by `detectTessellationDrift()`
11
+ * internally, preserving the existing detection semantics unchanged.
12
+ *
13
+ * ### Scan strategy
14
+ *
15
+ * Line-by-line regex over `target.substrateFiles`:
16
+ * 1. `'<slug>'` or `"<slug>"` — bare string-literal match.
17
+ * 2. `soul === '<slug>'` / `soulId === '<slug>'` — soul-discriminating branch.
18
+ *
19
+ * Soul slugs are supplied at construction time via `soulSlugs`. The registry
20
+ * dispatcher passes the shared `RuleScanTarget` to all rules; callers building
21
+ * the target should set `target.soulSlugs` to the tessellation's soul ID list.
22
+ * When `target.soulSlugs` is present it takes precedence; otherwise the rule
23
+ * falls back to the `soulSlugs` supplied at construction time.
24
+ *
25
+ * ### Severity
26
+ *
27
+ * Default `'warning'` (matches the pre-registry `TessellationDriftDetectedEvent`
28
+ * `severity: 'warning'` field in the original `tessellation-drift.ts`).
29
+ *
30
+ * @see spec/rfcs/RFC-0009-tessellated-design-intent-documents.md §7.2 Rule #1
31
+ * @see orchestrator/src/tessellation-drift.ts (original Rule #1 logic)
32
+ */
33
+ // ── Internal helpers ───────────────────────────────────────────────────
34
+ /**
35
+ * Validate a soul slug. Mirrors the validator in `tessellation-drift.ts`:
36
+ * lowercase alphanumeric + dashes, 1-64 chars.
37
+ */
38
+ function isValidSlug(slug) {
39
+ return /^[a-z0-9-]+$/.test(slug) && slug.length >= 1 && slug.length <= 64;
40
+ }
41
+ /** Escape a string for safe use inside a RegExp pattern. */
42
+ function escapeRegex(s) {
43
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
44
+ }
45
+ /**
46
+ * Scan one substrate file for soul-leakage patterns.
47
+ *
48
+ * Same two-pattern strategy as the internal `scanSubstrateFile` in
49
+ * `tessellation-drift.ts` — adapted to `SubstrateFileEntry` (which uses
50
+ * `contents` + `path`, the same field names) and extended return shape.
51
+ */
52
+ function scanSubstrateFileForSlugs(file, soulSlugs) {
53
+ const findings = [];
54
+ if (file.contents.length === 0 || soulSlugs.length === 0)
55
+ return findings;
56
+ const lines = file.contents.split('\n');
57
+ for (const slug of soulSlugs) {
58
+ if (!isValidSlug(slug))
59
+ continue;
60
+ const esc = escapeRegex(slug);
61
+ // Pattern 1: bare string literal (single or double quote, exact match).
62
+ const literalRe = new RegExp(`(['"])${esc}\\1`);
63
+ // Pattern 2: soul-discriminating conditional. Permissive on the soul
64
+ // identifier name so it catches `soul`, `soulId`, `soul_id`, etc.
65
+ const condRe = new RegExp(`(?:soul[A-Za-z_]*)\\s*===\\s*(['"])${esc}\\1`);
66
+ for (let i = 0; i < lines.length; i++) {
67
+ const line = lines[i];
68
+ const condMatch = line.match(condRe);
69
+ if (condMatch) {
70
+ findings.push({
71
+ filePath: file.path,
72
+ soulSlug: slug,
73
+ line: i + 1,
74
+ pattern: 'soul-conditional',
75
+ excerpt: line.trim().slice(0, 200),
76
+ });
77
+ continue; // Don't double-report same line as bare literal.
78
+ }
79
+ if (literalRe.test(line)) {
80
+ findings.push({
81
+ filePath: file.path,
82
+ soulSlug: slug,
83
+ line: i + 1,
84
+ pattern: 'string-literal',
85
+ excerpt: line.trim().slice(0, 200),
86
+ });
87
+ }
88
+ }
89
+ }
90
+ return findings;
91
+ }
92
+ // ── Rule implementation ────────────────────────────────────────────────
93
+ /**
94
+ * SoulSlugAstScanRule — RFC-0009 §13 Rule #1.
95
+ *
96
+ * Scans substrate files for soul-slug string literals and soul-discriminating
97
+ * conditionals. Emits a `DriftEvent` when any soul slug leaks into shared
98
+ * substrate code.
99
+ *
100
+ * ### Registration
101
+ *
102
+ * ```ts
103
+ * const registry = createTessellation13Registry();
104
+ * registry.register(new SoulSlugAstScanRule(['soul-a', 'soul-b']));
105
+ * // With target-side soul slugs (takes precedence):
106
+ * // target.soulSlugs = tessellation.souls.map(s => s.soulId);
107
+ * ```
108
+ */
109
+ export class SoulSlugAstScanRule {
110
+ name = 'soul-slug-ast-scan';
111
+ description = 'Scans shared substrate files for soul-slug string literals and soul-discriminating conditionals (RFC-0009 §7.2 Rule #1)';
112
+ severity;
113
+ constructionSlugs;
114
+ /**
115
+ * @param soulSlugs Default set of soul slugs to scan for. When the scan
116
+ * target carries `soulSlugs`, those take precedence.
117
+ * Pass an empty array when you always supply slugs via the target.
118
+ * @param severity Default `'warning'` (matches pre-registry behaviour).
119
+ */
120
+ constructor(soulSlugs = [], severity = 'warning') {
121
+ this.constructionSlugs = soulSlugs;
122
+ this.severity = severity;
123
+ }
124
+ scan(target) {
125
+ const { substrateFiles, tessellatedDid } = target;
126
+ // Accept soul slugs from target if provided (forward-compat field), else
127
+ // fall back to slugs supplied at construction time.
128
+ const soulSlugs = target.soulSlugs ?? this.constructionSlugs;
129
+ // No-op when no substrate files or no soul slugs to scan for.
130
+ if (!substrateFiles || substrateFiles.length === 0)
131
+ return [];
132
+ if (soulSlugs.length === 0)
133
+ return [];
134
+ const now = new Date().toISOString();
135
+ const allFindings = [];
136
+ for (const file of substrateFiles) {
137
+ const findings = scanSubstrateFileForSlugs(file, soulSlugs);
138
+ if (findings.length > 0)
139
+ allFindings.push(...findings);
140
+ }
141
+ if (allFindings.length === 0)
142
+ return [];
143
+ const involved = new Set(allFindings.map((f) => f.soulSlug));
144
+ return [
145
+ {
146
+ rule: this.name,
147
+ timestamp: now,
148
+ message: `AST scan: ${allFindings.length} soul-name leakage hit(s) across ${involved.size} soul(s) in shared substrate (tessellation: ${tessellatedDid})`,
149
+ severity: this.severity,
150
+ details: {
151
+ rule: 'soul-slug-ast-scan',
152
+ findings: allFindings,
153
+ },
154
+ },
155
+ ];
156
+ }
157
+ }
158
+ //# sourceMappingURL=soul-slug-ast-scan-rule.js.map