feat(m7-phase2): escapeAllowsEvent — panic-предикат escape-survivability (правило 7б)
This commit is contained in:
@@ -67,6 +67,22 @@ export function escapeGrantOpen(action, grants, consumed, now = Date.now()) {
|
||||
return findOpenGrant(action, grants, consumed, now) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Panic-предикат (M7 Фаза 2, правило 7б): покрывает ли открытый escape-грант
|
||||
* действие из события инструмента. Тотален (canonicalAction из Фазы 0 не бросает;
|
||||
* внешний try на любой остаток → false). Для panic-веток floor/supreme-gate:
|
||||
* escape оценивается даже когда per-tool-логика бросает (иначе баг = кирпич мимо escape).
|
||||
*/
|
||||
export function escapeAllowsEvent(event, grants, consumed, now = Date.now()) {
|
||||
try {
|
||||
const name = event && event.tool_name;
|
||||
const input = (event && event.tool_input) || {};
|
||||
return escapeGrantOpen(canonicalAction(name, input), grants, consumed, now);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** I/O: floor_escape-пропуски сессии (зеркало shell-content::loadApprovedGitOps). */
|
||||
export function loadFloorEscapes(sessionId, now = Date.now()) {
|
||||
return loadRecords(sessionId, 'floor_escape', 'action').filter((g) => now - g.ts <= FLOOR_ESCAPE_WINDOW_MS);
|
||||
|
||||
@@ -97,3 +97,25 @@ describe('canonicalAction тотальна (M7 Фаза 0, правило 7а, S
|
||||
expect(canonicalAction('PowerShell', { command: 'Get-ChildItem' })).toBe('powershell:Get-ChildItem');
|
||||
});
|
||||
});
|
||||
|
||||
import { escapeAllowsEvent } from './escape-grant.mjs';
|
||||
describe('escapeAllowsEvent — panic-предикат (M7 Фаза 2, правило 7б)', () => {
|
||||
const now = 1_000_000;
|
||||
const ev = (tool_name, tool_input) => ({ tool_name, tool_input });
|
||||
it('матч-грант на действие события → true', () => {
|
||||
const action = canonicalAction('Bash', { command: 'git push --force' });
|
||||
const grants = [{ action, ts: now - 1000 }];
|
||||
expect(escapeAllowsEvent(ev('Bash', { command: 'git push --force' }), grants, [], now)).toBe(true);
|
||||
});
|
||||
it('несовпавший грант → false', () => {
|
||||
const grants = [{ action: 'bash:reset --hard', ts: now - 1000 }];
|
||||
expect(escapeAllowsEvent(ev('Bash', { command: 'git push --force' }), grants, [], now)).toBe(false);
|
||||
});
|
||||
it('пустые гранты → false', () => {
|
||||
expect(escapeAllowsEvent(ev('Bash', { command: 'x' }), [], [], now)).toBe(false);
|
||||
});
|
||||
it('малформ-event не бросает → false', () => {
|
||||
expect(escapeAllowsEvent(undefined, [], [], now)).toBe(false);
|
||||
expect(escapeAllowsEvent(null, null, null, now)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user