@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,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC-0009 Phase 4.1 — Eρ₅ Compliance Clearance scoring.
|
|
3
|
+
*
|
|
4
|
+
* Implements the categorical 0/1 gating dimension from RFC-0009 §7.1:
|
|
5
|
+
*
|
|
6
|
+
* Eρ₅ = soul.triad.engineering.complianceRegimes[].clearance(work_item)
|
|
7
|
+
* = 0 if any named regime is violated
|
|
8
|
+
* = 1 otherwise
|
|
9
|
+
*
|
|
10
|
+
* **OQ-5 scope** (v3.3 resolution, 2026-05-03): HARD regulatory frameworks
|
|
11
|
+
* ONLY — anything with formal external-audit consequences. Internal best
|
|
12
|
+
* practices, code style, architectural preferences, and team conventions are
|
|
13
|
+
* OUT OF SCOPE and rejected at declaration time per the boundary test:
|
|
14
|
+
* "would an external regulator or auditor have grounds to act on a violation?"
|
|
15
|
+
*
|
|
16
|
+
* In scope:
|
|
17
|
+
* - GDPR (EU data protection)
|
|
18
|
+
* - HIPAA (US healthcare)
|
|
19
|
+
* - SOC2 (audit trust framework — variants like SOC2-T2 accepted)
|
|
20
|
+
* - PCI-DSS (payment card data — variants like PCI-DSS-L1 accepted)
|
|
21
|
+
* - FedRAMP (US federal cloud — variants like FedRAMP-Moderate accepted)
|
|
22
|
+
* - Regional data-residency (Schrems II, EU data-localization, China PIPL,
|
|
23
|
+
* Canadian PIPEDA cross-border)
|
|
24
|
+
* - Regulated-industry rules (financial KYC/AML, healthcare device
|
|
25
|
+
* certification, telecom regulations, ISO-27001 audit framework, etc.)
|
|
26
|
+
*
|
|
27
|
+
* Out of scope (rejected at declaration time):
|
|
28
|
+
* - Internal best-practices ("clean-code", "team-style", "house-style")
|
|
29
|
+
* - Code-quality rules ("eslint-recommended", "prettier-standard")
|
|
30
|
+
* - Architectural preferences ("hexagonal", "ddd-strict")
|
|
31
|
+
* - Team conventions ("our-conventions", "internal-coding-standard")
|
|
32
|
+
*
|
|
33
|
+
* **Adopter opt-in gate** (RFC-0009 §10 Phase 4): Eρ₅ is gated behind
|
|
34
|
+
* `ComplianceClearanceContext.enabled === true` initially. Promotion to
|
|
35
|
+
* default behavior is subject to ecosystem feedback. When disabled (default),
|
|
36
|
+
* the admission composite does not apply the Eρ₅ multiplier and behaves
|
|
37
|
+
* exactly as in Phase 2/3 — full backward compatibility.
|
|
38
|
+
*
|
|
39
|
+
* **RFC-0022 consumption surface**: when an adopter wires the loaded
|
|
40
|
+
* `CompliancePosture[]` into `ComplianceClearanceContext.posture`, the
|
|
41
|
+
* declared regimes from that resource compose with the soul's own
|
|
42
|
+
* `complianceRegimes` field. Both sources contribute to the regime set
|
|
43
|
+
* the work item is checked against; clearance is gating across the union.
|
|
44
|
+
*
|
|
45
|
+
* @see spec/rfcs/RFC-0009-tessellated-design-intent-documents.md §7.1 + §10
|
|
46
|
+
* @see spec/rfcs/RFC-0022-compliance-posture-audit-surface.md (consumption surface)
|
|
47
|
+
*/
|
|
48
|
+
// ── Hard-regulatory whitelist (OQ-5 scope) ─────────────────────────────
|
|
49
|
+
/**
|
|
50
|
+
* Canonical hard-regulatory framework prefixes per RFC-0009 §7.1 OQ-5 resolution.
|
|
51
|
+
*
|
|
52
|
+
* Matching is **prefix-based** to accept variants like `SOC2-T2`, `PCI-DSS-L1`,
|
|
53
|
+
* `FedRAMP-Moderate`, `FedRAMP-High`, `ISO-27001:2022`. The exact framework name
|
|
54
|
+
* (without tier/version suffix) is the prefix; anything starting with one of
|
|
55
|
+
* these prefixes is considered a hard-regulatory regime.
|
|
56
|
+
*
|
|
57
|
+
* Regional data-residency frameworks (Schrems II / PIPL / PIPEDA) are listed
|
|
58
|
+
* explicitly. Generic `DATA-RESIDENCY-*` prefix is also accepted for adopters
|
|
59
|
+
* who declare per-region constraints under a unified naming scheme.
|
|
60
|
+
*/
|
|
61
|
+
export const HARD_REGULATORY_REGIME_PREFIXES = Object.freeze([
|
|
62
|
+
// ── Core US/EU/global frameworks ────────────────────────────────────
|
|
63
|
+
'GDPR', // EU General Data Protection Regulation
|
|
64
|
+
'HIPAA', // US Health Insurance Portability and Accountability Act
|
|
65
|
+
'SOC2', // Service Organization Control 2 (T1 / T2 variants)
|
|
66
|
+
'PCI-DSS', // Payment Card Industry Data Security Standard (L1-L4 variants)
|
|
67
|
+
'FedRAMP', // US Federal Risk and Authorization Management Program
|
|
68
|
+
'ISO-27001', // ISO/IEC 27001 Information Security Management Systems
|
|
69
|
+
// ── Regional data-residency frameworks ──────────────────────────────
|
|
70
|
+
'PIPL', // China Personal Information Protection Law
|
|
71
|
+
'PIPEDA', // Canadian Personal Information Protection and Electronic Documents Act
|
|
72
|
+
'SCHREMS-II', // EU Schrems II ruling (cross-border data transfers)
|
|
73
|
+
'DATA-RESIDENCY', // Generic regional residency declaration
|
|
74
|
+
// ── Regulated-industry rules ────────────────────────────────────────
|
|
75
|
+
'KYC', // Know-Your-Customer (financial services)
|
|
76
|
+
'AML', // Anti-Money Laundering (financial services)
|
|
77
|
+
'GLBA', // Gramm-Leach-Bliley Act (US financial privacy)
|
|
78
|
+
'SOX', // Sarbanes-Oxley Act (US public-company audit)
|
|
79
|
+
'NERC-CIP', // North American Electric Reliability Corp. Critical Infrastructure Protection
|
|
80
|
+
'FDA-21CFR11', // FDA 21 CFR Part 11 (regulated healthcare/pharma device)
|
|
81
|
+
'CCPA', // California Consumer Privacy Act (state-level, regulator-enforced)
|
|
82
|
+
'CPRA', // California Privacy Rights Act (CCPA successor)
|
|
83
|
+
]);
|
|
84
|
+
/**
|
|
85
|
+
* Predicate: returns true iff the regime ID matches a hard-regulatory prefix
|
|
86
|
+
* per the OQ-5 scope whitelist.
|
|
87
|
+
*
|
|
88
|
+
* Comparison is case-insensitive on the regime ID (`HIPAA` and `hipaa` both
|
|
89
|
+
* match) but the canonical prefixes are uppercase by convention.
|
|
90
|
+
*
|
|
91
|
+
* **Boundary guard (AISDLC-316 round-2 code-review fix)**: a match requires the
|
|
92
|
+
* prefix to be either the WHOLE regime id OR followed by a separator
|
|
93
|
+
* (`-`, `_`, `:`, `.`) or a digit (tier-variant pattern). This prevents
|
|
94
|
+
* collision-prone soft regimes like `SOXophone`, `AMLET`, `KYCS-internal`,
|
|
95
|
+
* `HIPAAphobia` from being mis-classified as hard-regulatory by a naive
|
|
96
|
+
* `startsWith` check. Tier-variants like `SOC2-T2`, `PCI-DSS-L1`,
|
|
97
|
+
* `FedRAMP-Moderate`, `ISO-27001:2022`, `SOC2T2` (digit boundary) still match.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* isHardRegulatoryRegime('HIPAA') // true
|
|
101
|
+
* isHardRegulatoryRegime('SOC2-T2') // true (SOC2 prefix + '-' boundary)
|
|
102
|
+
* isHardRegulatoryRegime('PCI-DSS-L1') // true (PCI-DSS prefix + '-' boundary)
|
|
103
|
+
* isHardRegulatoryRegime('FedRAMP-Moderate')// true (FedRAMP prefix + '-' boundary)
|
|
104
|
+
* isHardRegulatoryRegime('ISO-27001:2022') // true (ISO-27001 prefix + ':' boundary)
|
|
105
|
+
* isHardRegulatoryRegime('clean-code') // false (soft / out of scope)
|
|
106
|
+
* isHardRegulatoryRegime('team-style') // false (soft / out of scope)
|
|
107
|
+
* isHardRegulatoryRegime('SOXophone') // false (SOX prefix but letter boundary)
|
|
108
|
+
* isHardRegulatoryRegime('AMLET') // false (AML prefix but letter boundary)
|
|
109
|
+
* isHardRegulatoryRegime('HIPAAphobia') // false (HIPAA prefix but letter boundary)
|
|
110
|
+
*/
|
|
111
|
+
export function isHardRegulatoryRegime(regimeId) {
|
|
112
|
+
if (typeof regimeId !== 'string' || regimeId.length === 0)
|
|
113
|
+
return false;
|
|
114
|
+
const upper = regimeId.toUpperCase();
|
|
115
|
+
return HARD_REGULATORY_REGIME_PREFIXES.some((prefix) => {
|
|
116
|
+
const upperPrefix = prefix.toUpperCase();
|
|
117
|
+
if (!upper.startsWith(upperPrefix))
|
|
118
|
+
return false;
|
|
119
|
+
// Whole-id match → accept.
|
|
120
|
+
if (upper.length === upperPrefix.length)
|
|
121
|
+
return true;
|
|
122
|
+
// Boundary char must be a separator or a digit (tier/version variant).
|
|
123
|
+
// Letters following the prefix would be a soft-regime collision (e.g.
|
|
124
|
+
// SOXophone, AMLET, HIPAAphobia) and MUST NOT match.
|
|
125
|
+
const next = upper.charCodeAt(upperPrefix.length);
|
|
126
|
+
// '-' = 45, '_' = 95, ':' = 58, '.' = 46, '0'-'9' = 48-57
|
|
127
|
+
return (next === 45 || // '-'
|
|
128
|
+
next === 95 || // '_'
|
|
129
|
+
next === 58 || // ':'
|
|
130
|
+
next === 46 || // '.'
|
|
131
|
+
(next >= 48 && next <= 57) // digit
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Validate a list of declared compliance regimes against the OQ-5 hard-regulatory
|
|
137
|
+
* scope. Used at declaration time (DID load, RFC-0022 posture parse) to reject
|
|
138
|
+
* soft regimes before they reach the Eρ₅ scoring path.
|
|
139
|
+
*
|
|
140
|
+
* Per RFC-0009 OQ-5 sub-decision: soft/advisory regimes are NOT in scope and
|
|
141
|
+
* MUST be filtered at declaration time. Internal best-practices, code style,
|
|
142
|
+
* and team conventions belong to other mechanisms (code review, lint).
|
|
143
|
+
*
|
|
144
|
+
* @param regimes - Declared regime IDs from a soul's `complianceRegimes`
|
|
145
|
+
* field or an RFC-0022 `CompliancePosture.spec.regimes[].id`.
|
|
146
|
+
* @returns Validation result with split accepted/rejected lists.
|
|
147
|
+
*/
|
|
148
|
+
export function validateComplianceRegimes(regimes) {
|
|
149
|
+
if (!regimes || regimes.length === 0) {
|
|
150
|
+
return { valid: true, accepted: [], rejected: [] };
|
|
151
|
+
}
|
|
152
|
+
const accepted = [];
|
|
153
|
+
const rejected = [];
|
|
154
|
+
for (const r of regimes) {
|
|
155
|
+
if (isHardRegulatoryRegime(r))
|
|
156
|
+
accepted.push(r);
|
|
157
|
+
else
|
|
158
|
+
rejected.push(r);
|
|
159
|
+
}
|
|
160
|
+
return { valid: rejected.length === 0, accepted, rejected };
|
|
161
|
+
}
|
|
162
|
+
// ── Core scoring function ──────────────────────────────────────────────
|
|
163
|
+
/**
|
|
164
|
+
* Compute Eρ₅ Compliance Clearance for a work item.
|
|
165
|
+
*
|
|
166
|
+
* Algorithm (RFC-0009 §7.1 + §10 Phase 4):
|
|
167
|
+
* 1. If `ctx.enabled !== true` → return `er5 = 1` with `routingPath = 'disabled'`.
|
|
168
|
+
* 2. Build the regime set from:
|
|
169
|
+
* - Each soul in `affectedSoulIds`'s `perSoulRegimes` entry, OR
|
|
170
|
+
* the `__platform` sentinel entry when `affectedSoulIds` is empty.
|
|
171
|
+
* - All RFC-0022 posture regimes (if `ctx.posture` present).
|
|
172
|
+
* Filter through `isHardRegulatoryRegime` (defense-in-depth on the OQ-5 boundary).
|
|
173
|
+
* 3. If regime set is empty → `er5 = 1`, `routingPath = 'no-regimes'`.
|
|
174
|
+
* 4. Look up violations for `workItemId` in `ctx.violations` (case-insensitive).
|
|
175
|
+
* Filter to violations whose `regimeId` is in the regime set.
|
|
176
|
+
* - If any remain → `er5 = 0`, `routingPath = 'clearance-violated'`.
|
|
177
|
+
* - Else → `er5 = 1`, `routingPath = 'clearance-holds'`.
|
|
178
|
+
*
|
|
179
|
+
* @param workItemId - Canonical work item ID (e.g. `AISDLC-316`).
|
|
180
|
+
* @param affectedSoulIds - Soul IDs the work item targets (from
|
|
181
|
+
* `resolveAffectedSouls()`). Empty array =
|
|
182
|
+
* non-tessellated or substrate-only; uses
|
|
183
|
+
* `__platform` sentinel.
|
|
184
|
+
* @param ctx - Compliance clearance context (or undefined =
|
|
185
|
+
* treated as disabled).
|
|
186
|
+
* @returns Eρ₅ clearance result with routing audit trail.
|
|
187
|
+
*/
|
|
188
|
+
export function computeComplianceClearance(workItemId, affectedSoulIds, ctx) {
|
|
189
|
+
// ── Opt-in gate ────────────────────────────────────────────────
|
|
190
|
+
if (!ctx || ctx.enabled !== true) {
|
|
191
|
+
return {
|
|
192
|
+
er5: 1,
|
|
193
|
+
routingPath: 'disabled',
|
|
194
|
+
checkedRegimes: [],
|
|
195
|
+
violations: [],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
// ── Build the regime set ───────────────────────────────────────
|
|
199
|
+
// Set keys are stored as UPPER-CASE canonical form so that case-collisions
|
|
200
|
+
// between declaration and violation reporting do not silently drop a
|
|
201
|
+
// gating violation. AISDLC-316 round-2 code-review MAJOR #1 fix: previously
|
|
202
|
+
// a soul declaring `['hipaa']` and an enforcement plugin asserting
|
|
203
|
+
// `regimeId: 'HIPAA'` failed the `regimeSet.has(v.regimeId)` lookup
|
|
204
|
+
// (case-sensitive `Set.has`) → violation silently dropped → composite NOT
|
|
205
|
+
// gated. Normalising both sides to uppercase is the defense.
|
|
206
|
+
// `displayCheckedRegimes` preserves the original (first-seen) casing so
|
|
207
|
+
// the audit trail shown in `checkedRegimes` stays human-friendly.
|
|
208
|
+
const regimeSet = new Set();
|
|
209
|
+
const displayCheckedRegimes = [];
|
|
210
|
+
function addRegime(rawId) {
|
|
211
|
+
if (!isHardRegulatoryRegime(rawId))
|
|
212
|
+
return;
|
|
213
|
+
const upper = rawId.toUpperCase();
|
|
214
|
+
if (regimeSet.has(upper))
|
|
215
|
+
return;
|
|
216
|
+
regimeSet.add(upper);
|
|
217
|
+
displayCheckedRegimes.push(rawId);
|
|
218
|
+
}
|
|
219
|
+
// Soul-declared regimes — use affectedSoulIds when present, else __platform.
|
|
220
|
+
const soulIdsToCheck = affectedSoulIds.length === 0 ? ['__platform'] : affectedSoulIds;
|
|
221
|
+
for (const soulId of soulIdsToCheck) {
|
|
222
|
+
const entry = ctx.perSoulRegimes.find((e) => e.soulId === soulId);
|
|
223
|
+
if (!entry)
|
|
224
|
+
continue;
|
|
225
|
+
for (const r of entry.regimes) {
|
|
226
|
+
addRegime(r);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// RFC-0022 posture regimes — added across the union.
|
|
230
|
+
if (ctx.posture && ctx.posture.length > 0) {
|
|
231
|
+
for (const posture of ctx.posture) {
|
|
232
|
+
for (const regime of posture.spec.regimes ?? []) {
|
|
233
|
+
addRegime(regime.id);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const checkedRegimes = displayCheckedRegimes;
|
|
238
|
+
// ── No regimes apply ───────────────────────────────────────────
|
|
239
|
+
if (checkedRegimes.length === 0) {
|
|
240
|
+
return {
|
|
241
|
+
er5: 1,
|
|
242
|
+
routingPath: 'no-regimes',
|
|
243
|
+
checkedRegimes: [],
|
|
244
|
+
violations: [],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
// ── Look up violations for this work item ──────────────────────
|
|
248
|
+
// Violation regimeId is normalised to UPPER-CASE before lookup so that a
|
|
249
|
+
// soul declaring `['hipaa']` matches an enforcement plugin asserting
|
|
250
|
+
// `regimeId: 'HIPAA'` (and vice versa). AISDLC-316 round-2 fix.
|
|
251
|
+
const normalizedId = workItemId.toLowerCase();
|
|
252
|
+
const entry = ctx.violations?.find((e) => e.id.toLowerCase() === normalizedId);
|
|
253
|
+
const applicableViolations = entry?.violations.filter((v) => typeof v.regimeId === 'string' && regimeSet.has(v.regimeId.toUpperCase())) ?? [];
|
|
254
|
+
if (applicableViolations.length > 0) {
|
|
255
|
+
return {
|
|
256
|
+
er5: 0,
|
|
257
|
+
routingPath: 'clearance-violated',
|
|
258
|
+
checkedRegimes,
|
|
259
|
+
violations: applicableViolations,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
er5: 1,
|
|
264
|
+
routingPath: 'clearance-holds',
|
|
265
|
+
checkedRegimes,
|
|
266
|
+
violations: [],
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=compliance-clearance.js.map
|
package/dist/config.js
CHANGED
|
@@ -46,6 +46,23 @@ export function loadConfig(configDir) {
|
|
|
46
46
|
continue;
|
|
47
47
|
}
|
|
48
48
|
const result = validateResource(doc);
|
|
49
|
+
if (result.skipped) {
|
|
50
|
+
// AISDLC-265 PR #474 review fix: skipped kinds get an INFO-level warning
|
|
51
|
+
// entry so typo'd canonical kinds (e.g. `kind: AutonomyPolcy` instead of
|
|
52
|
+
// `AutonomyPolicy`) don't silently disappear. The legitimate loader-private
|
|
53
|
+
// kinds (MaintainersList, SoulTrackMap) appear in this list too, but
|
|
54
|
+
// operators can recognise them and ignore — the dangerous case before the
|
|
55
|
+
// fix was a silent drop with NO surface, which let security-relevant
|
|
56
|
+
// misconfigurations (typo'd AutonomyPolicy → no policy loaded → permissive
|
|
57
|
+
// defaults) ship undetected. See docs/operations/schema-extensions.md.
|
|
58
|
+
const docKind = doc.kind;
|
|
59
|
+
const kindStr = typeof docKind === 'string' ? docKind : String(docKind);
|
|
60
|
+
warnings.push({
|
|
61
|
+
file,
|
|
62
|
+
error: `unknown kind '${kindStr}' — skipped (loader-private convention or typo of canonical kind?)`,
|
|
63
|
+
});
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
49
66
|
if (!result.valid) {
|
|
50
67
|
const msgs = (result.errors ?? []).map((e) => `${e.path}: ${e.message}`).join('; ');
|
|
51
68
|
// Forward-looking schemas are common during incremental adoption
|
package/dist/cost-tracker.d.ts
CHANGED
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
* and tracks budget status.
|
|
4
4
|
*
|
|
5
5
|
* RFC reference: Lines 618-720 (cost tracking).
|
|
6
|
+
*
|
|
7
|
+
* RFC-0019 §10 / AISDLC-337: embeddingTokens line item added via
|
|
8
|
+
* recordEmbeddingCost(). Embedding costs are recorded with
|
|
9
|
+
* pipelineType='embeddingTokens' and do NOT decrement SubscriptionLedger
|
|
10
|
+
* window quota when adapter billingModel='pay-per-token' (OQ-7 re-walkthrough).
|
|
6
11
|
*/
|
|
7
12
|
import type { StateStore } from './state/store.js';
|
|
8
13
|
import type { CostLedgerEntry } from './state/types.js';
|
|
14
|
+
import type { EmbeddingCostRecord } from './embedding/types.js';
|
|
9
15
|
export interface CostSummary {
|
|
10
16
|
totalCostUsd: number;
|
|
11
17
|
totalTokens: number;
|
|
@@ -71,5 +77,21 @@ export declare class CostTracker {
|
|
|
71
77
|
* Get cost time series at a given granularity.
|
|
72
78
|
*/
|
|
73
79
|
getCostTimeSeries(granularity?: 'day' | 'week' | 'month', since?: string): CostTimeSeriesPoint[];
|
|
80
|
+
/**
|
|
81
|
+
* Record an embedding cost event per RFC-0019 §10 / AISDLC-337.
|
|
82
|
+
*
|
|
83
|
+
* Records a 'embeddingTokens' line item in the cost ledger with full
|
|
84
|
+
* (provider, modelVersion, accountId, consumerLabel) attribution dimensions
|
|
85
|
+
* per OQ-6 re-walkthrough.
|
|
86
|
+
*
|
|
87
|
+
* When adapter.billingModel === 'pay-per-token', the cost is recorded
|
|
88
|
+
* but does NOT consume SubscriptionLedger window quota (OQ-7 re-walkthrough).
|
|
89
|
+
* When billingModel === 'subscription-quota', callers must separately update
|
|
90
|
+
* the SubscriptionLedger via the inputTokens/outputTokens mechanism.
|
|
91
|
+
*
|
|
92
|
+
* @param record - Embedding cost attribution data from the adapter.
|
|
93
|
+
* @param runId - Pipeline run ID for traceability (optional; defaults to 'embedding').
|
|
94
|
+
*/
|
|
95
|
+
recordEmbeddingCost(record: EmbeddingCostRecord, runId?: string): number;
|
|
74
96
|
}
|
|
75
97
|
//# sourceMappingURL=cost-tracker.d.ts.map
|
package/dist/cost-tracker.js
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* and tracks budget status.
|
|
4
4
|
*
|
|
5
5
|
* RFC reference: Lines 618-720 (cost tracking).
|
|
6
|
+
*
|
|
7
|
+
* RFC-0019 §10 / AISDLC-337: embeddingTokens line item added via
|
|
8
|
+
* recordEmbeddingCost(). Embedding costs are recorded with
|
|
9
|
+
* pipelineType='embeddingTokens' and do NOT decrement SubscriptionLedger
|
|
10
|
+
* window quota when adapter billingModel='pay-per-token' (OQ-7 re-walkthrough).
|
|
6
11
|
*/
|
|
7
12
|
import { DEFAULT_MODEL_COSTS, DEFAULT_COST_BUDGET_USD } from './defaults.js';
|
|
8
13
|
export class CostTracker {
|
|
@@ -173,5 +178,41 @@ export class CostTracker {
|
|
|
173
178
|
}
|
|
174
179
|
return [...buckets.values()].sort((a, b) => a.date.localeCompare(b.date));
|
|
175
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Record an embedding cost event per RFC-0019 §10 / AISDLC-337.
|
|
183
|
+
*
|
|
184
|
+
* Records a 'embeddingTokens' line item in the cost ledger with full
|
|
185
|
+
* (provider, modelVersion, accountId, consumerLabel) attribution dimensions
|
|
186
|
+
* per OQ-6 re-walkthrough.
|
|
187
|
+
*
|
|
188
|
+
* When adapter.billingModel === 'pay-per-token', the cost is recorded
|
|
189
|
+
* but does NOT consume SubscriptionLedger window quota (OQ-7 re-walkthrough).
|
|
190
|
+
* When billingModel === 'subscription-quota', callers must separately update
|
|
191
|
+
* the SubscriptionLedger via the inputTokens/outputTokens mechanism.
|
|
192
|
+
*
|
|
193
|
+
* @param record - Embedding cost attribution data from the adapter.
|
|
194
|
+
* @param runId - Pipeline run ID for traceability (optional; defaults to 'embedding').
|
|
195
|
+
*/
|
|
196
|
+
recordEmbeddingCost(record, runId = 'embedding') {
|
|
197
|
+
// Encoding convention for embeddingTokens line items:
|
|
198
|
+
// pipelineType = 'embeddingTokens' ← discriminates from LLM entries
|
|
199
|
+
// agentName = consumerLabel ← per-consumer attribution (OQ-6)
|
|
200
|
+
// model = provider@modelVersion ← identifies exact model snapshot
|
|
201
|
+
// inputTokens = tokens ← total embedding tokens consumed
|
|
202
|
+
// costUsd = pre-computed by adapter ← $0.02/1M for OpenAI small
|
|
203
|
+
// stageName = accountId (or 'self-hosted') ← per-credential attribution
|
|
204
|
+
const entry = {
|
|
205
|
+
runId,
|
|
206
|
+
agentName: record.consumerLabel,
|
|
207
|
+
pipelineType: 'embeddingTokens',
|
|
208
|
+
model: `${record.provider}@${record.modelVersion}`,
|
|
209
|
+
inputTokens: record.tokens,
|
|
210
|
+
outputTokens: 0,
|
|
211
|
+
totalTokens: record.tokens,
|
|
212
|
+
costUsd: record.costUsd,
|
|
213
|
+
stageName: record.accountId ?? 'self-hosted',
|
|
214
|
+
};
|
|
215
|
+
return this.store.saveCostEntry(entry);
|
|
216
|
+
}
|
|
176
217
|
}
|
|
177
218
|
//# sourceMappingURL=cost-tracker.js.map
|
package/dist/cycle-utils.js
CHANGED
|
@@ -6,9 +6,22 @@ import { NOTIFICATION_TITLES } from './defaults.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* Sanitize template content to prevent markdown/HTML injection.
|
|
8
8
|
* Strips HTML tags and limits length.
|
|
9
|
+
*
|
|
10
|
+
* A single-pass `<[^>]*>` strip is flagged by CodeQL as
|
|
11
|
+
* `js/incomplete-multi-character-sanitization` (alert #65) because a
|
|
12
|
+
* crafted string like `<scr<script>ipt>` leaves `<script>` after one pass.
|
|
13
|
+
* We iterate the replacement until stable so any re-introduced bad
|
|
14
|
+
* substring is eliminated on the next pass — making the sanitization
|
|
15
|
+
* demonstrably idempotent.
|
|
9
16
|
*/
|
|
10
17
|
function sanitizeTemplate(text) {
|
|
11
|
-
|
|
18
|
+
let prev;
|
|
19
|
+
let current = text;
|
|
20
|
+
do {
|
|
21
|
+
prev = current;
|
|
22
|
+
current = prev.replace(/<[^>]*>/g, '');
|
|
23
|
+
} while (current !== prev);
|
|
24
|
+
return current.slice(0, 2000);
|
|
12
25
|
}
|
|
13
26
|
/**
|
|
14
27
|
* Check for pipeline cycles and post notification if detected.
|
|
@@ -43,7 +43,11 @@ export class ExternalAdapter {
|
|
|
43
43
|
const exec = this.deps.exec ?? ((c, a) => execFileAsync(c, a));
|
|
44
44
|
let stdout;
|
|
45
45
|
try {
|
|
46
|
-
|
|
46
|
+
// Pass branchKey as positional $1 (not string-interpolated) so the shell
|
|
47
|
+
// quotes it safely — `cmd` is the operator's trusted hook, but branchKey
|
|
48
|
+
// is pipeline-derived and must not be able to inject (CodeQL
|
|
49
|
+
// js/shell-command-constructed-from-input).
|
|
50
|
+
const result = await exec('sh', ['-c', `${cmd} "$1"`, 'sh', branchKey]);
|
|
47
51
|
stdout = result.stdout;
|
|
48
52
|
}
|
|
49
53
|
catch (err) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default embedding adapter: openai-text-embedding-3-small per RFC-0019 §7.
|
|
3
|
+
*
|
|
4
|
+
* Model: text-embedding-3-small (OpenAI, snapshot 2024-01-25)
|
|
5
|
+
* Dimensions: 1536
|
|
6
|
+
* Max input: 8191 tokens
|
|
7
|
+
* Batch: up to 2048 inputs per call
|
|
8
|
+
* Billing: pay-per-token at ~$0.02 / 1M tokens
|
|
9
|
+
*
|
|
10
|
+
* Why text-embedding-3-small over -large:
|
|
11
|
+
* At $0.02/1M tokens, a 10K-token corpus re-embed costs ~$0.0002. The -large
|
|
12
|
+
* variant is 6.5x more expensive for marginal quality improvement on the
|
|
13
|
+
* short-text drift detection use case (RFC-0009 OQ-6). Adopters with
|
|
14
|
+
* quality-sensitive use cases MAY register the -large variant.
|
|
15
|
+
*
|
|
16
|
+
* Why snapshot 2024-01-25:
|
|
17
|
+
* Most recent stable snapshot as of RFC-0019 authoring. Pinning the snapshot
|
|
18
|
+
* date makes adapter upgrades a code change (visible in PR review) rather
|
|
19
|
+
* than a silent provider-side rollout. OpenAI has silently changed
|
|
20
|
+
* text-embedding-ada-002 behavior in the past — explicit pinning prevents that.
|
|
21
|
+
*/
|
|
22
|
+
import type { EmbeddingAdapter, EmbeddingAvailability, EmbeddingCapabilities, EmbeddingRequires } from '../types.js';
|
|
23
|
+
import type { EmbeddingCostRecord } from '../types.js';
|
|
24
|
+
/**
|
|
25
|
+
* Callback invoked by the adapter after each embed() / embedBatch() call
|
|
26
|
+
* with cost attribution data. The orchestrator wires this to CostTracker.
|
|
27
|
+
* Decoupled from CostTracker directly so the adapter can be unit-tested
|
|
28
|
+
* without a StateStore dependency.
|
|
29
|
+
*/
|
|
30
|
+
export type EmbeddingCostCallback = (record: EmbeddingCostRecord) => void;
|
|
31
|
+
export declare class OpenAITextEmbedding3Small implements EmbeddingAdapter {
|
|
32
|
+
readonly name = "openai-text-embedding-3-small";
|
|
33
|
+
readonly modelId = "text-embedding-3-small";
|
|
34
|
+
readonly modelVersion = "2024-01-25";
|
|
35
|
+
readonly dimensions = 1536;
|
|
36
|
+
readonly capabilities: EmbeddingCapabilities;
|
|
37
|
+
readonly requires: EmbeddingRequires;
|
|
38
|
+
/** Optional cost-tracking callback. Set by the orchestrator after adapter instantiation. */
|
|
39
|
+
private costCallback?;
|
|
40
|
+
constructor(costCallback?: EmbeddingCostCallback);
|
|
41
|
+
/**
|
|
42
|
+
* Wire a cost-tracking callback after construction.
|
|
43
|
+
* Called by the orchestrator when it has a CostTracker available.
|
|
44
|
+
*/
|
|
45
|
+
setCostCallback(callback: EmbeddingCostCallback): void;
|
|
46
|
+
isAvailable(): Promise<EmbeddingAvailability>;
|
|
47
|
+
getAccountId(): Promise<string | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Embed a single text string.
|
|
50
|
+
*
|
|
51
|
+
* @param text - Source text. MUST be non-empty.
|
|
52
|
+
* @param consumerLabel - Cost attribution label (default: 'unspecified').
|
|
53
|
+
* Examples: 'rfc-0009-tessellation-drift', 'rfc-0008-ppa-similarity'.
|
|
54
|
+
*/
|
|
55
|
+
embed(text: string, consumerLabel?: string): Promise<number[]>;
|
|
56
|
+
/**
|
|
57
|
+
* Embed a batch of texts.
|
|
58
|
+
* OpenAI accepts up to 2048 inputs per call; this method chunks above that.
|
|
59
|
+
* Input order is preserved in the returned array.
|
|
60
|
+
*
|
|
61
|
+
* @param texts - Array of source texts. Each MUST be non-empty.
|
|
62
|
+
* @param consumerLabel - Cost attribution label (applies to all texts in batch).
|
|
63
|
+
*/
|
|
64
|
+
embedBatch(texts: string[], consumerLabel?: string): Promise<number[][]>;
|
|
65
|
+
/**
|
|
66
|
+
* Record a cost event via the cost callback.
|
|
67
|
+
* No-op when no callback is wired (e.g., in unit tests without a CostTracker).
|
|
68
|
+
*/
|
|
69
|
+
private _recordCost;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=openai-text-embedding-3-small.d.ts.map
|