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