@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.
Files changed (156) hide show
  1. package/dist/adapters.d.ts +42 -3
  2. package/dist/adapters.js +133 -3
  3. package/dist/admission-composite.d.ts +112 -1
  4. package/dist/admission-composite.js +85 -4
  5. package/dist/admission-enrichment.d.ts +12 -3
  6. package/dist/admission-enrichment.js +64 -13
  7. package/dist/admission-score.d.ts +30 -0
  8. package/dist/admission-score.js +4 -1
  9. package/dist/backlog-adapter.d.ts +45 -0
  10. package/dist/backlog-adapter.js +145 -1
  11. package/dist/calibration.d.ts +81 -0
  12. package/dist/calibration.js +76 -0
  13. package/dist/cli/commands/init-features.d.ts +301 -2
  14. package/dist/cli/commands/init-features.js +634 -7
  15. package/dist/cli/commands/init-templates.d.ts +198 -1
  16. package/dist/cli/commands/init-templates.js +943 -1
  17. package/dist/cli/commands/init.d.ts +45 -0
  18. package/dist/cli/commands/init.js +147 -5
  19. package/dist/cli/commands/run.js +9 -1
  20. package/dist/cli/index.d.ts +11 -0
  21. package/dist/cli/index.js +42 -8
  22. package/dist/compliance/composer.d.ts +79 -0
  23. package/dist/compliance/composer.js +258 -0
  24. package/dist/compliance/errors.d.ts +64 -0
  25. package/dist/compliance/errors.js +85 -0
  26. package/dist/compliance/loader.d.ts +52 -0
  27. package/dist/compliance/loader.js +124 -0
  28. package/dist/compliance/types.d.ts +184 -0
  29. package/dist/compliance/types.js +41 -0
  30. package/dist/compliance-clearance.d.ts +269 -0
  31. package/dist/compliance-clearance.js +269 -0
  32. package/dist/config.js +17 -0
  33. package/dist/cost-tracker.d.ts +22 -0
  34. package/dist/cost-tracker.js +41 -0
  35. package/dist/database/adapters/external.js +5 -1
  36. package/dist/embedding/adapters/openai-text-embedding-3-small.d.ts +71 -0
  37. package/dist/embedding/adapters/openai-text-embedding-3-small.js +190 -0
  38. package/dist/embedding/consumers/tessellation-drift.d.ts +74 -0
  39. package/dist/embedding/consumers/tessellation-drift.js +76 -0
  40. package/dist/embedding/cross-provider.d.ts +78 -0
  41. package/dist/embedding/cross-provider.js +75 -0
  42. package/dist/embedding/deprecation.d.ts +151 -0
  43. package/dist/embedding/deprecation.js +229 -0
  44. package/dist/embedding/errors.d.ts +90 -0
  45. package/dist/embedding/errors.js +150 -0
  46. package/dist/embedding/index.d.ts +29 -0
  47. package/dist/embedding/index.js +24 -0
  48. package/dist/embedding/pipeline-load.d.ts +146 -0
  49. package/dist/embedding/pipeline-load.js +178 -0
  50. package/dist/embedding/registry.d.ts +45 -0
  51. package/dist/embedding/registry.js +61 -0
  52. package/dist/embedding/stale-vector.d.ts +110 -0
  53. package/dist/embedding/stale-vector.js +92 -0
  54. package/dist/embedding/storage/index.d.ts +51 -0
  55. package/dist/embedding/storage/index.js +43 -0
  56. package/dist/embedding/storage/jsonl-backend.d.ts +150 -0
  57. package/dist/embedding/storage/jsonl-backend.js +332 -0
  58. package/dist/embedding/storage/types.d.ts +135 -0
  59. package/dist/embedding/storage/types.js +13 -0
  60. package/dist/embedding/types.d.ts +180 -0
  61. package/dist/embedding/types.js +10 -0
  62. package/dist/execute.d.ts +29 -2
  63. package/dist/execute.js +171 -30
  64. package/dist/index.d.ts +14 -2
  65. package/dist/index.js +19 -2
  66. package/dist/journey/inheritance-validator.d.ts +396 -0
  67. package/dist/journey/inheritance-validator.js +370 -0
  68. package/dist/journey/state-id-drift-rule.d.ts +137 -0
  69. package/dist/journey/state-id-drift-rule.js +245 -0
  70. package/dist/journey-sa2-router.d.ts +395 -0
  71. package/dist/journey-sa2-router.js +308 -0
  72. package/dist/runners/review-agent.js +6 -2
  73. package/dist/runners/runner-registry.d.ts +36 -0
  74. package/dist/runners/runner-registry.js +90 -0
  75. package/dist/runtime/attestations.d.ts +173 -13
  76. package/dist/runtime/attestations.js +252 -40
  77. package/dist/runtime/index.d.ts +1 -1
  78. package/dist/runtime/index.js +1 -1
  79. package/dist/sa-scoring/layer3-llm.js +6 -1
  80. package/dist/sa-scoring/revision-proposal-config.d.ts +178 -0
  81. package/dist/sa-scoring/revision-proposal-config.js +198 -0
  82. package/dist/sa-scoring/revision-proposal.d.ts +285 -0
  83. package/dist/sa-scoring/revision-proposal.js +417 -0
  84. package/dist/signal-ingestion/adapters/community-thread.d.ts +43 -0
  85. package/dist/signal-ingestion/adapters/community-thread.js +55 -0
  86. package/dist/signal-ingestion/adapters/in-app-feedback.d.ts +67 -0
  87. package/dist/signal-ingestion/adapters/in-app-feedback.js +51 -0
  88. package/dist/signal-ingestion/adapters/manual.d.ts +78 -0
  89. package/dist/signal-ingestion/adapters/manual.js +112 -0
  90. package/dist/signal-ingestion/adapters/support-ticket.d.ts +47 -0
  91. package/dist/signal-ingestion/adapters/support-ticket.js +51 -0
  92. package/dist/signal-ingestion/classifier.d.ts +205 -0
  93. package/dist/signal-ingestion/classifier.js +494 -0
  94. package/dist/signal-ingestion/clustering-types.d.ts +36 -0
  95. package/dist/signal-ingestion/clustering-types.js +14 -0
  96. package/dist/signal-ingestion/clustering.d.ts +200 -0
  97. package/dist/signal-ingestion/clustering.js +413 -0
  98. package/dist/signal-ingestion/config.d.ts +351 -0
  99. package/dist/signal-ingestion/config.js +587 -0
  100. package/dist/signal-ingestion/d1.d.ts +252 -0
  101. package/dist/signal-ingestion/d1.js +235 -0
  102. package/dist/signal-ingestion/errors.d.ts +73 -0
  103. package/dist/signal-ingestion/errors.js +108 -0
  104. package/dist/signal-ingestion/governance-events.d.ts +181 -0
  105. package/dist/signal-ingestion/governance-events.js +189 -0
  106. package/dist/signal-ingestion/index.d.ts +35 -0
  107. package/dist/signal-ingestion/index.js +53 -0
  108. package/dist/signal-ingestion/manual-share-metric.d.ts +93 -0
  109. package/dist/signal-ingestion/manual-share-metric.js +106 -0
  110. package/dist/signal-ingestion/registry.d.ts +40 -0
  111. package/dist/signal-ingestion/registry.js +137 -0
  112. package/dist/signal-ingestion/residency.d.ts +227 -0
  113. package/dist/signal-ingestion/residency.js +238 -0
  114. package/dist/signal-ingestion/significance.d.ts +554 -0
  115. package/dist/signal-ingestion/significance.js +555 -0
  116. package/dist/signal-ingestion/types.d.ts +191 -0
  117. package/dist/signal-ingestion/types.js +8 -0
  118. package/dist/substrate/drift-composition.d.ts +270 -0
  119. package/dist/substrate/drift-composition.js +306 -0
  120. package/dist/substrate/drift-tui-surface.d.ts +61 -0
  121. package/dist/substrate/drift-tui-surface.js +102 -0
  122. package/dist/substrate/identity-class.d.ts +176 -0
  123. package/dist/substrate/identity-class.js +201 -0
  124. package/dist/tessellation/cross-soul-provenance-rule.d.ts +133 -0
  125. package/dist/tessellation/cross-soul-provenance-rule.js +171 -0
  126. package/dist/tessellation/inter-soul-embedding-distance-rule.d.ts +61 -0
  127. package/dist/tessellation/inter-soul-embedding-distance-rule.js +67 -0
  128. package/dist/tessellation/rule-registry.d.ts +269 -0
  129. package/dist/tessellation/rule-registry.js +92 -0
  130. package/dist/tessellation/soul-slug-ast-scan-rule.d.ts +90 -0
  131. package/dist/tessellation/soul-slug-ast-scan-rule.js +158 -0
  132. package/dist/tessellation-admission.d.ts +162 -0
  133. package/dist/tessellation-admission.js +146 -0
  134. package/dist/tessellation-drift.d.ts +246 -0
  135. package/dist/tessellation-drift.js +250 -0
  136. package/dist/validate-config.js +13 -0
  137. package/dist/validate-issue.js +2 -2
  138. package/dist/variant/cardinality-activation.d.ts +126 -0
  139. package/dist/variant/cardinality-activation.js +101 -0
  140. package/dist/variant/deprecation-lifecycle.d.ts +184 -0
  141. package/dist/variant/deprecation-lifecycle.js +208 -0
  142. package/dist/variant/drift-extension.d.ts +136 -0
  143. package/dist/variant/drift-extension.js +164 -0
  144. package/dist/variant/engineering-review.d.ts +185 -0
  145. package/dist/variant/engineering-review.js +142 -0
  146. package/dist/variant/index.d.ts +32 -0
  147. package/dist/variant/index.js +32 -0
  148. package/dist/variant/inheritance-validator.d.ts +165 -0
  149. package/dist/variant/inheritance-validator.js +139 -0
  150. package/dist/variant/internal-adopter/index.d.ts +11 -0
  151. package/dist/variant/internal-adopter/index.js +10 -0
  152. package/dist/variant/internal-adopter/products.d.ts +156 -0
  153. package/dist/variant/internal-adopter/products.js +366 -0
  154. package/dist/variant-admission.d.ts +316 -0
  155. package/dist/variant-admission.js +247 -0
  156. package/package.json +10 -8
