Files
Jyotisha/scripts/shared_full_report_authority.py
T
Jesse_Chen bab0718700
Independent Staging Quality Gate / validate (push) Failing after 9m41s
Independent Staging Quality Gate / publish (push) Has been skipped
feat(report): ship full-mode longform appendix beside the five-chapter report
Web export now calls the same full pack as the long skill report and caches an owner-only Markdown download. Appendix failure stays unavailable and does not change the main report status.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-05 11:10:44 +08:00

406 lines
17 KiB
Python
Executable File

"""Build the governed reference object for a PL9-grade full personal report.
The authority is intentionally a compact, read-only reference envelope. It
does not duplicate raw worksheets, render Markdown, alter quality-gate results,
or publish a report. Consumers use its stable identity and references to locate
the already assembled PL9 export packet through an approved transport layer.
"""
from __future__ import annotations
import hashlib
import json
from typing import Any
AUTHORITY_SCHEMA_VERSION = "jyotish.shared_full_report_authority.v1"
REPORT_SCHEMA_VERSION = "expert_report_output.v2"
DEFAULT_REPORT_VERSION = "pl9_personal_long_report.v2"
PL9_EXPORT_SCHEMA = "pl9_style_professional_export_v1"
QUALITY_GATE_SCHEMA = "jyotish.full_report_quality_gate.v1"
def build_shared_full_report_authority(
packet: dict[str, Any],
*,
expert_judgment_output: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Create a compact authority envelope without mutating an export packet.
A quality-gate result is mandatory, but it is not a publication approval.
Every non-blocked authority remains ``review_required`` until a future
governed review/promotion flow authorizes a consumer surface.
"""
source = _require_mapping(packet, "packet")
_require_value(source.get("schema"), "packet.schema")
if source.get("schema") != PL9_EXPORT_SCHEMA:
raise ValueError(f"packet.schema must be {PL9_EXPORT_SCHEMA}")
profile = _require_mapping(source.get("calculation_profile"), "calculation_profile")
profile_id = _require_value(
source.get("calculation_profile_id") or profile.get("profile_id"),
"calculation_profile_id",
)
result_hash = _require_value(source.get("result_hash"), "result_hash")
full_report_pack = _require_mapping(source.get("full_report_pack"), "full_report_pack")
full_report_pack_schema = _require_value(full_report_pack.get("schema"), "full_report_pack.schema")
quality_gate = _require_mapping(source.get("report_quality_gate"), "report_quality_gate")
if quality_gate.get("schema_version") != QUALITY_GATE_SCHEMA:
raise ValueError(f"report_quality_gate.schema_version must be {QUALITY_GATE_SCHEMA}")
report_version = str(source.get("report_version") or DEFAULT_REPORT_VERSION)
chart_identity = _build_chart_identity(source, profile, profile_id)
identity_seed = {
"report_schema_version": REPORT_SCHEMA_VERSION,
"report_version": report_version,
"calculation_profile_id": profile_id,
"result_hash": result_hash,
"full_report_pack_schema": full_report_pack_schema,
# The same calculation payload can be reached from different chart
# identity states. Keep those authorities distinct rather than letting
# an unreviewed and an approved chart share one report reference.
"chart_profile_id": chart_identity["chart_profile_id"],
"rectification_status": chart_identity["rectification_status"],
"approval_status": chart_identity["approval_status"],
}
identity_digest = _digest(identity_seed)
report_id = f"full-report://{profile_id}/{identity_digest[:24]}"
quality_gate_reference = _build_quality_gate_reference(quality_gate)
status, publication_status = _derive_status(quality_gate_reference["status"])
sections = _require_mapping(full_report_pack.get("sections"), "full_report_pack.sections")
report_pack_manifest = _as_mapping(source.get("report_pack_manifest"))
pack_ids = [
str(pack.get("id"))
for pack in report_pack_manifest.get("packs", [])
if isinstance(pack, dict) and pack.get("id")
]
judgment_lineage = _build_judgment_lineage(expert_judgment_output)
limitations = _build_limitations(source, quality_gate_reference, chart_identity, judgment_lineage)
return {
"schema_version": AUTHORITY_SCHEMA_VERSION,
"authority_id": f"shared-full-report://{identity_digest[:24]}",
"report_id": report_id,
"report_version": report_version,
"report_contract_version": REPORT_SCHEMA_VERSION,
"status": status,
"publication_status": publication_status,
"read_only": True,
"report_metadata": {
"generated_at": source.get("generated_at"),
"generated_at_status": "recorded" if source.get("generated_at") else "not_recorded",
},
"chart_identity": chart_identity,
"lineage": {
"source_schema": source.get("schema"),
"calculation_profile_id": profile_id,
"result_hash": result_hash,
"full_report_pack_schema": full_report_pack_schema,
"report_pack_ids": pack_ids,
"source_reference": "pl9_export_packet",
},
"quality_gate_reference": quality_gate_reference,
"content_reference": {
"full_report_pack_schema": full_report_pack_schema,
"section_keys": list(sections.keys()),
"professional_support_reference": _build_professional_support_reference(
sections.get("professional_support")
),
"professional_coverage_reference": _build_professional_coverage_reference(
quality_gate.get("professional_coverage_manifest")
),
"full_report_body_authority": _build_full_report_body_authority(
sections=sections,
professional_support=sections.get("professional_support"),
professional_coverage_manifest=quality_gate.get("professional_coverage_manifest"),
),
"available_render_formats": ["markdown", "reader-markdown", "pdf"],
"contains_raw_calculation": False,
"contains_shadow_artifact": False,
},
"surface_slices": _build_surface_slices(report_id),
"limitations": limitations,
"judgment_lineage": judgment_lineage,
"publication_boundary": (
"This reference does not publish, promote, render, or replace any "
"existing report consumer. It remains review-only until a governed "
"promotion path explicitly authorizes a surface."
),
}
def _require_mapping(value: Any, name: str) -> dict[str, Any]:
if not isinstance(value, dict) or not value:
raise ValueError(f"{name} is required")
return value
def _as_mapping(value: Any) -> dict[str, Any]:
return value if isinstance(value, dict) else {}
def _require_value(value: Any, name: str) -> str:
if value in (None, ""):
raise ValueError(f"{name} is required")
return str(value)
def _digest(value: dict[str, Any]) -> str:
encoded = json.dumps(value, ensure_ascii=True, sort_keys=True, separators=(",", ":"), default=str)
return hashlib.sha256(encoded.encode("utf-8")).hexdigest()
def _build_quality_gate_reference(quality_gate: dict[str, Any]) -> dict[str, Any]:
status = str(quality_gate.get("status") or "review_required")
reference_payload = {
"schema_version": quality_gate.get("schema_version"),
"status": status,
"blocking_reasons": list(quality_gate.get("blocking_reasons") or []),
"review_reasons": list(quality_gate.get("review_reasons") or []),
"warning_reasons": list(quality_gate.get("warning_reasons") or []),
"audit_reference": _as_mapping(quality_gate.get("audit_reference")),
}
return {
**reference_payload,
"quality_gate_reference_id": f"report-quality-gate://{_digest(reference_payload)[:24]}",
}
def _derive_status(quality_gate_status: str) -> tuple[str, str]:
if quality_gate_status == "blocked":
return "blocked", "blocked"
return "review_required", "pending_review"
def _build_chart_identity(
packet: dict[str, Any],
profile: dict[str, Any],
profile_id: str,
) -> dict[str, Any]:
existing = _as_mapping(packet.get("chart_identity"))
return {
"chart_profile_id": existing.get("chart_profile_id") or profile_id,
"birth_data_status": existing.get("birth_data_status") or packet.get("birth_data_status") or "user_provided",
"rectification_status": existing.get("rectification_status") or packet.get("rectification_status") or "not_reviewed",
"approval_status": existing.get("approval_status") or packet.get("approval_status") or "not_approved",
"calculation_profile_reference": profile.get("profile_id") or profile_id,
}
def _build_limitations(
packet: dict[str, Any],
quality_gate_reference: dict[str, Any],
chart_identity: dict[str, Any],
judgment_lineage: dict[str, Any],
) -> list[dict[str, str]]:
limitations = [
{
"type": "publication_review_required",
"detail": "A quality-gate result is advisory and cannot publish a report automatically.",
},
]
if judgment_lineage.get("status") != "attached":
limitations.append(
{
"type": "judgment_lineage_not_attached",
"detail": "The current PL9 packet is not yet linked to a production ExpertJudgmentOutput reference.",
}
)
if quality_gate_reference.get("status") != "passed":
limitations.append(
{
"type": "quality_gate_not_clean_pass",
"detail": f"Quality gate status is {quality_gate_reference.get('status')}.",
}
)
if chart_identity.get("approval_status") != "approved":
limitations.append(
{
"type": "chart_identity_not_approved",
"detail": f"Chart approval status is {chart_identity.get('approval_status')}.",
}
)
if not packet.get("generated_at"):
limitations.append(
{
"type": "generation_timestamp_not_recorded",
"detail": "The source export did not supply a generated_at timestamp.",
}
)
return limitations
def _build_judgment_lineage(expert_judgment_output: dict[str, Any] | None) -> dict[str, Any]:
output = expert_judgment_output if isinstance(expert_judgment_output, dict) else {}
if not output:
return {
"status": "not_attached",
"boundary": (
"Current PL9 export is a governed report candidate. A future "
"ExpertJudgmentOutput reference is required before it can claim "
"judgment-backed production authority."
),
}
return {
"status": "attached",
"schema_version": output.get("schema_version"),
"judgment_id": output.get("judgment_id"),
"judgment_status": output.get("status"),
"promotion_state": ((output.get("review_metadata") or {}).get("promotion_state") if isinstance(output.get("review_metadata"), dict) else None) or "unknown",
"review_status": ((output.get("review_metadata") or {}).get("review_status") if isinstance(output.get("review_metadata"), dict) else None) or "unknown",
"audit_reference": _as_mapping(output.get("audit_reference")),
"boundary": (
"A governed ExpertJudgmentOutput reference is attached. Publication and promotion still require "
"their own review boundary."
),
}
def _build_surface_slices(report_id: str) -> dict[str, dict[str, Any]]:
return {
"simple_view": {
"authority_mode": "reference_only",
"report_reference": report_id,
"allowed_sections": ["Executive Summary", "Chart Identity", "Conflict and Limitation"],
},
"deep_analysis_view": {
"authority_mode": "reference_only",
"report_reference": report_id,
"allowed_sections": [
"Core Promise Analysis",
"Strength Analysis",
"Domain Analysis",
"Varga Analysis",
"Dasha Analysis",
"KP and Transit Timing Layer",
"Annual / Tajika / Yearly Focus",
"Three-Year Ephemeris and Predictive Outlook",
"Conflict and Limitation",
"Evidence Summary",
],
},
"expert_workspace": {
"authority_mode": "reference_only",
"report_reference": report_id,
"allowed_sections": ["all_governed_sections", "Operator Appendix / Audit Appendix"],
},
}
def _build_professional_support_reference(section: Any) -> dict[str, Any]:
topic_section = section if isinstance(section, dict) else {}
topics = topic_section.get("topics") if isinstance(topic_section.get("topics"), dict) else {}
topic_statuses = {
str(key): str(value.get("status") or "blocked")
for key, value in topics.items()
if isinstance(value, dict)
}
restricted = topics.get("restricted_research_materials") if isinstance(topics.get("restricted_research_materials"), dict) else {}
materials = restricted.get("materials") if isinstance(restricted.get("materials"), dict) else {}
restricted_material_visibility = sorted(
key for key, value in materials.items()
if isinstance(value, dict) and value.get("status") not in (None, "", "not_available")
)
body_authority_topics = sorted(
key
for key, value in topics.items()
if key != "restricted_research_materials"
and isinstance(value, dict)
and value.get("status") not in (None, "", "blocked", "not_available")
)
appendix_only_topics = sorted(
key
for key, value in topics.items()
if isinstance(value, dict)
and (
key == "restricted_research_materials"
or value.get("status") in (None, "", "blocked", "not_available")
)
)
return {
"status": topic_section.get("status") or "blocked",
"topic_statuses": topic_statuses,
"body_authority_topics": body_authority_topics,
"appendix_only_topics": appendix_only_topics,
"restricted_material_visibility": restricted_material_visibility,
}
def _build_professional_coverage_reference(manifest: Any) -> list[dict[str, str]]:
rows = manifest if isinstance(manifest, list) else []
normalized: list[dict[str, str]] = []
for row in rows:
if not isinstance(row, dict):
continue
material_id = row.get("material_id")
status = row.get("status")
source_reference = row.get("source_reference")
if material_id in (None, ""):
continue
normalized.append(
{
"material_id": str(material_id),
"status": str(status or "unknown"),
"source_reference": str(source_reference or ""),
}
)
return normalized
def _build_full_report_body_authority(
*,
sections: dict[str, Any],
professional_support: Any,
professional_coverage_manifest: Any,
) -> dict[str, Any]:
section_statuses = {
str(key): str(value.get("status") or "blocked")
for key, value in sections.items()
if isinstance(value, dict)
}
professional_support_reference = _build_professional_support_reference(professional_support)
coverage_rows = _build_professional_coverage_reference(professional_coverage_manifest)
body_sections = [
key
for key, status in section_statuses.items()
if key not in {"audit_appendix", "professional_support"}
and status not in {"blocked", "not_available"}
]
restricted_topics = list(professional_support_reference.get("restricted_material_visibility") or [])
body_authority_topics = list(professional_support_reference.get("body_authority_topics") or [])
appendix_only_topics = list(professional_support_reference.get("appendix_only_topics") or [])
topic_promotion_state = {
topic: (
"body_authority"
if topic in body_authority_topics
else "restricted_registry"
if topic in restricted_topics
else "appendix_only"
)
for topic in sorted(set(body_authority_topics + restricted_topics + appendix_only_topics))
}
return {
"status": "authority_topics_promoted" if body_sections or body_authority_topics or coverage_rows else "blocked",
"body_sections": body_sections,
"body_authority_topics": body_authority_topics,
"restricted_professional_topics": restricted_topics,
"appendix_only_topics": appendix_only_topics,
"topic_promotion_state": topic_promotion_state,
"coverage_manifest_material_ids": [row["material_id"] for row in coverage_rows],
"coverage_manifest_authority_materials": [
{
"material_id": row["material_id"],
"status": row["status"],
"source_reference": row["source_reference"],
"promotion_state": "body_authority_reference" if row["status"] not in {"blocked", "not_available"} else "appendix_only",
}
for row in coverage_rows
],
"boundary": (
"This layer tracks which high-value support and coverage materials have an explicit body-authority "
"or restricted-professional registration inside the long-report contract. It does not auto-promote "
"parameter-sensitive or blocked material into judgment authority."
),
}