@ai-sdlc/orchestrator 0.10.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters.d.ts +42 -3
- package/dist/adapters.js +133 -3
- package/dist/admission-composite.d.ts +112 -1
- package/dist/admission-composite.js +85 -4
- package/dist/admission-enrichment.d.ts +12 -3
- package/dist/admission-enrichment.js +64 -13
- package/dist/admission-score.d.ts +30 -0
- package/dist/admission-score.js +4 -1
- package/dist/analysis/file-walker.js +5 -0
- package/dist/backlog-adapter.d.ts +45 -0
- package/dist/backlog-adapter.js +145 -1
- package/dist/calibration.d.ts +81 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/git-remote.js +1 -1
- package/dist/cli/commands/init-features.d.ts +301 -2
- package/dist/cli/commands/init-features.js +647 -7
- package/dist/cli/commands/init-templates.d.ts +198 -1
- package/dist/cli/commands/init-templates.js +943 -1
- package/dist/cli/commands/init.d.ts +45 -0
- package/dist/cli/commands/init.js +147 -5
- package/dist/cli/commands/run.js +9 -1
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.js +42 -8
- package/dist/compliance/composer.d.ts +79 -0
- package/dist/compliance/composer.js +258 -0
- package/dist/compliance/errors.d.ts +64 -0
- package/dist/compliance/errors.js +85 -0
- package/dist/compliance/loader.d.ts +52 -0
- package/dist/compliance/loader.js +124 -0
- package/dist/compliance/types.d.ts +184 -0
- package/dist/compliance/types.js +41 -0
- package/dist/compliance-clearance.d.ts +269 -0
- package/dist/compliance-clearance.js +269 -0
- package/dist/config.js +17 -0
- package/dist/cost-tracker.d.ts +22 -0
- package/dist/cost-tracker.js +41 -0
- package/dist/cycle-utils.js +14 -1
- package/dist/database/adapters/external.js +5 -1
- package/dist/embedding/adapters/openai-text-embedding-3-small.d.ts +71 -0
- package/dist/embedding/adapters/openai-text-embedding-3-small.js +190 -0
- package/dist/embedding/consumers/tessellation-drift.d.ts +74 -0
- package/dist/embedding/consumers/tessellation-drift.js +76 -0
- package/dist/embedding/cross-provider.d.ts +78 -0
- package/dist/embedding/cross-provider.js +75 -0
- package/dist/embedding/deprecation.d.ts +151 -0
- package/dist/embedding/deprecation.js +229 -0
- package/dist/embedding/errors.d.ts +90 -0
- package/dist/embedding/errors.js +150 -0
- package/dist/embedding/index.d.ts +29 -0
- package/dist/embedding/index.js +24 -0
- package/dist/embedding/pipeline-load.d.ts +146 -0
- package/dist/embedding/pipeline-load.js +178 -0
- package/dist/embedding/registry.d.ts +45 -0
- package/dist/embedding/registry.js +61 -0
- package/dist/embedding/stale-vector.d.ts +110 -0
- package/dist/embedding/stale-vector.js +92 -0
- package/dist/embedding/storage/index.d.ts +51 -0
- package/dist/embedding/storage/index.js +43 -0
- package/dist/embedding/storage/jsonl-backend.d.ts +150 -0
- package/dist/embedding/storage/jsonl-backend.js +332 -0
- package/dist/embedding/storage/types.d.ts +135 -0
- package/dist/embedding/storage/types.js +13 -0
- package/dist/embedding/types.d.ts +180 -0
- package/dist/embedding/types.js +10 -0
- package/dist/execute.d.ts +29 -2
- package/dist/execute.js +200 -38
- package/dist/fix-review.js +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.js +19 -2
- package/dist/journey/inheritance-validator.d.ts +396 -0
- package/dist/journey/inheritance-validator.js +370 -0
- package/dist/journey/metric-snapshot.d.ts +403 -0
- package/dist/journey/metric-snapshot.js +370 -0
- package/dist/journey/state-id-drift-rule.d.ts +137 -0
- package/dist/journey/state-id-drift-rule.js +245 -0
- package/dist/journey-sa2-router.d.ts +395 -0
- package/dist/journey-sa2-router.js +308 -0
- package/dist/otel-exporter.js +0 -3
- package/dist/runners/claude-code.js +3 -3
- package/dist/runners/review-agent.js +6 -2
- package/dist/runners/runner-registry.d.ts +36 -0
- package/dist/runners/runner-registry.js +90 -0
- package/dist/runtime/attestations.d.ts +173 -13
- package/dist/runtime/attestations.js +265 -43
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/sa-scoring/exemplar-bank.js +3 -1
- package/dist/sa-scoring/layer3-llm.js +6 -1
- package/dist/sa-scoring/revision-proposal-config.d.ts +178 -0
- package/dist/sa-scoring/revision-proposal-config.js +198 -0
- package/dist/sa-scoring/revision-proposal.d.ts +285 -0
- package/dist/sa-scoring/revision-proposal.js +417 -0
- package/dist/shared.d.ts +28 -0
- package/dist/shared.js +37 -0
- package/dist/signal-ingestion/adapters/community-thread.d.ts +43 -0
- package/dist/signal-ingestion/adapters/community-thread.js +55 -0
- package/dist/signal-ingestion/adapters/in-app-feedback.d.ts +67 -0
- package/dist/signal-ingestion/adapters/in-app-feedback.js +51 -0
- package/dist/signal-ingestion/adapters/manual.d.ts +78 -0
- package/dist/signal-ingestion/adapters/manual.js +112 -0
- package/dist/signal-ingestion/adapters/support-ticket.d.ts +47 -0
- package/dist/signal-ingestion/adapters/support-ticket.js +51 -0
- package/dist/signal-ingestion/classifier.d.ts +205 -0
- package/dist/signal-ingestion/classifier.js +494 -0
- package/dist/signal-ingestion/clustering-types.d.ts +36 -0
- package/dist/signal-ingestion/clustering-types.js +14 -0
- package/dist/signal-ingestion/clustering.d.ts +200 -0
- package/dist/signal-ingestion/clustering.js +413 -0
- package/dist/signal-ingestion/config.d.ts +351 -0
- package/dist/signal-ingestion/config.js +587 -0
- package/dist/signal-ingestion/d1.d.ts +252 -0
- package/dist/signal-ingestion/d1.js +235 -0
- package/dist/signal-ingestion/errors.d.ts +73 -0
- package/dist/signal-ingestion/errors.js +108 -0
- package/dist/signal-ingestion/governance-events.d.ts +181 -0
- package/dist/signal-ingestion/governance-events.js +189 -0
- package/dist/signal-ingestion/index.d.ts +35 -0
- package/dist/signal-ingestion/index.js +53 -0
- package/dist/signal-ingestion/manual-share-metric.d.ts +93 -0
- package/dist/signal-ingestion/manual-share-metric.js +106 -0
- package/dist/signal-ingestion/registry.d.ts +40 -0
- package/dist/signal-ingestion/registry.js +137 -0
- package/dist/signal-ingestion/residency.d.ts +227 -0
- package/dist/signal-ingestion/residency.js +238 -0
- package/dist/signal-ingestion/significance.d.ts +554 -0
- package/dist/signal-ingestion/significance.js +555 -0
- package/dist/signal-ingestion/types.d.ts +191 -0
- package/dist/signal-ingestion/types.js +8 -0
- package/dist/substrate/drift-composition.d.ts +270 -0
- package/dist/substrate/drift-composition.js +306 -0
- package/dist/substrate/drift-tui-surface.d.ts +61 -0
- package/dist/substrate/drift-tui-surface.js +102 -0
- package/dist/substrate/identity-class.d.ts +176 -0
- package/dist/substrate/identity-class.js +201 -0
- package/dist/tessellation/cross-soul-provenance-rule.d.ts +133 -0
- package/dist/tessellation/cross-soul-provenance-rule.js +171 -0
- package/dist/tessellation/inter-soul-embedding-distance-rule.d.ts +61 -0
- package/dist/tessellation/inter-soul-embedding-distance-rule.js +67 -0
- package/dist/tessellation/rule-registry.d.ts +269 -0
- package/dist/tessellation/rule-registry.js +92 -0
- package/dist/tessellation/soul-slug-ast-scan-rule.d.ts +90 -0
- package/dist/tessellation/soul-slug-ast-scan-rule.js +158 -0
- package/dist/tessellation-admission.d.ts +162 -0
- package/dist/tessellation-admission.js +146 -0
- package/dist/tessellation-drift.d.ts +246 -0
- package/dist/tessellation-drift.js +250 -0
- package/dist/validate-config.js +13 -0
- package/dist/validate-issue.js +2 -2
- package/dist/variant/cardinality-activation.d.ts +126 -0
- package/dist/variant/cardinality-activation.js +101 -0
- package/dist/variant/deprecation-lifecycle.d.ts +184 -0
- package/dist/variant/deprecation-lifecycle.js +208 -0
- package/dist/variant/drift-extension.d.ts +136 -0
- package/dist/variant/drift-extension.js +164 -0
- package/dist/variant/engineering-review.d.ts +185 -0
- package/dist/variant/engineering-review.js +142 -0
- package/dist/variant/index.d.ts +32 -0
- package/dist/variant/index.js +32 -0
- package/dist/variant/inheritance-validator.d.ts +165 -0
- package/dist/variant/inheritance-validator.js +139 -0
- package/dist/variant/internal-adopter/index.d.ts +11 -0
- package/dist/variant/internal-adopter/index.js +10 -0
- package/dist/variant/internal-adopter/products.d.ts +156 -0
- package/dist/variant/internal-adopter/products.js +366 -0
- package/dist/variant-admission.d.ts +316 -0
- package/dist/variant-admission.js +247 -0
- package/dist/webhook-manager.js +7 -1
- package/package.json +15 -12
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC-0018 Phase 2 — In-Soul Journey Pattern admission scorer composition.
|
|
3
|
+
*
|
|
4
|
+
* Implements the journey-scope routing algorithm described in RFC-0018 §5.4
|
|
5
|
+
* and §10.1 (OQ-4 resolution):
|
|
6
|
+
*
|
|
7
|
+
* resolveTargetedJourneys(w) = set of (soulId, [variantId,] journeyId) triples
|
|
8
|
+
* declared on the work item via `targetedJourneys[]` (URI shapes
|
|
9
|
+
* `<soul-id>/<journey-id>` or `<soul-id>/<variant-id>/<journey-id>`).
|
|
10
|
+
*
|
|
11
|
+
* If no `targetedJourneys` declared:
|
|
12
|
+
* Scoring proceeds at soul/variant scope (backward-compatible — unchanged
|
|
13
|
+
* from RFC-0009 + RFC-0017 baseline).
|
|
14
|
+
*
|
|
15
|
+
* Else if |targeted| == 1 (single-journey):
|
|
16
|
+
* Sα₂(w) = scoreSα₂(journey.designImperatives UNION variant's UNION soul's;
|
|
17
|
+
* most-specific wins: journey > variant > soul)
|
|
18
|
+
* Cκ(w) = boosted when journey.successMetrics.completion-rate < alertBelow
|
|
19
|
+
* Eρ₅(w) = elevated when journey.accessibility.wcagLevel > soul/variant default
|
|
20
|
+
*
|
|
21
|
+
* Else (multi-journey):
|
|
22
|
+
* Per-journey scores aggregated via `crossJourneyAggregation` config
|
|
23
|
+
* (default `min` per RFC-0018 §5.4 + RFC-0017 OQ-4 cross-variant pattern).
|
|
24
|
+
*
|
|
25
|
+
* ### Completion-criteria closed enum (OQ-4 resolution)
|
|
26
|
+
*
|
|
27
|
+
* v1 allows only: `terminal-success-state` | `all-states-reached`
|
|
28
|
+
* `custom-predicate` is rejected at schema validation time.
|
|
29
|
+
* Future activation via `Decision: journey-custom-predicate-activation-request`
|
|
30
|
+
* (Stage A counter, auto-promote at ≥2 distinct adopter requests).
|
|
31
|
+
* Future language: CEL (Google Common Expression Language) per OQ-4 resolution.
|
|
32
|
+
*
|
|
33
|
+
* ### Backward compatibility
|
|
34
|
+
*
|
|
35
|
+
* Work items without `targetedJourneys` score against soul / variant (existing
|
|
36
|
+
* RFC-0017 behavior preserved). Soul DIDs without `journeys[]` behave identically.
|
|
37
|
+
*
|
|
38
|
+
* @see spec/rfcs/RFC-0018-in-soul-journey-pattern.md §5.4 + §10 + §10.1
|
|
39
|
+
* @see orchestrator/src/variant-admission.ts — sibling RFC-0017 Phase 2 router
|
|
40
|
+
* @see orchestrator/src/compliance-clearance.ts — Eρ₅ base scorer
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* The set of valid v1 completion-criteria kind values.
|
|
44
|
+
* Used by `validateCompletionCriteriaKind` for closed-enum rejection.
|
|
45
|
+
*/
|
|
46
|
+
export const COMPLETION_CRITERIA_V1_KINDS = new Set([
|
|
47
|
+
'terminal-success-state',
|
|
48
|
+
'all-states-reached',
|
|
49
|
+
]);
|
|
50
|
+
/**
|
|
51
|
+
* Decision kind for the `custom-predicate` activation request counter.
|
|
52
|
+
* Stage A counter per RFC-0035 G0 non-blocking contract.
|
|
53
|
+
* Auto-promotes at ≥2 distinct adopter requests (RFC-0018 OQ-4 resolution).
|
|
54
|
+
*/
|
|
55
|
+
export const JOURNEY_CUSTOM_PREDICATE_DECISION_KIND = 'journey-custom-predicate-activation-request';
|
|
56
|
+
/**
|
|
57
|
+
* Validate a completion-criteria `kind` value against the v1 closed enum.
|
|
58
|
+
*
|
|
59
|
+
* Returns `{ valid: true }` for `terminal-success-state` and `all-states-reached`.
|
|
60
|
+
* Returns `{ valid: false, rejectedKind, decisionKind }` for `custom-predicate`
|
|
61
|
+
* (or any other unrecognized value). The caller is responsible for emitting the
|
|
62
|
+
* Decision event to the RFC-0035 catalog.
|
|
63
|
+
*/
|
|
64
|
+
export function validateCompletionCriteriaKind(kind) {
|
|
65
|
+
if (COMPLETION_CRITERIA_V1_KINDS.has(kind)) {
|
|
66
|
+
return { valid: true };
|
|
67
|
+
}
|
|
68
|
+
const result = {
|
|
69
|
+
valid: false,
|
|
70
|
+
rejectedKind: kind,
|
|
71
|
+
};
|
|
72
|
+
// Wire the Decision counter for custom-predicate specifically (OQ-4 auto-promote mechanism)
|
|
73
|
+
if (kind === 'custom-predicate') {
|
|
74
|
+
result.decisionKind = JOURNEY_CUSTOM_PREDICATE_DECISION_KIND;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Increment the Stage A counter for a `journey-custom-predicate-activation-request`.
|
|
80
|
+
*
|
|
81
|
+
* @param counter - The counter to increment (mutated in-place).
|
|
82
|
+
* @param adopterId - Adopter ID submitting the request (for deduplication).
|
|
83
|
+
* Use `'__unknown__'` when adopter identity is unavailable.
|
|
84
|
+
*/
|
|
85
|
+
export function incrementDecisionCounter(counter, adopterId) {
|
|
86
|
+
counter.totalRequests++;
|
|
87
|
+
counter.distinctAdopterIds.add(adopterId);
|
|
88
|
+
counter.shouldAutoPromote = counter.distinctAdopterIds.size >= 2;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Create a fresh Stage A counter for `journey-custom-predicate-activation-request`.
|
|
92
|
+
*/
|
|
93
|
+
export function createCustomPredicateDecisionCounter() {
|
|
94
|
+
return {
|
|
95
|
+
decisionKind: JOURNEY_CUSTOM_PREDICATE_DECISION_KIND,
|
|
96
|
+
distinctAdopterIds: new Set(),
|
|
97
|
+
totalRequests: 0,
|
|
98
|
+
shouldAutoPromote: false,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Ordinal index for WCAG levels — higher = stricter.
|
|
103
|
+
* `A` < `AA` < `AAA`.
|
|
104
|
+
*/
|
|
105
|
+
const WCAG_LEVEL_ORDER = {
|
|
106
|
+
A: 0,
|
|
107
|
+
AA: 1,
|
|
108
|
+
AAA: 2,
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Returns true when `journeyLevel` is strictly ABOVE `soulDefault`.
|
|
112
|
+
* Journeys MAY raise the WCAG level above the parent; they MAY NOT lower it.
|
|
113
|
+
*/
|
|
114
|
+
export function isWcagElevated(journeyLevel, soulDefault) {
|
|
115
|
+
return WCAG_LEVEL_ORDER[journeyLevel] > WCAG_LEVEL_ORDER[soulDefault];
|
|
116
|
+
}
|
|
117
|
+
// ── URI parsing ──────────────────────────────────────────────────────────
|
|
118
|
+
const SLUG_RE = /^[a-z][a-z0-9-]*$/;
|
|
119
|
+
/**
|
|
120
|
+
* Parse a targeted-journey reference.
|
|
121
|
+
*
|
|
122
|
+
* Accepts two URI forms per RFC-0018 §6.1:
|
|
123
|
+
*
|
|
124
|
+
* 1. `<soul-id>/<journey-id>` — soul-scoped journey
|
|
125
|
+
* 2. `<soul-id>/<variant-id>/<journey-id>` — variant-scoped journey
|
|
126
|
+
*
|
|
127
|
+
* All slug segments must match `^[a-z][a-z0-9-]*$`.
|
|
128
|
+
* Returns `undefined` for malformed input (silent skip; schema-side validator
|
|
129
|
+
* is Phase 1's concern per RFC-0018 §6.1 pattern).
|
|
130
|
+
*/
|
|
131
|
+
export function parseTargetedJourneyRef(raw) {
|
|
132
|
+
const parts = raw.split('/');
|
|
133
|
+
if (parts.length === 2) {
|
|
134
|
+
const [soulId, journeyId] = parts;
|
|
135
|
+
if (SLUG_RE.test(soulId) && SLUG_RE.test(journeyId)) {
|
|
136
|
+
return { soulId, journeyId, raw };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else if (parts.length === 3) {
|
|
140
|
+
const [soulId, variantId, journeyId] = parts;
|
|
141
|
+
if (SLUG_RE.test(soulId) && SLUG_RE.test(variantId) && SLUG_RE.test(journeyId)) {
|
|
142
|
+
return { soulId, variantId, journeyId, raw };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
// ── resolveTargetedJourneys ──────────────────────────────────────────────
|
|
148
|
+
/**
|
|
149
|
+
* Resolve the set of targeted (soulId, [variantId,] journeyId) triples for a work item.
|
|
150
|
+
*
|
|
151
|
+
* Algorithm:
|
|
152
|
+
* 1. Find the work item by case-insensitive ID match in `workItemTargeting`.
|
|
153
|
+
* 2. Parse each entry of `targetedJourneys[]` via `parseTargetedJourneyRef`.
|
|
154
|
+
* 3. Filter parsed refs against `journeysBySoul` — a (soulId, journeyId) pair
|
|
155
|
+
* only survives if the soul exists AND the journeyId is declared on it.
|
|
156
|
+
* 4. Return the validated intersection (empty = backward-compat soul/variant scope).
|
|
157
|
+
*/
|
|
158
|
+
export function resolveTargetedJourneys(workItemId, journeyCtx) {
|
|
159
|
+
if (!journeyCtx || !journeyCtx.workItemTargeting || journeyCtx.workItemTargeting.length === 0) {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
const normalizedId = workItemId.toLowerCase();
|
|
163
|
+
const entry = journeyCtx.workItemTargeting.find((e) => e.id.toLowerCase() === normalizedId);
|
|
164
|
+
if (!entry || !entry.targetedJourneys || entry.targetedJourneys.length === 0) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
const out = [];
|
|
168
|
+
for (const raw of entry.targetedJourneys) {
|
|
169
|
+
const parsed = parseTargetedJourneyRef(raw);
|
|
170
|
+
if (!parsed)
|
|
171
|
+
continue;
|
|
172
|
+
const journeys = journeyCtx.journeysBySoul[parsed.soulId];
|
|
173
|
+
if (!journeys)
|
|
174
|
+
continue;
|
|
175
|
+
if (!journeys.some((j) => j.id === parsed.journeyId))
|
|
176
|
+
continue;
|
|
177
|
+
out.push(parsed);
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
// ── Cross-journey aggregation ────────────────────────────────────────────
|
|
182
|
+
/**
|
|
183
|
+
* Apply a per-Soul `crossJourneyAggregation` rule over per-journey scores.
|
|
184
|
+
* Mirrors `applyCrossVariantRule` in `variant-admission.ts` but for journey scope.
|
|
185
|
+
*
|
|
186
|
+
* @param values - Per-journey score samples (one per targeted journey).
|
|
187
|
+
* @param rule - The aggregation rule (defaults to `min` per RFC-0018 §5.4 + OQ-4).
|
|
188
|
+
* @param fallback - Returned when `values` is empty.
|
|
189
|
+
*/
|
|
190
|
+
export function applyCrossJourneyRule(values, rule, fallback = 0.5) {
|
|
191
|
+
if (values.length === 0)
|
|
192
|
+
return fallback;
|
|
193
|
+
switch (rule ?? 'min') {
|
|
194
|
+
case 'min':
|
|
195
|
+
return Math.min(...values);
|
|
196
|
+
case 'max':
|
|
197
|
+
return Math.max(...values);
|
|
198
|
+
case 'mean':
|
|
199
|
+
return values.reduce((sum, v) => sum + v, 0) / values.length;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Compute the journey-scope-refined Sα₂ + Cκ + Eρ₅ for a work item.
|
|
204
|
+
*
|
|
205
|
+
* This runs AFTER variant-scope resolution: the caller has already routed the
|
|
206
|
+
* work item to its target Soul(s)/Variant(s) and obtained the soul/variant
|
|
207
|
+
* Sα₂ (the `fallbackSa2` argument) and Cκ (the `fallbackCk` argument).
|
|
208
|
+
* Journey routing refines those values when the work item declares
|
|
209
|
+
* `targetedJourneys` of one of the affected Souls (RFC-0018 §5.4).
|
|
210
|
+
*
|
|
211
|
+
* **Sα₂ Vibe Coherence** — journey's `designImperatives` UNION variant's UNION
|
|
212
|
+
* soul's; conflict resolution: most-specific wins (journey > variant > soul).
|
|
213
|
+
* The pre-computed `JourneyScores.sa2` in the context encodes this resolution.
|
|
214
|
+
*
|
|
215
|
+
* **Cκ Capability Coverage** — boosted when `completion-rate < alertBelow`.
|
|
216
|
+
* The pre-computed `JourneyScores.ck` encodes the boost; callers supply live
|
|
217
|
+
* metric snapshots to the loader that builds `journeyScores`.
|
|
218
|
+
*
|
|
219
|
+
* **Eρ₅ Compliance Clearance** — elevated when journey WCAG > soul-default.
|
|
220
|
+
* The pre-computed `JourneyScores.er5Elevated` flag captures this; the caller
|
|
221
|
+
* applies Eρ₅ gating at the journey's elevated level when `er5Elevated = true`.
|
|
222
|
+
*
|
|
223
|
+
* **Cross-journey aggregation** (work touches multiple journeys) — same `min`
|
|
224
|
+
* as RFC-0018 §5.4 / RFC-0009 §7.2 / RFC-0017 OQ-4 by default. Per-Soul
|
|
225
|
+
* override via `journeyConfig.crossJourneyAggregation`.
|
|
226
|
+
*
|
|
227
|
+
* @param workItemId - The canonical work item ID.
|
|
228
|
+
* @param fallbackSa2 - Soul/variant Sα₂ to use when no journey routing applies.
|
|
229
|
+
* @param fallbackCk - Soul/variant Cκ to use when no journey routing applies.
|
|
230
|
+
* @param fallbackEr5Elevated - Soul/variant Eρ₅ elevation flag (usually false).
|
|
231
|
+
* @param journeyCtx - Journey-scope context; undefined → backward-compat passthrough.
|
|
232
|
+
*/
|
|
233
|
+
export function computeJourneyScopedScores(workItemId, fallbackSa2, fallbackCk, fallbackEr5Elevated, journeyCtx) {
|
|
234
|
+
const targetedJourneys = resolveTargetedJourneys(workItemId, journeyCtx);
|
|
235
|
+
// ── Backward-compat: no targeted journeys → soul/variant scope passthrough ──
|
|
236
|
+
if (targetedJourneys.length === 0 || !journeyCtx) {
|
|
237
|
+
return {
|
|
238
|
+
sa2: fallbackSa2,
|
|
239
|
+
ck: fallbackCk,
|
|
240
|
+
er5Elevated: fallbackEr5Elevated,
|
|
241
|
+
routingPath: 'no-journey-routing',
|
|
242
|
+
targetedJourneys: [],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
// ── Single-journey fast path ──
|
|
246
|
+
if (targetedJourneys.length === 1) {
|
|
247
|
+
const ref = targetedJourneys[0];
|
|
248
|
+
const scores = journeyCtx.journeyScores[ref.soulId]?.[ref.journeyId];
|
|
249
|
+
return {
|
|
250
|
+
sa2: scores?.sa2 ?? fallbackSa2,
|
|
251
|
+
ck: scores?.ck ?? fallbackCk,
|
|
252
|
+
er5Elevated: scores?.er5Elevated ?? fallbackEr5Elevated,
|
|
253
|
+
routingPath: 'single-journey',
|
|
254
|
+
targetedJourneys,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
// ── Multi-journey: aggregate per-Soul, then aggregate cross-Soul ──
|
|
258
|
+
// Per-Soul aggregation uses the Soul's `crossJourneyAggregation` config
|
|
259
|
+
// (default `min`). When journeys span multiple souls (rare), the cross-Soul
|
|
260
|
+
// layer also uses `min` as the safest default (RFC-0009 §7.2).
|
|
261
|
+
const bySoul = new Map();
|
|
262
|
+
for (const ref of targetedJourneys) {
|
|
263
|
+
const bucket = bySoul.get(ref.soulId);
|
|
264
|
+
if (bucket)
|
|
265
|
+
bucket.push(ref);
|
|
266
|
+
else
|
|
267
|
+
bySoul.set(ref.soulId, [ref]);
|
|
268
|
+
}
|
|
269
|
+
const perSoulSa2 = [];
|
|
270
|
+
const perSoulCk = [];
|
|
271
|
+
const perSoulEr5Elevated = [];
|
|
272
|
+
let firstAggregationRule;
|
|
273
|
+
for (const [soulId, refs] of bySoul) {
|
|
274
|
+
const cfg = journeyCtx.configBySoul?.[soulId];
|
|
275
|
+
const rule = cfg?.crossJourneyAggregation ?? 'min';
|
|
276
|
+
if (firstAggregationRule === undefined)
|
|
277
|
+
firstAggregationRule = rule;
|
|
278
|
+
const sa2Samples = [];
|
|
279
|
+
const ckSamples = [];
|
|
280
|
+
const er5Samples = [];
|
|
281
|
+
for (const ref of refs) {
|
|
282
|
+
const scores = journeyCtx.journeyScores[soulId]?.[ref.journeyId];
|
|
283
|
+
sa2Samples.push(scores?.sa2 ?? fallbackSa2);
|
|
284
|
+
ckSamples.push(scores?.ck ?? fallbackCk);
|
|
285
|
+
er5Samples.push(scores?.er5Elevated ?? fallbackEr5Elevated);
|
|
286
|
+
}
|
|
287
|
+
perSoulSa2.push(applyCrossJourneyRule(sa2Samples, rule, fallbackSa2));
|
|
288
|
+
perSoulCk.push(applyCrossJourneyRule(ckSamples, rule, fallbackCk));
|
|
289
|
+
// Eρ₅ elevation: `true` wins if ANY journey requires elevation
|
|
290
|
+
// (conservative — if at least one journey has elevated WCAG, the
|
|
291
|
+
// work item is evaluated at that stricter level).
|
|
292
|
+
perSoulEr5Elevated.push(er5Samples.some(Boolean));
|
|
293
|
+
}
|
|
294
|
+
// Cross-Soul layering: `min` between Souls for numeric scores (RFC-0009 §7.2).
|
|
295
|
+
// Eρ₅ elevation: `true` if any Soul has elevated WCAG (conservative/safe).
|
|
296
|
+
const sa2 = perSoulSa2.length === 1 ? perSoulSa2[0] : Math.min(...perSoulSa2);
|
|
297
|
+
const ck = perSoulCk.length === 1 ? perSoulCk[0] : Math.min(...perSoulCk);
|
|
298
|
+
const er5Elevated = perSoulEr5Elevated.some(Boolean);
|
|
299
|
+
return {
|
|
300
|
+
sa2,
|
|
301
|
+
ck,
|
|
302
|
+
er5Elevated,
|
|
303
|
+
routingPath: 'multi-journey',
|
|
304
|
+
targetedJourneys,
|
|
305
|
+
aggregationRule: firstAggregationRule ?? 'min',
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=journey-sa2-router.js.map
|
package/dist/otel-exporter.js
CHANGED
|
@@ -87,13 +87,10 @@ export function createOTelBridge(metricStore, options) {
|
|
|
87
87
|
[ATTRIBUTE_KEYS.RUN_ID]: runId,
|
|
88
88
|
[ATTRIBUTE_KEYS.PIPELINE]: pipelineType,
|
|
89
89
|
};
|
|
90
|
-
// Use withSpan to create a span (fire-and-forget style)
|
|
91
|
-
let endFn;
|
|
92
90
|
// Since withSpan is async, we track spans manually
|
|
93
91
|
const handle = {
|
|
94
92
|
end(_status) {
|
|
95
93
|
activeSpans.delete(runId);
|
|
96
|
-
endFn?.();
|
|
97
94
|
},
|
|
98
95
|
setAttribute(key, value) {
|
|
99
96
|
attributes[key] = value;
|
|
@@ -47,7 +47,7 @@ export function buildPrompt(ctx) {
|
|
|
47
47
|
const ciVerify = buildVerificationSteps(step, lintCmd, fmtCmd, typecheckCmd);
|
|
48
48
|
lines.push(...ciVerify.lines);
|
|
49
49
|
step = ciVerify.nextStep;
|
|
50
|
-
lines.push(`${++step}. Write or update tests if needed to cover your fix.`, `${++step}. NEVER modify files matching the blocked paths below — violations will be automatically detected and the change will be rejected.`, `${
|
|
50
|
+
lines.push(`${++step}. Write or update tests if needed to cover your fix.`, `${++step}. NEVER modify files matching the blocked paths below — violations will be automatically detected and the change will be rejected.`, `${step + 1}. Keep your changes to at most ${ctx.constraints.maxFilesPerChange} files.`);
|
|
51
51
|
}
|
|
52
52
|
else if (ctx.reviewFindings) {
|
|
53
53
|
let step = 0;
|
|
@@ -55,7 +55,7 @@ export function buildPrompt(ctx) {
|
|
|
55
55
|
const reviewVerify = buildVerificationSteps(step, lintCmd, fmtCmd, typecheckCmd);
|
|
56
56
|
lines.push(...reviewVerify.lines);
|
|
57
57
|
step = reviewVerify.nextStep;
|
|
58
|
-
lines.push(`${++step}. NEVER modify files matching the blocked paths below — violations will be automatically detected and the change will be rejected.`, `${
|
|
58
|
+
lines.push(`${++step}. NEVER modify files matching the blocked paths below — violations will be automatically detected and the change will be rejected.`, `${step + 1}. Keep your changes to at most ${ctx.constraints.maxFilesPerChange} files.`);
|
|
59
59
|
}
|
|
60
60
|
else {
|
|
61
61
|
let step = 0;
|
|
@@ -63,7 +63,7 @@ export function buildPrompt(ctx) {
|
|
|
63
63
|
const defaultVerify = buildVerificationSteps(step, lintCmd, fmtCmd, typecheckCmd);
|
|
64
64
|
lines.push(...defaultVerify.lines);
|
|
65
65
|
step = defaultVerify.nextStep;
|
|
66
|
-
lines.push(`${++step}. NEVER modify files matching the blocked paths below — violations will be automatically detected and the change will be rejected.`, `${
|
|
66
|
+
lines.push(`${++step}. NEVER modify files matching the blocked paths below — violations will be automatically detected and the change will be rejected.`, `${step + 1}. Keep your changes to at most ${ctx.constraints.maxFilesPerChange} files.`);
|
|
67
67
|
}
|
|
68
68
|
lines.push('', '## Constraints (enforced — violations will be automatically rejected)', `- Maximum files to change: ${ctx.constraints.maxFilesPerChange}`, `- Tests required: ${ctx.constraints.requireTests}`, `- Blocked paths (NEVER modify — changes will be rejected): ${ctx.constraints.blockedPaths.join(', ') || 'none'}`);
|
|
69
69
|
// Append relevant episodic memory if available
|
|
@@ -291,8 +291,12 @@ export class ReviewAgentRunner {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
parseVerdict(text) {
|
|
294
|
-
// Strip markdown fences if the model wraps the JSON
|
|
295
|
-
|
|
294
|
+
// Strip markdown fences if the model wraps the JSON. Horizontal-whitespace
|
|
295
|
+
// classes ([ \t\r]) instead of \s so the trailing-whitespace group can't
|
|
296
|
+
// overlap the \n the anchors already consume — avoids polynomial
|
|
297
|
+
// backtracking on adversarial model output (CodeQL js/polynomial-redos).
|
|
298
|
+
// JSON.parse tolerates the leading/trailing newline left behind.
|
|
299
|
+
const cleaned = text.replace(/^```(?:json)?[ \t\r]*/m, '').replace(/[ \t\r]*```$/m, '');
|
|
296
300
|
try {
|
|
297
301
|
const parsed = JSON.parse(cleaned);
|
|
298
302
|
const rawFindings = Array.isArray(parsed.findings)
|
|
@@ -38,6 +38,17 @@ export declare class RunnerRegistry {
|
|
|
38
38
|
* Check if a runner is registered and available.
|
|
39
39
|
*/
|
|
40
40
|
has(name: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Load and register a runner from an external plugin module.
|
|
43
|
+
*
|
|
44
|
+
* The module must export a default export or a named `runner` export that
|
|
45
|
+
* satisfies the `AgentRunner` interface (i.e. has a `run(ctx)` method).
|
|
46
|
+
*
|
|
47
|
+
* @param pluginPath - Absolute or resolvable path to the plugin module (e.g. `/path/to/runner.mjs`).
|
|
48
|
+
* @param name - Registry name for the loaded runner (defaults to the basename of the path).
|
|
49
|
+
* @throws Error when the module cannot be imported or does not export a valid AgentRunner.
|
|
50
|
+
*/
|
|
51
|
+
loadFromPlugin(pluginPath: string, name?: string): Promise<string>;
|
|
41
52
|
/**
|
|
42
53
|
* Auto-discover runners from environment variables and register them.
|
|
43
54
|
*/
|
|
@@ -47,4 +58,29 @@ export declare class RunnerRegistry {
|
|
|
47
58
|
* Create a runner registry with auto-discovery.
|
|
48
59
|
*/
|
|
49
60
|
export declare function createRunnerRegistry(env?: Record<string, string | undefined>): RunnerRegistry;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the agent runner to use, applying the full precedence chain:
|
|
63
|
+
*
|
|
64
|
+
* 1. `injectedRunner` — programmatic override (options.runner from caller / tests)
|
|
65
|
+
* 2. `runnerName` — explicit `--runner <name>` flag (must already be registered after discoverFromEnv)
|
|
66
|
+
* 3. `AI_SDLC_RUNNER_PLUGIN` env — path to a dynamic plugin module (loaded + registered)
|
|
67
|
+
* 4. ClaudeCodeRunner (hard-coded default)
|
|
68
|
+
*
|
|
69
|
+
* IMPORTANT — env-discovered runners do NOT auto-win (AISDLC-529 code review). They are
|
|
70
|
+
* registered by `discoverFromEnv()` so they are *selectable by name* via `--runner <name>`,
|
|
71
|
+
* but the mere PRESENCE of an ambient env var (ANTHROPIC_API_KEY, OPENAI_API_KEY, GH_TOKEN,
|
|
72
|
+
* etc. — commonly set for unrelated tools) must NOT silently switch the runner. Before this
|
|
73
|
+
* seam existed the orchestrator always used ClaudeCodeRunner; preserving that as the default
|
|
74
|
+
* (absent an explicit selector) avoids a breaking, surprising change for existing adopters.
|
|
75
|
+
*
|
|
76
|
+
* This function is async because step 3 may dynamically import a module.
|
|
77
|
+
*
|
|
78
|
+
* @throws Error when `runnerName` is provided but not registered in the registry.
|
|
79
|
+
* @throws Error when `AI_SDLC_RUNNER_PLUGIN` points to an invalid module.
|
|
80
|
+
*/
|
|
81
|
+
export declare function resolveRunner(registry: RunnerRegistry, opts?: {
|
|
82
|
+
injectedRunner?: AgentRunner;
|
|
83
|
+
runnerName?: string;
|
|
84
|
+
env?: Record<string, string | undefined>;
|
|
85
|
+
}): Promise<AgentRunner>;
|
|
50
86
|
//# sourceMappingURL=runner-registry.d.ts.map
|
|
@@ -58,6 +58,45 @@ export class RunnerRegistry {
|
|
|
58
58
|
const entry = this.runners.get(name);
|
|
59
59
|
return entry?.available ?? false;
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Load and register a runner from an external plugin module.
|
|
63
|
+
*
|
|
64
|
+
* The module must export a default export or a named `runner` export that
|
|
65
|
+
* satisfies the `AgentRunner` interface (i.e. has a `run(ctx)` method).
|
|
66
|
+
*
|
|
67
|
+
* @param pluginPath - Absolute or resolvable path to the plugin module (e.g. `/path/to/runner.mjs`).
|
|
68
|
+
* @param name - Registry name for the loaded runner (defaults to the basename of the path).
|
|
69
|
+
* @throws Error when the module cannot be imported or does not export a valid AgentRunner.
|
|
70
|
+
*/
|
|
71
|
+
async loadFromPlugin(pluginPath, name) {
|
|
72
|
+
let mod;
|
|
73
|
+
try {
|
|
74
|
+
mod = await import(pluginPath);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
throw new Error(`AI_SDLC_RUNNER_PLUGIN: failed to import plugin module "${pluginPath}": ${err instanceof Error ? err.message : String(err)}.\n` +
|
|
78
|
+
`Ensure the path is correct and the module is a valid ESM/CJS module.`, { cause: err });
|
|
79
|
+
}
|
|
80
|
+
// Accept default export or named 'runner' export
|
|
81
|
+
const exported = mod.default ?? mod.runner;
|
|
82
|
+
if (!exported || typeof exported.run !== 'function') {
|
|
83
|
+
throw new Error(`AI_SDLC_RUNNER_PLUGIN: plugin module "${pluginPath}" does not export a valid AgentRunner.\n` +
|
|
84
|
+
`Expected a default export (or named 'runner' export) with a \`run(ctx: AgentContext): Promise<AgentResult>\` method.\n` +
|
|
85
|
+
`Got: ${JSON.stringify(Object.keys(mod))}`);
|
|
86
|
+
}
|
|
87
|
+
const runnerName = name ??
|
|
88
|
+
pluginPath
|
|
89
|
+
.split('/')
|
|
90
|
+
.pop()
|
|
91
|
+
.replace(/\.(m|c)?[jt]s$/, '');
|
|
92
|
+
this.runners.set(runnerName, {
|
|
93
|
+
name: runnerName,
|
|
94
|
+
runner: exported,
|
|
95
|
+
available: true,
|
|
96
|
+
source: 'manual',
|
|
97
|
+
});
|
|
98
|
+
return runnerName;
|
|
99
|
+
}
|
|
61
100
|
/**
|
|
62
101
|
* Auto-discover runners from environment variables and register them.
|
|
63
102
|
*/
|
|
@@ -163,4 +202,55 @@ export function createRunnerRegistry(env) {
|
|
|
163
202
|
registry.discoverFromEnv(env);
|
|
164
203
|
return registry;
|
|
165
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Resolve the agent runner to use, applying the full precedence chain:
|
|
207
|
+
*
|
|
208
|
+
* 1. `injectedRunner` — programmatic override (options.runner from caller / tests)
|
|
209
|
+
* 2. `runnerName` — explicit `--runner <name>` flag (must already be registered after discoverFromEnv)
|
|
210
|
+
* 3. `AI_SDLC_RUNNER_PLUGIN` env — path to a dynamic plugin module (loaded + registered)
|
|
211
|
+
* 4. ClaudeCodeRunner (hard-coded default)
|
|
212
|
+
*
|
|
213
|
+
* IMPORTANT — env-discovered runners do NOT auto-win (AISDLC-529 code review). They are
|
|
214
|
+
* registered by `discoverFromEnv()` so they are *selectable by name* via `--runner <name>`,
|
|
215
|
+
* but the mere PRESENCE of an ambient env var (ANTHROPIC_API_KEY, OPENAI_API_KEY, GH_TOKEN,
|
|
216
|
+
* etc. — commonly set for unrelated tools) must NOT silently switch the runner. Before this
|
|
217
|
+
* seam existed the orchestrator always used ClaudeCodeRunner; preserving that as the default
|
|
218
|
+
* (absent an explicit selector) avoids a breaking, surprising change for existing adopters.
|
|
219
|
+
*
|
|
220
|
+
* This function is async because step 3 may dynamically import a module.
|
|
221
|
+
*
|
|
222
|
+
* @throws Error when `runnerName` is provided but not registered in the registry.
|
|
223
|
+
* @throws Error when `AI_SDLC_RUNNER_PLUGIN` points to an invalid module.
|
|
224
|
+
*/
|
|
225
|
+
export async function resolveRunner(registry, opts = {}) {
|
|
226
|
+
const env = opts.env ?? process.env;
|
|
227
|
+
// 1. Programmatic injection (options.runner / test override) — always wins
|
|
228
|
+
if (opts.injectedRunner) {
|
|
229
|
+
return opts.injectedRunner;
|
|
230
|
+
}
|
|
231
|
+
// 2. Explicit --runner <name> flag
|
|
232
|
+
if (opts.runnerName) {
|
|
233
|
+
const named = registry.get(opts.runnerName);
|
|
234
|
+
if (!named) {
|
|
235
|
+
const available = registry.listAvailable().map((r) => r.name);
|
|
236
|
+
throw new Error(`--runner "${opts.runnerName}" is not registered. ` +
|
|
237
|
+
`Available runners: ${available.length > 0 ? available.join(', ') : '(none)'}.\n` +
|
|
238
|
+
`Tip: set AI_SDLC_RUNNER_PLUGIN=/path/to/runner.mjs to load a custom runner first.`);
|
|
239
|
+
}
|
|
240
|
+
return named;
|
|
241
|
+
}
|
|
242
|
+
// 3. AI_SDLC_RUNNER_PLUGIN env — dynamically load + register, then return
|
|
243
|
+
const pluginPath = env.AI_SDLC_RUNNER_PLUGIN;
|
|
244
|
+
if (pluginPath) {
|
|
245
|
+
const registeredName = await registry.loadFromPlugin(pluginPath);
|
|
246
|
+
const pluginRunner = registry.get(registeredName);
|
|
247
|
+
// loadFromPlugin throws on invalid module, so pluginRunner is guaranteed to exist here
|
|
248
|
+
return pluginRunner;
|
|
249
|
+
}
|
|
250
|
+
// 4. ClaudeCodeRunner default (always in registry after discoverFromEnv).
|
|
251
|
+
// Env-discovered runners are intentionally NOT auto-selected here — the mere presence
|
|
252
|
+
// of an ambient API-key env var must not silently override the default (AISDLC-529 code
|
|
253
|
+
// review). An adopter selects an env-discovered runner explicitly via `--runner <name>`.
|
|
254
|
+
return registry.get('claude-code') ?? new (await import('./claude-code.js')).ClaudeCodeRunner();
|
|
255
|
+
}
|
|
166
256
|
//# sourceMappingURL=runner-registry.js.map
|