@@ -180,7 +180,12 @@ export function buildSa2Prompt(ctx) {
180
180
  }
181
181
  /** Extract the first JSON object from a raw LLM reply. */
182
182
  export function extractJson(raw) {
183
- const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
183
+ // Drop the leading optional-whitespace group entirely: any `[ \t]*`/`\s*`
184
+ // before the lazy `[\s\S]*?` capture overlaps it (both match space/tab),
185
+ // which is the polynomial-backtracking source (CodeQL js/polynomial-redos).
186
+ // The bounded `\n?` (0-or-1, not a `*`) is safe, and `.trim()` below absorbs
187
+ // any residual leading whitespace the capture picks up.
188
+ const fenced = raw.match(/```(?:json)?\n?([\s\S]*?)```/i);
184
189
  const candidate = fenced ? fenced[1] : raw;
185
190
  try {
186
191
  return JSON.parse(candidate.trim());
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Per-org calibration.yaml configuration for RFC-0031 DIDRevisionProposal
3
+ * mechanism (Refit AISDLC-310).
4
+ *
5
+ * Exposes OQ-12.1 (confidence thresholds) and OQ-12.5 (rejection weights +
6
+ * penalty floor) as per-org configurable values. Defaults match the values
7
+ * shipped by AISDLC-271 / PR #476 and operator-affirmed during the 2026-05-16
8
+ * audit (RFC-0031 §12.1 + §12.5 resolutions).
9
+ *
10
+ * Usage:
11
+ * 1. Load the `.ai-sdlc/calibration.yaml` file.
12
+ * 2. Call `parseRevisionProposalCalibrationYaml(content)` — validates +
13
+ * returns a fully-resolved config with all defaults filled in.
14
+ * 3. Pass the resolved config to `computeConfidence()`,
15
+ * `recordRejection()`, and `computeRejectionPrecedentFactor()`.
16
+ *
17
+ * Validation rules (enforced at load time):
18
+ * - `highSampleSize > lowSampleSize > 0`
19
+ * - All weights in `[0, 1]`
20
+ * - `confidencePenaltyFloor` in `[0, 1]`
21
+ */
22
+ /** Minimum total signals (dismiss + escalate + drift) for HIGH confidence. */
23
+ export declare const DEFAULT_CONFIDENCE_HIGH_SAMPLE_SIZE = 20;
24
+ /** Below this total signal count the proposal is LOW confidence. */
25
+ export declare const DEFAULT_CONFIDENCE_LOW_SAMPLE_SIZE = 5;
26
+ /** Rejection precedent weight applied when the rejected proposal had HIGH confidence. */
27
+ export declare const DEFAULT_REJECTION_WEIGHT_HIGH = 0.8;
28
+ /** Rejection precedent weight applied when the rejected proposal had MEDIUM confidence. */
29
+ export declare const DEFAULT_REJECTION_WEIGHT_MEDIUM = 0.5;
30
+ /** Rejection precedent weight applied when the rejected proposal had LOW confidence. */
31
+ export declare const DEFAULT_REJECTION_WEIGHT_LOW = 0.2;
32
+ /**
33
+ * Minimum value for the rejection precedent factor.
34
+ * 0.2 = at most 80% suppression of future proposal confidence.
35
+ */
36
+ export declare const DEFAULT_CONFIDENCE_PENALTY_FLOOR = 0.2;
37
+ /**
38
+ * Confidence threshold configuration (OQ-12.1).
39
+ *
40
+ * Sample size = `dismissSignals + escalateSignals + driftEvents`.
41
+ */
42
+ export interface ConfidenceThresholdsConfig {
43
+ /**
44
+ * Total signal count ≥ this value contributes toward HIGH confidence
45
+ * (alongside non-ambiguous classification and `evolving` identityClass).
46
+ *
47
+ * @default 20
48
+ */
49
+ highSampleSize: number;
50
+ /**
51
+ * Total signal count < this value forces LOW confidence.
52
+ *
53
+ * Must be < `highSampleSize`.
54
+ * @default 5
55
+ */
56
+ lowSampleSize: number;
57
+ }
58
+ /**
59
+ * Rejection precedent weights (OQ-12.5).
60
+ *
61
+ * These weights are stored on each `ProposalRejectionRecord` and averaged
62
+ * by `computeRejectionPrecedentFactor()` to suppress future proposal
63
+ * confidence for repeatedly-rejected fields.
64
+ */
65
+ export interface RejectionPrecedentWeightsConfig {
66
+ /**
67
+ * Weight when the rejected proposal had HIGH confidence.
68
+ * Higher weight = stronger suppression of future proposals.
69
+ * @default 0.8
70
+ */
71
+ highConfidenceRejection: number;
72
+ /**
73
+ * Weight when the rejected proposal had MEDIUM confidence.
74
+ * @default 0.5
75
+ */
76
+ mediumConfidenceRejection: number;
77
+ /**
78
+ * Weight when the rejected proposal had LOW confidence.
79
+ * @default 0.2
80
+ */
81
+ lowConfidenceRejection: number;
82
+ }
83
+ /**
84
+ * Rejection precedent configuration block (OQ-12.5).
85
+ */
86
+ export interface RejectionPrecedentConfig {
87
+ weights: RejectionPrecedentWeightsConfig;
88
+ /**
89
+ * Minimum value returned by `computeRejectionPrecedentFactor()`.
90
+ * Prevents the precedent factor from suppressing proposals entirely.
91
+ *
92
+ * Formula: `factor = max(confidencePenaltyFloor, 1.0 - avgWeight × 0.5)`
93
+ * @default 0.2
94
+ */
95
+ confidencePenaltyFloor: number;
96
+ }
97
+ /**
98
+ * Raw (partial) calibration config shape as it appears in `calibration.yaml`.
99
+ * All fields are optional; missing values fall back to defaults.
100
+ */
101
+ export interface RevisionProposalCalibrationConfig {
102
+ /** Fields that should never receive auto-proposals (OQ-12.3 — existing). */
103
+ lockNoProposal?: string[];
104
+ /** Override confidence thresholds (OQ-12.1). */
105
+ confidenceThresholds?: Partial<ConfidenceThresholdsConfig>;
106
+ /** Override rejection precedent weights + floor (OQ-12.5). */
107
+ rejectionPrecedent?: {
108
+ weights?: Partial<RejectionPrecedentWeightsConfig>;
109
+ confidencePenaltyFloor?: number;
110
+ };
111
+ }
112
+ /**
113
+ * Fully-resolved calibration config — all optional fields replaced with
114
+ * defaults. This is the shape passed to the revision-proposal functions.
115
+ */
116
+ export interface ResolvedRevisionProposalCalibrationConfig {
117
+ lockNoProposal: string[];
118
+ confidenceThresholds: ConfidenceThresholdsConfig;
119
+ rejectionPrecedent: RejectionPrecedentConfig;
120
+ }
121
+ /** The compile-time default resolved config (shipped AISDLC-271 values). */
122
+ export declare const DEFAULT_RESOLVED_CALIBRATION_CONFIG: ResolvedRevisionProposalCalibrationConfig;
123
+ export interface ConfigValidationError {
124
+ field: string;
125
+ message: string;
126
+ }
127
+ export type ConfigValidationResult = {
128
+ valid: true;
129
+ } | {
130
+ valid: false;
131
+ errors: ConfigValidationError[];
132
+ };
133
+ /**
134
+ * Validate a partial calibration config against the RFC-0031 §12.6 rules.
135
+ *
136
+ * Rules:
137
+ * - `highSampleSize > lowSampleSize > 0`
138
+ * - all weights in `[0, 1]`
139
+ * - `confidencePenaltyFloor` in `[0, 1]`
140
+ *
141
+ * Missing fields default to the shipped values and are not validated
142
+ * (a partial config that omits a field is always valid for that field).
143
+ */
144
+ export declare function validateRevisionProposalCalibrationConfig(config: RevisionProposalCalibrationConfig): ConfigValidationResult;
145
+ /**
146
+ * Merge a partial `RevisionProposalCalibrationConfig` with defaults to
147
+ * produce a fully-resolved config. Does NOT validate — call
148
+ * `validateRevisionProposalCalibrationConfig()` first if you need to
149
+ * surface errors to the operator.
150
+ */
151
+ export declare function resolveRevisionProposalCalibrationConfig(config?: RevisionProposalCalibrationConfig): ResolvedRevisionProposalCalibrationConfig;
152
+ /**
153
+ * Parse and validate a `.ai-sdlc/calibration.yaml` file's content.
154
+ *
155
+ * Expected top-level shape:
156
+ * ```yaml
157
+ * calibration:
158
+ * lockNoProposal: [...]
159
+ * confidenceThresholds:
160
+ * highSampleSize: 20
161
+ * lowSampleSize: 5
162
+ * rejectionPrecedent:
163
+ * weights:
164
+ * highConfidenceRejection: 0.8
165
+ * mediumConfidenceRejection: 0.5
166
+ * lowConfidenceRejection: 0.2
167
+ * confidencePenaltyFloor: 0.2
168
+ * ```
169
+ *
170
+ * Missing fields are filled with defaults. Validation failures throw with
171
+ * a descriptive message listing all constraint violations.
172
+ *
173
+ * @param yamlContent Raw UTF-8 content of `calibration.yaml`.
174
+ * @returns Fully-resolved config ready to pass to revision-proposal functions.
175
+ * @throws {Error} When the YAML fails RFC-0031 §12.6 validation rules.
176
+ */
177
+ export declare function parseRevisionProposalCalibrationYaml(yamlContent: string): ResolvedRevisionProposalCalibrationConfig;
178
+ //# sourceMappingURL=revision-proposal-config.d.ts.map
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Per-org calibration.yaml configuration for RFC-0031 DIDRevisionProposal
3
+ * mechanism (Refit AISDLC-310).
4
+ *
5
+ * Exposes OQ-12.1 (confidence thresholds) and OQ-12.5 (rejection weights +
6
+ * penalty floor) as per-org configurable values. Defaults match the values
7
+ * shipped by AISDLC-271 / PR #476 and operator-affirmed during the 2026-05-16
8
+ * audit (RFC-0031 §12.1 + §12.5 resolutions).
9
+ *
10
+ * Usage:
11
+ * 1. Load the `.ai-sdlc/calibration.yaml` file.
12
+ * 2. Call `parseRevisionProposalCalibrationYaml(content)` — validates +
13
+ * returns a fully-resolved config with all defaults filled in.
14
+ * 3. Pass the resolved config to `computeConfidence()`,
15
+ * `recordRejection()`, and `computeRejectionPrecedentFactor()`.
16
+ *
17
+ * Validation rules (enforced at load time):
18
+ * - `highSampleSize > lowSampleSize > 0`
19
+ * - All weights in `[0, 1]`
20
+ * - `confidencePenaltyFloor` in `[0, 1]`
21
+ */
22
+ import { parse as parseYaml } from 'yaml';
23
+ // ── Default constants ─────────────────────────────────────────────────
24
+ /** Minimum total signals (dismiss + escalate + drift) for HIGH confidence. */
25
+ export const DEFAULT_CONFIDENCE_HIGH_SAMPLE_SIZE = 20;
26
+ /** Below this total signal count the proposal is LOW confidence. */
27
+ export const DEFAULT_CONFIDENCE_LOW_SAMPLE_SIZE = 5;
28
+ /** Rejection precedent weight applied when the rejected proposal had HIGH confidence. */
29
+ export const DEFAULT_REJECTION_WEIGHT_HIGH = 0.8;
30
+ /** Rejection precedent weight applied when the rejected proposal had MEDIUM confidence. */
31
+ export const DEFAULT_REJECTION_WEIGHT_MEDIUM = 0.5;
32
+ /** Rejection precedent weight applied when the rejected proposal had LOW confidence. */
33
+ export const DEFAULT_REJECTION_WEIGHT_LOW = 0.2;
34
+ /**
35
+ * Minimum value for the rejection precedent factor.
36
+ * 0.2 = at most 80% suppression of future proposal confidence.
37
+ */
38
+ export const DEFAULT_CONFIDENCE_PENALTY_FLOOR = 0.2;
39
+ /** The compile-time default resolved config (shipped AISDLC-271 values). */
40
+ export const DEFAULT_RESOLVED_CALIBRATION_CONFIG = {
41
+ lockNoProposal: [],
42
+ confidenceThresholds: {
43
+ highSampleSize: DEFAULT_CONFIDENCE_HIGH_SAMPLE_SIZE,
44
+ lowSampleSize: DEFAULT_CONFIDENCE_LOW_SAMPLE_SIZE,
45
+ },
46
+ rejectionPrecedent: {
47
+ weights: {
48
+ highConfidenceRejection: DEFAULT_REJECTION_WEIGHT_HIGH,
49
+ mediumConfidenceRejection: DEFAULT_REJECTION_WEIGHT_MEDIUM,
50
+ lowConfidenceRejection: DEFAULT_REJECTION_WEIGHT_LOW,
51
+ },
52
+ confidencePenaltyFloor: DEFAULT_CONFIDENCE_PENALTY_FLOOR,
53
+ },
54
+ };
55
+ /**
56
+ * Validate a partial calibration config against the RFC-0031 §12.6 rules.
57
+ *
58
+ * Rules:
59
+ * - `highSampleSize > lowSampleSize > 0`
60
+ * - all weights in `[0, 1]`
61
+ * - `confidencePenaltyFloor` in `[0, 1]`
62
+ *
63
+ * Missing fields default to the shipped values and are not validated
64
+ * (a partial config that omits a field is always valid for that field).
65
+ */
66
+ export function validateRevisionProposalCalibrationConfig(config) {
67
+ const errors = [];
68
+ // ── Confidence thresholds ────────────────────────────────────────
69
+ const thresholds = config.confidenceThresholds;
70
+ if (thresholds !== undefined) {
71
+ const high = thresholds.highSampleSize ?? DEFAULT_CONFIDENCE_HIGH_SAMPLE_SIZE;
72
+ const low = thresholds.lowSampleSize ?? DEFAULT_CONFIDENCE_LOW_SAMPLE_SIZE;
73
+ if (thresholds.lowSampleSize !== undefined && thresholds.lowSampleSize <= 0) {
74
+ errors.push({
75
+ field: 'confidenceThresholds.lowSampleSize',
76
+ message: `must be > 0 (got ${thresholds.lowSampleSize})`,
77
+ });
78
+ }
79
+ if (thresholds.highSampleSize !== undefined && thresholds.highSampleSize <= 0) {
80
+ errors.push({
81
+ field: 'confidenceThresholds.highSampleSize',
82
+ message: `must be > 0 (got ${thresholds.highSampleSize})`,
83
+ });
84
+ }
85
+ // highSampleSize must be strictly greater than lowSampleSize
86
+ if (high <= low) {
87
+ errors.push({
88
+ field: 'confidenceThresholds',
89
+ message: `highSampleSize (${high}) must be > lowSampleSize (${low})`,
90
+ });
91
+ }
92
+ }
93
+ // ── Rejection precedent ──────────────────────────────────────────
94
+ const rp = config.rejectionPrecedent;
95
+ if (rp !== undefined) {
96
+ const weights = rp.weights;
97
+ if (weights !== undefined) {
98
+ const weightFields = [
99
+ ['highConfidenceRejection', weights.highConfidenceRejection],
100
+ ['mediumConfidenceRejection', weights.mediumConfidenceRejection],
101
+ ['lowConfidenceRejection', weights.lowConfidenceRejection],
102
+ ];
103
+ for (const [key, value] of weightFields) {
104
+ if (value !== undefined && (value < 0 || value > 1)) {
105
+ errors.push({
106
+ field: `rejectionPrecedent.weights.${key}`,
107
+ message: `must be in [0, 1] (got ${value})`,
108
+ });
109
+ }
110
+ }
111
+ }
112
+ if (rp.confidencePenaltyFloor !== undefined &&
113
+ (rp.confidencePenaltyFloor < 0 || rp.confidencePenaltyFloor > 1)) {
114
+ errors.push({
115
+ field: 'rejectionPrecedent.confidencePenaltyFloor',
116
+ message: `must be in [0, 1] (got ${rp.confidencePenaltyFloor})`,
117
+ });
118
+ }
119
+ }
120
+ if (errors.length > 0)
121
+ return { valid: false, errors };
122
+ return { valid: true };
123
+ }
124
+ // ── Resolution (merge partial config with defaults) ───────────────────
125
+ /**
126
+ * Merge a partial `RevisionProposalCalibrationConfig` with defaults to
127
+ * produce a fully-resolved config. Does NOT validate — call
128
+ * `validateRevisionProposalCalibrationConfig()` first if you need to
129
+ * surface errors to the operator.
130
+ */
131
+ export function resolveRevisionProposalCalibrationConfig(config = {}) {
132
+ const d = DEFAULT_RESOLVED_CALIBRATION_CONFIG;
133
+ return {
134
+ lockNoProposal: config.lockNoProposal ?? d.lockNoProposal,
135
+ confidenceThresholds: {
136
+ highSampleSize: config.confidenceThresholds?.highSampleSize ?? d.confidenceThresholds.highSampleSize,
137
+ lowSampleSize: config.confidenceThresholds?.lowSampleSize ?? d.confidenceThresholds.lowSampleSize,
138
+ },
139
+ rejectionPrecedent: {
140
+ weights: {
141
+ highConfidenceRejection: config.rejectionPrecedent?.weights?.highConfidenceRejection ??
142
+ d.rejectionPrecedent.weights.highConfidenceRejection,
143
+ mediumConfidenceRejection: config.rejectionPrecedent?.weights?.mediumConfidenceRejection ??
144
+ d.rejectionPrecedent.weights.mediumConfidenceRejection,
145
+ lowConfidenceRejection: config.rejectionPrecedent?.weights?.lowConfidenceRejection ??
146
+ d.rejectionPrecedent.weights.lowConfidenceRejection,
147
+ },
148
+ confidencePenaltyFloor: config.rejectionPrecedent?.confidencePenaltyFloor ??
149
+ d.rejectionPrecedent.confidencePenaltyFloor,
150
+ },
151
+ };
152
+ }
153
+ // ── YAML loading ──────────────────────────────────────────────────────
154
+ /**
155
+ * Parse and validate a `.ai-sdlc/calibration.yaml` file's content.
156
+ *
157
+ * Expected top-level shape:
158
+ * ```yaml
159
+ * calibration:
160
+ * lockNoProposal: [...]
161
+ * confidenceThresholds:
162
+ * highSampleSize: 20
163
+ * lowSampleSize: 5
164
+ * rejectionPrecedent:
165
+ * weights:
166
+ * highConfidenceRejection: 0.8
167
+ * mediumConfidenceRejection: 0.5
168
+ * lowConfidenceRejection: 0.2
169
+ * confidencePenaltyFloor: 0.2
170
+ * ```
171
+ *
172
+ * Missing fields are filled with defaults. Validation failures throw with
173
+ * a descriptive message listing all constraint violations.
174
+ *
175
+ * @param yamlContent Raw UTF-8 content of `calibration.yaml`.
176
+ * @returns Fully-resolved config ready to pass to revision-proposal functions.
177
+ * @throws {Error} When the YAML fails RFC-0031 §12.6 validation rules.
178
+ */
179
+ export function parseRevisionProposalCalibrationYaml(yamlContent) {
180
+ const doc = parseYaml(yamlContent);
181
+ const raw = (doc?.calibration ?? {});
182
+ const config = {
183
+ lockNoProposal: Array.isArray(raw.lockNoProposal) ? raw.lockNoProposal : undefined,
184
+ confidenceThresholds: raw.confidenceThresholds !== null && typeof raw.confidenceThresholds === 'object'
185
+ ? raw.confidenceThresholds
186
+ : undefined,
187
+ rejectionPrecedent: raw.rejectionPrecedent !== null && typeof raw.rejectionPrecedent === 'object'
188
+ ? raw.rejectionPrecedent
189
+ : undefined,
190
+ };
191
+ const validation = validateRevisionProposalCalibrationConfig(config);
192
+ if (!validation.valid) {
193
+ const lines = validation.errors.map((e) => ` ${e.field}: ${e.message}`).join('\n');
194
+ throw new Error(`calibration.yaml validation failed (RFC-0031 §12.6):\n${lines}`);
195
+ }
196
+ return resolveRevisionProposalCalibrationConfig(config);
197
+ }
198
+ //# sourceMappingURL=revision-proposal-config.js.map
@@ -0,0 +1,285 @@
1
+ /**
2
+ * DIDRevisionProposal mechanism (RFC-0031).
3
+ *
4
+ * Implements the calibration-driven proposal mechanism that fires when the
5
+ * PPA flywheel accumulates sufficient evidence that a DID field has drifted
6
+ * from observed reality.
7
+ *
8
+ * What ships here:
9
+ * - DIDRevisionProposal event shape + classification logic (§3, §5)
10
+ * - SoulHealthDiagnostic event for unhealthy-drift path (§7)
11
+ * - Approval routing by identityClass (§8)
12
+ * - 14-day expiry + DIDRevisionProposalExpired event (§9)
13
+ * - lockNoProposal opt-out per field from calibration config (OQ-12.3)
14
+ * - Rejection learnings flowing back into the calibration log (OQ-12.5)
15
+ * - Multi-field bundling explicitly deferred to v2 (OQ-12.2)
16
+ * - Confidence derived from sample size + classification clarity + identityClass (OQ-12.1)
17
+ * - PPA generates proposal regardless of pillar; pillar lead approves (OQ-12.4)
18
+ *
19
+ * Trigger entry point: `evaluateRevisionProposal()` — called after drift
20
+ * is detected (i.e., a `SoulDriftDetected` event fires). One call per
21
+ * field; bundling is v2.
22
+ *
23
+ * Scope: Shard-DID only. Platform-level DID changes are human-initiated.
24
+ */
25
+ import { type ResolvedRevisionProposalCalibrationConfig } from './revision-proposal-config.js';
26
+ export type { ResolvedRevisionProposalCalibrationConfig };
27
+ export { type RevisionProposalCalibrationConfig, type ConfidenceThresholdsConfig, type RejectionPrecedentConfig, type RejectionPrecedentWeightsConfig, type ConfigValidationResult, type ConfigValidationError, DEFAULT_CONFIDENCE_HIGH_SAMPLE_SIZE, DEFAULT_CONFIDENCE_LOW_SAMPLE_SIZE, DEFAULT_REJECTION_WEIGHT_HIGH, DEFAULT_REJECTION_WEIGHT_MEDIUM, DEFAULT_REJECTION_WEIGHT_LOW, DEFAULT_CONFIDENCE_PENALTY_FLOOR, DEFAULT_RESOLVED_CALIBRATION_CONFIG, validateRevisionProposalCalibrationConfig, resolveRevisionProposalCalibrationConfig, parseRevisionProposalCalibrationYaml, } from './revision-proposal-config.js';
28
+ /** Default trigger thresholds (§6). Configurable per deployment. */
29
+ export declare const DEFAULT_DISMISS_THRESHOLD = 10;
30
+ export declare const DEFAULT_DEMAND_MISALIGNMENT_THRESHOLD = 0.3;
31
+ export declare const DEFAULT_DRIFT_EVENTS_THRESHOLD = 3;
32
+ export declare const DEFAULT_TRIGGER_WINDOW_DAYS = 60;
33
+ export declare const DEFAULT_PROPOSAL_EXPIRY_DAYS = 14;
34
+ /** Classification thresholds per §7. */
35
+ export declare const HEALTHY_ICP_MATCH_MIN = 0.6;
36
+ export declare const UNHEALTHY_ICP_MATCH_MAX = 0.3;
37
+ export type DriftClassification = 'healthy' | 'unhealthy' | 'ambiguous';
38
+ export type IdentityClass = 'core' | 'evolving';
39
+ export type ApprovalPath = 'triad' | 'pillarLead';
40
+ export type ProposalStatus = 'pending' | 'approved' | 'rejected' | 'expired';
41
+ export type ProposalConfidence = 'high' | 'medium' | 'low';
42
+ export interface ClassificationEvidence {
43
+ /** [0,1]; high = healthy signal source (ICP-matched demand clusters). */
44
+ demandClusterICPMatchRate: number;
45
+ /** [0,1]; high = validated loss signal (churn-correlated demand). */
46
+ demandClusterChurnCorrelation: number;
47
+ /** High dismiss + low escalate = DID is stale (healthy evolution). */
48
+ dismissToEscalateRatio: number;
49
+ /** True = core identity field affected; more caution warranted. */
50
+ coreDIDFieldsAffected: boolean;
51
+ }
52
+ export interface TriggerEvidence {
53
+ /** Dismiss-signal count over the trigger window. */
54
+ dismissSignals: number;
55
+ escalateSignals: number;
56
+ /** SA gap between demand clusters and DID field (0=aligned, 1=fully misaligned). */
57
+ demandMisalignment: number;
58
+ /** SoulDriftDetected events attributable to this field. */
59
+ driftEvents: number;
60
+ /** ISO-8601 duration string for the evaluation window, e.g. "P60D". */
61
+ triggerWindow: string;
62
+ }
63
+ export interface DIDRevisionProposalEvent {
64
+ type: 'DIDRevisionProposal';
65
+ proposalId: string;
66
+ /** MUST be 'shard' — platform proposals are not generated. */
67
+ scope: 'shard';
68
+ shardId: string;
69
+ field: string;
70
+ currentValue: unknown;
71
+ proposedValue: unknown;
72
+ identityClass: IdentityClass | undefined;
73
+ classification: DriftClassification;
74
+ classificationEvidence: ClassificationEvidence;
75
+ triggerEvidence: TriggerEvidence;
76
+ confidence: ProposalConfidence;
77
+ approvalPath: ApprovalPath;
78
+ expiresAt: string;
79
+ createdAt: string;
80
+ /** Current lifecycle status. */
81
+ status: ProposalStatus;
82
+ }
83
+ /** Emitted on unhealthy drift (§7) instead of a DIDRevisionProposalEvent. */
84
+ export interface SoulHealthDiagnosticEvent {
85
+ type: 'SoulHealthDiagnostic';
86
+ proposalId: string;
87
+ shardId: string;
88
+ field: string;
89
+ classification: 'unhealthy';
90
+ classificationEvidence: ClassificationEvidence;
91
+ triggerEvidence: TriggerEvidence;
92
+ /** Recommendation for unhealthy drift — tighten admission or review demand source. */
93
+ recommendation: 'tighten-admission-threshold' | 'review-demand-source';
94
+ createdAt: string;
95
+ }
96
+ /** Emitted by `archiveExpiredProposals()` for each expired proposal. */
97
+ export interface DIDRevisionProposalExpiredEvent {
98
+ type: 'DIDRevisionProposalExpired';
99
+ proposalId: string;
100
+ shardId: string;
101
+ field: string;
102
+ classification: DriftClassification;
103
+ createdAt: string;
104
+ expiredAt: string;
105
+ }
106
+ /** Emitted when a proposal is rejected; flows back into the calibration log. */
107
+ export interface ProposalRejectionRecord {
108
+ proposalId: string;
109
+ shardId: string;
110
+ field: string;
111
+ rejectedBy: string;
112
+ rationale: string;
113
+ rejectedAt: string;
114
+ classification: DriftClassification;
115
+ /** Confidence weight carried forward into future trigger evaluations. */
116
+ rejectionPrecedentWeight: number;
117
+ }
118
+ /**
119
+ * Classify drift as healthy / unhealthy / ambiguous per RFC-0031 §7.
120
+ *
121
+ * ```
122
+ * healthy: icpMatchRate > 0.6 AND NOT coreDIDFieldsAffected
123
+ * unhealthy: icpMatchRate < 0.3 OR (coreDIDFieldsAffected AND dismissToEscalateRatio < 1.0)
124
+ * ambiguous: everything else
125
+ * ```
126
+ */
127
+ export declare function classifyDrift(evidence: ClassificationEvidence): DriftClassification;
128
+ /**
129
+ * Derive approval path from identityClass per RFC-0031 §8.
130
+ *
131
+ * - `core` → `triad` (all three pillar leads required)
132
+ * - `evolving` → `pillarLead` (owning lead + one other)
133
+ * - `undefined` → `triad` (default-tighten when class is undeclared)
134
+ * - ambiguous classification always → `triad` (§7 override)
135
+ */
136
+ export declare function deriveApprovalPath(identityClass: IdentityClass | undefined, classification: DriftClassification): ApprovalPath;
137
+ /**
138
+ * Compute proposal confidence from trigger evidence + classification clarity
139
+ * + identityClass (OQ-12.1 resolved position: confidence = f(sample size,
140
+ * classification clarity, identityClass)).
141
+ *
142
+ * Thresholds are per-org configurable via `.ai-sdlc/calibration.yaml`
143
+ * (Refit AISDLC-310). Defaults match the shipped AISDLC-271 values:
144
+ *
145
+ * - high: sampleSize ≥ highSampleSize (default 20) AND classification != 'ambiguous' AND identityClass = 'evolving'
146
+ * - low: sampleSize < lowSampleSize (default 5) OR classification = 'ambiguous' OR identityClass = 'core'
147
+ * - medium: everything else
148
+ *
149
+ * @param calibrationConfig Optional resolved config from `parseRevisionProposalCalibrationYaml()`.
150
+ * When omitted, shipped defaults (20 / 5) are used.
151
+ */
152
+ export declare function computeConfidence(triggerEvidence: TriggerEvidence, classification: DriftClassification, identityClass: IdentityClass | undefined, calibrationConfig?: ResolvedRevisionProposalCalibrationConfig): ProposalConfidence;
153
+ export interface TriggerConditions {
154
+ /** Dismiss-signal count over the last `triggerWindowDays`. */
155
+ dismissSignals: number;
156
+ escalateSignals: number;
157
+ /** SA gap between demand clusters and DID field — sustained over 3 sprints. */
158
+ demandMisalignment: number;
159
+ /** SoulDriftDetected events attributable to this field (indefinite window). */
160
+ driftEvents: number;
161
+ }
162
+ export interface TriggerConfig {
163
+ dismissThreshold?: number;
164
+ demandMisalignmentThreshold?: number;
165
+ driftEventsThreshold?: number;
166
+ triggerWindowDays?: number;
167
+ }
168
+ /**
169
+ * Returns true if ANY trigger condition fires per RFC-0031 §6.
170
+ */
171
+ export declare function triggerConditionMet(conditions: TriggerConditions, config?: TriggerConfig): boolean;
172
+ export interface CalibrationLockConfig {
173
+ /** List of JSON-path field identifiers that should never receive auto-proposals. */
174
+ lockNoProposal?: string[];
175
+ }
176
+ /**
177
+ * Returns true if the given field is locked from auto-proposals per OQ-12.3.
178
+ *
179
+ * Proposal generation SKIPS locked fields. Operators remove entries from
180
+ * `lockNoProposal` to opt back in.
181
+ */
182
+ export declare function isFieldLocked(field: string, lockConfig: CalibrationLockConfig): boolean;
183
+ export interface EvaluateRevisionProposalInput {
184
+ shardId: string;
185
+ /** JSON path for the DID field being evaluated, e.g. "soulPurpose.mission". */
186
+ field: string;
187
+ currentValue: unknown;
188
+ /** PPA's best inferred proposed value based on flywheel evidence. */
189
+ proposedValue: unknown;
190
+ identityClass: IdentityClass | undefined;
191
+ triggerConditions: TriggerConditions;
192
+ classificationEvidence: ClassificationEvidence;
193
+ lockConfig?: CalibrationLockConfig;
194
+ config?: TriggerConfig & {
195
+ expiryDays?: number;
196
+ };
197
+ /**
198
+ * Per-org calibration config loaded from `.ai-sdlc/calibration.yaml`.
199
+ * When omitted, shipped AISDLC-271 defaults are used for confidence
200
+ * thresholds (Refit AISDLC-310 / RFC-0031 §12.6).
201
+ */
202
+ calibrationConfig?: ResolvedRevisionProposalCalibrationConfig;
203
+ /** Clock injection for tests. */
204
+ now?: () => number;
205
+ }
206
+ export type EvaluateRevisionProposalResult = {
207
+ kind: 'proposal';
208
+ event: DIDRevisionProposalEvent;
209
+ } | {
210
+ kind: 'diagnostic';
211
+ event: SoulHealthDiagnosticEvent;
212
+ } | {
213
+ kind: 'ambiguous';
214
+ proposal: DIDRevisionProposalEvent;
215
+ diagnostic: SoulHealthDiagnosticEvent;
216
+ } | {
217
+ kind: 'skipped';
218
+ reason: 'locked' | 'no-trigger';
219
+ };
220
+ /**
221
+ * Evaluate whether a DIDRevisionProposal should be generated for a given
222
+ * DID field. Entry point called once per field (one-field-per-proposal in
223
+ * v1 per OQ-12.2).
224
+ *
225
+ * Returns a discriminated union describing what was produced:
226
+ * - `proposal` — healthy drift; one DIDRevisionProposalEvent
227
+ * - `diagnostic` — unhealthy drift; one SoulHealthDiagnosticEvent
228
+ * - `ambiguous` — both events produced, both flagged for triad review
229
+ * - `skipped` — field is locked or trigger conditions not met
230
+ */
231
+ export declare function evaluateRevisionProposal(input: EvaluateRevisionProposalInput): EvaluateRevisionProposalResult;
232
+ /**
233
+ * Check whether a proposal has passed its `expiresAt` timestamp.
234
+ *
235
+ * AISDLC-271 PR #476 round-2: validates expiresAt is parseable. Pre-fix
236
+ * a malformed (or attacker-mutated) expiresAt would silently NEVER
237
+ * expire because Date.parse returns NaN and `NaN <= now` is always false.
238
+ */
239
+ export declare function isProposalExpired(proposal: DIDRevisionProposalEvent, nowMs?: number): boolean;
240
+ /**
241
+ * Archive expired proposals and emit `DIDRevisionProposalExpired` events.
242
+ * Returns the list of expiry events for caller to process (persist / alert).
243
+ *
244
+ * Per §9: expiry without resolution emits the event — operator alert, not
245
+ * silent dismissal. Auto-archives by setting status to 'expired'.
246
+ */
247
+ export declare function archiveExpiredProposals(proposals: DIDRevisionProposalEvent[], nowMs?: number): {
248
+ expired: DIDRevisionProposalExpiredEvent[];
249
+ remaining: DIDRevisionProposalEvent[];
250
+ };
251
+ /**
252
+ * Record a proposal rejection and compute the `rejectionPrecedentWeight`
253
+ * to feed back into future trigger evaluations (OQ-12.5 resolved position:
254
+ * rejection rationale captured in calibration log; future trigger evaluations
255
+ * weight rejection-precedent into confidence).
256
+ *
257
+ * Weight formula (defaults from AISDLC-271, per-org configurable via
258
+ * `calibration.yaml` — Refit AISDLC-310 / RFC-0031 §12.6):
259
+ * - high-confidence proposals that were rejected → 0.8 (default)
260
+ * - medium-confidence → 0.5 (default)
261
+ * - low-confidence → 0.2 default (expected noise level, lower weight)
262
+ *
263
+ * Callers persist this record to their calibration log; the next trigger
264
+ * evaluation should factor it in by reducing effective confidence.
265
+ *
266
+ * @param calibrationConfig Optional resolved config from `parseRevisionProposalCalibrationYaml()`.
267
+ * When omitted, shipped AISDLC-271 weights (0.8 / 0.5 / 0.2) are used.
268
+ */
269
+ export declare function recordRejection(proposal: DIDRevisionProposalEvent, rejectedBy: string, rationale: string, now?: () => number, calibrationConfig?: ResolvedRevisionProposalCalibrationConfig): ProposalRejectionRecord;
270
+ /**
271
+ * Apply rejection precedent to reduce effective trigger confidence.
272
+ * When a field has prior rejection records, the aggregated precedent
273
+ * weight penalises future proposals for the same field.
274
+ *
275
+ * Formula (RFC-0031 §12.6): `factor = max(floor, 1.0 - avgWeight × 0.5)`
276
+ * where `floor` is `confidencePenaltyFloor` from calibration config
277
+ * (default 0.2 — at most 80% suppression).
278
+ *
279
+ * Returns a factor in `[floor, 1.0]`. Callers decide how to interpret.
280
+ *
281
+ * @param calibrationConfig Optional resolved config from `parseRevisionProposalCalibrationYaml()`.
282
+ * When omitted, shipped AISDLC-271 floor (0.2) is used.
283
+ */
284
+ export declare function computeRejectionPrecedentFactor(field: string, rejections: readonly ProposalRejectionRecord[], calibrationConfig?: ResolvedRevisionProposalCalibrationConfig): number;
285
+ //# sourceMappingURL=revision-proposal.d.ts.map