@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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Admission-subset composite (RFC-0008 §A.6).
2
+ * Admission-subset composite (RFC-0008 §A.6 + RFC-0009 Phase 2.1).
3
3
  *
4
4
  * The admission gate uses a subset of the full PPA composite:
5
5
  *
@@ -23,11 +23,18 @@
23
23
  * The `override` bypass is preserved at position 1: when the input has
24
24
  * `override: true`, we return `composite = Infinity` without running
25
25
  * the admission math — identical to the legacy PPA behaviour.
26
+ *
27
+ * **RFC-0009 Phase 2.1 tessellation extension:**
28
+ * When `AdmissionCompositeOptions.tessellationContext` is provided, the
29
+ * composite routes SA and Eρ₄ (designSystemReadiness) through soul scope
30
+ * per RFC-0009 §6. See `tessellation-admission.ts` for the routing algorithm.
26
31
  */
27
32
  import { mapIssueToPriorityInput } from './admission-score.js';
28
33
  import { computeAutonomyFactor, computeDefectRiskFactor, computeReadinessFromDesignSystemContext, } from './admission-enrichment.js';
29
34
  import { computeAdmissionHumanCurve } from './admission-hc.js';
30
- import { computeConfidence } from './priority.js';
35
+ import { computeTessellatedScores, } from './tessellation-admission.js';
36
+ import { computeVariantScopedScores, } from './variant-admission.js';
37
+ import { computeComplianceClearance, } from './compliance-clearance.js';
31
38
  const DEFAULT_SIGNAL = 0.5;
32
39
  export function computeAdmissionComposite(input, config, options) {
33
40
  const timestamp = new Date().toISOString();
@@ -78,7 +85,7 @@ export function computeAdmissionComposite(input, config, options) {
78
85
  };
79
86
  }
80
87
  // ── SA (soul alignment) — SA-1 override (M5) or label-based fallback ──
81
- const soulAlignment = clamp01(options?.soulAlignmentOverride ?? priorityInput.soulAlignment ?? DEFAULT_SIGNAL);
88
+ const baseSoulAlignment = clamp01(options?.soulAlignmentOverride ?? priorityInput.soulAlignment ?? DEFAULT_SIGNAL);
82
89
  // ── D-pi_adjusted ─────────────────────────────────────────────
83
90
  const demand = priorityInput.demandSignal ?? DEFAULT_SIGNAL;
84
91
  const consensus = priorityInput.teamConsensus ?? DEFAULT_SIGNAL;
