Flamehaven commited on
Commit
115d72c
·
verified ·
1 Parent(s): 5bf53dd

release: v1.8.2 ICH M15 citation alignment

Browse files
Files changed (1) hide show
  1. stem_ai/render.py +89 -18
stem_ai/render.py CHANGED
@@ -161,10 +161,12 @@ def render_markdown(result: dict[str, Any], mode: str, pages: int) -> str:
161
  f"`{calibration.get('profile_read_mode', 'unknown')}`, "
162
  f"`{calibration.get('profile_status', 'unknown')}`)",
163
  *([f"**Calibration Effect:** {calibration_effect}"] if calibration_effect else []),
 
164
  f"**Final Score:** **{score['final_score']} / 100**",
165
  f"**Formal Tier:** **{score['formal_tier']}**",
166
  f"**Tier Meaning:** {score['formal_tier']} = {score['use_scope']}",
167
  f"**Use Scope:** {score['use_scope']}",
 
168
  "",
169
  "## Score Matrix",
170
  "",
@@ -172,7 +174,7 @@ def render_markdown(result: dict[str, Any], mode: str, pages: int) -> str:
172
  "| --- | ---: | ---: |",
173
  f"| Stage 1 README Evidence Signal | 0.40 | {score['stage_1_readme_intent']} |",
174
  f"| Stage 2R Repo-Local Consistency | 0.20 | {score['stage_2_repo_local_consistency']} |",
175
- f"| Stage 3 Code/Bio Responsibility | 0.40 | {score['stage_3_code_bio']} |",
176
  f"| Risk Penalty | -- | {score['risk_penalty']} |",
177
  "",
178
  "## Replication Evidence Lane",
@@ -291,6 +293,43 @@ def render_explain(result: dict[str, Any]) -> str:
291
  return "\n".join(out) + "\n"
292
 
293
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  def _markdown_reasoning_summary(reasoning: dict[str, Any]) -> str:
295
  if not reasoning:
296
  return "Reasoning diagnostics are not available."
@@ -303,9 +342,9 @@ def _markdown_reasoning_summary(reasoning: dict[str, Any]) -> str:
303
  f"Diagnostic-only heuristic `{reasoning.get('version', 'unknown')}` "
304
  f"({policy.get('weights', 'uncalibrated')}); "
305
  f"lane consistency `{coherence.get('status', 'unknown')}` "
306
- f"({coherence.get('overall', 'n/a')}), "
307
  f"uncertainty band `{uncertainty.get('status', 'unknown')}` "
308
- f"({uncertainty.get('uncertainty', 'n/a')}), "
309
  f"risk heuristic `{gate.get('status', 'unknown')}` "
310
  f"({gate.get('evidence_risk', 'n/a')}), "
311
  f"confidence envelope {envelope.get('lower', 'n/a')}-"
@@ -418,9 +457,11 @@ def _markdown_regulatory_section(result: dict[str, Any]) -> list[str]:
418
  continue
419
  lines.append(f"### {stage_key.replace('_', ' ').title()}")
420
  for item in items:
 
 
421
  lines.append(
422
  f"- **{item['requirement_id']}** — {item['status']} "
423
- f"(mapping confidence: {item['mapping_confidence']}, evidence strength: {item['evidence_strength']})"
424
  )
425
  lines.append(f" - {item['note']}")
426
  lines.append("")
@@ -651,24 +692,49 @@ def _stage1_semantics_suffix(key: str, item: dict[str, Any]) -> str:
651
 
652
 
653
  def _markdown_remediation_targets(result: dict[str, Any]) -> list[str]:
654
- targets: list[str] = []
655
  stage2 = result.get("stage_2r_rubric", {})
656
  code_integrity = result.get("code_integrity", {})
657
  if "R2R_D2_missing_clinical_use_boundary" in stage2:
658
- targets.append("Add an explicit non-clinical/non-diagnostic boundary to README and adjacent docs before treating the repository as review-ready.")
 
 
 
 
659
  if "R2R_D4_unsupported_workflow_claim" in stage2:
