@ai-sdlc/orchestrator 0.10.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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/init-features.d.ts +301 -2
- package/dist/cli/commands/init-features.js +634 -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/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 +171 -30
- 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/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 +252 -40
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -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/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 +10 -8
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC-0030 Phase 4 — Tier 2 significance threshold, SA resonance filter,
|
|
3
|
+
* flooding detection (z-score on rolling per-source baseline), residency-
|
|
4
|
+
* violation gate.
|
|
5
|
+
*
|
|
6
|
+
* This module operates on Phase 3 `DemandCluster[]` output and produces:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Tier 2 significance gate** (RFC-0030 §8): clusters must meet
|
|
9
|
+
* `minSignalCount` + `minUniqueSources` + `minTier1SignalCount` +
|
|
10
|
+
* `minClusterAgeDays` before they qualify for D1 scoring. Below-threshold
|
|
11
|
+
* clusters are marked `monitored` (not silently dropped — they remain
|
|
12
|
+
* visible for operator review).
|
|
13
|
+
*
|
|
14
|
+
* 2. **SA resonance filter** (RFC-0030 §9 + RFC-0029 Principle 4): clusters
|
|
15
|
+
* are bucketed by SA resonance score against the current Soul DID:
|
|
16
|
+
* - `>= fullWeight` → `full` (no D1 weight discount)
|
|
17
|
+
* - `>= discounted` → `discounted` (D1 weight × 0.7)
|
|
18
|
+
* - `>= excluded` (exclusive) → `low-sa-review` (D1 weight × 0.3 +
|
|
19
|
+
* Decision logged for Product Lead batch review per AC #3)
|
|
20
|
+
* - `<= excluded` (== 0.0) → `out-of-scope` (excluded from D1;
|
|
21
|
+
* logged as out-of-scope demand)
|
|
22
|
+
*
|
|
23
|
+
* 3. **OQ-13.5 flooding detection (AISDLC-433 v0.3 refinement)** — REPLACES
|
|
24
|
+
* the previous fixed-multiplier detector with z-score on the same
|
|
25
|
+
* per-source rolling baseline:
|
|
26
|
+
* - For every source in the detection window, compute its
|
|
27
|
+
* `windowCount` (signals in the last `windowMinutes`) and compare
|
|
28
|
+
* to the per-source baseline `{mean, stddev}` computed from the
|
|
29
|
+
* rolling `baselineDays`-day history.
|
|
30
|
+
* - Trigger condition (RFC-0030 §13.5):
|
|
31
|
+
* `windowCount > (mean + zScoreThreshold × stddev)`
|
|
32
|
+
* AND `uniqueSources_in_window < minUniqueSourcesForSuspicion`
|
|
33
|
+
* → emit `Decision: signal-flooding-detected`.
|
|
34
|
+
* - **Cold-start handling** (AC #4): when the rolling baseline has
|
|
35
|
+
* fewer than `baselineDays` of history, the detector returns the
|
|
36
|
+
* `calibrating` status and emits NO Decision — Tier 2 significance
|
|
37
|
+
* is the sole defense during the calibration window.
|
|
38
|
+
* - **Quarantine** (AC #6, #7): flooding signals are tagged
|
|
39
|
+
* `quarantined: true` with an `expiresAt` timestamp; quarantined
|
|
40
|
+
* signals do NOT feed D1 (D1 path filters them out). Default
|
|
41
|
+
* duration 24h; per-org `flooding.quarantineDurationHours`
|
|
42
|
+
* override. Auto-expiry releases signals back to D1 candidacy.
|
|
43
|
+
* - **Operator one-click unquarantine** (AC #8, #9): the surface
|
|
44
|
+
* composes with RFC-0023 Blockers pane via the `QuarantineStore`
|
|
45
|
+
* API + the `unquarantineFlooded()` helper. Unquarantine emits
|
|
46
|
+
* `Decision: signal-flooding-false-positive` referencing the
|
|
47
|
+
* original flooding Decision (feedback signal for v2
|
|
48
|
+
* reputation-weighting calibration).
|
|
49
|
+
* Pipeline NEVER halts on flooding (G0 non-blocking contract).
|
|
50
|
+
*
|
|
51
|
+
* 4. **OQ-13.3 residency-violation gate** (adapter-level): per `checkSignalResidency`,
|
|
52
|
+
* a signal whose `region` falls outside the adopter's declared regime
|
|
53
|
+
* `allowedRegions` is refused at adapter level. Pipeline never halts.
|
|
54
|
+
*
|
|
55
|
+
* @module signal-ingestion/significance
|
|
56
|
+
*/
|
|
57
|
+
import type { DemandCluster } from './clustering.js';
|
|
58
|
+
import type { FloodingDetectionConfig, FloodingQuarantineConfig, SignalIngestionConfig, Tier2SignificanceThreshold, SaResonanceThresholds } from './config.js';
|
|
59
|
+
import type { RawSignal, SignalResidencyViolationDecision, SignalSourceName } from './types.js';
|
|
60
|
+
/**
|
|
61
|
+
* Outcome of running the Tier 2 significance gate on a single cluster.
|
|
62
|
+
* - `qualified` — meets all four threshold conditions; eligible for D1.
|
|
63
|
+
* - `monitored` — at least one condition unmet; cluster persisted but
|
|
64
|
+
* does NOT feed D1 (RFC-0030 §8).
|
|
65
|
+
*/
|
|
66
|
+
export type Tier2SignificanceState = 'qualified' | 'monitored';
|
|
67
|
+
/**
|
|
68
|
+
* Which threshold conditions failed, when the cluster is `monitored`.
|
|
69
|
+
* `[]` when the cluster is `qualified`.
|
|
70
|
+
*/
|
|
71
|
+
export interface Tier2SignificanceReasons {
|
|
72
|
+
signalCount: boolean;
|
|
73
|
+
uniqueSources: boolean;
|
|
74
|
+
tier1SignalCount: boolean;
|
|
75
|
+
clusterAgeDays: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Per RFC-0030 §9: cluster SA resonance bucket. The bucket directly determines
|
|
79
|
+
* the D1 weight multiplier (see `SA_WEIGHT_MULTIPLIERS`).
|
|
80
|
+
*
|
|
81
|
+
* - `full` — `saResonance >= fullWeight` (default 0.7). Full D1 weight.
|
|
82
|
+
* - `discounted` — `>= discounted` (default 0.4) and `< fullWeight`.
|
|
83
|
+
* D1 weight × 0.7.
|
|
84
|
+
* - `low-sa-review` — `> excluded` (default 0.0, exclusive) and `< discounted`.
|
|
85
|
+
* D1 weight × 0.3 + logged via Decision for Product Lead
|
|
86
|
+
* batch review (AC #3).
|
|
87
|
+
* - `out-of-scope` — `<= excluded` (default 0.0). Excluded from D1; logged
|
|
88
|
+
* as out-of-scope demand for separate triage.
|
|
89
|
+
* - `pending` — `cluster.saResonance` is `undefined` (Phase 3 default).
|
|
90
|
+
* Caller must populate `saResonance` before invoking
|
|
91
|
+
* the filter (typically via the Soul DID adapter); the
|
|
92
|
+
* bucket reports `pending` and the cluster is excluded
|
|
93
|
+
* from D1 by default (fail-closed).
|
|
94
|
+
*/
|
|
95
|
+
export type SaResonanceBucket = 'full' | 'discounted' | 'low-sa-review' | 'out-of-scope' | 'pending';
|
|
96
|
+
/** Per-bucket D1 weight multiplier per RFC-0030 §9. */
|
|
97
|
+
export declare const SA_WEIGHT_MULTIPLIERS: Readonly<Record<SaResonanceBucket, number>>;
|
|
98
|
+
/**
|
|
99
|
+
* A `DemandCluster` annotated with its Phase 4 verdict on significance + SA.
|
|
100
|
+
* The augmented record is what downstream Phase 5 D1 reformulation consumes.
|
|
101
|
+
*
|
|
102
|
+
* `eligibleForD1` is `true` IFF the cluster `qualified` for the significance
|
|
103
|
+
* threshold AND its SA bucket is NOT `out-of-scope` / `pending`.
|
|
104
|
+
*
|
|
105
|
+
* `d1WeightMultiplier` is the combined product of:
|
|
106
|
+
* - significance flag (qualified → 1.0, monitored → 0.0)
|
|
107
|
+
* - SA bucket multiplier (per SA_WEIGHT_MULTIPLIERS)
|
|
108
|
+
* Downstream Phase 5 multiplies this against the existing `baseWeight ×
|
|
109
|
+
* tierMultiplier × icpResonanceWeight × recencyDecay` chain.
|
|
110
|
+
*/
|
|
111
|
+
export interface SignificanceAssessedCluster {
|
|
112
|
+
cluster: DemandCluster;
|
|
113
|
+
tier2Significance: Tier2SignificanceState;
|
|
114
|
+
tier2Reasons: Tier2SignificanceReasons;
|
|
115
|
+
saResonanceBucket: SaResonanceBucket;
|
|
116
|
+
eligibleForD1: boolean;
|
|
117
|
+
d1WeightMultiplier: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Emitted when a cluster's SA resonance bucket is `low-sa-review` per AC #3 —
|
|
121
|
+
* the demand is real but adjacent-to-soul, and Product Lead should review it
|
|
122
|
+
* (NOT silently dropped). Routes via the RFC-0035 G0 catalog.
|
|
123
|
+
*/
|
|
124
|
+
export interface SignalLowSaForReviewDecision {
|
|
125
|
+
type: 'Decision';
|
|
126
|
+
decision: 'signal-low-sa-for-review';
|
|
127
|
+
clusterId: string;
|
|
128
|
+
saResonance: number;
|
|
129
|
+
signalCount: number;
|
|
130
|
+
message: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Emitted when the SA bucket is `out-of-scope` (saResonance <= excluded
|
|
134
|
+
* threshold, default 0.0). Per RFC-0030 §9 the demand is logged for separate
|
|
135
|
+
* triage rather than fed into D1. Distinct from `low-sa-review` so operators
|
|
136
|
+
* can prioritise reviewing soul-adjacent demand over fully-out-of-scope
|
|
137
|
+
* demand.
|
|
138
|
+
*/
|
|
139
|
+
export interface SignalOutOfScopeDecision {
|
|
140
|
+
type: 'Decision';
|
|
141
|
+
decision: 'signal-out-of-scope';
|
|
142
|
+
clusterId: string;
|
|
143
|
+
saResonance: number;
|
|
144
|
+
signalCount: number;
|
|
145
|
+
message: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Detection status returned by `detectFlooding()`.
|
|
149
|
+
* - `flooded` — the trigger condition fired; a Decision was emitted.
|
|
150
|
+
* - `clean` — detector ran, baseline sufficient, no trigger.
|
|
151
|
+
* - `calibrating` — rolling baseline has <`baselineDays` of history; no
|
|
152
|
+
* Decisions emitted (Tier 2 significance is sole defense
|
|
153
|
+
* during calibration). Per AC #4.
|
|
154
|
+
* - `empty-window`— the detection window contains zero signals.
|
|
155
|
+
*/
|
|
156
|
+
export type FloodingDetectionStatus = 'flooded' | 'clean' | 'calibrating' | 'empty-window';
|
|
157
|
+
/**
|
|
158
|
+
* Z-score detector output for one detection window.
|
|
159
|
+
*
|
|
160
|
+
* `status` describes the outcome (cold-start / clean / flooded / empty).
|
|
161
|
+
* `decision` is populated iff `status === 'flooded'`; it's the Decision the
|
|
162
|
+
* caller routes to the RFC-0035 catalog.
|
|
163
|
+
*/
|
|
164
|
+
export interface FloodingDetectionResult {
|
|
165
|
+
status: FloodingDetectionStatus;
|
|
166
|
+
/** Populated when `status === 'flooded'`. */
|
|
167
|
+
decision?: SignalFloodingDetectedDecision;
|
|
168
|
+
/** Number of signals in the detection window. */
|
|
169
|
+
signalCount: number;
|
|
170
|
+
/** Distinct sources in the detection window. */
|
|
171
|
+
uniqueSources: number;
|
|
172
|
+
/** Days of baseline data the detector had access to. */
|
|
173
|
+
baselineDaysObserved: number;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Per-source baseline samples. Each entry is the daily signal-count
|
|
177
|
+
* observation for that source over the rolling baseline window. The detector
|
|
178
|
+
* computes `{mean, stddev}` from the samples.
|
|
179
|
+
*
|
|
180
|
+
* Operators wire this map from their historical-data substrate; persistence
|
|
181
|
+
* is the caller's responsibility (orchestrator events.jsonl roll-up, etc.).
|
|
182
|
+
* AISDLC-433 ships the detection algorithm; the substrate plumbing is the
|
|
183
|
+
* caller's domain.
|
|
184
|
+
*/
|
|
185
|
+
export type PerSourceBaseline = Record<string, number[]>;
|
|
186
|
+
/**
|
|
187
|
+
* Per-source baseline statistic. Internal — exposed for unit testing.
|
|
188
|
+
*/
|
|
189
|
+
export interface BaselineStat {
|
|
190
|
+
/** Mean of the samples. */
|
|
191
|
+
mean: number;
|
|
192
|
+
/** Population standard deviation of the samples. */
|
|
193
|
+
stddev: number;
|
|
194
|
+
/** Number of samples (days observed). */
|
|
195
|
+
sampleCount: number;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Source-level flooding flag — surfaces in the Decision so the operator
|
|
199
|
+
* sees which source tripped the trigger.
|
|
200
|
+
*/
|
|
201
|
+
export interface FloodingSourceFlag {
|
|
202
|
+
/** Source identifier (adapter name when available, falls back to sourceId prefix). */
|
|
203
|
+
sourceId: string;
|
|
204
|
+
/** Z-score for this source's window count vs its baseline. */
|
|
205
|
+
zScore: number;
|
|
206
|
+
/** Window signal count. */
|
|
207
|
+
windowCount: number;
|
|
208
|
+
/** Baseline mean signals/day. */
|
|
209
|
+
baselineMean: number;
|
|
210
|
+
/** Baseline standard deviation signals/day. */
|
|
211
|
+
baselineStddev: number;
|
|
212
|
+
/** Days of baseline data observed for this source. */
|
|
213
|
+
baselineDays: number;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Decision emitted when the z-score detector trips per RFC-0030 §13.5
|
|
217
|
+
* (AISDLC-433 v0.3 refinement). Replaces the legacy multi-indicator Decision.
|
|
218
|
+
*/
|
|
219
|
+
export interface SignalFloodingDetectedDecision {
|
|
220
|
+
type: 'Decision';
|
|
221
|
+
decision: 'signal-flooding-detected';
|
|
222
|
+
/** Stable Decision ID — the operator unquarantine path references this. */
|
|
223
|
+
decisionId: string;
|
|
224
|
+
/** Detection-window cutoff used when this Decision was emitted. */
|
|
225
|
+
detectedAt: string;
|
|
226
|
+
/** Total signal count in the detection window. */
|
|
227
|
+
signalCount: number;
|
|
228
|
+
/** Number of distinct sources contributing to the window. */
|
|
229
|
+
uniqueSources: number;
|
|
230
|
+
/** Per-source z-scores + baseline stats for sources that exceeded threshold. */
|
|
231
|
+
flaggedSources: FloodingSourceFlag[];
|
|
232
|
+
/** Source IDs of signals quarantined as a result of this Decision (empty when quarantine disabled). */
|
|
233
|
+
quarantinedSourceIds: string[];
|
|
234
|
+
/** Quarantine duration applied (hours; 0 when disabled). */
|
|
235
|
+
quarantineDurationHours: number;
|
|
236
|
+
message: string;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Re-export of the live default for back-compat with the previous shipped
|
|
240
|
+
* surface. Phase 4 originally exposed `DEFAULT_FLOODING_DETECTION_CONFIG`
|
|
241
|
+
* as a frozen object; AISDLC-433 routes the same name to the v0.3 z-score
|
|
242
|
+
* defaults (sourced from `DEFAULT_SIGNAL_INGESTION_CONFIG.flooding.detection`)
|
|
243
|
+
* so downstream callers continue to import a working default object.
|
|
244
|
+
*/
|
|
245
|
+
export declare const DEFAULT_FLOODING_DETECTION_CONFIG: FloodingDetectionConfig;
|
|
246
|
+
/**
|
|
247
|
+
* Apply the RFC-0030 §8 Tier 2 significance threshold to a single cluster.
|
|
248
|
+
*
|
|
249
|
+
* The four conditions are evaluated independently so reasons can be reported
|
|
250
|
+
* back; the cluster `qualified` IFF all four pass.
|
|
251
|
+
*
|
|
252
|
+
* `asOf` defaults to `new Date()` for cluster-age computation.
|
|
253
|
+
*/
|
|
254
|
+
export declare function assessTier2Significance(cluster: DemandCluster, threshold?: Tier2SignificanceThreshold, asOf?: Date): {
|
|
255
|
+
state: Tier2SignificanceState;
|
|
256
|
+
reasons: Tier2SignificanceReasons;
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Classify a cluster's SA resonance into one of five buckets per RFC-0030 §9.
|
|
260
|
+
*
|
|
261
|
+
* When `cluster.saResonance` is `undefined`, returns `pending` — the caller
|
|
262
|
+
* is expected to populate the field via the Soul DID adapter before
|
|
263
|
+
* invocation. Fail-closed: `pending` results in `eligibleForD1 = false`.
|
|
264
|
+
*/
|
|
265
|
+
export declare function classifySaResonance(cluster: DemandCluster, thresholds?: SaResonanceThresholds): SaResonanceBucket;
|
|
266
|
+
/**
|
|
267
|
+
* Options for `assessClusterSignificance()`.
|
|
268
|
+
*/
|
|
269
|
+
export interface AssessClusterSignificanceOptions {
|
|
270
|
+
config?: SignalIngestionConfig;
|
|
271
|
+
asOf?: Date;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Result returned by `assessClusterSignificance()`.
|
|
275
|
+
*/
|
|
276
|
+
export interface AssessClusterSignificanceResult {
|
|
277
|
+
assessments: SignificanceAssessedCluster[];
|
|
278
|
+
/**
|
|
279
|
+
* `signal-low-sa-for-review` Decisions emitted for clusters whose SA bucket
|
|
280
|
+
* is `low-sa-review` — surfaces low-SA demand to the catalog for Product
|
|
281
|
+
* Lead batch review (AC #3).
|
|
282
|
+
*/
|
|
283
|
+
lowSaDecisions: SignalLowSaForReviewDecision[];
|
|
284
|
+
/**
|
|
285
|
+
* `signal-out-of-scope` Decisions emitted for clusters whose SA bucket is
|
|
286
|
+
* `out-of-scope` — the demand is logged for separate triage rather than
|
|
287
|
+
* fed into D1.
|
|
288
|
+
*/
|
|
289
|
+
outOfScopeDecisions: SignalOutOfScopeDecision[];
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Apply Phase 4 significance + SA filter to a batch of `DemandCluster`s.
|
|
293
|
+
*
|
|
294
|
+
* The resulting `assessments` carry the final `d1WeightMultiplier` that
|
|
295
|
+
* Phase 5 multiplies into the D1 formula. Below-threshold clusters retain
|
|
296
|
+
* `eligibleForD1 = false` (not silently dropped) per RFC-0030 §8.
|
|
297
|
+
*
|
|
298
|
+
* `lowSaDecisions` are emitted for every cluster whose SA bucket is
|
|
299
|
+
* `low-sa-review` — these are the AC #3 "low-SA-but-high-volume" Decisions.
|
|
300
|
+
*/
|
|
301
|
+
export declare function assessClusterSignificance(clusters: DemandCluster[], options?: AssessClusterSignificanceOptions): AssessClusterSignificanceResult;
|
|
302
|
+
/**
|
|
303
|
+
* Options for `detectFlooding()`. The z-score detector REPLACES the
|
|
304
|
+
* multiplier-based path that shipped with AISDLC-346.
|
|
305
|
+
*/
|
|
306
|
+
export interface DetectFloodingOptions {
|
|
307
|
+
/**
|
|
308
|
+
* Detection thresholds. Defaults to the loaded config's `flooding.detection`
|
|
309
|
+
* block (or `DEFAULT_FLOODING_DETECTION_CONFIG` when no config supplied).
|
|
310
|
+
*/
|
|
311
|
+
config?: FloodingDetectionConfig;
|
|
312
|
+
/**
|
|
313
|
+
* Quarantine sub-config. When omitted defaults to the framework default
|
|
314
|
+
* (`enabled: true`, `durationHours: 24`). Used to populate the Decision's
|
|
315
|
+
* `quarantineDurationHours` field + the `QuarantineStore` entries.
|
|
316
|
+
*/
|
|
317
|
+
quarantineConfig?: FloodingQuarantineConfig;
|
|
318
|
+
/**
|
|
319
|
+
* Per-source rolling baseline. Each entry is a list of daily signal-count
|
|
320
|
+
* observations for that source over the rolling `baselineDays` window.
|
|
321
|
+
* The detector computes `{mean, stddev}` from the samples.
|
|
322
|
+
*
|
|
323
|
+
* Source key resolution (matches the pre-AISDLC-433 contract): prefers
|
|
324
|
+
* `metadata.adapterName`, falls back to the `sourceId` prefix before the
|
|
325
|
+
* first `-`.
|
|
326
|
+
*/
|
|
327
|
+
perSourceBaselines?: PerSourceBaseline;
|
|
328
|
+
/** Detection-window cutoff. Defaults to `new Date()`. */
|
|
329
|
+
asOf?: Date;
|
|
330
|
+
/**
|
|
331
|
+
* Optional quarantine store the detector writes to. When supplied, every
|
|
332
|
+
* flooded source has its signals tagged in the store. When omitted, the
|
|
333
|
+
* Decision still carries the `quarantinedSourceIds` list but no
|
|
334
|
+
* persistence happens — useful for pure unit testing.
|
|
335
|
+
*/
|
|
336
|
+
quarantineStore?: QuarantineStore;
|
|
337
|
+
/**
|
|
338
|
+
* Decision ID factory. Defaults to a UTC timestamp + 8-char random suffix.
|
|
339
|
+
* Tests override this to make assertions deterministic.
|
|
340
|
+
*/
|
|
341
|
+
generateDecisionId?: () => string;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Z-score detector — RFC-0030 §13.5 v0.3 trigger condition is
|
|
345
|
+
* `windowCount > (mean + zScoreThreshold × stddev) AND uniqueSources_in_window
|
|
346
|
+
* < minUniqueSourcesForSuspicion`.
|
|
347
|
+
*
|
|
348
|
+
* Returns a `FloodingDetectionResult` whose `status` discriminates the four
|
|
349
|
+
* outcomes (flooded / clean / calibrating / empty-window). The caller routes
|
|
350
|
+
* `result.decision` to the catalog when `status === 'flooded'`.
|
|
351
|
+
*
|
|
352
|
+
* Cold-start (AC #4): when the maximum per-source `baselineDays` observed
|
|
353
|
+
* across all in-window sources is < `config.baselineDays`, the detector
|
|
354
|
+
* returns `status: 'calibrating'` with no Decision — Tier 2 significance is
|
|
355
|
+
* the sole defense until the rolling baseline has built up.
|
|
356
|
+
*
|
|
357
|
+
* Quarantine (AC #6, #7): when `quarantineConfig.enabled`, every in-window
|
|
358
|
+
* signal from a flagged source is tagged in the supplied `quarantineStore`
|
|
359
|
+
* with `expiresAt = asOf + quarantineDurationHours`. Auto-expiry happens
|
|
360
|
+
* lazily on read (see `isSignalQuarantined()`).
|
|
361
|
+
*/
|
|
362
|
+
export declare function detectFlooding(signals: RawSignal[], options?: DetectFloodingOptions): FloodingDetectionResult;
|
|
363
|
+
/**
|
|
364
|
+
* Population-standard-deviation baseline stat. Exposed for unit testing.
|
|
365
|
+
* Returns `{mean: 0, stddev: 0, sampleCount: 0}` on empty input — caller's
|
|
366
|
+
* cold-start gate should prevent that from reaching `computeZScore()`.
|
|
367
|
+
*/
|
|
368
|
+
export declare function computeBaselineStat(samples: readonly number[]): BaselineStat;
|
|
369
|
+
/**
|
|
370
|
+
* Z-score of a single observation against `BaselineStat`. When `stddev === 0`
|
|
371
|
+
* (degenerate baseline — every sample identical), returns `+Infinity` if the
|
|
372
|
+
* observation exceeds the mean, `0` if it equals the mean — matches the
|
|
373
|
+
* statistical convention that any deviation from a zero-variance baseline is
|
|
374
|
+
* infinitely surprising. Exposed for unit testing.
|
|
375
|
+
*/
|
|
376
|
+
export declare function computeZScore(observation: number, stats: BaselineStat): number;
|
|
377
|
+
/**
|
|
378
|
+
* A single entry in the quarantine store. Created by `detectFlooding()` when
|
|
379
|
+
* a flooded source's signals are quarantined; consumed by:
|
|
380
|
+
* - The D1 path, to exclude quarantined signals from cluster scoring
|
|
381
|
+
* (`isSignalQuarantined()`).
|
|
382
|
+
* - The operator unquarantine flow (`unquarantineFlooded()`) for one-click
|
|
383
|
+
* release after a false-positive review.
|
|
384
|
+
* - The RFC-0023 Blockers pane, which renders pending Decisions + offers
|
|
385
|
+
* the unquarantine action.
|
|
386
|
+
*/
|
|
387
|
+
export interface QuarantineEntry {
|
|
388
|
+
/** Signal `sourceId` quarantined. */
|
|
389
|
+
sourceId: string;
|
|
390
|
+
/** Adapter-level source key (the key used by the detector + baselines). */
|
|
391
|
+
adapterSource: string;
|
|
392
|
+
/** Originating Decision ID. The false-positive Decision references this. */
|
|
393
|
+
decisionId: string;
|
|
394
|
+
quarantinedAt: Date;
|
|
395
|
+
/** When quarantine auto-expires. */
|
|
396
|
+
expiresAt: Date;
|
|
397
|
+
/** Free-form rationale (z-score + baseline stats). */
|
|
398
|
+
reason: string;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Minimal in-memory `QuarantineStore` implementation. Production deployments
|
|
402
|
+
* persist to events.jsonl + a sidecar quarantine state file; the in-memory
|
|
403
|
+
* shape is the contract the persistent implementation honours.
|
|
404
|
+
*
|
|
405
|
+
* The store is intentionally narrow: `quarantine`, `isQuarantined`,
|
|
406
|
+
* `getActiveEntries`, `getEntryByDecisionId`, `release`. No batch
|
|
407
|
+
* operations — operators unquarantine one Decision at a time per the
|
|
408
|
+
* RFC-0023 one-click model.
|
|
409
|
+
*/
|
|
410
|
+
export interface QuarantineStore {
|
|
411
|
+
/** Record a new quarantine entry. */
|
|
412
|
+
quarantine(entry: QuarantineEntry): void;
|
|
413
|
+
/** Whether a signal sourceId is currently quarantined (NOT auto-expired). */
|
|
414
|
+
isQuarantined(sourceId: string, asOf?: Date): boolean;
|
|
415
|
+
/**
|
|
416
|
+
* All currently-active quarantine entries (auto-expired entries excluded).
|
|
417
|
+
* Used by the TUI Blockers pane to render pending decisions.
|
|
418
|
+
*/
|
|
419
|
+
getActiveEntries(asOf?: Date): QuarantineEntry[];
|
|
420
|
+
/** Entries created by a specific Decision (for unquarantine routing). */
|
|
421
|
+
getEntryByDecisionId(decisionId: string): QuarantineEntry[];
|
|
422
|
+
/** Release all entries for the given Decision (idempotent). */
|
|
423
|
+
release(decisionId: string, releasedAt?: Date): QuarantineEntry[];
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Default in-memory store. Tests use this directly; production callers should
|
|
427
|
+
* implement a persistent variant satisfying the same interface.
|
|
428
|
+
*/
|
|
429
|
+
export declare class InMemoryQuarantineStore implements QuarantineStore {
|
|
430
|
+
private readonly entries;
|
|
431
|
+
private readonly released;
|
|
432
|
+
quarantine(entry: QuarantineEntry): void;
|
|
433
|
+
isQuarantined(sourceId: string, asOf?: Date): boolean;
|
|
434
|
+
getActiveEntries(asOf?: Date): QuarantineEntry[];
|
|
435
|
+
getEntryByDecisionId(decisionId: string): QuarantineEntry[];
|
|
436
|
+
release(decisionId: string, _releasedAt?: Date): QuarantineEntry[];
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Convenience: check whether a single `RawSignal` is currently quarantined by
|
|
440
|
+
* looking up its `sourceId` in the store. The D1 path calls this when
|
|
441
|
+
* computing `eligibleForD1` — quarantined signals are excluded from the
|
|
442
|
+
* D1(cluster) formula per AC #6.
|
|
443
|
+
*
|
|
444
|
+
* Returns `false` when no store is supplied (back-compat — pre-AISDLC-433
|
|
445
|
+
* D1 paths didn't have a quarantine layer to consult).
|
|
446
|
+
*/
|
|
447
|
+
export declare function isSignalQuarantined(signal: RawSignal, store: QuarantineStore | undefined, asOf?: Date): boolean;
|
|
448
|
+
/**
|
|
449
|
+
* Operator unquarantine Decision — emitted by `unquarantineFlooded()` per
|
|
450
|
+
* AC #9. References the original flooding Decision so the v2
|
|
451
|
+
* reputation-weighting layer can calibrate against false-positive feedback.
|
|
452
|
+
*/
|
|
453
|
+
export interface SignalFloodingFalsePositiveDecision {
|
|
454
|
+
type: 'Decision';
|
|
455
|
+
decision: 'signal-flooding-false-positive';
|
|
456
|
+
/** Stable ID for this false-positive Decision (audit trail). */
|
|
457
|
+
decisionId: string;
|
|
458
|
+
/** Original `signal-flooding-detected` Decision ID. */
|
|
459
|
+
originalDecisionId: string;
|
|
460
|
+
releasedAt: string;
|
|
461
|
+
/** Source IDs released back to D1 candidacy. */
|
|
462
|
+
releasedSourceIds: string[];
|
|
463
|
+
/** Free-form operator note explaining the false-positive call. */
|
|
464
|
+
operatorNote?: string;
|
|
465
|
+
message: string;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Options for `unquarantineFlooded()`.
|
|
469
|
+
*/
|
|
470
|
+
export interface UnquarantineFloodedOptions {
|
|
471
|
+
store: QuarantineStore;
|
|
472
|
+
/** Decision ID of the original `signal-flooding-detected` Decision. */
|
|
473
|
+
originalDecisionId: string;
|
|
474
|
+
/** Optional operator note (free-form rationale). */
|
|
475
|
+
operatorNote?: string;
|
|
476
|
+
/** Clock override for the `releasedAt` timestamp. */
|
|
477
|
+
asOf?: Date;
|
|
478
|
+
/** Decision-ID factory for the false-positive Decision (tests override). */
|
|
479
|
+
generateDecisionId?: () => string;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Operator one-click unquarantine — releases every signal entry tagged with
|
|
483
|
+
* `originalDecisionId` and emits the `signal-flooding-false-positive`
|
|
484
|
+
* Decision per RFC-0030 §13.5 + AC #8 + AC #9.
|
|
485
|
+
*
|
|
486
|
+
* Returns `null` when no active entries match the Decision ID (idempotent —
|
|
487
|
+
* the operator's second click on the same row is a no-op + null result).
|
|
488
|
+
*/
|
|
489
|
+
export declare function unquarantineFlooded(options: UnquarantineFloodedOptions): SignalFloodingFalsePositiveDecision | null;
|
|
490
|
+
/**
|
|
491
|
+
* Per-adopter regime declaration consumed by `checkSignalResidency`. Composes
|
|
492
|
+
* with RFC-0022 Compliance Posture per RFC-0030 OQ-13.3 — the adopter declares
|
|
493
|
+
* which regimes are active and the allowed regions per regime; signal-ingestion
|
|
494
|
+
* refuses signals from outside those regions.
|
|
495
|
+
*
|
|
496
|
+
* `regimes` is the active regime set (e.g. `['gdpr']`, `['hipaa', 'gdpr']`).
|
|
497
|
+
* `allowedRegionsByRegime` maps each regime to the set of region tags that
|
|
498
|
+
* regime permits (e.g. `gdpr: ['eu', 'gb']`, `hipaa: ['us']`). A signal is
|
|
499
|
+
* permitted IFF its `region` is in EVERY active regime's allowed-regions list.
|
|
500
|
+
*
|
|
501
|
+
* `allowedRegionsByRegime` keys not present in `regimes` are ignored (the
|
|
502
|
+
* regime isn't active for this adopter).
|
|
503
|
+
*
|
|
504
|
+
* `allowedRegionsByRegime` of `{}` (empty) for an active regime means NO
|
|
505
|
+
* regions are explicitly permitted → all signals are refused for that regime.
|
|
506
|
+
* Operators should declare regions OR remove the regime from the active set.
|
|
507
|
+
*/
|
|
508
|
+
export interface ResidencyRegimeDeclaration {
|
|
509
|
+
regimes: string[];
|
|
510
|
+
allowedRegionsByRegime: Record<string, string[]>;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Outcome of residency-check on a single signal.
|
|
514
|
+
* - `{ permitted: true }` — signal MAY pass; adapter may emit it.
|
|
515
|
+
* - `{ permitted: false, decision }` — signal MUST be refused; adapter logs
|
|
516
|
+
* the Decision + emits the regimeOverrides clarification task.
|
|
517
|
+
*/
|
|
518
|
+
export type SignalResidencyCheck = {
|
|
519
|
+
permitted: true;
|
|
520
|
+
} | {
|
|
521
|
+
permitted: false;
|
|
522
|
+
decision: SignalResidencyViolationDecision;
|
|
523
|
+
};
|
|
524
|
+
/**
|
|
525
|
+
* Check a single signal against the adopter's declared residency regimes.
|
|
526
|
+
*
|
|
527
|
+
* **Behaviour**:
|
|
528
|
+
* - When `declaration.regimes` is empty → signal is permitted (no regime
|
|
529
|
+
* constraints declared). Adopters not declaring a regime are not subject
|
|
530
|
+
* to residency gating.
|
|
531
|
+
* - When `signal.region` is `undefined` AND at least one regime is active →
|
|
532
|
+
* signal is permitted (the adapter didn't surface region metadata, which
|
|
533
|
+
* is treated as "not subject to gating" rather than "fails the gate" to
|
|
534
|
+
* avoid false-positives on adapters that don't yet plumb region — a
|
|
535
|
+
* visible-gap metric for the operator's regime config rollout).
|
|
536
|
+
* - When `signal.region` is present AND at least one active regime's
|
|
537
|
+
* `allowedRegions` does NOT include it → signal is REFUSED and the
|
|
538
|
+
* `Decision` records every regime that rejected the signal.
|
|
539
|
+
*
|
|
540
|
+
* The adapter SHOULD NOT call `fetchSignals` for refused signals — the
|
|
541
|
+
* return-value pattern lets the adapter short-circuit per-signal.
|
|
542
|
+
*/
|
|
543
|
+
export declare function checkSignalResidency(signal: RawSignal, declaration: ResidencyRegimeDeclaration, adapterName: SignalSourceName): SignalResidencyCheck;
|
|
544
|
+
/**
|
|
545
|
+
* Convenience wrapper: filter a batch of signals against the residency gate,
|
|
546
|
+
* returning the (possibly empty) list of permitted signals + the list of
|
|
547
|
+
* Decision records for refused signals. Adapter implementations can call this
|
|
548
|
+
* in their `fetchSignals()` body to enforce residency before returning.
|
|
549
|
+
*/
|
|
550
|
+
export declare function filterSignalsByResidency(signals: RawSignal[], declaration: ResidencyRegimeDeclaration, adapterName: SignalSourceName): {
|
|
551
|
+
permitted: RawSignal[];
|
|
552
|
+
decisions: SignalResidencyViolationDecision[];
|
|
553
|
+
};
|
|
554
|
+
//# sourceMappingURL=significance.d.ts.map
|