Files
brain/tools/seal-orchestration.test.mjs
T
Дмитрий bbc053e0a6 feat: D1 — благословлённый ops-runbook (деплой выполняет агент под ревью)
Деплой, помеченный **Kind:** deploy и опечатанный (наставник+судья GO,
judge_mode=live-block), агент выполняет по белому списку шагов под ОДНИМ
согласием владельца `FLOOR-ESCAPE: ops-runbook:<plan-hash>` — без аварийного
выхода на каждую команду. «Ядерный» набор (rm -rf/force-push/migrate:fresh/
db:wipe) остаётся на per-command escape.

- plan-lock: freezePlan принимает kind (в подписанную базу + хеш, как delivery);
  не-'normal' добавляет поле, обычные планы байт-идентичны старым печатям.
- plan-skills: parsePlanKind (**Kind:** deploy|normal, default normal).
- seal-orchestration: sealablePlan/sealPlan прокидывают kind в печать.
- escape-grant: loadOpsRunbookGrants (окно = существование плана, БЕЗ 5-мин
  фильтра) + opsRunbookGrantOpen (точный матч на plan_id).
- floor-decide: floorDecide получает инъектируемый blessedOps(cmd); content-block
  команда из набора пропускается, ЯДЕРНЫЙ набор (bashIsFloor) исключён из послабления.
- blessed-ops (новый модуль-мост): buildBlessedOps + loadBlessedOpsForSession —
  знает план+пол, чтобы СОХРАНИТЬ Δ9 (enforce-floor не зависит от модуля печати плана).
  Предикат пускает команду только дословно из Bash-листов опечатанного deploy-плана.
- enforce-floor: gated — blessed-ops грузит план/гранты ТОЛЬКО при открытом
  ops-runbook-гранте; без согласия владельца пол плана не касается (Δ9 цел).

План: docs/superpowers/plans/2026-06-18-blessed-ops-runbook-plan.md
Спека: docs/superpowers/specs/2026-06-18-blessed-ops-runbook-design.md §3.1-3.7.
+33 теста, свод 4299 passed / 2 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 13:19:22 +03:00

