From cadbdf177ae4d7592d52edf90a55bed4e4e6b25c Mon Sep 17 00:00:00 2001 From: mptyl Date: Tue, 7 Jul 2026 13:22:07 +0200 Subject: [PATCH] feat(memory): memory_promoted/memory_promotion_declined decision types (F8 emits) Co-Authored-By: Claude Fable 5 --- harness/tests/test_memory_promotion.py | 26 ++++++++++++++++++++++++++ harness/tht/decisions.py | 5 +++++ harness/workflow.yaml | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 harness/tests/test_memory_promotion.py diff --git a/harness/tests/test_memory_promotion.py b/harness/tests/test_memory_promotion.py new file mode 100644 index 00000000..acde0b81 --- /dev/null +++ b/harness/tests/test_memory_promotion.py @@ -0,0 +1,26 @@ +"""L1: gate di promozione memorie (F8) — tipi di decisione + filtro candidati. + +La promozione era solo CLI facoltativa (mai innescata): il gate reviewer_memory_promote +la rende un passo del workflow. Questi test fissano il contratto harness-side: +- i due nuovi decision type esistono e sono ammessi dalla Fase 8 (workflow.yaml emits) +- i candidati rifiutati al gate (memory_promotion_declined, detail "seq:") non + vengono riproposti da reusable_promotions/preview_promotions. +""" +from datetime import datetime + +from tht.decisions import DecisionRecord +from tht.workflow import load_workflow + + +def test_promotion_decision_types_are_valid(): + for t in ("memory_promoted", "memory_promotion_declined"): + d = DecisionRecord( + seq=1, ts=datetime(2026, 1, 1), type=t, subject="fact_x", detail="seq:5" + ) + assert d.type == t + + +def test_promotion_decision_types_min_phase_is_f8(): + wf = load_workflow() + assert wf.decision_min_phase("memory_promoted") == 8 + assert wf.decision_min_phase("memory_promotion_declined") == 8 diff --git a/harness/tht/decisions.py b/harness/tht/decisions.py index 2f1d75c6..35916e69 100644 --- a/harness/tht/decisions.py +++ b/harness/tht/decisions.py @@ -34,6 +34,11 @@ DecisionType = Literal[ "phase_skipped", "datamart_requested", "datamart_declined", + # F8: promozione memorie riusabili al gate reviewer_memory_promote. subject = + # subject della decisione originale, detail = "seq:" (usato da + # declined_promotion_seqs per non riproporre i candidati rifiutati). + "memory_promoted", + "memory_promotion_declined", # D15: marker di ritrazione. subject = "phase:N", retracts = decision_seq ritirata. # Resta nel log di audit (append-only); effective_decisions() la esclude dalla vista. "decision_retracted", diff --git a/harness/workflow.yaml b/harness/workflow.yaml index 366e4d41..8303b5c2 100644 --- a/harness/workflow.yaml +++ b/harness/workflow.yaml @@ -68,7 +68,7 @@ phases: - decision_exists: datamart_requested - decision_exists: datamart_declined artifacts_out: [] - emits: [datamart_requested, datamart_declined] + emits: [datamart_requested, datamart_declined, memory_promoted, memory_promotion_declined] decision_min_phase: auto max_phase: auto