99
1010
1111BUILD_SCHEMA = "issue_fix_pr_description_build_v0"
12+ PUBLICATION_GATE_SCHEMA = "issue_fix_pr_description_publication_gate_v0"
1213SURFACE = "issue_fix.pr_description"
1314ISSUE_REFERENCE_BLOCK_SCHEMA = "issue_fix_pr_issue_reference_block_v0"
1415
3435 "resolves" : "Resolves" ,
3536 "resolved" : "Resolves" ,
3637}
37-
3838PreferenceApplier = Callable [
3939 [str , Sequence [Mapping [str , Any ]]],
4040 Mapping [str , Any ],
4141]
4242Recall = Callable [..., dict [str , Any ]]
4343
4444
45+ def validate_issue_fix_pr_description_publication (
46+ * ,
47+ project : str | Path ,
48+ build_packet : Mapping [str , Any ] | None = None ,
49+ ) -> dict [str , Any ]:
50+ """Require only proof that the PR-description recall path executed."""
51+
52+ recall_required = (
53+ Path (project ).expanduser ().resolve ()
54+ / ".loopx/config/semantic-preference.json"
55+ ).exists ()
56+ preference = (
57+ build_packet .get ("semantic_preference" )
58+ if isinstance (build_packet , Mapping )
59+ and build_packet .get ("schema_version" ) == BUILD_SCHEMA
60+ else None
61+ )
62+ preference = preference if isinstance (preference , Mapping ) else {}
63+ recall_executed = preference .get ("recall_executed" ) is True
64+ ok = not recall_required or recall_executed
65+
66+ return {
67+ "ok" : ok ,
68+ "schema_version" : PUBLICATION_GATE_SCHEMA ,
69+ "status" : (
70+ "verified"
71+ if recall_executed
72+ else "not_required"
73+ if ok
74+ else "blocked"
75+ ),
76+ "blocker" : None if ok else "pr_description_recall_evidence_required" ,
77+ "recall_required" : recall_required ,
78+ "recall_executed" : recall_executed ,
79+ "recall_status" : str (preference .get ("recall_status" ) or "" ) or None ,
80+ "required_evidence_inputs" : (
81+ ["pr_description_build_json" ] if recall_required else []
82+ ),
83+ }
84+
85+
4586def _normalise_issue_references (
4687 values : Sequence [str ] | None , * , field : str
4788) -> list [str ]:
@@ -162,6 +203,7 @@ def _result(
162203 description : str ,
163204 recall_status : str ,
164205 application_status : str ,
206+ recall_executed : bool = False ,
165207 recalled_item_count : int = 0 ,
166208 applied_preference_count : int = 0 ,
167209 receipt : Mapping [str , Any ] | None = None ,
@@ -182,6 +224,7 @@ def _result(
182224 "description_changed" : description_changed or issue_reference_changed ,
183225 "semantic_preference" : {
184226 "surface" : SURFACE ,
227+ "recall_executed" : recall_executed ,
185228 "recall_status" : recall_status ,
186229 "application_status" : application_status ,
187230 "recalled_item_count" : recalled_item_count ,
@@ -294,6 +337,7 @@ def build_issue_fix_pr_description(
294337 description = base_description ,
295338 recall_status = recall_status ,
296339 application_status = recall_status ,
340+ recall_executed = True ,
297341 fail_open_preserved_base = recall_status == "provider_unavailable" ,
298342 issue_reference_policy = issue_reference_policy ,
299343 corpus_inventory = corpus_inventory ,
@@ -304,6 +348,7 @@ def build_issue_fix_pr_description(
304348 description = base_description ,
305349 recall_status = recall_status ,
306350 application_status = "available_not_applied" ,
351+ recall_executed = True ,
307352 recalled_item_count = len (items ),
308353 issue_reference_policy = issue_reference_policy ,
309354 corpus_inventory = corpus_inventory ,
@@ -337,6 +382,7 @@ def build_issue_fix_pr_description(
337382 description = base_description ,
338383 recall_status = recall_status ,
339384 application_status = "application_failed" ,
385+ recall_executed = True ,
340386 recalled_item_count = len (items ),
341387 receipt = application_receipt (
342388 surface = SURFACE ,
@@ -355,6 +401,7 @@ def build_issue_fix_pr_description(
355401 description = description ,
356402 recall_status = recall_status ,
357403 application_status = outcome ,
404+ recall_executed = True ,
358405 recalled_item_count = len (items ),
359406 applied_preference_count = len (set (applied_refs )),
360407 receipt = application_receipt (
0 commit comments