feat: round-memory owner-seal decideSeal carve-out via-labels SP3-b1

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-06-16 19:10:24 +03:00
parent 171f7fb44a
commit 96a6204b73
2 changed files with 27 additions and 10 deletions
+15 -6
View File
@@ -54,11 +54,20 @@ 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' };
/** Решение печати с учётом owner-seal и carve-out §6 (деньги/тупик).
* - Чистый GO без чувствительной темы → печать обычным путём (wired-go).
* - Владелец подписал owner-seal:<хеш тела> → печать. Повод различаем для аудита:
* carveout — судья дал GO, но тема денежная/тупик (§6), владелец подтвердил;
* override — судья NO-GO/не дозвонился, владелец перевесил.
* - GO + чувствительная тема, но владелец ещё не подписал → штатно зовём владельца
* (ownerRequired, carve-out §6) — это ожидаемый путь, не спор.
* - Ни GO, ни owner-seal → нет печати.
* ownerSealRequired = requiresOwnerSeal({artifact,verdict}) — единственный детектор «тема
* чувствительная» (не дублируется здесь). Интеграция owner-seal ↔ requiresOwnerSeal: один
* механизм подписи owner-seal закрывает И carve-out, И override. */
export function decideSeal({ verdict, ownerSealOpen = false, ownerSealRequired = false } = {}) {
if (isRealGo(verdict) && !ownerSealRequired) return { seal: true, via: 'wired-go' };
if (ownerSealOpen) return { seal: true, via: isRealGo(verdict) ? 'owner-seal-carveout' : 'owner-seal-override' };
if (isRealGo(verdict) && ownerSealRequired) return { seal: false, via: null, ownerRequired: true };
return { seal: false, via: null };
}
+12 -4
View File
@@ -52,13 +52,21 @@ describe('owner-seal (SP3)', () => {
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 + 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', () => {
expect(decideSeal({ verdict: { wired: false, decision: 'GO' }, ownerSealOpen: true })).toEqual({ seal: true, via: 'owner-seal' });
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' });
});
});