feat: round-memory owner-seal ownerSealAction decideSeal SP3-a ядро

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-06-16 18:41:29 +03:00
parent 3f24deddca
commit 171f7fb44a
2 changed files with 41 additions and 1 deletions
+19
View File
@@ -43,3 +43,22 @@ export function sealPlan({ md, currentArtifact, verdict, key, judgeMode, nowMs,
const seal = freezeImpl({ steps: planObj.steps, skills: planObj.skills, artifactId: currentArtifact.artifact_id, judgeMode, key, nowMs });
return { sealed: true, seal };
}
// ── owner-seal (SP3 / Фикс 3) ──
// Владелец на арбитраже опечатывает артефакт/план как есть, перевешивая NO-GO судьи И
// наставника. Каноническая строка действия = `owner-seal:<hash>` — её подписывает escape-грант
// владельца (router-mentor-receipts); контроллер подделать не может (protected runtime).
/** Каноническая метка owner-seal для escape-гранта. hash = judged_hash артефакта/плана. */
export function ownerSealAction(hash) {
return `owner-seal:${String(hash == null ? '' : hash)}`;
}
/** Решение печати с учётом owner-seal. Реальный GO (wired && GO) → печать обычным путём.
* Иначе ownerSealOpen (владелец подписал owner-seal на этот hash) → печать несмотря на
* NO-GO/degraded. Ни того, ни другого → нет печати. */
export function decideSeal({ verdict, ownerSealOpen = false } = {}) {
if (isRealGo(verdict)) return { seal: true, via: 'wired-go' };
if (ownerSealOpen) return { seal: true, via: 'owner-seal' };
return { seal: false, via: null };
}
+22 -1
View File
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { sealableArtifact, sealablePlan, judgedHashOf, sealArtifact, sealPlan } from './seal-orchestration.mjs';
import { sealableArtifact, sealablePlan, judgedHashOf, sealArtifact, sealPlan, ownerSealAction, decideSeal } from './seal-orchestration.mjs';
import { contentHash } from './judge-seal-channel.mjs';
const specMd = '## Реш {#dec-a}\nтекст';
@@ -41,3 +41,24 @@ describe('seal-orchestration', () => {
expect(r.sealed).toBe(false);
});
});
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 (перевешивает)', () => {
expect(decideSeal({ verdict: { wired: true, decision: 'NO-GO' }, ownerSealOpen: true })).toEqual({ seal: true, via: 'owner-seal' });
});
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', () => {
expect(decideSeal({ verdict: { wired: false, decision: 'GO' }, ownerSealOpen: true })).toEqual({ seal: true, via: 'owner-seal' });
});
});