@@ -92,12 +99,49 @@ export function computeAdmissionComposite(input, config, options) {
92
99
  const complexity = priorityInput.complexity ?? 5;
93
100
  const baseExecutionReality = clamp01(1 - complexity / 10);
94
101
  const autonomyFactor = computeAutonomyFactor(input.autonomyContext);
95
- const designSystemReadiness = computeReadinessFromDesignSystemContext(input.designSystemContext);
102
+ const baseDesignSystemReadiness = computeReadinessFromDesignSystemContext(input.designSystemContext);
103
+ // ── RFC-0009 Phase 2.1: tessellation soul-scope routing ────────
104
+ // When tessellationContext is present, route SA and Eρ₄ through soul
105
+ // scope per RFC-0009 §6. When absent, single-DID path applies.
106
+ //
107
+ // Prefer the canonical workItemId (e.g. "AISDLC-313") over the GitHub-style
108
+ // "#313" so the dep-graph snapshot lookup matches the backlog task ID format.
109
+ const workItemId = input.workItemId ?? `#${input.issueNumber}`;
110
+ const tessellationResult = computeTessellatedScores(workItemId, baseSoulAlignment, baseDesignSystemReadiness, options?.tessellationContext);
111
+ // ── RFC-0017 Phase 2: variant-scope refinement ────────────────
112
+ // When `variantContext` is provided AND the work item declares
113
+ // `targetedVariants[]`, the per-variant Sα₁ + Sα₂ scores REFINE the
114
+ // soul-aggregate `soulAlignment` from tessellation. The combined SA score
115
+ // fed to the composite is the mean of the variant-resolved Sα₁ + Sα₂
116
+ // (both feed soul-alignment per RFC-0008 §A.6 SA pillar definition).
117
+ // When no targeting declared OR variantContext is absent, this is a
118
+ // passthrough — the tessellation result is preserved unchanged
119
+ // (backward-compat per RFC-0017 §7).
120
+ const variantResult = computeVariantScopedScores(workItemId, tessellationResult.soulAlignment, tessellationResult.soulAlignment, options?.variantContext);
121
+ const soulAlignment = variantResult.routingPath === 'no-variant-routing'
122
+ ? tessellationResult.soulAlignment
123
+ : combineVariantSaForSoulAlignment(variantResult.sa1, variantResult.sa2);
124
+ const designSystemReadiness = tessellationResult.er4;
96
125
  const executionReality = Math.min(baseExecutionReality * autonomyFactor, designSystemReadiness);
126
+ // ── RFC-0009 Phase 4.1: Eρ₅ Compliance Clearance ──────────────
127
+ // Categorical 0/1 gating per §7.1. When the adopter has opted in AND any
128
+ // declared regime is violated, the composite is gated to 0 (work item
129
+ // cannot proceed without resolving the violation). When the adopter has
130
+ // not opted in (default), this returns er5=1 and the composite is
131
+ // unchanged — full backward compatibility with Phase 2/3 behavior.
132
+ //
133
+ // Uses the same `affectedSoulIds` resolved by tessellation routing, so the
134
+ // regime check follows the work item's actual soul scope. Substrate-only
135
+ // work (affectedSoulIds = []) falls through to the `__platform` sentinel.
136
+ const complianceClearanceResult = computeComplianceClearance(workItemId, tessellationResult.affectedSoulIds, options?.complianceClearanceContext);
97
137
  // ── HC (tanh-compressed with HC_design) ───────────────────────
98
138
  const humanCurve = computeAdmissionHumanCurve(input);
99
139
  // ── §A.6 admission subset composite ───────────────────────────
100
- const composite = soulAlignment * demandPressureAdjusted * executionReality * (1 + humanCurve.hcComposite);
140
+ const composite = soulAlignment *
141
+ demandPressureAdjusted *
142
+ executionReality *
143
+ complianceClearanceResult.er5 *
144
+ (1 + humanCurve.hcComposite);
101
145
  const score = {
102
146
  composite,
103
147
  dimensions: {
@@ -114,9 +158,23 @@ export function computeAdmissionComposite(input, config, options) {
114
158
  // display continuity but do not multiply it into `composite`.
115
159
  calibration: clampCalibration(config?.calibrationCoefficient),
116
160
  },
117
- confidence: computeConfidence(priorityInput),
161
+ confidence: computeAdmissionConfidence(input, priorityInput, options),
118
162
  timestamp,
119
163
  };
164
+ // Only include tessellation breakdown when the caller provided a context
165
+ // (non-tessellated path produces routingPath: 'non-tessellated' but we
166
+ // elide the field to preserve backward compat with callers that don't
167
+ // destructure the breakdown exhaustively).
168
+ const tessellationBreakdown = options?.tessellationContext !== undefined ? tessellationResult : undefined;
169
+ // Variant breakdown surfaced ONLY when the caller wired a variant context
170
+ // AND the work item actually targeted variants. Pure backward-compat for
171
+ // callers on the soul-aggregate path.
172
+ const variantBreakdown = options?.variantContext !== undefined && variantResult.routingPath !== 'no-variant-routing'
173
+ ? variantResult
174
+ : undefined;
175
+ // Compliance clearance breakdown surfaced ONLY when the adopter opted in
176
+ // (avoids polluting the breakdown shape for Phase 2/3 callers).
177
+ const complianceClearanceBreakdown = options?.complianceClearanceContext?.enabled === true ? complianceClearanceResult : undefined;
120
178
  return {
121
179
  score,
122
180
  breakdown: {
@@ -129,9 +187,115 @@ export function computeAdmissionComposite(input, config, options) {
129
187
  designSystemReadiness,
130
188
  executionReality,
131
189
  humanCurve,
190
+ ...(tessellationBreakdown !== undefined ? { tessellation: tessellationBreakdown } : {}),
191
+ ...(variantBreakdown !== undefined ? { variant: variantBreakdown } : {}),
192
+ ...(complianceClearanceBreakdown !== undefined
193
+ ? { complianceClearance: complianceClearanceBreakdown }
194
+ : {}),
132
195
  },
133
196
  };
134
197
  }
198
+ /**
199
+ * Combine variant-scope Sα₁ (audience resonance) + Sα₂ (vibe coherence) into
200
+ * a single soul-alignment value for the admission composite's SA pillar.
201
+ *
202
+ * The legacy admission composite uses a single `soulAlignment` scalar that
203
+ * conceptually rolls Sα₁ + Sα₂ together (RFC-0008 §A.6 SA pillar). When
204
+ * variant routing splits them out per RFC-0017 §5.4, we recombine via the
205
+ * arithmetic mean — equal weighting matches the soul-aggregate combination
206
+ * implied by `pillar-breakdown.ts` and avoids favouring one Sα facet over
207
+ * the other when both are equally variant-bounded.
208
+ *
209
+ * Exported for unit testing + so downstream callers can replicate the
210
+ * combination logic if they need to surface the constituent pieces.
211
+ */
212
+ export function combineVariantSaForSoulAlignment(sa1, sa2) {
213
+ return clamp01((sa1 + sa2) / 2);
214
+ }
215
+ // ── Admission confidence (AISDLC-172) ──────────────────────────────────
216
+ /**
217
+ * `PriorityInput` fields that the admission mapper (`mapIssueToPriorityInput`)
218
+ * actually populates from issue/backlog signals. The full PPA `SCORABLE_FIELDS`
219
+ * list (used by `computeConfidence` in `priority.ts`) includes runtime-only
220
+ * dimensions like `regulatoryUrgency`, `techInflection`, `marketDivergence`,
221
+ * `meetingDecision`, `budgetUtilization`, and `dependencyClearance` — none of
222
+ * which the admission mapper ever sets. Counting against the full 16-field
223
+ * list capped admit confidence at ~9/16 ≈ 0.56 even for fully-shaped issues
224
+ * (RFC-0009 §13 OQ-9 "0.5 ceiling" bug). The mapper-relevant subset is the
225
+ * correct denominator for the mapper-evidence half of the blend.
226
+ */
227
+ const ADMISSION_MAPPER_FIELDS = [
228
+ 'soulAlignment',
229
+ 'demandSignal',
230
+ 'teamConsensus',
231
+ 'builderConviction',
232
+ 'complexity',
233
+ 'bugSeverity',
234
+ 'explicitPriority',
235
+ 'competitiveDrift',
236
+ 'customerRequestCount',
237
+ ];
238
+ /**
239
+ * RFC-0008 enrichment slots whose presence on `AdmissionInput` indicates
240
+ * an enrichment reader successfully loaded its context. This is the
241
+ * "enrichment-success signal" referenced in the OQ-9 hypothesis:
242
+ *
243
+ * - `designSystemContext` ← DSB loader (catalog/token coverage)
244
+ * - `autonomyContext` ← AutonomyPolicy (DID-driven) loader
245
+ * - `codeAreaQuality` ← code-area metrics loader
246
+ * - `designAuthoritySignal` ← maintainers/principals loader
247
+ * - `soulAlignmentOverride` ← soul-tracks SA-1 loader (M5 path)
248
+ *
249
+ * Total slots is `5`. Each loaded slot contributes `1/5` to the
250
+ * enrichment-evidence half of the confidence blend.
251
+ */
252
+ const ADMISSION_ENRICHMENT_SLOT_COUNT = 5;
253
+ function countLoadedEnrichmentSlots(input, options) {
254
+ let loaded = 0;
255
+ if (input.designSystemContext)
256
+ loaded++;
257
+ if (input.autonomyContext)
258
+ loaded++;
259
+ if (input.codeAreaQuality)
260
+ loaded++;
261
+ if (input.designAuthoritySignal)
262
+ loaded++;
263
+ if (options?.soulAlignmentOverride !== undefined)
264
+ loaded++;
265
+ return loaded;
266
+ }
267
+ /**
268
+ * Compute admission confidence in [0, 1] as a 50/50 blend of two
269
+ * independent evidence channels:
270
+ *
271
+ * 1. **Mapper coverage** — fraction of `ADMISSION_MAPPER_FIELDS`
272
+ * explicitly populated on the derived `PriorityInput`. Captures
273
+ * "how much issue/backlog signal did `mapIssueToPriorityInput`
274
+ * extract".
275
+ *
276
+ * 2. **Enrichment loaded** — fraction of RFC-0008 enrichment slots
277
+ * present on the input (or supplied via `options`). Captures "how
278
+ * much external context did the enrichment readers contribute".
279
+ *
280
+ * Bug fixed (AISDLC-172 / RFC-0009 §13 OQ-9): previously
281
+ * `computeConfidence(priorityInput)` from `priority.ts` was used, which
282
+ * counts against the full 16-field `SCORABLE_FIELDS` list and ignores
283
+ * enrichment success entirely. The result was a hard ~0.5 ceiling on
284
+ * admit confidence even when DID + DSB + maintainers + soul-tracks all
285
+ * loaded — those four positive observations contributed exactly zero
286
+ * to the formula's denominator and zero to its numerator.
287
+ */
288
+ export function computeAdmissionConfidence(input, priorityInput, options) {
289
+ let providedMapper = 0;
290
+ for (const field of ADMISSION_MAPPER_FIELDS) {
291
+ if (priorityInput[field] !== undefined)
292
+ providedMapper++;
293
+ }
294
+ const mapperFraction = providedMapper / ADMISSION_MAPPER_FIELDS.length;
295
+ const loadedEnrichment = countLoadedEnrichmentSlots(input, options);
296
+ const enrichmentFraction = loadedEnrichment / ADMISSION_ENRICHMENT_SLOT_COUNT;
297
+ return 0.5 * mapperFraction + 0.5 * enrichmentFraction;
298
+ }
135
299
  function clamp01(value) {
136
300
  return Math.min(1, Math.max(0, value));
137
301
  }
@@ -92,15 +92,24 @@ export declare function complexityToAutonomyLevel(complexity: number): number;
92
92
  */
93
93
  export declare function computeAutonomyFactor(ctx: AutonomyContext | undefined): number;
94
94
  /**
95
- * Compute the C5 design-authority weight per §A.5.
95
+ * Compute the C5 design-authority weight per §A.5 (Source 1) plus
96
+ * the RFC-0008 §C5 Source 3 compliance-assessment signal.
96
97
  *
98
+ * Source 1 (principal participation):
97
99
  * non-authority → 0.0
98
100
  * authority + positive → +0.6 × modulation
99
101
  * authority + negative → -0.4 × modulation
100
102
  * authority + no type → +0.3 × modulation
101
103
  *
102
- * where `modulation = 1.2 - areaComplianceScore` when the score is
103
- * supplied, else `1.0` (unmodulated base weight).
104
+ * where `modulation = 1.2 - areaComplianceScore` when the score is
105
+ * supplied, else `1.0` (unmodulated base weight).
106
+ *
107
+ * Source 3 (compliance assessment — additive):
108
+ * `signal.complianceSignal` contributes directly when present (see
109
+ * `buildDesignAuthoritySignal`). This fires from DSB health data
110
+ * alone — no principal participation required — so a fully-loaded DSB
111
+ * with high token compliance produces `hcDesign > 0` regardless of
112
+ * whether a design-authority principal commented on the issue.
104
113
  */
105
114
  export declare function computeDesignAuthorityWeight(signal: DesignAuthoritySignal | undefined): number;
106
115
  /**
@@ -204,37 +204,98 @@ const DESIGN_AUTHORITY_BASE_WEIGHT = Object.freeze({
204
204
  unspecified: 0.3,
205
205
  });
206
206
  /**
207
- * Compute the C5 design-authority weight per §A.5.
207
+ * Compute the C5 design-authority weight per §A.5 (Source 1) plus
208
+ * the RFC-0008 §C5 Source 3 compliance-assessment signal.
208
209
  *
210
+ * Source 1 (principal participation):
209
211
  * non-authority → 0.0
210
212
  * authority + positive → +0.6 × modulation
211
213
  * authority + negative → -0.4 × modulation
212
214
  * authority + no type → +0.3 × modulation
213
215
  *
214
- * where `modulation = 1.2 - areaComplianceScore` when the score is
215
- * supplied, else `1.0` (unmodulated base weight).
216
+ * where `modulation = 1.2 - areaComplianceScore` when the score is
217
+ * supplied, else `1.0` (unmodulated base weight).
218
+ *
219
+ * Source 3 (compliance assessment — additive):
220
+ * `signal.complianceSignal` contributes directly when present (see
221
+ * `buildDesignAuthoritySignal`). This fires from DSB health data
222
+ * alone — no principal participation required — so a fully-loaded DSB
223
+ * with high token compliance produces `hcDesign > 0` regardless of
224
+ * whether a design-authority principal commented on the issue.
216
225
  */
217
226
  export function computeDesignAuthorityWeight(signal) {
218
- if (!signal || !signal.isDesignAuthority)
227
+ if (!signal)
219
228
  return 0;
220
- const signalType = signal.signalType ?? 'unspecified';
221
- const base = DESIGN_AUTHORITY_BASE_WEIGHT[signalType];
222
- const modulation = signal.areaComplianceScore !== undefined ? 1.2 - signal.areaComplianceScore : 1;
223
- return base * modulation;
229
+ // Source 1: principal-participation weight.
230
+ const participationWeight = signal.isDesignAuthority
231
+ ? (() => {
232
+ const signalType = signal.signalType ?? 'unspecified';
233
+ const base = DESIGN_AUTHORITY_BASE_WEIGHT[signalType];
234
+ const modulation = signal.areaComplianceScore !== undefined ? 1.2 - signal.areaComplianceScore : 1;
235
+ return base * modulation;
236
+ })()
237
+ : 0;
238
+ // Source 3: compliance-assessment signal (additive).
239
+ const complianceWeight = signal.complianceSignal ?? 0;
240
+ return participationWeight + complianceWeight;
241
+ }
242
+ /**
243
+ * RFC-0008 §C5 Source 3 — map DSB token-compliance coverage to a
244
+ * small automatic signal for HC_design.
245
+ *
246
+ * >= 0.8 (80 %) → +0.3 (design system is healthy — small positive)
247
+ * < 0.4 (40 %) → −0.2 (design system is fragile — small negative)
248
+ * otherwise → 0.0 (neutral)
249
+ *
250
+ * The signal is intentionally small so it cannot outweigh Source 1
251
+ * (principal-participation, which carries base weights up to ±0.6).
252
+ * A DSB with missing status data returns 0 (graceful degradation).
253
+ */
254
+ function computeComplianceSignal(binding) {
255
+ const rawCoverage = binding.status?.tokenCompliance?.currentCoverage;
256
+ if (rawCoverage === undefined || Number.isNaN(rawCoverage))
257
+ return 0;
258
+ // Normalise: values > 1 are percent (0-100), values ≤ 1 are already fractions.
259
+ const coverage = rawCoverage > 1 ? rawCoverage / 100 : rawCoverage;
260
+ const clamped = Math.min(1, Math.max(0, coverage));
261
+ if (clamped >= 0.8)
262
+ return 0.3;
263
+ if (clamped < 0.4)
264
+ return -0.2;
265
+ return 0;
224
266
  }
225
267
  function buildDesignAuthoritySignal(input, ctx) {
226
268
  if (!ctx.designSystemBinding)
227
269
  return undefined;
270
+ // AISDLC-171: surface whether the DSB declares any design authority
271
+ // principals at all. This is a diagnostic flag — it does NOT participate
272
+ // in the HC_design weight calculation (per RFC-0008 §14.2, only
273
+ // principals who participate as author/commenter can emit HC_design via
274
+ // Source 1). The flag lets pillarBreakdown distinguish "DSB has no
275
+ // design authority structure" from "DSB has design authority but no
276
+ // principal participated in this issue".
277
+ const principalsDeclared = (ctx.designSystemBinding.spec.stewardship.designAuthority.principals?.length ?? 0) > 0;
228
278
  const { isDesignAuthority, signalType } = checkDesignAuthority({
229
279
  authorLogin: input.authorLogin,
230
280
  commenterLogins: input.commenterLogins,
231
281
  labels: input.labels,
232
282
  }, ctx.designSystemBinding);
233
- if (!isDesignAuthority)
234
- return { isDesignAuthority: false };
283
+ // RFC-0008 §C5 Source 3: compliance-assessment auto-signal from DSB health.
284
+ // Fires regardless of principal participation — gives a fully-loaded DSB
285
+ // a non-zero hcDesign even when no design-authority principal commented.
286
+ const complianceSignal = computeComplianceSignal(ctx.designSystemBinding);
287
+ if (!isDesignAuthority) {
288
+ return {
289
+ isDesignAuthority: false,
290
+ principalsDeclared,
291
+ ...(complianceSignal !== 0 ? { complianceSignal } : {}),
292
+ };
293
+ }
235
294
  return {
236
295
  isDesignAuthority: true,
237
296
  signalType,
297
+ principalsDeclared,
298
+ ...(complianceSignal !== 0 ? { complianceSignal } : {}),
238
299
  ...(ctx.areaComplianceScore !== undefined
239
300
  ? { areaComplianceScore: ctx.areaComplianceScore }
240
301
  : {}),
@@ -34,8 +34,27 @@ export interface AdmissionHumanCurveResult {
34
34
  hcConsensus: number;
35
35
  /** Signed meeting-decision signal in [-1, 1] (0 = neutral when absent). */
36
36
  hcDecision: number;
37
- /** Signed design-authority signal in [-1, 1]. */
37
+ /**
38
+ * Signed design-authority signal in [-1, 1].
39
+ *
40
+ * AISDLC-171: this channel ONLY fires when one of the issue's
41
+ * participants (author or commenters) is a principal listed in
42
+ * `DesignSystemBinding.spec.stewardship.designAuthority.principals`.
43
+ * Per RFC-0008 §14.2, non-principal participants' design opinions
44
+ * route through `hcConsensus` instead — `hcDesign` carries
45
+ * specifically the design-authority leadership signal. To distinguish
46
+ * "no DSB at all" from "DSB exists but no design principal
47
+ * participated", consult `designAuthorityConfigured` on this result
48
+ * (and on `pillarBreakdown.shared.hcComposite`).
49
+ */
38
50
  hcDesign: number;
51
+ /**
52
+ * Diagnostic flag (AISDLC-171) — true when the resolved DSB declared
53
+ * `stewardship.designAuthority.principals` entries. Always undefined
54
+ * when no DSB was supplied (preDesignSystem). Surfaces the
55
+ * "configured but inactive" state without altering hcDesign weight.
56
+ */
57
+ designAuthorityConfigured?: boolean;
39
58
  /** Weighted sum before tanh. */
40
59
  hcRaw: number;
41
60
  /** `tanh(hcRaw)` — the HC value consumed by the admission composite. */
@@ -70,12 +70,23 @@ export function computeAdmissionHumanCurve(input) {
70
70
  const hcConsensus = deriveHcConsensus(input);
71
71
  const hcDecision = deriveHcDecision(input);
72
72
  const hcDesign = deriveHcDesign(input.designAuthoritySignal);
73
+ // AISDLC-171: surface the diagnostic flag from the enriched signal so
74
+ // pillarBreakdown can render the "configured but inactive" state.
75
+ const designAuthorityConfigured = input.designAuthoritySignal?.principalsDeclared;
73
76
  const hcRaw = HC_WEIGHTS.explicit * hcExplicit +
74
77
  HC_WEIGHTS.consensus * hcConsensus +
75
78
  HC_WEIGHTS.decision * hcDecision +
76
79
  HC_WEIGHTS.design * hcDesign;
77
80
  const hcComposite = Math.tanh(hcRaw);
78
- return { hcExplicit, hcConsensus, hcDecision, hcDesign, hcRaw, hcComposite };
81
+ return {
82
+ hcExplicit,
83
+ hcConsensus,
84
+ hcDecision,
85
+ hcDesign,
86
+ hcRaw,
87
+ hcComposite,
88
+ ...(designAuthorityConfigured !== undefined ? { designAuthorityConfigured } : {}),
89
+ };
79
90
  }
80
91
  function clamp(value, min, max) {
81
92
  return Math.min(max, Math.max(min, value));
@@ -25,6 +25,18 @@ export interface AdmissionInput {
25
25
  commentCount: number;
26
26
  /** ISO timestamp of issue creation. */
27
27
  createdAt: string;
28
+ /**
29
+ * Canonical work item ID for dep-graph lookups (RFC-0014 / RFC-0009 Phase 2.1).
30
+ *
31
+ * When present, used instead of `#${issueNumber}` to match the work item
32
+ * against dep-graph snapshot entries (which use backlog task IDs like
33
+ * `AISDLC-313`). Backlog adapters set this from the task's frontmatter `id`.
34
+ *
35
+ * Absent for GitHub-issue based inputs — the admission composite falls back
36
+ * to `#${issueNumber}` which is consistent with `mapIssueToPriorityInput`'s
37
+ * `itemId` field.
38
+ */
39
+ workItemId?: string;
28
40
  /** GitHub author_association — determines trust-based signal boosting. */
29
41
  authorAssociation?: AuthorAssociation;
30
42
  /** GitHub login of the issue author (used by C5 principal match). */
@@ -102,6 +114,38 @@ export interface DesignAuthoritySignal {
102
114
  signalType?: DesignAuthoritySignalType;
103
115
  /** The issue's code area compliance score in [0, 1] (used to modulate weight). */
104
116
  areaComplianceScore?: number;
117
+ /**
118
+ * Diagnostic flag (AISDLC-171) — true when the resolved DSB declared
119
+ * one or more `stewardship.designAuthority.principals` entries,
120
+ * regardless of whether any of them participated in this issue.
121
+ *
122
+ * This is intentionally NOT consumed by `computeDesignAuthorityWeight`
123
+ * — RFC-0008 §14.2 requires principal participation for HC_design to
124
+ * fire at any weight. The flag exists so `pillarBreakdown.shared.
125
+ * hcComposite.designAuthorityConfigured` can surface "design authority
126
+ * is configured but absent from this issue" — distinct from "no
127
+ * design authority structure exists at all" — without violating the
128
+ * RFC's §14.2 weighting constraint.
129
+ */
130
+ principalsDeclared?: boolean;
131
+ /**
132
+ * RFC-0008 §C5 Source 3 — automated compliance-assessment signal in [-1, 1].
133
+ *
134
+ * Populated by `buildDesignAuthoritySignal` when the resolved DSB carries
135
+ * `status.tokenCompliance.currentCoverage`:
136
+ * >= 0.8 (80 %) → small positive signal (+0.3): design system is healthy here
137
+ * < 0.4 (40 %) → small negative signal (-0.2): design system is fragile here
138
+ * otherwise → 0 (neutral)
139
+ *
140
+ * This signal fires from the DSB health data alone — no principal participation
141
+ * required. It is additive to the principal-participation weight in
142
+ * `computeDesignAuthorityWeight`, so a fully-loaded DSB with high compliance
143
+ * produces `hcDesign > 0` even when no design-authority principal commented.
144
+ *
145
+ * Absent (undefined) when no DSB is resolved or the DSB lacks token-compliance
146
+ * status — callers treat absence the same as 0.
147
+ */
148
+ complianceSignal?: number;
105
149
  }
106
150
  export interface AdmissionThresholds {
107
151
  minimumScore: number;
@@ -24,7 +24,13 @@ export function mapIssueToPriorityInput(input) {
24
24
  const backlog = input.backlogContext;
25
25
  // ── Backlog status veto ─────────────────────────────────────
26
26
  // Drafts aren't ready to admit; orchestrator should ignore them.
27
- if (backlog?.status === 'Draft') {
27
+ // RFC-0011 Phase 4 (AISDLC-115.5): the same veto applies to
28
+ // `Needs Clarification` — running PPA on an unready issue burns
29
+ // scoring effort that gets invalidated on clarification (RFC §2.3 +
30
+ // §7.2). The PPA admission step short-circuits with soulAlignment=0
31
+ // so the composite scorer treats the issue as not-admissible without
32
+ // calling out to the design / autonomy / trust enrichment layers.
33
+ if (backlog?.status === 'Draft' || backlog?.status === 'Needs Clarification') {
28
34
  return {
29
35
  itemId: `#${input.issueNumber}`,
30
36
  title: input.title,
@@ -39,7 +45,10 @@ export function mapIssueToPriorityInput(input) {
39
45
  const isTrusted = assoc === 'OWNER' || assoc === 'MEMBER' || assoc === 'COLLABORATOR';
40
46
  const isContributor = assoc === 'CONTRIBUTOR';
41
47
  // ── Complexity from issue body, AC count, or backlog context ─
42
- const complexityMatch = input.body?.match(/###?\s*Complexity\s*\n+\s*(\d+)/i);
48
+ // Horizontal-whitespace classes ([ \t]) around \n+ so the sub-patterns can't
49
+ // overlap (\s includes \n) — avoids polynomial backtracking on attacker-
50
+ // controlled issue bodies (CodeQL js/polynomial-redos).
51
+ const complexityMatch = input.body?.match(/###?[ \t]*Complexity[ \t]*\n+[ \t]*(\d+)/i);
43
52
  let complexity = complexityMatch ? Number(complexityMatch[1]) : undefined;
44
53
  // Backlog tasks rarely include a `### Complexity` header — fall back to AC count
45
54
  // (each AC is roughly one logical unit; clamp at 10).
@@ -18,6 +18,36 @@
18
18
  */
19
19
  import type { PriorityInput, QualityFlag } from '@ai-sdlc/reference';
20
20
  import { type AdmissionInput } from './admission-score.js';
21
+ /**
22
+ * Extract a code-area string from a list of file/path references.
23
+ *
24
+ * Algorithm:
25
+ * 1. Filter references down to path-like strings (not URLs, not AISDLC-N
26
+ * IDs, not RFC-NNNN IDs) — the path-like entries are the ones that
27
+ * carry blast-radius signal.
28
+ * 2. Compute the deepest common path prefix across all filtered paths
29
+ * at the directory level (e.g. `pipeline-cli/src/orchestrator/filters/x.ts`
30
+ * + `pipeline-cli/src/orchestrator/loop.ts` → `pipeline-cli/src/orchestrator`).
31
+ * 3. Return the prefix, or `undefined` when there are no path references.
32
+ *
33
+ * The caller uses the result to populate `EnrichmentContext.codeArea` so
34
+ * `buildCodeAreaQuality()` can look up per-area metrics instead of the
35
+ * uniform Eρ variance of 0.30.
36
+ *
37
+ * @param references Raw reference strings from frontmatter `references:` AND
38
+ * any `## References` body section — callers are responsible
39
+ * for merging both sources before calling here.
40
+ */
41
+ export declare function extractCodeAreaFromReferences(references: string[]): string | undefined;
42
+ /**
43
+ * Parse file-path references from a `## References` markdown section.
44
+ *
45
+ * The body format is a bullet list where each item may be a bare path,
46
+ * a backtick-quoted path, a description with an inline path, or a mix.
47
+ * We extract the first backtick-quoted segment or the first slash-
48
+ * containing word from each bullet.
49
+ */
50
+ export declare function parseBodyReferences(body: string): string[];
21
51
  export interface BacklogAcceptanceCriterion {
22
52
  index: number;
23
53
  text: string;
@@ -38,6 +68,11 @@ export interface BacklogTaskSnapshot {
38
68
  createdBy?: string;
39
69
  acceptanceCriteria: BacklogAcceptanceCriterion[];
40
70
  references: string[];
71
+ /**
72
+ * References extracted from the `## References` body section.
73
+ * Merged with frontmatter `references` when computing `codeArea`.
74
+ */
75
+ bodyReferences: string[];
41
76
  /** Task IDs this task is blocked by (frontmatter `dependencies`). */
42
77
  dependencies: string[];
43
78
  /** Filesystem path the snapshot was read from. */
@@ -83,6 +118,16 @@ export interface BacklogAdmissionMapping {
83
118
  priorityInputOverrides: Partial<PriorityInput>;
84
119
  /** Quality flags surfaced for renderers (zombie close, etc). */
85
120
  qualityFlags: QualityFlag[];
121
+ /**
122
+ * Deepest common path prefix computed from all references in the task
123
+ * (frontmatter `references:` + `## References` body section).
124
+ *
125
+ * Pass to `EnrichmentContext.codeArea` when calling `enrichAdmissionInput()`
126
+ * so `buildCodeAreaQuality()` can look up per-area metrics instead of the
127
+ * uniform Eρ variance of 0.30. Undefined when the task has no file-path
128
+ * references — callers should fall through to the uniform default.
129
+ */
130
+ codeArea: string | undefined;
86
131
  }
87
132
  /**
88
133
  * Map a Backlog snapshot onto an `AdmissionInput` plus the