fix(report,chart): blank columns — drop Kranti, split Ashtakavarga house SAV, ascendant nakshatra, engine varga dignity, no duplicate charts (BUG-1199..1203)
Independent Staging Quality Gate / validate (push) Successful in 15m27s
Independent Staging Quality Gate / publish (push) Successful in 3m42s

- Report p33 table: no Kranti column (no producer; materials.kranti stays blocked).
- Report p53 table: the "SAV" column was the with-Lagna total (386). Split in the
  reader into SAV / Lagna BAV / SAV + Lagna with a totals row; ashtakavarga.py is
  untouched (frozen rectification identity, ERR-115).
- /api/chart ascendant carries nakshatra / pada / lord (_nakshatra_fields shared with
  the nine bodies); chart cache schema 4 -> 5.
- /api/varga_full bodies carry dignity_level from varga_dignity_level, the function
  the Vimsopaka path now shares (A/B identical); the chart page shows the D1 status
  wording and no longer derives a three-level dignity itself.
- Report page draws each chart once: the trailing block skips charts the body already
  fenced (same rule as the export); the birth-time caveat moves under the body's
  chart heading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
Jesse_Chen
2026-10-03 09:11:15 +08:00
co-authored by Claude Opus 5.5
parent 0e856f9cb6
commit ae18952e51
28 changed files with 1011 additions and 346 deletions
@@ -78,7 +78,8 @@ function PlanetRow({ row }: { row: ChartViewPlanetRow }) {
}
/**
* The ascendant is a chart point: the engine returns no nakshatra for it.
* The ascendant is a chart point: nakshatra, lord and pada come from the
* engine (BUG-1201); it has no dignity, so the status cell stays empty.
* `house` is 1 except on the Moon chart, which counts from the Moon.
*/
function AscendantRow({ row, house = row.house }: { row: ChartViewAscendantRow; house?: number }) {
@@ -566,7 +566,7 @@ export function PersonalReportPage({ reportId, initialLanguage = "zh" }: { repor
<div className="personal-report-reader-body" lang={shown === "en" ? "en" : "zh-CN"}>
{/* Siblings remount per edition under distinct keys; a shared key left the old edition on screen (BUG-1126). */}
<PersonalReportMarkdownView key={`markdown-${shown}`} language={shown} markdown={markdown} drawnThrough={drawnThrough} />
{state.calculationCharts && <ReportCalculationCharts charts={state.calculationCharts} language={shown} />}
{state.calculationCharts && <ReportCalculationCharts charts={state.calculationCharts} markdown={markdown} language={shown} />}
{factTables && <ReportFactTables key={`facts-${shown}`} language={shown} tables={factTables} />}
</div>
</div>
@@ -1,22 +1,34 @@
"use client";
import { memo } from "react";
import { memo, useMemo } from "react";
import type { ChartV1, ReportDocumentV2 } from "@/lib/personal-report-contract";
import { fencedReportChartIds } from "@/lib/report-chart-block";
import { hasRealChartData, VedicChartSvg } from "./vedic-chart-svg";
import type { ReportLanguage } from "@/lib/report-language";
const COPY = {
zh: { heading: "分盘核对", note: "原始计算供核对,分盘对出生时间敏感,不单独增加结论的确定性。" },
en: { heading: "Divisional charts for checking", note: "Raw calculations for checking. Divisional charts are sensitive to birth time and do not by themselves make a conclusion more certain." },
zh: { heading: "分盘核对" },
en: { heading: "Divisional charts for checking" },
} as const;
export const ReportCalculationCharts = memo(function ReportCalculationCharts({ charts, language = "zh" }: { charts: ReportDocumentV2["charts"]; language?: ReportLanguage }) {
/**
* Structured charts the report body did not already draw (BUG-1203): the same
* rule as the export. A current report draws every chart in its body, so this
* renders nothing; an older report without chart fences still gets its charts.
* The birth-time caveat lives under the body's chart heading.
*/
export const ReportCalculationCharts = memo(function ReportCalculationCharts({ charts, markdown, language = "zh" }: {
charts: ReportDocumentV2["charts"];
/** The body shown on the page, whose chart fences are already drawn. */
markdown: string;
language?: ReportLanguage;
}) {
const copy = COPY[language];
const actual = charts.filter(chart => hasRealChartData(chart as ChartV1));
const drawn = useMemo(() => fencedReportChartIds(markdown), [markdown]);
const actual = charts.filter(chart => hasRealChartData(chart as ChartV1) && !drawn.has(chart.id));
if (!actual.length) return null;
return <section className="personal-report-section" aria-label={copy.heading}>
<h2>{copy.heading}</h2>
<p>{copy.note}</p>
<div className="personal-report-chart-grid">
{actual.map(chart => <article className="personal-report-chart-card" key={chart.id}>
<h3>{language === "en" ? `${chart.id} chart` : chart.title}</h3>