660
- targets.append("Bring workflow, demo, and CLI claims into line with actual local support surfaces, or narrow the README claims.")
 
 
 
 
661
  if code_integrity.get("C2_dependency_pinning", {}).get("status") in {"WARN", "FAIL"}:
662
- targets.append("Pin production dependencies and document external-service dependence explicitly when local or self-host claims are part of the positioning.")
 
 
 
 
663
  if code_integrity.get("C5_compliance_boundary_integrity", {}).get("status") in {"WARN", "FAIL"}:
664
- targets.append("Remove unsupported legal/compliance language or add the governance and security evidence needed to defend it.")
 
 
 
 
665
  if code_integrity.get("C6_mock_auth_or_fail_open_boundary", {}).get("status") in {"WARN", "FAIL"}:
666
- targets.append("Separate mock-auth or auto-login convenience flows from any production, privacy, or self-host trust boundary narrative.")
667
- if not targets:
 
 
 
 
668
  return []
669
- lines = ["", "## Remediation Targets"]
670
- for target in targets:
671
- lines.append(f"- {target}")
 
 
 
 
 
672
  return lines
673
 
674
 
@@ -787,9 +853,11 @@ def _explain_regulatory_section(result: dict[str, Any]) -> list[str]:
787
  continue
788
  lines.append(f" {stage_key:<31}")
789
  for item in items:
 
790
  lines.append(
791
  f" {item['requirement_id']}: {item['status']} "
792
  f"(mapping={item['mapping_confidence']}, evidence={item['evidence_strength']})"
 
793
  )
794
  lines.append(f" note: {item['note']}")
795
  summary = result.get("regulatory_traceability", {}).get("summary")
@@ -1900,12 +1968,14 @@ def _page5_compact_closure(result: dict[str, Any]) -> list[Any]:
1900
  _style(f"AIRC_{str(risk.get('id', 'risk'))[:8]}", 8, 10, _DGRAY),
1901
  ))
1902
  if airi.get("known_gaps_in_bundle"):
 
1903
  gap_preview = ", ".join(
1904
  f"{g.get('id', '—')} {_xt(str(g.get('title', '')))}"
1905
- for g in airi.get("known_gaps_in_bundle", [])[:2]
1906
  )
 
1907
  story.append(Paragraph(
1908
- f'<font color="{_DGRAY}" size="8">Known gaps preview: {gap_preview}</font>',
1909
  _style("AIRI_GAPC", 8, 10, _DGRAY),
1910
  ))
1911
 
@@ -2024,10 +2094,11 @@ def _page6_method_airi(result: dict[str, Any]) -> list[Any]:
2024
  gaps = airi.get("known_gaps_in_bundle", [])
2025
  if gaps:
2026
  gap_preview = ", ".join(
2027
- f"{g.get('id', '—')} {_xt(str(g.get('title', '')))}" for g in gaps[:2]
2028
  )
 
2029
  story.append(Paragraph(
2030
- f'<font color="{_DGRAY}" size="8">Known gaps preview: {gap_preview}</font>',
2031
  _style("AIRIPDF_GAPS", 8, 11, _DGRAY),
2032
  ))
2033
 
 
161
  f"`{calibration.get('profile_read_mode', 'unknown')}`, "
162
  f"`{calibration.get('profile_status', 'unknown')}`)",
163
  *([f"**Calibration Effect:** {calibration_effect}"] if calibration_effect else []),
164
+ *_classification_applied_md(result),
165
  f"**Final Score:** **{score['final_score']} / 100**",
166
  f"**Formal Tier:** **{score['formal_tier']}**",
167
  f"**Tier Meaning:** {score['formal_tier']} = {score['use_scope']}",
168
  f"**Use Scope:** {score['use_scope']}",
169
+ *_tier_lock_label_md(result),
170
  "",
171
  "## Score Matrix",
172
  "",
 
174
  "| --- | ---: | ---: |",
175
  f"| Stage 1 README Evidence Signal | 0.40 | {score['stage_1_readme_intent']} |",
176
  f"| Stage 2R Repo-Local Consistency | 0.20 | {score['stage_2_repo_local_consistency']} |",
