(caption: dict[str, Any], plan_item: dict[str, Any] | None)
| 141 | |
| 142 | |
| 143 | def decide(caption: dict[str, Any], plan_item: dict[str, Any] | None) -> dict[str, Any]: |
| 144 | label = normalize_whitespace(str(caption.get("label", ""))) |
| 145 | fallback_caption = normalize_whitespace(str(caption.get("caption", "")))[:40] |
| 146 | base = { |
| 147 | "item_id": f"{caption.get('kind', 'item')}:{label or fallback_caption}", |
| 148 | "source_id": label, |
| 149 | "kind": caption.get("kind", ""), |
| 150 | "label": label, |
| 151 | "caption": caption.get("caption", ""), |
| 152 | "pages": caption.get("pages", []), |
| 153 | "section_id": caption.get("section_id", ""), |
| 154 | "decision": "low_priority", |
| 155 | "reason": "caption_detected_but_not_selected_by_figure_plan", |
| 156 | "skip_reason": "", |
| 157 | "visual_quality_status": "", |
| 158 | "priority": 99, |
| 159 | "target_section": "", |
| 160 | } |
| 161 | if not plan_item: |
| 162 | return base |
| 163 | |
| 164 | status = quality_status(plan_item) |
| 165 | base["priority"] = int(plan_item.get("priority", 99) or 99) |
| 166 | base["target_section"] = plan_item.get("section", "") |
| 167 | base["plan_kind"] = plan_item.get("kind", "") |
| 168 | base["visual_quality_status"] = status |
| 169 | base["reason"] = plan_item.get("reason", "") or "selected_by_figure_plan" |
| 170 | filename = source_image_filename(plan_item) |
| 171 | image_path = source_image_path(plan_item) |
| 172 | if filename: |
| 173 | base["source_image_filename"] = filename |
| 174 | base["relative_markdown_embed"] = f"" |
| 175 | if image_path: |
| 176 | base["source_image_path"] = image_path |
| 177 | if status in {"reject", "reject_visual_quality"}: |
| 178 | base["decision"] = "visual_defect" |
| 179 | base["skip_reason"] = "visual_quality_gate_rejected_candidate" |
| 180 | elif should_insert(caption, plan_item, status): |
| 181 | base["decision"] = "insert" |
| 182 | base["materialization_status"] = "pending" |
| 183 | base["skip_reason"] = "" |
| 184 | else: |
| 185 | base["decision"] = "placeholder" |
| 186 | if status == "usable_candidate": |
| 187 | base["skip_reason"] = "asset_candidate_missing" |
| 188 | elif status: |
| 189 | base["skip_reason"] = "visual_quality_requires_review" |
| 190 | else: |
| 191 | base["skip_reason"] = "asset_candidate_missing" |
| 192 | return base |
| 193 | |
| 194 | |
| 195 | def build_decisions( |
no test coverage detected