sela-core 0.1.0-alpha.2 → 0.1.0-alpha.31
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/README.md +809 -56
- package/dist/config/ConfigLoader.d.ts +5 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +89 -19
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +3 -1
- package/dist/errors/SelaError.d.ts +4 -0
- package/dist/errors/SelaError.d.ts.map +1 -1
- package/dist/errors/SelaError.js +4 -0
- package/dist/services/LLMService.d.ts.map +1 -1
- package/dist/services/LLMService.js +40 -4
- package/dist/services/MutationApplier.d.ts +2 -0
- package/dist/services/MutationApplier.d.ts.map +1 -1
- package/dist/services/MutationApplier.js +7 -0
- package/dist/services/SafetyGuard.d.ts +1 -0
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +41 -0
- package/dist/services/safety/rules/DangerousSinkRule.d.ts +10 -2
- package/dist/services/safety/rules/DangerousSinkRule.d.ts.map +1 -1
- package/dist/services/safety/rules/DangerousSinkRule.js +37 -16
- package/dist/services/safety/rules/IntentAuditorRule.d.ts.map +1 -1
- package/dist/services/safety/rules/IntentAuditorRule.js +36 -4
- package/dist/services/safety/rules/StructuralOnlyRule.d.ts.map +1 -1
- package/dist/services/safety/rules/StructuralOnlyRule.js +13 -0
- package/dist/utils/TerminalLink.d.ts +2 -0
- package/dist/utils/TerminalLink.d.ts.map +1 -0
- package/dist/utils/TerminalLink.js +7 -0
- package/package.json +1 -1
|
@@ -5,6 +5,11 @@ export declare class ConfigLoader {
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns the fully resolved config. First call does all I/O; subsequent
|
|
7
7
|
* calls return the cached result at zero cost.
|
|
8
|
+
*
|
|
9
|
+
* Throws when:
|
|
10
|
+
* - `SELA_CONFIG_PATH` is set but points to a non-existent file.
|
|
11
|
+
* - A config file is found but cannot be loaded or has invalid keys.
|
|
12
|
+
* - `SELA_DRY_RUN` / `SELA_NO_CACHE` contain an unrecognised value.
|
|
8
13
|
*/
|
|
9
14
|
static getInstance(): ResolvedConfig;
|
|
10
15
|
private static logEnvBanners;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigLoader.d.ts","sourceRoot":"","sources":["../../src/config/ConfigLoader.ts"],"names":[],"mappings":"AAQA,OAAO,EAAc,cAAc,EAAiB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfigLoader.d.ts","sourceRoot":"","sources":["../../src/config/ConfigLoader.ts"],"names":[],"mappings":"AAQA,OAAO,EAAc,cAAc,EAAiB,MAAM,cAAc,CAAC;AAwLzE,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA+B;IACtD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAuB;IAE5C;;;;;;;;OAQG;IACH,MAAM,CAAC,WAAW,IAAI,cAAc;IAmCpC,OAAO,CAAC,MAAM,CAAC,aAAa;IAW5B,yEAAyE;IACzE,MAAM,CAAC,SAAS,IAAI,MAAM,GAAG,IAAI;IAKjC,uDAAuD;IACvD,MAAM,CAAC,KAAK,IAAI,IAAI;CAIrB"}
|
|
@@ -43,10 +43,41 @@ const path = __importStar(require("path"));
|
|
|
43
43
|
const child_process_1 = require("child_process");
|
|
44
44
|
const SelaConfig_1 = require("./SelaConfig");
|
|
45
45
|
const logger_1 = require("../utils/logger");
|
|
46
|
+
// All public top-level keys of SelaConfig — used to catch typos at load time.
|
|
47
|
+
const KNOWN_CONFIG_KEYS = new Set([
|
|
48
|
+
"healingPolicy",
|
|
49
|
+
"thresholds",
|
|
50
|
+
"agenticDiscovery",
|
|
51
|
+
"updateStrategy",
|
|
52
|
+
"autoCommit",
|
|
53
|
+
"prAutomation",
|
|
54
|
+
"dnaStoragePath",
|
|
55
|
+
"allowedPaths",
|
|
56
|
+
"maxHealsPerRun",
|
|
57
|
+
"sanitization",
|
|
58
|
+
"branchOverrides",
|
|
59
|
+
]);
|
|
46
60
|
// ─────────────────────────────────────────────────────────────────
|
|
47
61
|
// FILE DISCOVERY
|
|
48
62
|
// ─────────────────────────────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* Resolves the config file path.
|
|
65
|
+
*
|
|
66
|
+
* Resolution order:
|
|
67
|
+
* 1. `SELA_CONFIG_PATH` env var — absolute or cwd-relative; throws immediately
|
|
68
|
+
* if set but the file does not exist (typo in CI config is a hard error).
|
|
69
|
+
* 2. Upward traversal from `startDir`, checking both .ts and .js extensions,
|
|
70
|
+
* stopping at the filesystem root.
|
|
71
|
+
*/
|
|
49
72
|
function findConfigFile(startDir) {
|
|
73
|
+
const envPath = process.env.SELA_CONFIG_PATH;
|
|
74
|
+
if (envPath) {
|
|
75
|
+
const resolved = path.resolve(envPath);
|
|
76
|
+
if (fs.existsSync(resolved))
|
|
77
|
+
return resolved;
|
|
78
|
+
throw new Error(`[Sela] SELA_CONFIG_PATH points to a non-existent file: "${resolved}". ` +
|
|
79
|
+
`Check the env var in your CI configuration.`);
|
|
80
|
+
}
|
|
50
81
|
let dir = startDir;
|
|
51
82
|
while (true) {
|
|
52
83
|
for (const name of ["sela.config.ts", "sela.config.js"]) {
|
|
@@ -65,13 +96,11 @@ function findConfigFile(startDir) {
|
|
|
65
96
|
// ─────────────────────────────────────────────────────────────────
|
|
66
97
|
function getCurrentBranch() {
|
|
67
98
|
try {
|
|
68
|
-
// Fail fast if not a git repo or git is unavailable
|
|
69
99
|
const branch = (0, child_process_1.execSync)("git rev-parse --abbrev-ref HEAD", {
|
|
70
100
|
encoding: "utf-8",
|
|
71
101
|
stdio: ["pipe", "pipe", "pipe"],
|
|
72
102
|
}).trim();
|
|
73
103
|
if (branch === "HEAD") {
|
|
74
|
-
// Detached HEAD (common in CI with explicit SHA checkout)
|
|
75
104
|
logger_1.logger.warn("Detached HEAD detected - branch overrides skipped");
|
|
76
105
|
return null;
|
|
77
106
|
}
|
|
@@ -83,25 +112,64 @@ function getCurrentBranch() {
|
|
|
83
112
|
}
|
|
84
113
|
}
|
|
85
114
|
// ─────────────────────────────────────────────────────────────────
|
|
115
|
+
// STRUCTURAL VALIDATION
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────
|
|
117
|
+
function validateConfigKeys(config, filePath) {
|
|
118
|
+
const unknown = Object.keys(config).filter((k) => !KNOWN_CONFIG_KEYS.has(k));
|
|
119
|
+
if (unknown.length > 0) {
|
|
120
|
+
throw new Error(`[Sela] Unknown key(s) in ${path.basename(filePath)}: ` +
|
|
121
|
+
`${unknown.map((k) => `"${k}"`).join(", ")}. ` +
|
|
122
|
+
`Valid top-level keys: ${[...KNOWN_CONFIG_KEYS].join(", ")}.`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// ─────────────────────────────────────────────────────────────────
|
|
86
126
|
// CONFIG FILE LOADING
|
|
87
127
|
// ─────────────────────────────────────────────────────────────────
|
|
128
|
+
/**
|
|
129
|
+
* Loads and validates the raw config from `configPath`.
|
|
130
|
+
*
|
|
131
|
+
* Load strategy (in order):
|
|
132
|
+
* 1. `configPath` as-is — works when a TS loader is registered
|
|
133
|
+
* (Playwright, ts-node, tsx).
|
|
134
|
+
* 2. `.js` sibling — for compiled CI builds where only the transpiled file
|
|
135
|
+
* is present alongside the source `.ts`.
|
|
136
|
+
*
|
|
137
|
+
* Throws (never silently returns {}) when the file is found but every load
|
|
138
|
+
* attempt fails, so CI surfaces the root cause instead of running on defaults.
|
|
139
|
+
*/
|
|
88
140
|
function loadRawConfig(configPath) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
93
|
-
const mod = require(configPath);
|
|
94
|
-
const config = mod?.default ?? mod ?? {};
|
|
95
|
-
if (typeof config !== "object" || config === null) {
|
|
96
|
-
logger_1.logger.warn(`sela.config did not export an object - using defaults`);
|
|
97
|
-
return {};
|
|
98
|
-
}
|
|
99
|
-
return config;
|
|
141
|
+
const candidates = [configPath];
|
|
142
|
+
if (configPath.endsWith(".ts")) {
|
|
143
|
+
candidates.push(configPath.replace(/\.ts$/, ".js"));
|
|
100
144
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
145
|
+
let lastError = null;
|
|
146
|
+
for (const candidate of candidates) {
|
|
147
|
+
if (!fs.existsSync(candidate))
|
|
148
|
+
continue;
|
|
149
|
+
try {
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
151
|
+
const mod = require(candidate);
|
|
152
|
+
const config = mod?.default ?? mod ?? {};
|
|
153
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
154
|
+
throw new Error(`${path.basename(candidate)} must export a plain object — ` +
|
|
155
|
+
`got ${Array.isArray(config) ? "array" : typeof config}`);
|
|
156
|
+
}
|
|
157
|
+
validateConfigKeys(config, candidate);
|
|
158
|
+
return config;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
// Key-validation failures are definitive — skip fallback candidates.
|
|
162
|
+
if (err.message?.startsWith("[Sela]"))
|
|
163
|
+
throw err;
|
|
164
|
+
lastError = err;
|
|
165
|
+
}
|
|
104
166
|
}
|
|
167
|
+
// Every attempt failed — throw with a diagnostic instead of silently
|
|
168
|
+
// falling back to defaults. Missing sela.config → defaults is intentional
|
|
169
|
+
// (handled at the call site); found-but-broken is always a hard error.
|
|
170
|
+
throw new Error(`[Sela] Failed to load ${path.basename(configPath)}: ${lastError?.message ?? "unknown error"}. ` +
|
|
171
|
+
`Ensure a TypeScript loader (Playwright, ts-node, tsx) is active, ` +
|
|
172
|
+
`or provide a compiled sela.config.js alongside sela.config.ts.`);
|
|
105
173
|
}
|
|
106
174
|
// ─────────────────────────────────────────────────────────────────
|
|
107
175
|
// DEEP MERGE (plain objects only - arrays are replaced, not merged)
|
|
@@ -134,20 +202,23 @@ class ConfigLoader {
|
|
|
134
202
|
/**
|
|
135
203
|
* Returns the fully resolved config. First call does all I/O; subsequent
|
|
136
204
|
* calls return the cached result at zero cost.
|
|
205
|
+
*
|
|
206
|
+
* Throws when:
|
|
207
|
+
* - `SELA_CONFIG_PATH` is set but points to a non-existent file.
|
|
208
|
+
* - A config file is found but cannot be loaded or has invalid keys.
|
|
209
|
+
* - `SELA_DRY_RUN` / `SELA_NO_CACHE` contain an unrecognised value.
|
|
137
210
|
*/
|
|
138
211
|
static getInstance() {
|
|
139
212
|
if (ConfigLoader.resolved)
|
|
140
213
|
return ConfigLoader.resolved;
|
|
141
214
|
const configPath = findConfigFile(process.cwd());
|
|
142
215
|
if (!configPath) {
|
|
143
|
-
// Zero-breaking-change guarantee: no config → pure defaults
|
|
144
216
|
ConfigLoader.resolved = (0, SelaConfig_1.resolveConfig)({});
|
|
145
217
|
ConfigLoader.logEnvBanners(ConfigLoader.resolved);
|
|
146
218
|
return ConfigLoader.resolved;
|
|
147
219
|
}
|
|
148
220
|
logger_1.logger.debug(`Loaded config from: ${path.relative(process.cwd(), configPath)}`);
|
|
149
221
|
let rawConfig = loadRawConfig(configPath);
|
|
150
|
-
// Apply branch overrides using exact-string matching
|
|
151
222
|
const detectedBranch = getCurrentBranch();
|
|
152
223
|
ConfigLoader.branch = detectedBranch;
|
|
153
224
|
if (rawConfig.branchOverrides && detectedBranch) {
|
|
@@ -171,7 +242,6 @@ class ConfigLoader {
|
|
|
171
242
|
}
|
|
172
243
|
/** Returns the detected Git branch name, or null if detection failed. */
|
|
173
244
|
static getBranch() {
|
|
174
|
-
// Ensure getInstance() has been called at least once
|
|
175
245
|
if (!ConfigLoader.resolved)
|
|
176
246
|
ConfigLoader.getInstance();
|
|
177
247
|
return ConfigLoader.branch;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelaEngine.d.ts","sourceRoot":"","sources":["../../src/engine/SelaEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAexC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EAAe,QAAQ,EAAkB,MAAM,yBAAyB,CAAC;AA0FhF,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,eAAe,CAAkC;;IAuBnD,YAAY,CAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA0DnB,IAAI,CACR,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,QAAmB,GAC5B,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"SelaEngine.d.ts","sourceRoot":"","sources":["../../src/engine/SelaEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAexC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EAAe,QAAQ,EAAkB,MAAM,yBAAyB,CAAC;AA0FhF,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,eAAe,CAAkC;;IAuBnD,YAAY,CAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA0DnB,IAAI,CACR,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,QAAmB,GAC5B,OAAO,CAAC,MAAM,CAAC;IAirBlB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,aAAa;IAerB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,2BAA2B;IA4BnC,OAAO,CAAC,yBAAyB;IAgDjC,OAAO,CAAC,kBAAkB;IA8D1B,OAAO,CAAC,oBAAoB;IAyD5B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;YAgLtB,aAAa;IAsB3B,OAAO,CAAC,0BAA0B;IA+BlC,OAAO,CAAC,WAAW;IA6Bb,wBAAwB,CAC5B,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;CAqBnB;AAED,OAAO,EAAE,CAAC"}
|
|
@@ -481,6 +481,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
481
481
|
// branch / autoCommit / dry-run keep the legacy git-aware path).
|
|
482
482
|
// Falls back to the legacy regex/semantic engine on ABORT / PROMPT.
|
|
483
483
|
let updateResult;
|
|
484
|
+
let composedMicroDiff;
|
|
484
485
|
const plainInPlace = (!this.config.updateStrategy ||
|
|
485
486
|
this.config.updateStrategy === "inPlace") &&
|
|
486
487
|
!this.config.autoCommit;
|
|
@@ -500,6 +501,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
500
501
|
composed.outcome?.status === "applied" &&
|
|
501
502
|
(strat === "MUTATE_IN_PLACE" || strat === "FORK_AT_TEST")) {
|
|
502
503
|
WorkspaceSnapshotService_1.sharedWorkspaceSnapshot.postWrite(resolvedForCompose);
|
|
504
|
+
composedMicroDiff = composed.outcome?.microDiff;
|
|
503
505
|
const wl = composed.writtenLine ?? line;
|
|
504
506
|
logger_1.logger.stage("ANALYZING AST", `composeHeal primary path (${strat})`);
|
|
505
507
|
updateResult = {
|
|
@@ -550,7 +552,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
550
552
|
// any developer dirty edits sit identically in both buffers and
|
|
551
553
|
// therefore cancel out. Falls back to "" when the snapshot is
|
|
552
554
|
// missing (cross-file healing into an untracked path, etc.).
|
|
553
|
-
const gitUnifiedDiff = this._captureIsolatedDiff(mutatedFilePath);
|
|
555
|
+
const gitUnifiedDiff = composedMicroDiff || this._captureIsolatedDiff(mutatedFilePath);
|
|
554
556
|
const firstChange = this._extractFirstChangeFromDiff(gitUnifiedDiff);
|
|
555
557
|
// Backward-compat: keep oldCodeLine/newCodeLine populated from the
|
|
556
558
|
// first -/+ pair inside the diff. When git produced no diff (file
|
|
@@ -63,6 +63,10 @@ export declare const SAFETY_GUARD_CODES: {
|
|
|
63
63
|
readonly BLOCKED_ROLE_SWAP_ASSERTION: "SG_BLOCKED_ROLE_SWAP_ASSERTION";
|
|
64
64
|
readonly FAIL_HARD_AUDITOR_INCONSISTENT: "SG_FAIL_HARD_AUDITOR_INCONSISTENT";
|
|
65
65
|
readonly BLOCKED_AUDITOR_SUSPICIOUS: "SG_BLOCKED_AUDITOR_SUSPICIOUS";
|
|
66
|
+
/** Structural pre-LLM check: old element had a destructive/auth role and
|
|
67
|
+
* the candidate does not — replacing a DELETE/AUTH action is always
|
|
68
|
+
* semantically inconsistent regardless of AI confidence. */
|
|
69
|
+
readonly FAIL_HARD_DESTRUCTIVE_ROLE_INVERSION: "SG_FAIL_HARD_DESTRUCTIVE_ROLE_INVERSION";
|
|
66
70
|
/**
|
|
67
71
|
* Heal was REQUIRES_REVIEW → quarantined (decision #1): proposed but not
|
|
68
72
|
* written to source and not executed this run, pending explicit approval.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelaError.d.ts","sourceRoot":"","sources":["../../src/errors/SelaError.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SelaError.d.ts","sourceRoot":"","sources":["../../src/errors/SelaError.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,aAAa,GACrB,aAAa,GACb,eAAe,GACf,YAAY,GACZ,OAAO,GACP,YAAY,GACZ,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,QAAQ,CAAC;AAMb,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,iBAAiB,GAAG,MAAM,CAAC;IACnE,0EAA0E;IAC1E,cAAc,CAAC,EAAE,YAAY,GAAG,cAAc,GAAG,YAAY,CAAC;IAC9D,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAMD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,aAAa,CAAC;IACzB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAYD,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;gBAEvB,IAAI,EAAE,aAAa;IAsC/B,MAAM,CAAC,aAAa,CAClB,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,MAAM;IAIT,MAAM,IAAI,aAAa;CAexB;AAMD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,SAAS,CAE1D;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,aAAa,GACvB,GAAG,IAAI,SAAS,CAElB;AAUD,eAAO,MAAM,kBAAkB;;;;;;;;;IAS7B;;iEAE6D;;IAE7D;;;;OAIG;;IAEH,+EAA+E;;CAEvE,CAAC;AACX,MAAM,MAAM,eAAe,GACzB,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE/D,eAAO,MAAM,oBAAoB;IAC/B,wEAAwE;;;;CAIhE,CAAC;AACX,MAAM,MAAM,iBAAiB,GAC3B,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,OAAO,oBAAoB,CAAC,CAAC;AAEnE,eAAO,MAAM,iBAAiB;;;;;;CAMpB,CAAC;AACX,MAAM,MAAM,cAAc,GACxB,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAE7D,eAAO,MAAM,iBAAiB;;;;;;;IAO5B;;;;OAIG;;CAEK,CAAC;AACX,MAAM,MAAM,cAAc,GACxB,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAE7D,eAAO,MAAM,WAAW;;;;CAId,CAAC;AACX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEvE,eAAO,MAAM,cAAc;;;CAGjB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAEhF,eAAO,MAAM,YAAY;IACvB,2EAA2E;;IAE3E,wDAAwD;;IAExD,gEAAgE;;IAEhE,gDAAgD;;IAEhD,8CAA8C;;IAE9C;;;;OAIG;;IAEH,gFAAgF;;CAExE,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE1E,eAAO,MAAM,qBAAqB;;;;;;CAMxB,CAAC;AACX,MAAM,MAAM,kBAAkB,GAC5B,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC"}
|
package/dist/errors/SelaError.js
CHANGED
|
@@ -94,6 +94,10 @@ exports.SAFETY_GUARD_CODES = {
|
|
|
94
94
|
BLOCKED_ROLE_SWAP_ASSERTION: "SG_BLOCKED_ROLE_SWAP_ASSERTION",
|
|
95
95
|
FAIL_HARD_AUDITOR_INCONSISTENT: "SG_FAIL_HARD_AUDITOR_INCONSISTENT",
|
|
96
96
|
BLOCKED_AUDITOR_SUSPICIOUS: "SG_BLOCKED_AUDITOR_SUSPICIOUS",
|
|
97
|
+
/** Structural pre-LLM check: old element had a destructive/auth role and
|
|
98
|
+
* the candidate does not — replacing a DELETE/AUTH action is always
|
|
99
|
+
* semantically inconsistent regardless of AI confidence. */
|
|
100
|
+
FAIL_HARD_DESTRUCTIVE_ROLE_INVERSION: "SG_FAIL_HARD_DESTRUCTIVE_ROLE_INVERSION",
|
|
97
101
|
/**
|
|
98
102
|
* Heal was REQUIRES_REVIEW → quarantined (decision #1): proposed but not
|
|
99
103
|
* written to source and not executed this run, pending explicit approval.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LLMService.d.ts","sourceRoot":"","sources":["../../src/services/LLMService.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAWtC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,iBAAiB,GACzB;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC1C;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GACD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAEpD;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACrD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACD;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC;IAC9B;;;OAGG;IACH,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B;;OAEG;IACH,aAAa,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACpC,iBAAiB,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACxC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;
|
|
1
|
+
{"version":3,"file":"LLMService.d.ts","sourceRoot":"","sources":["../../src/services/LLMService.ts"],"names":[],"mappings":"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"}
|
|
@@ -109,7 +109,17 @@ intended element on the next test run. Use this rubric:
|
|
|
109
109
|
- 90-100: stable id, data-testid, or unique role+name - zero ambiguity in the DOM.
|
|
110
110
|
- 70-89: strong semantic match, but some structural drift or competing candidates.
|
|
111
111
|
- 50-69: best-guess match - multiple plausible candidates required disambiguation.
|
|
112
|
-
-
|
|
112
|
+
- 20-49: speculative match - return this only when no better candidate exists.
|
|
113
|
+
- 1-19: SEMANTIC MISMATCH - the best candidate in the DOM has a different action
|
|
114
|
+
type or intent than the original (e.g. original was "Delete" but you can
|
|
115
|
+
only find "Update", or the element is completely absent from the DOM).
|
|
116
|
+
Use this range whenever the semantic role/purpose of the match differs
|
|
117
|
+
from what the DNA describes.
|
|
118
|
+
CRITICAL: The 90-100 range is ONLY valid when the match is unambiguous AND semantically
|
|
119
|
+
identical to the original element. If there is ANY doubt about semantic equivalence
|
|
120
|
+
(different button text implying a different action, different role, different purpose),
|
|
121
|
+
you MUST score below 70. A high confidence on a semantically wrong match is a
|
|
122
|
+
critical failure - it will silently corrupt a test suite.
|
|
113
123
|
NEVER omit this field. NEVER return it as a string, boolean, or null.
|
|
114
124
|
Reports rendered downstream will show "n/a" when this field is missing - which
|
|
115
125
|
is a clear failure signal to the developer reviewing the heal.
|
|
@@ -117,10 +127,23 @@ is a clear failure signal to the developer reviewing the heal.
|
|
|
117
127
|
### MANDATORY OUTPUT FORMAT
|
|
118
128
|
Return ONLY valid JSON.
|
|
119
129
|
|
|
120
|
-
Example output -
|
|
130
|
+
Example output A - Unambiguous match via stable testid (confidence 90-100):
|
|
121
131
|
{
|
|
122
132
|
"status": "FIXED",
|
|
123
|
-
"confidence":
|
|
133
|
+
"confidence": 95,
|
|
134
|
+
"chainSegments": [
|
|
135
|
+
{ "type": "getByTestId", "testId": "submit-order-btn" }
|
|
136
|
+
],
|
|
137
|
+
"segments": [
|
|
138
|
+
{ "type": "element", "selector": "[data-testid=\"submit-order-btn\"]" }
|
|
139
|
+
],
|
|
140
|
+
"explanation": "Element found by stable data-testid - zero ambiguity."
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Example output B - Semantic drift with structural change (confidence 70-89):
|
|
144
|
+
{
|
|
145
|
+
"status": "FIXED",
|
|
146
|
+
"confidence": 74,
|
|
124
147
|
"chainSegments": [
|
|
125
148
|
{ "type": "locator", "selector": ".user-item" },
|
|
126
149
|
{ "type": "filter", "hasText": "Bob Smith" },
|
|
@@ -132,7 +155,20 @@ Example output - Identifying a specific button in a list row:
|
|
|
132
155
|
"selector": ".user-item >> internal:has-text=\"Bob Smith\"i >> internal:role=button[name=\"Modify Bob\"i]"
|
|
133
156
|
}
|
|
134
157
|
],
|
|
135
|
-
"explanation": "
|
|
158
|
+
"explanation": "Button name changed from 'Edit Bob' to 'Modify Bob' - semantically equivalent action, structural drift in container class."
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
Example output C - Semantic mismatch, original element absent (confidence 1-19):
|
|
162
|
+
{
|
|
163
|
+
"status": "FIXED",
|
|
164
|
+
"confidence": 12,
|
|
165
|
+
"chainSegments": [
|
|
166
|
+
{ "type": "getByRole", "role": "button", "name": "Update System" }
|
|
167
|
+
],
|
|
168
|
+
"segments": [
|
|
169
|
+
{ "type": "element", "selector": "internal:role=button[name=\"Update System\"i]" }
|
|
170
|
+
],
|
|
171
|
+
"explanation": "Original 'Delete System' button is absent from the DOM. Nearest candidate is 'Update System' which performs a different action - semantic mismatch, very low confidence."
|
|
136
172
|
}`;
|
|
137
173
|
// ─────────────────────────────────────────────────────────────────
|
|
138
174
|
// HELPERS
|
|
@@ -33,6 +33,8 @@ export interface ApplyOutcome {
|
|
|
33
33
|
skipped: Array<{
|
|
34
34
|
reason: string;
|
|
35
35
|
}>;
|
|
36
|
+
/** Isolated per-batch unified diff — only set when status === "applied". */
|
|
37
|
+
microDiff?: string;
|
|
36
38
|
}
|
|
37
39
|
export interface ApplyOptions {
|
|
38
40
|
/** When false, compute + gate but never touch disk. Default true. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MutationApplier.d.ts","sourceRoot":"","sources":["../../src/services/MutationApplier.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MutationApplier.d.ts","sourceRoot":"","sources":["../../src/services/MutationApplier.ts"],"names":[],"mappings":"AA6BA,OAAO,EACL,SAAS,EAET,UAAU,EACX,MAAM,kBAAkB,CAAC;AAY1B;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,SAAS,CAAC;AAE1E,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAeD,qBAAa,eAAe;IAC1B,gEAAgE;IAChE,MAAM,CAAC,YAAY,CACjB,OAAO,EAAE,SAAS,EAAE,EACpB,IAAI,GAAE,YAAiB,GACtB,YAAY,EAAE;IAIjB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,cAAc,CACnB,KAAK,EAAE,SAAS,EAChB,IAAI,GAAE,YAAiB,GACtB,YAAY;IAqCf,OAAO,CAAC,MAAM,CAAC,sBAAsB;CAiLtC;AA2CD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkB5D"}
|
|
@@ -60,7 +60,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
60
60
|
exports.MutationApplier = void 0;
|
|
61
61
|
exports.isLocatorReturningExpr = isLocatorReturningExpr;
|
|
62
62
|
const fs = __importStar(require("fs"));
|
|
63
|
+
const path = __importStar(require("path"));
|
|
63
64
|
const ts_morph_1 = require("ts-morph");
|
|
65
|
+
const IsolatedDiff_1 = require("../utils/IsolatedDiff");
|
|
64
66
|
const AnchorResolver_1 = require("./AnchorResolver");
|
|
65
67
|
const DecisionEngine_1 = require("./DecisionEngine");
|
|
66
68
|
const TraceBackEngine_1 = require("./TraceBackEngine");
|
|
@@ -236,6 +238,10 @@ class MutationApplier {
|
|
|
236
238
|
skipped,
|
|
237
239
|
};
|
|
238
240
|
}
|
|
241
|
+
const microDiff = (0, IsolatedDiff_1.generateUnifiedDiff)(original, newContent, {
|
|
242
|
+
filePath: path.relative(process.cwd(), filePath),
|
|
243
|
+
context: 3,
|
|
244
|
+
}) || undefined;
|
|
239
245
|
// ── Type-safety gate: baseline-diff diagnostics ──────────────────
|
|
240
246
|
const afterSf = project.createSourceFile(filePath, newContent, {
|
|
241
247
|
overwrite: true,
|
|
@@ -286,6 +292,7 @@ class MutationApplier {
|
|
|
286
292
|
written: write && !dryRun,
|
|
287
293
|
finalContent: newContent,
|
|
288
294
|
skipped,
|
|
295
|
+
microDiff,
|
|
289
296
|
};
|
|
290
297
|
}
|
|
291
298
|
}
|
|
@@ -115,6 +115,7 @@ export declare class SafetyGuard {
|
|
|
115
115
|
constructor(arg?: ResolvedThresholds | SafetyGuardOptions);
|
|
116
116
|
private static normalizeArgs;
|
|
117
117
|
evaluate(context: HealContext, aiFix: AIFixSummary): Promise<SafetyDecision>;
|
|
118
|
+
private applyTextDriftGuard;
|
|
118
119
|
classifyFunctionalRole(text: string): FunctionalRole;
|
|
119
120
|
}
|
|
120
121
|
//# sourceMappingURL=SafetyGuard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SafetyGuard.d.ts","sourceRoot":"","sources":["../../src/services/SafetyGuard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAEL,cAAc,EACf,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"SafetyGuard.d.ts","sourceRoot":"","sources":["../../src/services/SafetyGuard.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ const IntentAuditor_1 = require("./IntentAuditor");
|
|
|
7
7
|
const PipelineContext_1 = require("./safety/PipelineContext");
|
|
8
8
|
const rules_1 = require("./safety/rules");
|
|
9
9
|
const roleClassifier_1 = require("./safety/roleClassifier");
|
|
10
|
+
const logger_1 = require("../utils/logger");
|
|
10
11
|
function dispositionFor(level) {
|
|
11
12
|
switch (level) {
|
|
12
13
|
case "SAFE":
|
|
@@ -61,6 +62,24 @@ class SafetyGuard {
|
|
|
61
62
|
// ─────────────────────────────────────────────────────────────
|
|
62
63
|
async evaluate(context, aiFix) {
|
|
63
64
|
const ctx = (0, PipelineContext_1.buildContext)(context, aiFix, this.thresholds, this.intentAuditor);
|
|
65
|
+
// ── Text-Drift Pre-Flight ─────────────────────────────────────
|
|
66
|
+
// Compare DNA baseline text vs live candidate text BEFORE any rule
|
|
67
|
+
// runs. This is the primary defence against the "Delete System →
|
|
68
|
+
// Update System same-container mis-identification" class of bug:
|
|
69
|
+
//
|
|
70
|
+
// 1. Drift confirmed (both texts present, differ):
|
|
71
|
+
// synthesise effectiveContentChange so StructuralOnlyRule
|
|
72
|
+
// cannot short-circuit to SAFE, and set forceAudit so the
|
|
73
|
+
// Intent Auditor is mandatory regardless of confidence.
|
|
74
|
+
//
|
|
75
|
+
// 2. Drift unverifiable (DNA text present but liveText absent
|
|
76
|
+
// AND AI did not self-report a contentChange):
|
|
77
|
+
// set forceAudit so StructuralOnlyRule downgrades SAFE → HOLD.
|
|
78
|
+
//
|
|
79
|
+
// This guard runs once, synchronously, before the rule loop.
|
|
80
|
+
// ZeroTrustRule (step 2) may re-derive effectiveContentChange from
|
|
81
|
+
// the same inputs; that is harmless — it will set the same value.
|
|
82
|
+
this.applyTextDriftGuard(ctx, aiFix);
|
|
64
83
|
for (const rule of rules_1.SAFETY_RULES) {
|
|
65
84
|
const decision = await rule.run(ctx);
|
|
66
85
|
if (decision) {
|
|
@@ -73,6 +92,28 @@ class SafetyGuard {
|
|
|
73
92
|
// Unreachable: ConfidenceTierRule is terminal and always decides.
|
|
74
93
|
throw new Error("[SafetyGuard] rule pipeline exhausted without a decision - this is a bug");
|
|
75
94
|
}
|
|
95
|
+
applyTextDriftGuard(ctx, aiFix) {
|
|
96
|
+
const dnaText = aiFix.dnaBaselineText?.trim();
|
|
97
|
+
const liveText = aiFix.liveText?.trim();
|
|
98
|
+
if (dnaText && liveText && dnaText !== liveText) {
|
|
99
|
+
// Drift confirmed — synthesise contentChange if ZeroTrustRule has not
|
|
100
|
+
// yet run (it will run next and will produce the same pair; no conflict).
|
|
101
|
+
if (!ctx.effectiveContentChange) {
|
|
102
|
+
ctx.effectiveContentChange = { oldText: dnaText, newText: liveText };
|
|
103
|
+
}
|
|
104
|
+
ctx.forceAudit = true;
|
|
105
|
+
logger_1.logger.warn(`SafetyGuard text-drift pre-flight: DNA="${dnaText}" ≠ live="${liveText}" — ` +
|
|
106
|
+
`forceAudit=true, Intent Auditor is mandatory`);
|
|
107
|
+
}
|
|
108
|
+
else if (dnaText && !liveText && !aiFix.contentChange) {
|
|
109
|
+
// Cannot verify: DNA baseline exists but live text could not be fetched
|
|
110
|
+
// and the AI did not self-report a contentChange. Conservative stance:
|
|
111
|
+
// force a hold so StructuralOnlyRule cannot silently approve as SAFE.
|
|
112
|
+
ctx.forceAudit = true;
|
|
113
|
+
logger_1.logger.warn(`SafetyGuard text-drift pre-flight: liveText unavailable, ` +
|
|
114
|
+
`DNA="${dnaText}" unverified — forceAudit=true`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
76
117
|
// ─────────────────────────────────────────────────────────────
|
|
77
118
|
// FUNCTIONAL ROLE CLASSIFICATION (public for tests / external use)
|
|
78
119
|
//
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { FunctionalRole } from "../roleClassifier";
|
|
2
2
|
import type { SafetyRule } from "../PipelineContext";
|
|
3
3
|
/**
|
|
4
|
-
* Roles whose
|
|
5
|
-
* obviously mean "delete" or "authenticate" before, but now does.
|
|
4
|
+
* Roles whose arrival OR departure is treated as high-risk.
|
|
6
5
|
*/
|
|
7
6
|
export declare const DANGEROUS_SINK_ROLES: ReadonlySet<FunctionalRole>;
|
|
7
|
+
/** Transition INTO a dangerous role (e.g. UNKNOWN → DELETE). */
|
|
8
8
|
export declare function isDangerousSinkTransition(oldRole: FunctionalRole, newRole: FunctionalRole): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Transition OUT OF a dangerous role (e.g. DELETE → UNKNOWN / SUBMIT).
|
|
11
|
+
*
|
|
12
|
+
* Replacing a destructive/auth control with any other role is semantically
|
|
13
|
+
* inconsistent: the test was authored against a destructive action and the
|
|
14
|
+
* proposed candidate performs a different operation.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isDangerousSourceTransition(oldRole: FunctionalRole, newRole: FunctionalRole): boolean;
|
|
9
17
|
export declare const DangerousSinkRule: SafetyRule;
|
|
10
18
|
//# sourceMappingURL=DangerousSinkRule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DangerousSinkRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/DangerousSinkRule.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DangerousSinkRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/DangerousSinkRule.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,oBAAoB,CAAC;AAExF;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,cAAc,CAG3D,CAAC;AAEH,gEAAgE;AAChE,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,cAAc,GACtB,OAAO,CAET;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,cAAc,GACtB,OAAO,CAET;AAED,eAAO,MAAM,iBAAiB,EAAE,UA4B/B,CAAC"}
|
|
@@ -1,40 +1,55 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// src/services/safety/rules/DangerousSinkRule.ts
|
|
3
3
|
//
|
|
4
|
-
// Step 5.5 - Dangerous-Sink escalation (context-mutator,
|
|
4
|
+
// Step 5.5 - Dangerous-Sink / Dangerous-Source escalation (context-mutator,
|
|
5
|
+
// returns null).
|
|
5
6
|
//
|
|
6
|
-
// Closes
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
7
|
+
// Closes two related blind spots:
|
|
8
|
+
//
|
|
9
|
+
// SINK (X → DELETE/AUTH): a control that did not obviously mean "delete"
|
|
10
|
+
// or "authenticate" before, but now does. The cross-role swap rule
|
|
11
|
+
// (step 5) only fires when BOTH roles are known; this catches
|
|
12
|
+
// UNKNOWN → DELETE.
|
|
13
|
+
//
|
|
14
|
+
// SOURCE (DELETE/AUTH → X): replacing a destructive/auth control with a
|
|
15
|
+
// non-destructive one (e.g. "Delete System" → "Update System")
|
|
16
|
+
// is equally dangerous — it masks the test's original destructive
|
|
17
|
+
// intent. FunctionalRoleRule misses this because newRole=UNKNOWN
|
|
18
|
+
// fails its "both roles known" guard.
|
|
12
19
|
//
|
|
13
20
|
// Per design decision #2, this rule does NOT decide on its own; it FORCES
|
|
14
|
-
// the Intent Auditor to run (ctx.forceAudit = true)
|
|
15
|
-
// a dangerous sink and differs from the old role. The auditor's existing
|
|
21
|
+
// the Intent Auditor to run (ctx.forceAudit = true). The auditor's existing
|
|
16
22
|
// verdict mapping then applies: INCONSISTENT → FAIL_HARD, SUSPICIOUS →
|
|
17
|
-
// BLOCKED/REQUIRES_REVIEW, CONSISTENT → continue.
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// is already short-circuited by FunctionalRoleRule before reaching here, so
|
|
21
|
-
// in practice this rule fires for the UNKNOWN→sink case it was built for.
|
|
23
|
+
// BLOCKED/REQUIRES_REVIEW, CONSISTENT → continue. In practice the
|
|
24
|
+
// pre-LLM structural check in IntentAuditorRule will FAIL_HARD immediately
|
|
25
|
+
// for SOURCE transitions before the LLM is even consulted.
|
|
22
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
27
|
exports.DangerousSinkRule = exports.DANGEROUS_SINK_ROLES = void 0;
|
|
24
28
|
exports.isDangerousSinkTransition = isDangerousSinkTransition;
|
|
29
|
+
exports.isDangerousSourceTransition = isDangerousSourceTransition;
|
|
25
30
|
const roleClassifier_1 = require("../roleClassifier");
|
|
26
31
|
const logger_1 = require("../../../utils/logger");
|
|
27
32
|
/**
|
|
28
|
-
* Roles whose
|
|
29
|
-
* obviously mean "delete" or "authenticate" before, but now does.
|
|
33
|
+
* Roles whose arrival OR departure is treated as high-risk.
|
|
30
34
|
*/
|
|
31
35
|
exports.DANGEROUS_SINK_ROLES = new Set([
|
|
32
36
|
"DELETE",
|
|
33
37
|
"AUTH",
|
|
34
38
|
]);
|
|
39
|
+
/** Transition INTO a dangerous role (e.g. UNKNOWN → DELETE). */
|
|
35
40
|
function isDangerousSinkTransition(oldRole, newRole) {
|
|
36
41
|
return exports.DANGEROUS_SINK_ROLES.has(newRole) && newRole !== oldRole;
|
|
37
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Transition OUT OF a dangerous role (e.g. DELETE → UNKNOWN / SUBMIT).
|
|
45
|
+
*
|
|
46
|
+
* Replacing a destructive/auth control with any other role is semantically
|
|
47
|
+
* inconsistent: the test was authored against a destructive action and the
|
|
48
|
+
* proposed candidate performs a different operation.
|
|
49
|
+
*/
|
|
50
|
+
function isDangerousSourceTransition(oldRole, newRole) {
|
|
51
|
+
return exports.DANGEROUS_SINK_ROLES.has(oldRole) && newRole !== oldRole;
|
|
52
|
+
}
|
|
38
53
|
exports.DangerousSinkRule = {
|
|
39
54
|
id: "dangerous-sink-escalation",
|
|
40
55
|
run(ctx) {
|
|
@@ -48,6 +63,12 @@ exports.DangerousSinkRule = {
|
|
|
48
63
|
`forcing Intent Auditor`);
|
|
49
64
|
ctx.forceAudit = true;
|
|
50
65
|
}
|
|
66
|
+
if (isDangerousSourceTransition(oldRole, newRole)) {
|
|
67
|
+
logger_1.logger.warn(`SafetyGuard dangerous-source transition: ` +
|
|
68
|
+
`"${ctx.oldText}" (${oldRole}) → "${ctx.newText}" (${newRole}) - ` +
|
|
69
|
+
`outbound from destructive role, forcing Intent Auditor`);
|
|
70
|
+
ctx.forceAudit = true;
|
|
71
|
+
}
|
|
51
72
|
return null;
|
|
52
73
|
},
|
|
53
74
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntentAuditorRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/IntentAuditorRule.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"IntentAuditorRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/IntentAuditorRule.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,oBAAoB,CAAC;AAExF,eAAO,MAAM,iBAAiB,EAAE,UAoI/B,CAAC"}
|