feat: round-memory owner-seal sealOnApproval bypass SP3-b2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -47,10 +47,21 @@ export function requiresOwnerSeal({ artifact, verdict }) {
|
||||
* @param {Function} a.freezeImpl - ({artifact,key,nowMs}) => sealed-object. ОБЯЗАТЕЛЕН;
|
||||
* в проде оркестрация 4-E передаёт freezeArtifact Машины 2 (ESM-импортом, не require).
|
||||
*/
|
||||
export function sealOnApproval({ artifact, verdict, key, nowMs, freezeImpl }) {
|
||||
export function sealOnApproval({ artifact, verdict, key, nowMs, freezeImpl, ownerSealOpen = false }) {
|
||||
if (typeof freezeImpl !== 'function') {
|
||||
return { sealed: false, reason: 'freezeImpl не передан — печать-движок Машины 2 не подключён' };
|
||||
}
|
||||
// SP3-b (owner-seal): владелец подписал owner-seal:<хеш ЭТОГО тела> (escape-грант) → печать
|
||||
// минуя GO/судью/carve-out. hash-интегрити обеспечен совпадением гранта над contentHash(artifact)
|
||||
// в sealTurnProd (тот же объект — подмена тела ломает совпадение гранта). Штамп owner_sealed.
|
||||
if (ownerSealOpen) {
|
||||
const ownerHash = contentHash(artifact);
|
||||
const seal = freezeImpl({
|
||||
artifact: { ...artifact, judged_by: (verdict && verdict.verdict_id) ?? null, judged_hash: ownerHash, owner_sealed: true },
|
||||
key, nowMs,
|
||||
});
|
||||
return { sealed: true, seal, via: 'owner-seal' };
|
||||
}
|
||||
if (!verdict || verdict.decision !== 'GO') {
|
||||
return { sealed: false, reason: 'нет GO судьи — печать не ставится' };
|
||||
}
|
||||
|
||||
@@ -37,6 +37,23 @@ describe('sealOnApproval (K1 печать только по одобрению +
|
||||
expect(r.sealed).toBe(false);
|
||||
expect(r.ownerRequired).toBe(true);
|
||||
});
|
||||
it('SP3-b: ownerSealOpen → печать минуя GO (override NO-GO), штамп owner_sealed', () => {
|
||||
const r = sealOnApproval({ artifact: ART, verdict: { decision: 'NO-GO' }, key: 'k', freezeImpl: stubFreeze, ownerSealOpen: true });
|
||||
expect(r.sealed).toBe(true);
|
||||
expect(r.seal.owner_sealed).toBe(true);
|
||||
expect(r.via).toBe('owner-seal');
|
||||
});
|
||||
it('SP3-b: ownerSealOpen перевешивает денежный carve-out → печать ставится', () => {
|
||||
const moneyArt = { sections: { '§1': 'списание 100 ₽ с баланса' }, goal: 'биллинг' };
|
||||
const r = sealOnApproval({ artifact: moneyArt, verdict: verdict(moneyArt), key: 'k', freezeImpl: stubFreeze, ownerSealOpen: true });
|
||||
expect(r.sealed).toBe(true);
|
||||
expect(r.seal.owner_sealed).toBe(true);
|
||||
});
|
||||
it('SP3-b: ownerSealOpen без вердикта (degraded) → печать, judged_hash = хеш тела', () => {
|
||||
const r = sealOnApproval({ artifact: ART, verdict: null, key: 'k', freezeImpl: stubFreeze, ownerSealOpen: true });
|
||||
expect(r.sealed).toBe(true);
|
||||
expect(r.seal.judged_hash).toBe(contentHash(ART));
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectMoney (осторожный детерминированный детектор)', () => {
|
||||
|
||||
Reference in New Issue
Block a user