139 lines
7.8 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { sealableArtifact, sealablePlan, judgedHashOf, sealArtifact, sealPlan, ownerSealAction, decideSeal, ownerSealActionForContent } from './seal-orchestration.mjs';
import { contentHash } from './judge-seal-channel.mjs';
import { planId } from './plan-lock.mjs';
const specMd = '## Реш {#dec-a}\nтекст';
const planMd = '```steps-json\n[{"op":"Edit","object":"app/Foo.php","ref":"dec-a"}]\n```';
const KEY = 'k';
const goVerdict = (obj) => ({ wired: true, decision: 'GO', judged_hash: judgedHashOf(obj), verdict_id: 'v1' });
describe('seal-orchestration', () => {
it('judgedHashOf == contentHash of same sealable object (SD-1)', () => {
const a = sealableArtifact(specMd);
expect(judgedHashOf(a)).toBe(contentHash(a));
});
it('sealArtifact on real GO → sealed, verifiable', () => {
const a = sealableArtifact(specMd);
const r = sealArtifact({ md: specMd, verdict: goVerdict(a), key: KEY, judgeMode: 'live-block' });
expect(r.sealed).toBe(true);
expect(r.seal.sections['dec-a']).toBeTruthy();
expect(r.seal.judge_mode).toBe('live-block');
});
it('sealArtifact NOT sealed on degraded GO (wired:false, SE-3)', () => {
const a = sealableArtifact(specMd);
const r = sealArtifact({ md: specMd, verdict: { wired: false, decision: 'GO' }, key: KEY, judgeMode: 'shadow' });
expect(r.sealed).toBe(false);
});
it('sealArtifact NOT sealed on judged_hash mismatch (SD-1/TOCTOU)', () => {
const r = sealArtifact({ md: specMd, verdict: { wired: true, decision: 'GO', judged_hash: 'WRONG' }, key: KEY, judgeMode: 'live-block' });
expect(r.sealed).toBe(false);
});
it('sealPlan stamps artifact_id from current artifact (SD-3)', () => {
const planObj = sealablePlan(planMd);
const r = sealPlan({ md: planMd, currentArtifact: { artifact_id: 'AID' }, verdict: goVerdict(planObj), key: KEY, judgeMode: 'live-block' });
expect(r.sealed).toBe(true);
expect(r.seal.artifact_id).toBe('AID');
expect(r.seal.judge_mode).toBe('live-block');
});
it('sealPlan fail-CLOSE without current artifact (VA-1/SD-3)', () => {
const planObj = sealablePlan(planMd);
const r = sealPlan({ md: planMd, currentArtifact: null, verdict: goVerdict(planObj), key: KEY, judgeMode: 'live-block' });
expect(r.sealed).toBe(false);
});
// D1: тип плана прокидывается из markdown в печать.
const deployMd = '# План\n**Kind:** deploy\n```steps-json\n[{"op":"Bash","object":"composer install","ref":"dec-a"}]\n```';
it('sealablePlan несёт kind: deploy-план → "deploy", обычный → "normal"', () => {
expect(sealablePlan(deployMd).kind).toBe('deploy');
expect(sealablePlan(planMd).kind).toBe('normal');
});
it('sealPlan печатает kind:"deploy" в опечатанный план', () => {
const planObj = sealablePlan(deployMd);
const r = sealPlan({ md: deployMd, currentArtifact: { artifact_id: 'AID' }, verdict: goVerdict(planObj), key: KEY, judgeMode: 'live-block' });
expect(r.sealed).toBe(true);
expect(r.seal.kind).toBe('deploy');
});
});
describe('owner-seal (SP3)', () => {
it('ownerSealAction → каноническая метка owner-seal:<hash>', () => {
expect(ownerSealAction('abc123')).toBe('owner-seal:abc123');
});
it('ownerSealAction нулевой hash → owner-seal:', () => {
expect(ownerSealAction(null)).toBe('owner-seal:');
});
it('decideSeal: реальный GO → печать via wired-go', () => {
expect(decideSeal({ verdict: { wired: true, decision: 'GO' } })).toEqual({ seal: true, via: 'wired-go' });
});
it('decideSeal: NO-GO + ownerSealOpen → печать via owner-seal-override (перевешивает)', () => {
expect(decideSeal({ verdict: { wired: true, decision: 'NO-GO' }, ownerSealOpen: true })).toEqual({ seal: true, via: 'owner-seal-override' });
});
it('decideSeal: NO-GO без owner-seal → нет печати', () => {
expect(decideSeal({ verdict: { wired: true, decision: 'NO-GO' }, ownerSealOpen: false })).toEqual({ seal: false, via: null });
});
it('decideSeal: degraded (wired:false) + ownerSealOpen → печать via owner-seal-override', () => {
expect(decideSeal({ verdict: { wired: false, decision: 'GO' }, ownerSealOpen: true })).toEqual({ seal: true, via: 'owner-seal-override' });
});
it('decideSeal: GO + ownerSealRequired (деньги/тупик) без owner-seal → ownerRequired, нет печати (carve-out §6)', () => {
expect(decideSeal({ verdict: { wired: true, decision: 'GO' }, ownerSealRequired: true }))
.toEqual({ seal: false, via: null, ownerRequired: true });
});
it('decideSeal: GO + ownerSealRequired + ownerSealOpen → печать via owner-seal-carveout', () => {
expect(decideSeal({ verdict: { wired: true, decision: 'GO' }, ownerSealRequired: true, ownerSealOpen: true }))
.toEqual({ seal: true, via: 'owner-seal-carveout' });
});
});
describe('owner-seal в seal-функциях (SP3-b3)', () => {
const moneyMd = '## Реш {#dec-a}\nсписание 100 ₽ с баланса';
it('sealArtifact: NO-GO + ownerSealOpen → печать (override), owner_sealed', () => {
const r = sealArtifact({ md: specMd, verdict: { wired: true, decision: 'NO-GO' }, key: KEY, judgeMode: 'live-block', ownerSealOpen: true });
expect(r.sealed).toBe(true);
expect(r.seal.owner_sealed).toBe(true);
});
it('sealArtifact: GO + деньги без owner-seal → ownerRequired (carve-out §6)', () => {
const a = sealableArtifact(moneyMd);
const r = sealArtifact({ md: moneyMd, verdict: goVerdict(a), key: KEY, judgeMode: 'live-block' });
expect(r.sealed).toBe(false);
expect(r.ownerRequired).toBe(true);
});
it('sealArtifact: GO + деньги + ownerSealOpen → печать (carveout перевешивает)', () => {
const a = sealableArtifact(moneyMd);
const r = sealArtifact({ md: moneyMd, verdict: goVerdict(a), key: KEY, judgeMode: 'live-block', ownerSealOpen: true });
expect(r.sealed).toBe(true);
expect(r.seal.owner_sealed).toBe(true);
});
it('sealPlan: NO-GO + ownerSealOpen + currentArtifact → печать (override)', () => {
const r = sealPlan({ md: planMd, currentArtifact: { artifact_id: 'AID' }, verdict: { wired: true, decision: 'NO-GO' }, key: KEY, judgeMode: 'live-block', ownerSealOpen: true });
expect(r.sealed).toBe(true);
expect(r.seal.artifact_id).toBe('AID');
});
it('sealPlan: GO + deadlock без owner-seal → ownerRequired (новый carve-out плана, §0)', () => {
const planObj = sealablePlan(planMd);
const r = sealPlan({ md: planMd, currentArtifact: { artifact_id: 'AID' }, verdict: { ...goVerdict(planObj), deadlock: true }, key: KEY, judgeMode: 'live-block' });
expect(r.sealed).toBe(false);
expect(r.ownerRequired).toBe(true);
});
});
describe('ownerSealActionForContent (SP3-c)', () => {
it('план (есть steps-json) → owner-seal:planId(steps) (как в sealTurnProd)', () => {
expect(ownerSealActionForContent(planMd)).toBe(ownerSealAction(planId(sealablePlan(planMd).steps)));
});
it('спека (нет steps) → owner-seal:judgedHashOf(sealableArtifact) (как в sealTurnProd)', () => {
expect(ownerSealActionForContent(specMd)).toBe(ownerSealAction(judgedHashOf(sealableArtifact(specMd))));
});
});
describe('sealablePlan delivery', () => {
it('несёт delivery из тела плана', () => {
const md = '## Цель\nx\n**Delivery:** user-result\n```steps-json\n[{"op":"Write","object":"a.mjs","ref":"x"}]\n```';
expect(sealablePlan(md).delivery).toBe('user-result');
});
it('без пометки → internal', () => {
const md = '```steps-json\n[{"op":"Write","object":"a.mjs","ref":"x"}]\n```';
expect(sealablePlan(md).delivery).toBe('internal');
});
});