177
+ f"| Stage 3 Code/Bio Responsibility | 0.40 | {score['stage_3_code_bio']}{_s3_formula(result)} |",
178
  f"| Risk Penalty | -- | {score['risk_penalty']} |",
179
  "",
180
  "## Replication Evidence Lane",
 
293
  return "\n".join(out) + "\n"
294
 
295
 
296
+ def _classification_applied_md(result: dict[str, Any]) -> list[str]:
297
+ cls = result.get("classification", {})
298
+ ca = cls.get("ca_severity", "none")
299
+ cap = cls.get("score_cap")
300
+ t0 = cls.get("t0_hard_floor", False)
301
+ cap_str = str(cap) if cap is not None else "none"
302
+ t0_str = "active" if t0 else "clear"
303
+ return [f"**Classification Applied:** ca_severity={ca} | score_cap={cap_str} | t0_floor={t0_str}"]
304
+
305
+
306
+ def _tier_lock_label_md(result: dict[str, Any]) -> list[str]:
307
+ cls = result.get("classification", {})
308
+ score_cap = cls.get("score_cap")
309
+ if score_cap is None:
310
+ return []
311
+ if cls.get("t0_hard_floor"):
312
+ return [
313
+ f"**Tier Lock [T0-FLOOR]:** Score ceiling active at **39** (T0 maximum). "
314
+ f"CA-DIRECT classification with insufficient code presence. "
315
+ f"Resolving this condition is required before any tier advancement."
316
+ ]
317
+ return [
318
+ f"**Tier Lock [CA-CAP]:** Score ceiling active at **{score_cap}** (T2 maximum). "
319
+ f"Clinical-adjacent surface detected without explicit non-clinical boundary. "
320
+ f"Adding a non-diagnostic disclaimer resolves this lock."
321
+ ]
322
+
323
+
324
+ def _s3_formula(result: dict[str, Any]) -> str:
325
+ s3_raw = result.get("stage_3_rubric", {}).get("stage_3_raw_total", {})
326
+ raw = s3_raw.get("score")
327
+ max_val = s3_raw.get("max")
328
+ if raw is None or not max_val:
329
+ return ""
330
+ return f" (raw: {raw}/{max_val})"
331
+
332
+
333
  def _markdown_reasoning_summary(reasoning: dict[str, Any]) -> str:
334
  if not reasoning:
335
  return "Reasoning diagnostics are not available."
 
342
  f"Diagnostic-only heuristic `{reasoning.get('version', 'unknown')}` "
343
  f"({policy.get('weights', 'uncalibrated')}); "
344
  f"lane consistency `{coherence.get('status', 'unknown')}` "
345
+ f"({coherence.get('overall', 'n/a')}; >=0.80=consistent, >=0.55=mixed, <0.55=divergent), "
346
  f"uncertainty band `{uncertainty.get('status', 'unknown')}` "
347
+ f"({uncertainty.get('uncertainty', 'n/a')}; <0.20=low-spread, <=0.45=review-advised, >0.45=manual-review), "
348
  f"risk heuristic `{gate.get('status', 'unknown')}` "
349
  f"({gate.get('evidence_risk', 'n/a')}), "
350
  f"confidence envelope {envelope.get('lower', 'n/a')}-"
 
457
  continue
458
  lines.append(f"### {stage_key.replace('_', ' ').title()}")
459
  for item in items:
460
+ _src = ", ".join(item.get("source_ids", []))
461
+ _src_tag = f" `[{_src}]`" if _src else ""
462
  lines.append(
463
  f"- **{item['requirement_id']}** — {item['status']} "
464
+ f"(mapping confidence: {item['mapping_confidence']}, evidence strength: {item['evidence_strength']}){_src_tag}"
465
  )
466
  lines.append(f" - {item['note']}")
467
  lines.append("")
 
692
 
693
 
694
  def _markdown_remediation_targets(result: dict[str, Any]) -> list[str]:
695
+ rows: list[tuple[str, str, str]] = []
696
  stage2 = result.get("stage_2r_rubric", {})
697
  code_integrity = result.get("code_integrity", {})
698
  if "R2R_D2_missing_clinical_use_boundary" in stage2:
699
+ rows.append((
700
+ "R2R_D2 missing clinical boundary",
701
+ "Add non-clinical/non-diagnostic disclaimer to README and all adjacent docs",
702
+ "+20 S2R (+4 final) | unlocks tier cap",
703
+ ))
704
  if "R2R_D4_unsupported_workflow_claim" in stage2:
705
+ rows.append((
706
+ "R2R_D4 unsupported workflow claim",
707
+ "Align README workflow/demo/CLI claims with actual local support surfaces",
708
+ "+15 S2R (+3 final)",
709
+ ))
710
  if code_integrity.get("C2_dependency_pinning", {}).get("status") in {"WARN", "FAIL"}:
711
+ rows.append((
712
+ "C2 dependency pinning WARN",
713
+ "Pin production dependencies; document external-service dependence explicitly",
714
+ "C2 -> PASS (no direct score delta)",
715
+ ))
716
  if code_integrity.get("C5_compliance_boundary_integrity", {}).get("status") in {"WARN", "FAIL"}:
717
+ rows.append((
718
+ "C5 compliance boundary WARN",
719
+ "Remove unsupported legal/compliance language or add backing governance evidence",
720
+ "C5 -> PASS (no direct score delta)",
721
+ ))
722
  if code_integrity.get("C6_mock_auth_or_fail_open_boundary", {}).get("status") in {"WARN", "FAIL"}:
723
+ rows.append((
724
+ "C6 mock-auth boundary WARN",
725
+ "Separate mock-auth/auto-login flows from production trust boundary narrative",
726
+ "C6 -> PASS (no direct score delta)",
727
+ ))
728
+ if not rows:
729
  return []
730
+ lines = [
731
+ "", "## Remediation Roadmap",
732
+ "",
733
+ "| Finding | Action | Expected Impact |",
734
+ "| --- | --- | --- |",
735
+ ]
736
+ for finding, action, impact in rows:
737
+ lines.append(f"| {finding} | {action} | {impact} |")
738
  return lines
739
 
740
 
 
853
  continue
854
  lines.append(f" {stage_key:<31}")
855
  for item in items:
856
+ _src = ", ".join(item.get("source_ids", []))
857
  lines.append(
858
  f" {item['requirement_id']}: {item['status']} "
859
  f"(mapping={item['mapping_confidence']}, evidence={item['evidence_strength']})"
860
+ + (f" | source: {_src}" if _src else "")
861
  )
862
  lines.append(f" note: {item['note']}")
863
  summary = result.get("regulatory_traceability", {}).get("summary")
 
1968
  _style(f"AIRC_{str(risk.get('id', 'risk'))[:8]}", 8, 10, _DGRAY),
1969
  ))
1970
  if airi.get("known_gaps_in_bundle"):
1971
+ _all_gaps = airi.get("known_gaps_in_bundle", [])
1972
  gap_preview = ", ".join(
1973
  f"{g.get('id', '—')} {_xt(str(g.get('title', '')))}"
1974
+ for g in _all_gaps[:5]
1975
  )
1976
+ _gap_extra = f" (+{len(_all_gaps) - 5} more)" if len(_all_gaps) > 5 else ""
1977
  story.append(Paragraph(
1978
+ f'<font color="{_DGRAY}" size="8">Known gaps: {gap_preview}{_gap_extra}</font>',
1979
  _style("AIRI_GAPC", 8, 10, _DGRAY),
1980
  ))
1981
 
 
2094
  gaps = airi.get("known_gaps_in_bundle", [])
2095
  if gaps:
2096
  gap_preview = ", ".join(
2097
+ f"{g.get('id', '—')} {_xt(str(g.get('title', '')))}" for g in gaps[:5]
2098
  )
2099
+ _gap_extra = f" (+{len(gaps) - 5} more)" if len(gaps) > 5 else ""
2100
  story.append(Paragraph(
2101
+ f'<font color="{_DGRAY}" size="8">Known gaps: {gap_preview}{_gap_extra}</font>',
2102
  _style("AIRIPDF_GAPS", 8, 11, _DGRAY),
2103
  ))
2104