sela-core 0.1.0-alpha.3 → 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/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/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/package.json +1 -1
|
@@ -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":"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"}
|
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.
|
|
@@ -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"}
|
|
@@ -5,18 +5,25 @@
|
|
|
5
5
|
// LLM auditor when:
|
|
6
6
|
// a) zero-trust synthesized the contentChange (AI silent → extra scrutiny), OR
|
|
7
7
|
// b) we are in assertion mode (the test explicitly checked the text), OR
|
|
8
|
-
// c) both roles are UNKNOWN (no functional category to fall back on)
|
|
8
|
+
// c) both roles are UNKNOWN (no functional category to fall back on), OR
|
|
9
|
+
// d) forceAudit was set by the text-drift pre-flight or DangerousSinkRule.
|
|
10
|
+
//
|
|
11
|
+
// PRE-LLM STRUCTURAL CHECK (runs before shouldAudit):
|
|
12
|
+
// Transitioning OUT of a destructive/auth role into any non-destructive role
|
|
13
|
+
// is always semantically inconsistent — the test was authored against a
|
|
14
|
+
// DELETE/AUTH action and the candidate performs a different operation. This
|
|
15
|
+
// is caught here rather than relying solely on the LLM auditor because the
|
|
16
|
+
// LLM can return CONSISTENT for superficially similar labels (e.g. "Delete
|
|
17
|
+
// System" → "Update System" sharing the same container at 95% confidence).
|
|
9
18
|
//
|
|
10
19
|
// INCONSISTENT → FAIL_HARD. SUSPICIOUS → BLOCKED in assertion (unless
|
|
11
20
|
// allowSuspicious) else REQUIRES_REVIEW. CONSISTENT → fall through,
|
|
12
21
|
// stashing the auditor score breakdown on the context for the final tier.
|
|
13
|
-
//
|
|
14
|
-
// Extracted verbatim - identical trigger condition, reasons, codes,
|
|
15
|
-
// logs, and meta shape.
|
|
16
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
23
|
exports.IntentAuditorRule = void 0;
|
|
18
24
|
const SelaError_1 = require("../../../errors/SelaError");
|
|
19
25
|
const logger_1 = require("../../../utils/logger");
|
|
26
|
+
const DangerousSinkRule_1 = require("./DangerousSinkRule");
|
|
20
27
|
exports.IntentAuditorRule = {
|
|
21
28
|
id: "intent-auditor-gate",
|
|
22
29
|
async run(ctx) {
|
|
@@ -24,6 +31,31 @@ exports.IntentAuditorRule = {
|
|
|
24
31
|
const newText = ctx.newText;
|
|
25
32
|
const oldRole = ctx.oldRole;
|
|
26
33
|
const newRole = ctx.newRole;
|
|
34
|
+
// ── Pre-LLM structural check: destructive role inversion ─────────
|
|
35
|
+
// When the DNA element had a destructive/auth role and the candidate does
|
|
36
|
+
// NOT, reject immediately without consulting the LLM. AI confidence is
|
|
37
|
+
// irrelevant — no valid heal should replace a DELETE/AUTH control with a
|
|
38
|
+
// non-destructive one. The LLM can return CONSISTENT for "Delete System →
|
|
39
|
+
// Update System" because both appear in the same container; this pure-code
|
|
40
|
+
// gate closes that bypass regardless of the shouldAudit flag below.
|
|
41
|
+
if (DangerousSinkRule_1.DANGEROUS_SINK_ROLES.has(oldRole) &&
|
|
42
|
+
!DangerousSinkRule_1.DANGEROUS_SINK_ROLES.has(newRole)) {
|
|
43
|
+
logger_1.logger.warn(`SafetyGuard destructive role inversion (pre-LLM): ` +
|
|
44
|
+
`"${oldText}" (${oldRole}) → "${newText}" (${newRole}) - FAIL_HARD`);
|
|
45
|
+
return {
|
|
46
|
+
level: "FAIL_HARD",
|
|
47
|
+
code: SelaError_1.SAFETY_GUARD_CODES.FAIL_HARD_DESTRUCTIVE_ROLE_INVERSION,
|
|
48
|
+
reason: `Destructive role inversion: "${oldText}" (${oldRole}) → "${newText}" (${newRole}) — ` +
|
|
49
|
+
`replacing a ${oldRole} action is semantically inconsistent regardless of confidence`,
|
|
50
|
+
canProceed: false,
|
|
51
|
+
meta: {
|
|
52
|
+
auditorVerdict: "INCONSISTENT",
|
|
53
|
+
auditorConfidence: 100,
|
|
54
|
+
auditorReason: `Source role ${oldRole} is destructive/auth; ` +
|
|
55
|
+
`candidate role ${newRole} is not — structural semantic mismatch`,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
27
59
|
const shouldAudit = ctx.forceAudit === true ||
|
|
28
60
|
ctx.source === "zero-trust" ||
|
|
29
61
|
ctx.mode === "assertion" ||
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StructuralOnlyRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/StructuralOnlyRule.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,oBAAoB,CAAC;AAExF,eAAO,MAAM,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"StructuralOnlyRule.d.ts","sourceRoot":"","sources":["../../../../src/services/safety/rules/StructuralOnlyRule.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,oBAAoB,CAAC;AAExF,eAAO,MAAM,kBAAkB,EAAE,UAwChC,CAAC"}
|
|
@@ -19,6 +19,19 @@ exports.StructuralOnlyRule = {
|
|
|
19
19
|
id: "structural-only-heal",
|
|
20
20
|
run(ctx) {
|
|
21
21
|
if (!ctx.effectiveContentChange) {
|
|
22
|
+
// Text-drift pre-flight set forceAudit because live text was unavailable
|
|
23
|
+
// and we cannot verify the candidate has the same text as the DNA
|
|
24
|
+
// baseline. Approving as SAFE here would bypass every downstream guard
|
|
25
|
+
// (semantic-opposite, functional-role, intent-auditor). Downgrade to
|
|
26
|
+
// REQUIRES_REVIEW (HOLD) so the heal is quarantined pending manual review.
|
|
27
|
+
if (ctx.forceAudit) {
|
|
28
|
+
return {
|
|
29
|
+
level: "REQUIRES_REVIEW",
|
|
30
|
+
reason: `Text drift unverifiable: DNA baseline present but live text unavailable ` +
|
|
31
|
+
`— cannot approve as structural-only (confidence=${ctx.confidence})`,
|
|
32
|
+
canProceed: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
22
35
|
const level = ctx.confidence < ctx.thresholds.confidenceReviewThreshold
|
|
23
36
|
? "REQUIRES_REVIEW"
|
|
24
37
|
: "SAFE";
|
package/package.json
CHANGED