From d82218e70a7f93cc0037d3ff98a8181031e0bddb Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 15:36:56 +0800 Subject: [PATCH 01/25] fix: block unconfirmed birth-time candidate adoption --- .../birth-time-candidate-completion/route.ts | 63 +----- .../birth-time-candidate-result.tsx | 14 +- .../lib/birth-time-candidate-completion.ts | 35 +-- .../src/lib/birth-time-guided-terminal.ts | 30 +-- .../birth-time-candidate-completion.test.ts | 205 +----------------- .../birth-time-guided-review-fixes.test.ts | 21 +- .../birth-time-rectification-contract.test.ts | 5 +- 7 files changed, 36 insertions(+), 337 deletions(-) diff --git a/frontend/src/app/api/birth-time-candidate-completion/route.ts b/frontend/src/app/api/birth-time-candidate-completion/route.ts index d86196bb..b8b0d8cd 100644 --- a/frontend/src/app/api/birth-time-candidate-completion/route.ts +++ b/frontend/src/app/api/birth-time-candidate-completion/route.ts @@ -1,9 +1,5 @@ import { NextResponse } from "next/server"; import { z } from "zod"; -import { candidateWorkingTime } from "@/lib/birth-time-candidate-completion"; -import { createAdminSupabaseClient } from "@/lib/supabase/admin"; -import { isSupabaseConfigurationError } from "@/lib/supabase/config"; -import { createServerSupabaseClient } from "@/lib/supabase/server"; export const runtime = "nodejs"; @@ -13,57 +9,12 @@ const requestSchema = z.object({ time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/), }).strict(); +/** Compatibility endpoint for stale clients; unconfirmed candidates never write profiles. */ export async function POST(request: Request) { - try { - const supabase = await createServerSupabaseClient(); - const { data: { user }, error: authError } = await supabase.auth.getUser(); - if (authError || !user) { - return NextResponse.json({ error: "请先登录" }, { status: 401 }); - } - - const parsed = requestSchema.safeParse(await request.json().catch(() => null)); - if (!parsed.success) { - return NextResponse.json({ error: "候选时间格式不正确" }, { status: 400 }); - } - - const admin = createAdminSupabaseClient(); - const { data: stored, error: caseError } = await admin - .from("birth_time_rectification_cases") - .select("id,user_id,status,candidate_result_id,candidate_result,turn_state") - .eq("id", parsed.data.caseId) - .eq("user_id", user.id) - .maybeSingle(); - const time = candidateWorkingTime(stored, { ...parsed.data, userId: user.id }); - if (caseError || !time) { - return NextResponse.json( - { error: "候选结果已变化", message: "请使用当前评估结果继续。" }, - { status: 409 }, - ); - } - - const { data: profile, error: profileError } = await admin - .from("profiles") - .update({ - active_birth_time: time, - birth_time_status: "candidate", - updated_at: new Date().toISOString(), - }) - .eq("id", user.id) - .eq("rectification_case_id", parsed.data.caseId) - .select("id") - .maybeSingle(); - if (profileError || !profile) { - return NextResponse.json( - { error: "候选时间暂时无法保存", message: "当前评估结果仍已保留,请稍后重试。" }, - { status: 503 }, - ); - } - - return NextResponse.json({ ok: true, activeTime: time, birthTimeStatus: "candidate" }); - } catch (error) { - if (isSupabaseConfigurationError(error)) { - return NextResponse.json({ error: "Supabase 尚未配置" }, { status: 503 }); - } - return NextResponse.json({ error: "候选时间暂时无法保存" }, { status: 500 }); - } + const parsed = requestSchema.safeParse(await request.json().catch(() => null)); + if (!parsed.success) return NextResponse.json({ error: "候选时间格式不正确" }, { status: 400 }); + return NextResponse.json( + { error: "候选时间不能直接采用", message: "候选范围已保留;请补充资料,或在高置信结果出现后通过正式确认继续。" }, + { status: 409 }, + ); } diff --git a/frontend/src/components/birth-time-candidate-result.tsx b/frontend/src/components/birth-time-candidate-result.tsx index ed1358f7..50708638 100644 --- a/frontend/src/components/birth-time-candidate-result.tsx +++ b/frontend/src/components/birth-time-candidate-result.tsx @@ -121,20 +121,10 @@ function TerminalAction({ controller, error, path }: { readonly error: string; readonly path: NonNullable>; }) { - if (path.kind === "complete_with_candidate") { - return ( -
- 评估已完成,下一步 -

点击后将使用 {path.time} 作为当前排盘时间并进入对话;原始填报和本次候选结果仍会保留

- - {error ?

{error}

: null} -
- ); - } return (
+ 尚未达到采用条件 +

候选范围已保留,但当前证据不足以将具体分钟写入当前排盘时间。补充经历后可重新评估。

{error ?

{error}

: null} 会建立新的记录,当前结果仍会保留。 diff --git a/frontend/src/lib/birth-time-candidate-completion.ts b/frontend/src/lib/birth-time-candidate-completion.ts index a018bf2d..50af9a20 100644 --- a/frontend/src/lib/birth-time-candidate-completion.ts +++ b/frontend/src/lib/birth-time-candidate-completion.ts @@ -5,37 +5,10 @@ type CandidateCompletionRequest = { readonly time: string; }; -function record(value: unknown): Record | null { - return value !== null && typeof value === "object" - ? value as Record - : null; -} - +/** Direct adoption was superseded by versioned high-confidence confirmation. */ export function candidateWorkingTime( - stored: unknown, - request: CandidateCompletionRequest, + _stored: unknown, + _request: CandidateCompletionRequest, ): string | null { - const assessment = record(stored); - const candidate = record(assessment?.candidate_result); - const winner = record(candidate?.winningSegment); - const turn = record(assessment?.turn_state); - const action = record(turn?.nextAction); - const actionKind = action?.kind; - const terminal = actionKind === "present_low_result" - || actionKind === "present_medium_result" - || actionKind === "candidate_saved"; - const terminalStatusMatches = actionKind === "present_low_result" - ? assessment?.status === "rectifying" - : (actionKind === "present_medium_result" || actionKind === "candidate_saved") - && assessment?.status === "candidate"; - - return assessment?.id === request.caseId - && assessment?.user_id === request.userId - && terminalStatusMatches - && assessment.candidate_result_id === request.resultId - && action?.resultId === request.resultId - && terminal - && winner?.representativeTime === request.time - ? request.time - : null; + return null; } diff --git a/frontend/src/lib/birth-time-guided-terminal.ts b/frontend/src/lib/birth-time-guided-terminal.ts index bcd2de3f..5107cf3c 100644 --- a/frontend/src/lib/birth-time-guided-terminal.ts +++ b/frontend/src/lib/birth-time-guided-terminal.ts @@ -1,32 +1,14 @@ import type { JourneyClientResponse } from "./birth-time-journey-response-schema.ts"; -export type GuidedTerminalPath = - | { - readonly kind: "edit_birth_time_details"; - readonly preservesCase: true; - readonly appliesCandidateTime: false; - } - | { - readonly kind: "complete_with_candidate"; - readonly time: string; - readonly preservesCase: true; - readonly appliesCandidateTime: true; - }; +export type GuidedTerminalPath = { + readonly kind: "edit_birth_time_details"; + readonly preservesCase: true; + readonly appliesCandidateTime: false; +}; export function guidedTerminalPath(journey: JourneyClientResponse): GuidedTerminalPath | null { const kind = journey.nextAction.kind; - const winner = journey.candidateResult?.winningSegment; - if (journey.journeyProtocol === "dynamic-choice-v2" - && winner - && (kind === "present_low_result" || kind === "present_medium_result" || kind === "candidate_saved")) { - return { - kind: "complete_with_candidate", - time: winner.representativeTime, - preservesCase: true, - appliesCandidateTime: true, - }; - } - return kind === "present_low_result" || kind === "candidate_saved" + return kind === "present_low_result" || kind === "present_medium_result" || kind === "candidate_saved" ? { kind: "edit_birth_time_details", preservesCase: true, appliesCandidateTime: false } : null; } diff --git a/frontend/tests/birth-time-candidate-completion.test.ts b/frontend/tests/birth-time-candidate-completion.test.ts index 0e69ecf5..a5674e72 100644 --- a/frontend/tests/birth-time-candidate-completion.test.ts +++ b/frontend/tests/birth-time-candidate-completion.test.ts @@ -2,204 +2,11 @@ import assert from "node:assert/strict"; import test from "node:test"; import { candidateWorkingTime } from "../src/lib/birth-time-candidate-completion.ts"; -const terminalCase = { - id: "5425f9e7-3d45-491d-aab3-24cfd4261d51", - user_id: "07e583fc-90b9-4fcb-a9d3-8de654eeac9a", - status: "candidate", - candidate_result_id: "d9133ba2-afcf-56da-b40b-ace3d7124a7d", - candidate_result: { - confidence: "medium", - winningSegment: { representativeTime: "04:53" }, - }, - turn_state: { - nextAction: { - kind: "present_medium_result", - resultId: "d9133ba2-afcf-56da-b40b-ace3d7124a7d", - }, - }, -}; - -const lowTerminalCase = { - ...terminalCase, - status: "rectifying", - candidate_result: { - ...terminalCase.candidate_result, - confidence: "low", - }, - turn_state: { - ...terminalCase.turn_state, - nextAction: { - kind: "present_low_result", - resultId: terminalCase.candidate_result_id, - }, - }, -}; - -const completionRequest = { - userId: terminalCase.user_id, - caseId: terminalCase.id, - resultId: terminalCase.candidate_result_id, - time: "04:53", -} as const; - -test("candidate completion only accepts the persisted terminal representative time", () => { - assert.equal(candidateWorkingTime(terminalCase, { - ...completionRequest, - }), "04:53"); - - assert.equal(candidateWorkingTime(terminalCase, { - ...completionRequest, - time: "04:54", +test("unconfirmed candidate results cannot directly become the active consultation time", () => { + assert.equal(candidateWorkingTime({}, { + userId: "07e583fc-90b9-4fcb-a9d3-8de654eeac9a", + caseId: "5425f9e7-3d45-491d-aab3-24cfd4261d51", + resultId: "d9133ba2-afcf-56da-b40b-ace3d7124a7d", + time: "04:53", }), null); }); - -test("accepts a matching low-confidence result from the rectifying state", () => { - assert.equal(candidateWorkingTime(lowTerminalCase, { - ...completionRequest, - }), "04:53"); -}); - -test("accepts a persisted candidate-saved compatibility action", () => { - assert.equal(candidateWorkingTime({ - ...terminalCase, - turn_state: { - nextAction: { - kind: "candidate_saved", - resultId: terminalCase.candidate_result_id, - }, - }, - }, completionRequest), "04:53"); -}); - -test("does not accept a medium terminal action from the rectifying state", () => { - assert.equal(candidateWorkingTime({ - ...lowTerminalCase, - turn_state: { - ...lowTerminalCase.turn_state, - nextAction: { - kind: "present_medium_result", - resultId: terminalCase.candidate_result_id, - }, - }, - }, { - ...completionRequest, - }), null); -}); - -test("non-terminal cases cannot be adopted for consultation", () => { - assert.equal(candidateWorkingTime({ - ...terminalCase, - turn_state: { nextAction: { kind: "ask_dynamic_choice" } }, - }, { - ...completionRequest, - }), null); -}); - -const rejectedCompletions = [ - { - name: "case owned by another user", - stored: { ...terminalCase, user_id: "f6cf99a5-9af7-4980-93ea-0298ee1dc95e" }, - request: completionRequest, - }, - { - name: "request from another user", - stored: terminalCase, - request: { ...completionRequest, userId: "f6cf99a5-9af7-4980-93ea-0298ee1dc95e" }, - }, - { - name: "missing case owner", - stored: { ...terminalCase, user_id: null }, - request: completionRequest, - }, - { - name: "empty case owner", - stored: { ...terminalCase, user_id: "" }, - request: completionRequest, - }, - { - name: "wrong case ID", - stored: terminalCase, - request: { ...completionRequest, caseId: "c84052ca-bcea-40a8-a32a-56980bbf7b22" }, - }, - { - name: "wrong persisted result ID", - stored: { ...terminalCase, candidate_result_id: "a3e41512-9fa0-4866-a187-e3b3aa07aee0" }, - request: completionRequest, - }, - { - name: "wrong action result ID", - stored: { - ...terminalCase, - turn_state: { - nextAction: { - ...terminalCase.turn_state.nextAction, - resultId: "a3e41512-9fa0-4866-a187-e3b3aa07aee0", - }, - }, - }, - request: completionRequest, - }, - { - name: "wrong requested result ID", - stored: terminalCase, - request: { ...completionRequest, resultId: "a3e41512-9fa0-4866-a187-e3b3aa07aee0" }, - }, - { - name: "missing winning segment", - stored: { ...terminalCase, candidate_result: { confidence: "medium" } }, - request: completionRequest, - }, - { - name: "missing representative time", - stored: { - ...terminalCase, - candidate_result: { confidence: "medium", winningSegment: {} }, - }, - request: completionRequest, - }, - { - name: "low-result action paired with candidate status", - stored: { - ...terminalCase, - turn_state: { - nextAction: { - kind: "present_low_result", - resultId: terminalCase.candidate_result_id, - }, - }, - }, - request: completionRequest, - }, - { - name: "medium-result action paired with rectifying status", - stored: { - ...lowTerminalCase, - turn_state: { - nextAction: { - kind: "present_medium_result", - resultId: terminalCase.candidate_result_id, - }, - }, - }, - request: completionRequest, - }, - { - name: "candidate-saved action paired with rectifying status", - stored: { - ...lowTerminalCase, - turn_state: { - nextAction: { - kind: "candidate_saved", - resultId: terminalCase.candidate_result_id, - }, - }, - }, - request: completionRequest, - }, -] as const; - -for (const scenario of rejectedCompletions) { - test(`rejects candidate completion with ${scenario.name}`, () => { - assert.equal(candidateWorkingTime(scenario.stored, scenario.request), null); - }); -} diff --git a/frontend/tests/birth-time-guided-review-fixes.test.ts b/frontend/tests/birth-time-guided-review-fixes.test.ts index 3c950bc6..61c62405 100644 --- a/frontend/tests/birth-time-guided-review-fixes.test.ts +++ b/frontend/tests/birth-time-guided-review-fixes.test.ts @@ -56,14 +56,13 @@ test("low without a result and saved medium both return to declared-time editing }); }); -test("dynamic medium terminal completes with its candidate working time", () => { +test("dynamic medium terminal preserves its candidate range without direct adoption", () => { const medium = dynamicBirthTimePreview("medium"); assert.deepEqual(guidedTerminalPath(medium), { - kind: "complete_with_candidate", - time: "05:43", + kind: "edit_birth_time_details", preservesCase: true, - appliesCandidateTime: true, + appliesCandidateTime: false, }); }); @@ -121,31 +120,29 @@ test("ready completion is explicit and terminal low has no finish mutation", () assert.doesNotMatch(candidateSource, /controller\.finish/); }); -test("terminal candidate owns one explicit next step and its completion error", () => { +test("unconfirmed terminal candidates preserve the range without offering direct adoption", () => { const candidateResultSource = readFileSync(new URL("../src/components/birth-time-candidate-result.tsx", import.meta.url), "utf8"); const choiceQuestionSource = readFileSync(new URL("../src/components/birth-time-choice-question.tsx", import.meta.url), "utf8"); const rectificationSource = readFileSync(new URL("../src/components/birth-time-rectification.tsx", import.meta.url), "utf8"); const legacyRectificationSource = readFileSync(new URL("../src/components/birth-time-legacy-rectification.tsx", import.meta.url), "utf8"); - const globalCssSource = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); - assert.match(candidateResultSource, /评估已完成,下一步/); - assert.match(candidateResultSource, /采用 \$\{path\.time\} 并进入对话/); - assert.match(candidateResultSource, /正在采用 \$\{path\.time\}…/); - assert.match(candidateResultSource, /birth-time-next-step/); + assert.match(candidateResultSource, /尚未达到采用条件/); + assert.match(candidateResultSource, /补充资料并重新评估/); + assert.doesNotMatch(candidateResultSource, /采用 \$\{path\.time\} 并进入对话/); + assert.doesNotMatch(candidateResultSource, /birth-time-next-step/); assert.match(rectificationSource, /error=\{error\}/); assert.match(rectificationSource, /const childOwnsError = action\.kind === "ask_dynamic_choice"\s*\|\| action\.kind === "clarify_unmatched_answer"/); assert.match(rectificationSource, /error && !showsCandidate && !childOwnsError/); assert.equal(choiceQuestionSource.match(/role="alert"/g)?.length, 1); assert.match(legacyRectificationSource, /error=\{error\}/); assert.match(legacyRectificationSource, /error && !showsCandidate/); - assert.match(globalCssSource, /\.birth-time-next-step/); }); test("terminal and entrypoint CJK phrases stay intact at narrow widths", () => { const candidateResultSource = readFileSync(new URL("../src/components/birth-time-candidate-result.tsx", import.meta.url), "utf8"); const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8"); - assert.match(candidateResultSource, /作为当前排盘时间<\/span>并进入对话;原始填报<\/span>和本次候选结果<\/span>仍会保留<\/span>。/); + assert.match(candidateResultSource, /候选范围已保留,但当前证据不足以将具体分钟写入当前排盘时间。补充经历后可重新评估。/); assert.match(pageSource, /当前使用候选时间排盘;原始填报范围<\/span>仍保留。/); }); diff --git a/frontend/tests/birth-time-rectification-contract.test.ts b/frontend/tests/birth-time-rectification-contract.test.ts index decb96d3..eaf2a67c 100644 --- a/frontend/tests/birth-time-rectification-contract.test.ts +++ b/frontend/tests/birth-time-rectification-contract.test.ts @@ -75,9 +75,8 @@ test("low-confidence preview mirrors the persisted dynamic terminal state", () = assert.equal(low.candidateResult.winningSegment?.representativeTime, "05:21"); assert.equal(low.nextAction.resultId, low.candidateResult.resultId); assert.deepEqual(guidedTerminalPath(low), { - kind: "complete_with_candidate", - time: low.candidateResult.winningSegment?.representativeTime, + kind: "edit_birth_time_details", preservesCase: true, - appliesCandidateTime: true, + appliesCandidateTime: false, }); }); From 9154ec5ef1367b91cf2283bd2125cb2f41653a39 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 15:52:01 +0800 Subject: [PATCH 02/25] fix: keep unconfirmed candidates in rectification --- frontend/src/app/page.tsx | 18 +------------ .../hooks/use-birth-time-guided-journey.ts | 26 +------------------ frontend/src/lib/birth-time-guided-client.ts | 20 -------------- frontend/src/lib/birth-time-intake-model.ts | 2 +- .../birth-time-guided-review-fixes.test.ts | 2 ++ frontend/tests/birth-time-intake.test.ts | 5 ++-- 6 files changed, 8 insertions(+), 65 deletions(-) diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 1959c3cd..fb1dc796 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -747,7 +747,6 @@ export default function Home() { preview: process.env.NODE_ENV === "development" && uiPreview.current, onJourney: setBirthTimeJourney, onReady: completeGuidedBirthTime, - onCandidateComplete: completeCandidateBirthTime, onEditBirthTimeDetails: editDeclaredBirthTimeDetails, }); @@ -1019,7 +1018,7 @@ export default function Home() { setSessions(nextSessions); setActiveSessionId(nextSessions[0].id); if ((nextProfile.birthTimeStatus === "rectifying" - || (nextProfile.birthTimeStatus === "candidate" && !nextProfile.time)) + || nextProfile.birthTimeStatus === "candidate") && nextProfile.rectificationCaseId) { try { const resumed = await resumeBirthTimeJourney(nextProfile.rectificationCaseId); @@ -1657,21 +1656,6 @@ export default function Home() { setOnboardingJustCompleted(true); } - function completeCandidateBirthTime(result: JourneyClientResponse, time: string) { - const candidateProfile: Profile = { - ...profileDraft, - time, - birthTimeStatus: "candidate", - rectificationCaseId: result.caseId, - }; - setProfile(candidateProfile); - setProfileDraft(candidateProfile); - setBirthTimeJourney(null); - setPresetMessageLength(0); - setStartGreeting(createStartGreeting(candidateProfile.name)); - setOnboardingJustCompleted(true); - } - async function retryBirthTimeAssessment() { if (!account || profileSaving) return; setProfileSaving(true); diff --git a/frontend/src/hooks/use-birth-time-guided-journey.ts b/frontend/src/hooks/use-birth-time-guided-journey.ts index 8fac6fbb..7d005fa8 100644 --- a/frontend/src/hooks/use-birth-time-guided-journey.ts +++ b/frontend/src/hooks/use-birth-time-guided-journey.ts @@ -14,7 +14,6 @@ import { } from "@/lib/birth-time-journey-client"; import type { JourneyClientResponse } from "@/lib/birth-time-journey-client"; import { - completeGuidedBirthTimeCandidate, confirmGuidedBirthTimeCandidate, reviseBirthTimeEvidenceDraft, saveGuidedBirthTimeCandidate, @@ -37,7 +36,6 @@ type GuidedJourneyInput = { readonly preview: boolean; readonly onJourney: (journey: JourneyClientResponse) => void; readonly onReady: (journey: JourneyClientResponse) => void; - readonly onCandidateComplete: (journey: JourneyClientResponse, time: string) => void; readonly onEditBirthTimeDetails: () => void; }; @@ -53,7 +51,6 @@ export type BirthTimeGuidedController = { readonly resume: () => void; readonly editBirthTimeDetails: () => void; readonly acknowledgeReady: () => void; - readonly completeCandidate: (time: string) => void; readonly retryScoring: () => void; readonly saveCandidate: (resultId: string) => void; readonly confirmCandidate: (resultId: string, time: string) => void; @@ -70,7 +67,7 @@ function previewAction(turn: JourneyClientResponse, command: DynamicPreviewComma } export function useBirthTimeGuidedJourney(input: GuidedJourneyInput): BirthTimeGuidedController { - const { journey, onJourney, onReady, onCandidateComplete, onEditBirthTimeDetails, preview } = input; + const { journey, onJourney, onReady, onEditBirthTimeDetails, preview } = input; const latest = useRef(journey); const busy = useRef(false); const [actionRegistry] = useState(() => createStableActionIdentityRegistry()); @@ -185,26 +182,6 @@ export function useBirthTimeGuidedJourney(input: GuidedJourneyInput): BirthTimeG const acknowledgeReady = () => { if (journey?.nextAction.kind === "ready") onReady(journey); }; - const completeCandidate = (time: string) => { - const turn = journey; - const resultId = turn?.candidateResult?.resultId; - const winner = turn?.candidateResult?.winningSegment; - if (!turn || !resultId || winner?.representativeTime !== time) return; - const release = claimMutation(busy); - if (release === null) return; - setPending(true); - setError(""); - const completion = preview - ? Promise.resolve() - : completeGuidedBirthTimeCandidate({ caseId: turn.caseId, resultId, time }); - void completion - .then(() => onCandidateComplete(turn, time)) - .catch((caught) => setError(birthTimeUserError(caught))) - .finally(() => { - release(); - setPending(false); - }); - }; const retryScoring = () => { const turn = journey; if (preview && turn?.journeyProtocol === "dynamic-choice-v2" @@ -277,7 +254,6 @@ export function useBirthTimeGuidedJourney(input: GuidedJourneyInput): BirthTimeG resume, editBirthTimeDetails: onEditBirthTimeDetails, acknowledgeReady, - completeCandidate, retryScoring, saveCandidate, confirmCandidate, diff --git a/frontend/src/lib/birth-time-guided-client.ts b/frontend/src/lib/birth-time-guided-client.ts index 89bd035c..48261848 100644 --- a/frontend/src/lib/birth-time-guided-client.ts +++ b/frontend/src/lib/birth-time-guided-client.ts @@ -16,7 +16,6 @@ type DraftRevision = GuidedMutation & { }; type CandidateSave = GuidedMutation & { readonly resultId: string }; type CandidateConfirmation = CandidateSave & { readonly time: string }; -type CandidateCompletion = Pick & { readonly time: string }; const errorPayloadSchema = z.object({ message: z.string().optional(), @@ -69,22 +68,3 @@ export function confirmGuidedBirthTimeCandidate( ) { return send({ type: "confirm_guided_candidate", ...input }); } - -export async function completeGuidedBirthTimeCandidate( - input: CandidateCompletion, -) { - const { response, payload } = await postJson({ - url: "/api/birth-time-candidate-completion", - body: JSON.stringify(input), - retryLostResponse: false, - }); - if (!response.ok) { - const parsed = errorPayloadSchema.safeParse(payload); - throw new GuidedBirthTimeRequestError( - response.status, - parsed.success - ? parsed.data.message ?? parsed.data.error ?? "候选时间暂时无法保存" - : "候选时间暂时无法保存", - ); - } -} diff --git a/frontend/src/lib/birth-time-intake-model.ts b/frontend/src/lib/birth-time-intake-model.ts index 5f8fd379..a79f2685 100644 --- a/frontend/src/lib/birth-time-intake-model.ts +++ b/frontend/src/lib/birth-time-intake-model.ts @@ -154,7 +154,7 @@ export function isBirthTimeDraftReady(draft: BirthTimeDraft) { export function isBirthTimeReadyForConsultation(draft: BirthTimeDraft) { return Boolean(draft.time) - && (draft.birthTimeStatus === "candidate" || draft.birthTimeStatus === "confirmed"); + && draft.birthTimeStatus === "confirmed"; } export function birthTimePersistenceValues(draft: BirthTimeDraft) { diff --git a/frontend/tests/birth-time-guided-review-fixes.test.ts b/frontend/tests/birth-time-guided-review-fixes.test.ts index 61c62405..50101679 100644 --- a/frontend/tests/birth-time-guided-review-fixes.test.ts +++ b/frontend/tests/birth-time-guided-review-fixes.test.ts @@ -118,6 +118,8 @@ test("ready completion is explicit and terminal low has no finish mutation", () assert.doesNotMatch(hookSource, /turn\.nextAction\.kind === "ready"\) onReady/); assert.match(candidateSource, /acknowledgeReady/); assert.doesNotMatch(candidateSource, /controller\.finish/); + assert.doesNotMatch(hookSource, /completeGuidedBirthTimeCandidate/); + assert.doesNotMatch(hookSource, /completeCandidate:/); }); test("unconfirmed terminal candidates preserve the range without offering direct adoption", () => { diff --git a/frontend/tests/birth-time-intake.test.ts b/frontend/tests/birth-time-intake.test.ts index 08ac8b03..65feb293 100644 --- a/frontend/tests/birth-time-intake.test.ts +++ b/frontend/tests/birth-time-intake.test.ts @@ -47,16 +47,17 @@ test("birth time intake requires only the fields selected by the source", () => assert.equal(isBirthTimeDraftReady({ ...emptyDraft, birthTimeSource: "unknown" }), true); }); -test("a persisted candidate working time can leave rectification onboarding", () => { +test("an unconfirmed candidate working time remains in rectification onboarding", () => { const candidate = { ...emptyDraft, time: "04:53", birthTimeStatus: "candidate", } satisfies BirthTimeDraft; - assert.equal(isBirthTimeReadyForConsultation(candidate), true); + assert.equal(isBirthTimeReadyForConsultation(candidate), false); assert.equal(isBirthTimeReadyForConsultation({ ...candidate, time: "" }), false); assert.equal(isBirthTimeReadyForConsultation({ ...candidate, birthTimeStatus: "rectifying" }), false); + assert.equal(isBirthTimeReadyForConsultation({ ...candidate, birthTimeStatus: "confirmed" }), true); }); test("a persisted candidate working time takes precedence over the reported range", () => { From de37342a3eee1fce411634031abdc92f23a5991f Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 12:15:49 +0800 Subject: [PATCH 03/25] sync: import runtime closure oracle packets --- ...olden_oss_case_ci_contract_2026_07_21.json | 22 ++ ...tartup_self_check_contract_2026_07_21.json | 36 +++ .../evidence_packet_index_2026_07_19.json | 98 +++++++- ...shganit_field_closure_rows_2026_07_21.json | 61 +++++ ...hree_engine_closure_bridge_2026_07_21.json | 31 +++ ...ed_example_candidate_queue_2026_07_21.json | 64 +++++ ...urce_runtime_closure_queue_2026_07_21.json | 70 ++++++ ...n_audit_kp_gochara_muhurta_2026_07_19.json | 8 +- ...yotishganit_bridge_applied_2026_07_21.json | 190 +++++++++++++++ ...th_source_runtime_identity_2026_07_21.json | 54 +++++ ...dicastro_kp_house_cusp_raw_2026_07_21.json | 220 ++++++++++++++++++ ...castro_kp_tmp_env_identity_2026_07_21.json | 23 ++ scripts/jyotishganit_field_probe.py | 133 +++++++++++ ...jyotishganit_mismatch_attribution_queue.py | 62 +++++ scripts/prepare_vedicastro_kp_tmp_env.py | 116 +++++++++ ...ique_promotion_audit_kp_gochara_muhurta.py | 108 +++++++++ scripts/three_engine_high_rigor_parity.py | 3 - scripts/three_engine_mismatch_arbitrator.py | 36 +++ .../three_engine_mismatch_closure_queue.py | 72 ++++++ ..._commercial_startup_self_check_contract.py | 44 ++++ ...test_jyotishganit_and_vedicastro_probes.py | 56 +++++ tests/test_jyotishganit_field_closure_rows.py | 37 +++ ...st_jyotishganit_node_source_attribution.py | 24 ++ ...yotishganit_three_engine_closure_bridge.py | 37 +++ ...p_public_worked_example_candidate_queue.py | 39 ++++ ...source_runtime_closure_queue_2026_07_21.py | 60 +++++ ...ique_promotion_audit_kp_gochara_muhurta.py | 38 +++ tests/test_three_engine_high_rigor_parity.py | 45 ---- ...hree_engine_jyotishganit_bridge_applied.py | 46 ++++ .../test_three_engine_mismatch_arbitrator.py | 19 +- ...t_three_engine_parity_artifact_contract.py | 66 ++++++ tests/test_truth_source_runtime_identity.py | 45 ++++ tests/test_vedicastro_kp_house_cusp_probe.py | 49 ++++ tests/test_vedicastro_kp_runtime_tmp_probe.py | 1 + tests/test_vedicastro_kp_tmp_env_preparer.py | 36 +++ 35 files changed, 1988 insertions(+), 61 deletions(-) create mode 100644 references/oracle/commercial_golden_oss_case_ci_contract_2026_07_21.json create mode 100644 references/oracle/commercial_startup_self_check_contract_2026_07_21.json create mode 100644 references/oracle/jyotishganit_field_closure_rows_2026_07_21.json create mode 100644 references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json create mode 100644 references/oracle/kp_public_worked_example_candidate_queue_2026_07_21.json create mode 100644 references/oracle/source_runtime_closure_queue_2026_07_21.json create mode 100644 references/oracle/three_engine_jyotishganit_bridge_applied_2026_07_21.json create mode 100644 references/oracle/truth_source_runtime_identity_2026_07_21.json create mode 100644 references/oracle/vedicastro_kp_house_cusp_raw_2026_07_21.json create mode 100644 references/oracle/vedicastro_kp_tmp_env_identity_2026_07_21.json create mode 100644 scripts/jyotishganit_field_probe.py create mode 100644 scripts/jyotishganit_mismatch_attribution_queue.py create mode 100644 scripts/prepare_vedicastro_kp_tmp_env.py create mode 100644 scripts/technique_promotion_audit_kp_gochara_muhurta.py create mode 100644 scripts/three_engine_mismatch_closure_queue.py create mode 100644 tests/test_commercial_startup_self_check_contract.py create mode 100644 tests/test_jyotishganit_and_vedicastro_probes.py create mode 100644 tests/test_jyotishganit_field_closure_rows.py create mode 100644 tests/test_jyotishganit_node_source_attribution.py create mode 100644 tests/test_jyotishganit_three_engine_closure_bridge.py create mode 100644 tests/test_kp_public_worked_example_candidate_queue.py create mode 100644 tests/test_source_runtime_closure_queue_2026_07_21.py create mode 100644 tests/test_technique_promotion_audit_kp_gochara_muhurta.py create mode 100644 tests/test_three_engine_jyotishganit_bridge_applied.py create mode 100644 tests/test_truth_source_runtime_identity.py create mode 100644 tests/test_vedicastro_kp_house_cusp_probe.py create mode 100644 tests/test_vedicastro_kp_tmp_env_preparer.py diff --git a/references/oracle/commercial_golden_oss_case_ci_contract_2026_07_21.json b/references/oracle/commercial_golden_oss_case_ci_contract_2026_07_21.json new file mode 100644 index 00000000..c64d07d4 --- /dev/null +++ b/references/oracle/commercial_golden_oss_case_ci_contract_2026_07_21.json @@ -0,0 +1,22 @@ +{ + "scope": "commercial_golden_oss_case_ci_contract", + "created_at": "2026-07-21", + "status": "ready_contract", + "purpose": "Commercial CI must replay at least one public OSS golden observation from the main research repo to detect accidental legacy WorkBuddy or incomplete artifact source wiring.", + "golden_case": { + "name": "pyjhora_jhora_sphuta_oss_case", + "probe": "scripts/prashna_sphuta_oss_case_probe.py", + "test": "tests/test_prashna_sphuta_oss_case_probe.py", + "source_packet": "references/oracle/prashna_sphuta_oss_case_probe_2026_07_20.json", + "expected_raw_hash": "f0705d205440ea8d7f39116042a0723d971f4ab79c94f7d448fc42d683d52326", + "claim_status": "tooling_observation_only" + }, + "gate_policy": { + "fail_if_probe_missing": true, + "fail_if_raw_hash_mismatch": true, + "fail_if_claim_status_upgraded": true, + "truth_upgrade_allowed": false, + "allow_business_runtime_to_continue_after_warning": false + }, + "claim_boundary": "ci_golden_observation_gate_only_not_oracle_truth" +} diff --git a/references/oracle/commercial_startup_self_check_contract_2026_07_21.json b/references/oracle/commercial_startup_self_check_contract_2026_07_21.json new file mode 100644 index 00000000..0eeca119 --- /dev/null +++ b/references/oracle/commercial_startup_self_check_contract_2026_07_21.json @@ -0,0 +1,36 @@ +{ + "scope": "commercial_startup_self_check_contract", + "created_at": "2026-07-21", + "status": "ready_contract", + "purpose": "Commercial runtime must prove which research truth source, skill version, evidence index and oracle gates it is using before claiming website capability readiness.", + "required_fields": [ + "truth_source_path", + "truth_source_git_commit", + "skill_version", + "evidence_packet_count", + "artifact_gate_status", + "privacy_artifact_status", + "oracle_ready_summary" + ], + "reject_if": { + "truth_source_path_contains": [ + "/WorkBuddy/", + "/.workbuddy/" + ], + "artifact_gate_status": [ + "missing", + "unknown" + ], + "privacy_artifact_status": [ + "private_artifact_present", + "unknown" + ] + }, + "recommended_surface": { + "startup_log": true, + "health_endpoint": "/api/runtime-identity", + "admin_status_panel": true, + "ci_snapshot": true + }, + "claim_boundary": "startup_identity_gate_only_not_business_runtime" +} diff --git a/references/oracle/evidence_packet_index_2026_07_19.json b/references/oracle/evidence_packet_index_2026_07_19.json index 5b990aab..996c1dd2 100644 --- a/references/oracle/evidence_packet_index_2026_07_19.json +++ b/references/oracle/evidence_packet_index_2026_07_19.json @@ -5,7 +5,7 @@ "production_tuning_allowed": false, "boundary": "Index of current governance packets only. Raw oracle artifacts remain in references/oracle/artifacts and are not all duplicated here.", "summary": { - "packet_count": 103, + "packet_count": 115, "blocked_or_partial_count": 51, "human_review_required_count": 3 }, @@ -833,6 +833,102 @@ "claim_status": "ready_contract", "consumer_policy": "research_local_ui_contract_only", "claim_boundary": "Ports commercial birth-time journey behavior contracts only; no Supabase, credits, auth, or commercial code imported." + }, + { + "packet_id": "truth_source_runtime_identity", + "path": "references/oracle/truth_source_runtime_identity_2026_07_21.json", + "domain": "truth_source_governance", + "claim_status": "ready_contract", + "consumer_policy": "research_to_commercial_startup_contract", + "claim_boundary": "Pins the main research repo as the only truth source, exposes runtime identity, and quarantines old WorkBuddy fragments; no oracle truth upgrade." + }, + { + "packet_id": "commercial_startup_self_check_contract_2026_07_21", + "path": "references/oracle/commercial_startup_self_check_contract_2026_07_21.json", + "domain": "commercial_sync_governance", + "claim_status": "ready_contract", + "consumer_policy": "commercial_startup_gate_contract_only", + "claim_boundary": "Requires commercial runtime to expose truth source identity, artifact gate and privacy status; no business runtime code imported into research." + }, + { + "packet_id": "commercial_golden_oss_case_ci_contract_2026_07_21", + "path": "references/oracle/commercial_golden_oss_case_ci_contract_2026_07_21.json", + "domain": "commercial_sync_governance", + "claim_status": "ready_contract", + "consumer_policy": "commercial_ci_gate_contract_only", + "claim_boundary": "Requires commercial CI to replay the PyJHora/JHora Sphuta OSS observation and hash; observation-only, no truth upgrade." + }, + { + "packet_id": "whole_machine_jyotish_fragment_scan_2026_07_21", + "path": "references/oracle/whole_machine_jyotish_fragment_scan_2026_07_21.json", + "domain": "fragment_governance", + "claim_status": "open_queue", + "consumer_policy": "research_only_no_direct_fragment_copy", + "claim_boundary": "Whole-machine Jyotish signal scan classifies main, WorkBuddy, temp and user-drop candidates; reviewable fragments are not truth source until privacy/license/invocation checks close." + }, + { + "packet_id": "source_runtime_closure_queue_2026_07_21", + "path": "references/oracle/source_runtime_closure_queue_2026_07_21.json", + "domain": "source_runtime_closure", + "claim_status": "open_queue", + "consumer_policy": "research_observation_only", + "claim_boundary": "Prioritizes not-fully-invoked source assets such as jyotishganit and VedicAstro KP; queue only, no adapter or truth upgrade." + }, + { + "packet_id": "vedicastro_kp_tmp_env_identity_2026_07_21", + "path": "references/oracle/vedicastro_kp_tmp_env_identity_2026_07_21.json", + "domain": "kp_precision_timing", + "claim_status": "observation_only", + "consumer_policy": "research_observation_only", + "claim_boundary": "Pins isolated /tmp VedicAstro KP dependency identity; dependency readiness only, no oracle truth." + }, + { + "packet_id": "vedicastro_kp_house_cusp_raw_2026_07_21", + "path": "references/oracle/vedicastro_kp_house_cusp_raw_2026_07_21.json", + "domain": "kp_precision_timing", + "claim_status": "observation_only", + "consumer_policy": "research_observation_only", + "claim_boundary": "Archives VedicAstro KP house cusp star/sub/sub-sub raw for Steve Jobs public case; observation-only until public numeric worked-example replay." + }, + { + "packet_id": "jyotishganit_three_engine_closure_bridge_2026_07_21", + "path": "references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "observation_only", + "consumer_policy": "research_observation_only", + "claim_boundary": "Routes available jyotishganit D2/D4/D9/D10, Panchanga and BAV/SAV fields to closure queues; Shadbala remains explicit gap." + }, + { + "packet_id": "kp_public_worked_example_candidate_queue_2026_07_21", + "path": "references/oracle/kp_public_worked_example_candidate_queue_2026_07_21.json", + "domain": "kp_precision_timing", + "claim_status": "open_queue", + "consumer_policy": "research_observation_only", + "claim_boundary": "Public KP sources were triaged as formula/runtime candidates; none yet provide stable full numeric cusp star/sub/sub-sub rows for oracle upgrade." + }, + { + "packet_id": "jyotishganit_field_closure_rows_2026_07_21", + "path": "references/oracle/jyotishganit_field_closure_rows_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "observation_only", + "consumer_policy": "research_observation_only", + "claim_boundary": "Routes jyotishganit D2/D4/D9/D10, Panchanga and BAV/SAV selected raw fields into closure rows; Shadbala remains explicit gap." + }, + { + "packet_id": "three_engine_jyotishganit_bridge_applied_2026_07_21", + "path": "references/oracle/three_engine_jyotishganit_bridge_applied_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "observation_only", + "consumer_policy": "research_observation_only", + "claim_boundary": "Applies jyotishganit field bridge to existing TEMCQ D2/D4/D9/D10/BAV/SAV tickets and marks Panchanga as new-ticket-needed; no truth upgrade." + }, + { + "packet_id": "three_engine_field_status_batch_2026_07_21", + "path": "references/oracle/three_engine_field_status_batch_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "observation_only", + "consumer_policy": "research_observation_only", + "claim_boundary": "Classifies 18 jyotishganit-applied TEMCQ rows and creates a Panchanga ticket placeholder; status only, no numeric truth upgrade." } ] } diff --git a/references/oracle/jyotishganit_field_closure_rows_2026_07_21.json b/references/oracle/jyotishganit_field_closure_rows_2026_07_21.json new file mode 100644 index 00000000..0d299bd1 --- /dev/null +++ b/references/oracle/jyotishganit_field_closure_rows_2026_07_21.json @@ -0,0 +1,61 @@ +{ + "scope": "jyotishganit_field_closure_rows", + "created_at": "2026-07-21", + "claim_status": "observation_only", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_probe": "scripts/jyotishganit_field_probe.py", + "source_raw_hash": "19fa9ea862b68c4cffb6756fa6cbf0466daf47a8b008b3fe8809e9fc6a1ed30c", + "source_selected_hash": "4709b8ade84efdea4d0a67c15f3e32cea516a5aa2e8abe3885578feda20cb3f4", + "rows": [ + { + "field": "D2", + "source_path": "selected_raw.varga_sign_table.D2", + "closure_status": "ready_for_field_comparison", + "comparison_unit": "planet_sign_table" + }, + { + "field": "D4", + "source_path": "selected_raw.varga_sign_table.D4", + "closure_status": "ready_for_field_comparison", + "comparison_unit": "planet_sign_table" + }, + { + "field": "D9", + "source_path": "selected_raw.varga_sign_table.D9", + "closure_status": "ready_for_field_comparison", + "comparison_unit": "planet_sign_table" + }, + { + "field": "D10", + "source_path": "selected_raw.varga_sign_table.D10", + "closure_status": "ready_for_field_comparison", + "comparison_unit": "planet_sign_table" + }, + { + "field": "Panchanga", + "source_path": "selected_raw.panchanga", + "closure_status": "ready_for_field_comparison", + "comparison_unit": "schema_and_named_fields" + }, + { + "field": "BAV", + "source_path": "selected_raw.ashtakavarga.bav", + "closure_status": "ready_for_field_comparison", + "comparison_unit": "per_planet_bindus" + }, + { + "field": "SAV", + "source_path": "selected_raw.ashtakavarga.sav", + "closure_status": "ready_for_field_comparison", + "comparison_unit": "sarvashtakavarga_bindus" + } + ], + "explicit_gaps": [ + { + "field": "Shadbala", + "reason": "jyotishganit probe did not expose shadbala/strengths in selected raw" + } + ], + "boundary": "field_rows_only_no_formula_truth" +} diff --git a/references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json b/references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json new file mode 100644 index 00000000..60dd0ee7 --- /dev/null +++ b/references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json @@ -0,0 +1,31 @@ +{ + "scope": "jyotishganit_three_engine_closure_bridge", + "created_at": "2026-07-21", + "claim_status": "observation_only", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_probe": "scripts/jyotishganit_field_probe.py", + "source_raw_hash": "19fa9ea862b68c4cffb6756fa6cbf0466daf47a8b008b3fe8809e9fc6a1ed30c", + "source_selected_hash": "4709b8ade84efdea4d0a67c15f3e32cea516a5aa2e8abe3885578feda20cb3f4", + "field_routes": { + "ready_for_field_comparison": [ + "D2", + "D4", + "D9", + "D10", + "Panchanga", + "BAV_SAV" + ], + "explicit_gaps": [ + "Shadbala" + ], + "comparison_policy": "route sign/table/schema fields into closure queue only; do not infer formula truth from matching signs" + }, + "next_packet_targets": [ + "three_engine_closure_rows_D2_D4_D9_D10", + "three_engine_closure_rows_panchanga", + "three_engine_closure_rows_bav_sav", + "shadbala_missing_field_gap" + ], + "boundary": "bridge_only_no_truth_upgrade" +} diff --git a/references/oracle/kp_public_worked_example_candidate_queue_2026_07_21.json b/references/oracle/kp_public_worked_example_candidate_queue_2026_07_21.json new file mode 100644 index 00000000..a07e58e0 --- /dev/null +++ b/references/oracle/kp_public_worked_example_candidate_queue_2026_07_21.json @@ -0,0 +1,64 @@ +{ + "scope": "kp_public_worked_example_candidate_queue", + "created_at": "2026-07-21", + "claim_status": "open_queue", + "numeric_oracle_ready_count": 0, + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "runtime_observation_packet": "references/oracle/vedicastro_kp_house_cusp_raw_2026_07_21.json", + "candidates": [ + { + "id": "astrosage_kp_chapter_2", + "url": "https://kpastrology.astrosage.com/kp-learning-home/tutorial/chapter-2-fundamental-principles", + "candidate_type": "formula_and_text_worked_example", + "useful_fields": [ + "sign lord", + "star lord", + "sub lord", + "significator logic", + "ruling planets text examples" + ], + "has_numeric_cusp_table": false, + "upgrade_status": "reference_only_until_numeric_cusp_rows" + }, + { + "id": "astrosage_kp_sign_star_sub_table", + "url": "https://kpastrology.astrosage.com/kp-learning-home/tutorial/chapter-2-fundamental-principles", + "candidate_type": "reference_table", + "useful_fields": [ + "sign-star-sub division rules", + "KP star/sub conceptual table" + ], + "has_numeric_cusp_table": false, + "upgrade_status": "formula_reference_only" + }, + { + "id": "onlinejyotish_kp_horoscope", + "url": "https://www.onlinejyotish.com/free-astrology/kp-horoscope.aspx", + "candidate_type": "runtime_form_candidate", + "useful_fields": [ + "possible KP horoscope table", + "possible cusp/sub-lord output" + ], + "has_numeric_cusp_table": false, + "upgrade_status": "blocked_until_stable_public_input_output_packet" + }, + { + "id": "astrobix_kp_houses", + "url": "https://astrobix.com/astrosight/1291-houses-in-kp-system.html", + "candidate_type": "runtime_form_candidate", + "useful_fields": [ + "KP house/cusp explanation", + "possible public worked chart context" + ], + "has_numeric_cusp_table": false, + "upgrade_status": "reference_only_until_numeric_rows" + } + ], + "next_actions": [ + "Capture a stable public KP chart with full cusp degree, star lord, sub lord and sub-sub lord rows.", + "Normalize VedicAstro KP raw to the same 12-row table.", + "Only then create a numeric oracle packet; until then KP timing remains observation-only." + ], + "boundary": "public_source_candidate_queue_only_no_kp_numeric_oracle" +} diff --git a/references/oracle/source_runtime_closure_queue_2026_07_21.json b/references/oracle/source_runtime_closure_queue_2026_07_21.json new file mode 100644 index 00000000..73146dc6 --- /dev/null +++ b/references/oracle/source_runtime_closure_queue_2026_07_21.json @@ -0,0 +1,70 @@ +{ + "scope": "source_runtime_closure_queue", + "created_at": "2026-07-21", + "claim_status": "open_queue", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_scan": "references/oracle/whole_machine_jyotish_fragment_scan_2026_07_21.json", + "not_fully_closed_reference_layers": [ + "references/open_source_sources/jyotishganit", + "references/open_source_sources/VedicAstro", + "references/open_source_sources/jaimini-tropical", + "references/open_source_sources/rishi-ai-mcp", + "references/open_source_sources/vedic-astro-skills", + "references/open_source_sources/dashaflow" + ], + "rows": [ + { + "id": "jyotishganit_field_closure", + "source": "references/open_source_sources/jyotishganit", + "existing_probe": "scripts/jyotishganit_field_probe.py", + "existing_test": "tests/test_jyotishganit_and_vedicastro_probes.py", + "current_status": "probe_runs_observation_only", + "latest_observed_raw_hash": "19fa9ea862b68c4cffb6756fa6cbf0466daf47a8b008b3fe8809e9fc6a1ed30c", + "latest_observed_selected_hash": "4709b8ade84efdea4d0a67c15f3e32cea516a5aa2e8abe3885578feda20cb3f4", + "coverage": { + "panchanga": true, + "D2": true, + "D4": true, + "D9": true, + "D10": true, + "BAV_SAV": true, + "Shadbala": false + }, + "next_closure_step": "attach field-level comparison rows to the three-engine closure queue; keep Shadbala missing as explicit gap" + }, + { + "id": "vedicastro_kp_house_cusp_closure", + "source": "references/open_source_sources/VedicAstro", + "existing_probe": "scripts/vedicastro_kp_house_cusp_probe.py", + "existing_test": "tests/test_vedicastro_kp_runtime_tmp_probe.py", + "current_status": "raw_ready_observation_only", + "next_closure_step": "compare these 12 cusp star/sub/sub-sub rows against public numeric KP worked examples before any event timing use", + "latest_raw_packet": "references/oracle/vedicastro_kp_house_cusp_raw_2026_07_21.json", + "latest_env_identity": "references/oracle/vedicastro_kp_tmp_env_identity_2026_07_21.json", + "latest_observed_raw_hash": "2e7a6b17eb2965a60846f625f6bd8bc03555216d6225983647bcbfa23d0e345b", + "coverage": { + "house_cusps": 12, + "rasi": true, + "nakshatra": true, + "rasi_lord": true, + "nakshatra_lord": true, + "sub_lord": true, + "sub_sub_lord": true + } + }, + { + "id": "rishi_ai_mcp_vedic_astro_skills_reference", + "source": "references/open_source_sources/rishi-ai-mcp + references/open_source_sources/vedic-astro-skills", + "current_status": "reference_only_prompt_or_product_flow", + "next_closure_step": "extract skill/process wording only after license/source review; never numeric oracle or formula truth" + }, + { + "id": "jaimini_tropical_dashaflow_reference", + "source": "references/open_source_sources/jaimini-tropical + references/open_source_sources/dashaflow", + "current_status": "reference_only_until_contract", + "next_closure_step": "pin license, version, input contract, raw hash and replay tests before any adapter or truth matrix use" + } + ], + "boundary": "queue_only_no_adapter_or_truth_upgrade" +} diff --git a/references/oracle/technique_promotion_audit_kp_gochara_muhurta_2026_07_19.json b/references/oracle/technique_promotion_audit_kp_gochara_muhurta_2026_07_19.json index 19f41750..60595216 100644 --- a/references/oracle/technique_promotion_audit_kp_gochara_muhurta_2026_07_19.json +++ b/references/oracle/technique_promotion_audit_kp_gochara_muhurta_2026_07_19.json @@ -3,14 +3,14 @@ "items": [ { "claim_boundary": "Panchanga is runtime-visible but still needs field-level external oracle examples for high-rigor claims.", - "current_call_status": "formally_called_in_api_and_web", + "current_call_status": "partial", "external_or_reference_artifacts": [ "references/open_source_sources/panchanga_api" ], "main_artifacts": [ "scripts/jyotish_api_server.py", - "jyotish-app/main.js", - "jyotish-app/index.html" + "frontend/src/app/page.tsx", + "jyotish-app/main.js (research static UI only, absent in commercial repo)" ], "next_action": "add panchanga claim/display contract and source/oracle packet for tithi/nakshatra/yoga/karana/rahu-kalam outputs", "reuse_decision": "do_not_duplicate_runtime", @@ -57,7 +57,7 @@ "production_tuning_allowed": false, "scope": "technique_promotion_audit_kp_gochara_muhurta", "summary": { - "formally_called_count": 1, + "formally_called_count": 0, "items_checked": 4, "reference_only_count": 3 }, diff --git a/references/oracle/three_engine_jyotishganit_bridge_applied_2026_07_21.json b/references/oracle/three_engine_jyotishganit_bridge_applied_2026_07_21.json new file mode 100644 index 00000000..e49eb61d --- /dev/null +++ b/references/oracle/three_engine_jyotishganit_bridge_applied_2026_07_21.json @@ -0,0 +1,190 @@ +{ + "scope": "three_engine_jyotishganit_bridge_applied", + "created_at": "2026-07-21", + "claim_status": "observation_only", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_bridge": "references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json", + "source_queue": "references/oracle/three_engine_mismatch_closure_queue_2026_07_19.json", + "source_selected_hash": "4709b8ade84efdea4d0a67c15f3e32cea516a5aa2e8abe3885578feda20cb3f4", + "summary": { + "existing_ticket_rows": 18, + "no_existing_ticket_rows": 1, + "truth_upgrades": 0 + }, + "rows": [ + { + "source_field": "D2", + "field": "Sun.sign", + "existing_ticket_id": "TEMCQ-001", + "existing_ticket_section": "D2", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D2", + "field": "Moon.sign", + "existing_ticket_id": "TEMCQ-002", + "existing_ticket_section": "D2", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D2", + "field": "Mars.sign", + "existing_ticket_id": "TEMCQ-003", + "existing_ticket_section": "D2", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D2", + "field": "Mercury.sign", + "existing_ticket_id": "TEMCQ-004", + "existing_ticket_section": "D2", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D2", + "field": "Jupiter.sign", + "existing_ticket_id": "TEMCQ-005", + "existing_ticket_section": "D2", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D2", + "field": "Venus.sign", + "existing_ticket_id": "TEMCQ-006", + "existing_ticket_section": "D2", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D2", + "field": "Saturn.sign", + "existing_ticket_id": "TEMCQ-007", + "existing_ticket_section": "D2", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D4", + "field": "Moon.sign", + "existing_ticket_id": "TEMCQ-008", + "existing_ticket_section": "D4", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D9", + "field": "Moon.sign", + "existing_ticket_id": "TEMCQ-009", + "existing_ticket_section": "D9", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "D10", + "field": "Moon.sign", + "existing_ticket_id": "TEMCQ-010", + "existing_ticket_section": "D10", + "owner_track": "endpoint_contract", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "BAV", + "field": "Sun", + "existing_ticket_id": "TEMCQ-011", + "existing_ticket_section": "ashtakavarga_bav", + "owner_track": "worked_example", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "BAV", + "field": "Moon", + "existing_ticket_id": "TEMCQ-012", + "existing_ticket_section": "ashtakavarga_bav", + "owner_track": "worked_example", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "BAV", + "field": "Mars", + "existing_ticket_id": "TEMCQ-013", + "existing_ticket_section": "ashtakavarga_bav", + "owner_track": "worked_example", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "BAV", + "field": "Mercury", + "existing_ticket_id": "TEMCQ-014", + "existing_ticket_section": "ashtakavarga_bav", + "owner_track": "worked_example", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "BAV", + "field": "Jupiter", + "existing_ticket_id": "TEMCQ-015", + "existing_ticket_section": "ashtakavarga_bav", + "owner_track": "worked_example", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "BAV", + "field": "Venus", + "existing_ticket_id": "TEMCQ-016", + "existing_ticket_section": "ashtakavarga_bav", + "owner_track": "worked_example", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "BAV", + "field": "Saturn", + "existing_ticket_id": "TEMCQ-017", + "existing_ticket_section": "ashtakavarga_bav", + "owner_track": "worked_example", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "SAV", + "field": "12_sign_scores", + "existing_ticket_id": "TEMCQ-018", + "existing_ticket_section": "ashtakavarga_sav", + "owner_track": "worked_example", + "closure_status": "ready_for_field_comparison", + "claim_boundary": "field_comparison_only_no_formula_truth" + }, + { + "source_field": "Panchanga", + "field": "Panchanga", + "existing_ticket_id": null, + "existing_ticket_section": null, + "owner_track": "new_ticket_required", + "closure_status": "no_existing_ticket_create_next", + "claim_boundary": "field_comparison_only_no_formula_truth" + } + ], + "boundary": "applied_bridge_only_no_truth_upgrade", + "human_report": "docs/research/three_engine_jyotishganit_bridge_applied_2026_07_21.md" +} diff --git a/references/oracle/truth_source_runtime_identity_2026_07_21.json b/references/oracle/truth_source_runtime_identity_2026_07_21.json new file mode 100644 index 00000000..51589391 --- /dev/null +++ b/references/oracle/truth_source_runtime_identity_2026_07_21.json @@ -0,0 +1,54 @@ +{ + "scope": "truth_source_runtime_identity", + "created_at": "2026-07-21", + "status": "active_contract_v1", + "truth_source": { + "path": "/Users/wuyongnaren/Documents/印度占星", + "role": "sole_main_research_truth_source", + "branch_at_capture": "main", + "git_commit": "737683edc976d28558d81737501943790da07b34", + "git_commit_semantics": "commit at packet capture time; later packet commits supersede this value through git history" + }, + "skill": { + "path": "SKILL.md", + "version": "6.9.14" + }, + "evidence_packets": { + "index_path": "references/oracle/evidence_packet_index_2026_07_19.json", + "packet_count": 106, + "blocked_or_partial_count": 51, + "human_review_required_count": 3 + }, + "oracle_summary": { + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "vedastro_hosted_identity": "blocked", + "timing_holdout": "blocked_until_independent_human_labels", + "sphuta_oss_probe": "tooling_observation_only", + "three_engine_parity": "open_queue" + }, + "runtime_contract": { + "research_web_panel_required": true, + "commercial_startup_self_check_required": true, + "commercial_ci_golden_case_required": "pyjhora_jhora_sphuta_probe", + "must_not_accept_legacy_workbuddy_as_truth_source": true + }, + "fragment_quarantine": { + "workbuddy_old_copies": { + "status": "quarantined", + "labels": [ + "not_for_truth_source", + "privacy_review_required", + "artifact_incomplete", + "historical_fragment_only" + ], + "paths": [ + "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing", + "/Users/wuyongnaren/.workbuddy/backups/jyotish-vedic-astrology-20260711-154109", + "/Users/wuyongnaren/.workbuddy/backups/jyotish-vedic-astrology-retired-20260711-154109" + ], + "boundary": "May be read as fragment candidates only after ledger review; never a runtime, oracle, commercial, or truth source." + } + }, + "claim_boundary": "identity_ready_governance_only_not_oracle_truth" +} diff --git a/references/oracle/vedicastro_kp_house_cusp_raw_2026_07_21.json b/references/oracle/vedicastro_kp_house_cusp_raw_2026_07_21.json new file mode 100644 index 00000000..4aec2c59 --- /dev/null +++ b/references/oracle/vedicastro_kp_house_cusp_raw_2026_07_21.json @@ -0,0 +1,220 @@ +{ + "boundary": "KP house cusp star/sub/sub-sub raw from VedicAstro runtime. Observation-only until matched against a public numeric KP worked example.", + "claim_status": "observation_only", + "created_at": "2026-07-19", + "dependency_identity": { + "flatlib": "0.3.1.dev0", + "observed_pinned_flatlib_commit": "2618c348ce1ab2588548f935ff65f031630b4872", + "polars": "1.42.1", + "required_flatlib_source": "git+https://github.com/diliprk/flatlib.git@sidereal", + "timezonefinder": "8.2.5" + }, + "engine": "VedicAstro", + "production_tuning_allowed": false, + "raw": { + "houses": [ + { + "DegSize": 26.059, + "HouseNr": 1, + "LonDecDeg": 149.434, + "Nakshatra": "UttaraPhalgunī", + "NakshatraLord": "Sun", + "Object": "I", + "Rasi": "Leo", + "RasiLord": "Sun", + "SignLonDMS": "+29:26:02", + "SignLonDecDeg": 29.434, + "SubLord": "Rahu", + "SubSubLord": "Rahu" + }, + { + "DegSize": 30.141, + "HouseNr": 2, + "LonDecDeg": 175.493, + "Nakshatra": "Chitra", + "NakshatraLord": "Mars", + "Object": "II", + "Rasi": "Virgo", + "RasiLord": "Mercury", + "SignLonDMS": "+25:29:35", + "SignLonDecDeg": 25.493, + "SubLord": "Rahu", + "SubSubLord": "Venus" + }, + { + "DegSize": 32.894, + "HouseNr": 3, + "LonDecDeg": 205.634, + "Nakshatra": "Vishakha", + "NakshatraLord": "Jupiter", + "Object": "III", + "Rasi": "Libra", + "RasiLord": "Venus", + "SignLonDMS": "+25:38:01", + "SignLonDecDeg": 25.634, + "SubLord": "Mercury", + "SubSubLord": "Saturn" + }, + { + "DegSize": 33.066, + "HouseNr": 4, + "LonDecDeg": 238.527, + "Nakshatra": "Jyeshtha", + "NakshatraLord": "Mercury", + "Object": "IV", + "Rasi": "Scorpio", + "RasiLord": "Mars", + "SignLonDMS": "+28:31:38", + "SignLonDecDeg": 28.527, + "SubLord": "Saturn", + "SubSubLord": "Ketu" + }, + { + "DegSize": 30.713, + "HouseNr": 5, + "LonDecDeg": 271.594, + "Nakshatra": "UttaraAshadha", + "NakshatraLord": "Sun", + "Object": "V", + "Rasi": "Capricorn", + "RasiLord": "Saturn", + "SignLonDMS": "+01:35:37", + "SignLonDecDeg": 1.594, + "SubLord": "Jupiter", + "SubSubLord": "Saturn" + }, + { + "DegSize": 27.128, + "HouseNr": 6, + "LonDecDeg": 302.306, + "Nakshatra": "Dhanishta", + "NakshatraLord": "Mars", + "Object": "VI", + "Rasi": "Aquarius", + "RasiLord": "Saturn", + "SignLonDMS": "+02:18:22", + "SignLonDecDeg": 2.306, + "SubLord": "Ketu", + "SubSubLord": "Rahu" + }, + { + "DegSize": 26.059, + "HouseNr": 7, + "LonDecDeg": 329.434, + "Nakshatra": "PurvaBhādrapadā", + "NakshatraLord": "Jupiter", + "Object": "VII", + "Rasi": "Aquarius", + "RasiLord": "Saturn", + "SignLonDMS": "+29:26:02", + "SignLonDecDeg": 29.434, + "SubLord": "Sun", + "SubSubLord": "Venus" + }, + { + "DegSize": 30.141, + "HouseNr": 8, + "LonDecDeg": 355.493, + "Nakshatra": "Revati", + "NakshatraLord": "Mercury", + "Object": "VIII", + "Rasi": "Pisces", + "RasiLord": "Jupiter", + "SignLonDMS": "+25:29:35", + "SignLonDecDeg": 25.493, + "SubLord": "Rahu", + "SubSubLord": "Venus" + }, + { + "DegSize": 32.894, + "HouseNr": 9, + "LonDecDeg": 25.634, + "Nakshatra": "Bharani", + "NakshatraLord": "Venus", + "Object": "IX", + "Rasi": "Aries", + "RasiLord": "Mars", + "SignLonDMS": "+25:38:01", + "SignLonDecDeg": 25.634, + "SubLord": "Mercury", + "SubSubLord": "Saturn" + }, + { + "DegSize": 33.066, + "HouseNr": 10, + "LonDecDeg": 58.527, + "Nakshatra": "Mrigashīrsha", + "NakshatraLord": "Mars", + "Object": "X", + "Rasi": "Taurus", + "RasiLord": "Venus", + "SignLonDMS": "+28:31:38", + "SignLonDecDeg": 28.527, + "SubLord": "Saturn", + "SubSubLord": "Ketu" + }, + { + "DegSize": 30.713, + "HouseNr": 11, + "LonDecDeg": 91.594, + "Nakshatra": "Punarvasu", + "NakshatraLord": "Jupiter", + "Object": "XI", + "Rasi": "Cancer", + "RasiLord": "Moon", + "SignLonDMS": "+01:35:37", + "SignLonDecDeg": 1.594, + "SubLord": "Rahu", + "SubSubLord": "Rahu" + }, + { + "DegSize": 27.128, + "HouseNr": 12, + "LonDecDeg": 122.306, + "Nakshatra": "Maghā", + "NakshatraLord": "Ketu", + "Object": "XII", + "Rasi": "Leo", + "RasiLord": "Sun", + "SignLonDMS": "+02:18:22", + "SignLonDecDeg": 2.306, + "SubLord": "Venus", + "SubSubLord": "Saturn" + } + ], + "request": { + "ayanamsa": "Krishnamurti", + "day": 24, + "hour": 19, + "house_system": "Placidus", + "latitude": 37.3382, + "longitude": -122.0383, + "minute": 15, + "month": 2, + "second": 0, + "timezone": "America/Los_Angeles", + "year": 1955 + } + }, + "raw_hash": "2e7a6b17eb2965a60846f625f6bd8bc03555216d6225983647bcbfa23d0e345b", + "schema_fingerprint": { + "fields": [ + "Object", + "HouseNr", + "Rasi", + "LonDecDeg", + "SignLonDMS", + "SignLonDecDeg", + "DegSize", + "Nakshatra", + "RasiLord", + "NakshatraLord", + "SubLord", + "SubSubLord" + ], + "house_count": 12 + }, + "scope": "vedicastro_kp_house_cusp_probe", + "status": "complete", + "truth_matrix_allowed": false +} diff --git a/references/oracle/vedicastro_kp_tmp_env_identity_2026_07_21.json b/references/oracle/vedicastro_kp_tmp_env_identity_2026_07_21.json new file mode 100644 index 00000000..f5a8ba0a --- /dev/null +++ b/references/oracle/vedicastro_kp_tmp_env_identity_2026_07_21.json @@ -0,0 +1,23 @@ +{ + "boundary": "dependency_preparation_only_no_kp_oracle_truth", + "claim_status": "runtime_dependency_ready", + "created_at": "2026-07-21", + "package_versions": { + "flatlib": "0.3.1.dev0", + "polars": "1.42.1", + "pyswisseph": "2.10.3.2", + "timezonefinder": "8.2.5" + }, + "production_tuning_allowed": false, + "project_dependency_mutation_allowed": false, + "required_packages": { + "flatlib": "git+https://github.com/diliprk/flatlib.git@sidereal#egg=flatlib", + "polars": "polars", + "pyswisseph": "pyswisseph", + "timezonefinder": "timezonefinder" + }, + "scope": "vedicastro_kp_tmp_env_preparer", + "target": "/tmp/vedicastro_flatlib_probe", + "target_tree_hash": "7a75855cd9f641a30a4d466d44a282571e3d797c39de0d975f5f6647a3e1bf74", + "truth_matrix_allowed": false +} diff --git a/scripts/jyotishganit_field_probe.py b/scripts/jyotishganit_field_probe.py new file mode 100644 index 00000000..4a2f1344 --- /dev/null +++ b/scripts/jyotishganit_field_probe.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +"""Run a local jyotishganit same-case raw field probe. + +Observation-only: records raw/hash/schema for D2/D4/D9/D10, Panchanga, +BAV/SAV and Shadbala availability without promoting truth. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +import sys +from datetime import datetime +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +JYOTISHGANIT_ROOT = ROOT / "references/open_source_sources/jyotishganit" +TARGET_VARGAS = ["d2", "d4", "d9", "d10"] + + +def stable_json(data: Any) -> str: + return json.dumps(data, ensure_ascii=False, sort_keys=True, separators=(",", ":"), default=str) + + +def schema_fingerprint(data: Any) -> Any: + if isinstance(data, dict): + return {k: schema_fingerprint(v) for k, v in sorted(data.items())} + if isinstance(data, list): + if not data: + return [] + return [schema_fingerprint(data[0])] + return type(data).__name__ + + +def sign_table(chart: dict[str, Any]) -> dict[str, Any]: + out: dict[str, Any] = {} + for code in TARGET_VARGAS: + section = chart.get("divisionalCharts", {}).get(code) + if not isinstance(section, dict): + out[code.upper()] = {"status": "missing"} + continue + rows = [] + for house in section.get("houses", []): + for occ in house.get("occupants", []): + rows.append( + { + "planet": occ.get("celestialBody"), + "sign": occ.get("sign"), + "d1HousePlacement": occ.get("d1HousePlacement"), + } + ) + out[code.upper()] = { + "status": "present", + "ascendant_sign": section.get("ascendant", {}).get("sign"), + "planet_signs": sorted(rows, key=lambda r: str(r.get("planet"))), + } + return out + + +def build_probe(args: argparse.Namespace) -> dict[str, Any]: + sys.path.insert(0, str(JYOTISHGANIT_ROOT)) + from jyotishganit.main import calculate_birth_chart # type: ignore + + dt = datetime.fromisoformat(args.datetime) + chart = calculate_birth_chart(dt, args.latitude, args.longitude, args.timezone, args.location, args.name) + raw = chart.to_dict() + selected = { + "panchanga": raw.get("panchanga"), + "varga_sign_table": sign_table(raw), + "ashtakavarga": raw.get("ashtakavarga"), + "shadbala": raw.get("shadbala"), + "strengths": raw.get("strengths"), + } + payload = { + "scope": "jyotishganit_field_probe", + "created_at": "2026-07-19", + "status": "complete", + "claim_status": "observation_only", + "production_tuning_allowed": False, + "truth_matrix_allowed": False, + "engine": { + "name": "jyotishganit", + "local_path": str(JYOTISHGANIT_ROOT.relative_to(ROOT)), + }, + "request": { + "name": args.name, + "datetime": args.datetime, + "latitude": args.latitude, + "longitude": args.longitude, + "timezone": args.timezone, + "location": args.location, + }, + "coverage": { + "panchanga": raw.get("panchanga") is not None, + "D2": selected["varga_sign_table"]["D2"]["status"] == "present", + "D4": selected["varga_sign_table"]["D4"]["status"] == "present", + "D9": selected["varga_sign_table"]["D9"]["status"] == "present", + "D10": selected["varga_sign_table"]["D10"]["status"] == "present", + "BAV_SAV": isinstance(raw.get("ashtakavarga"), dict) + and "sav" in raw.get("ashtakavarga", {}), + "Shadbala": raw.get("shadbala") is not None or raw.get("strengths") is not None, + }, + "raw_hash": hashlib.sha256(stable_json(raw).encode("utf-8")).hexdigest(), + "selected_hash": hashlib.sha256(stable_json(selected).encode("utf-8")).hexdigest(), + "schema_fingerprint": schema_fingerprint(selected), + "selected_raw": selected, + "boundary": "Raw/hash observation only. Missing Shadbala field or matching signs do not prove formula truth or production timing readiness.", + } + return payload + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--datetime", default="1955-02-24T19:15:00") + ap.add_argument("--latitude", type=float, default=37.3382) + ap.add_argument("--longitude", type=float, default=-122.0383) + ap.add_argument("--timezone", type=float, default=-8.0) + ap.add_argument("--location", default="San Francisco, CA") + ap.add_argument("--name", default="Steve Jobs public") + ap.add_argument("--output") + args = ap.parse_args() + payload = build_probe(args) + text = json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + if args.output: + Path(args.output).write_text(text + "\n", encoding="utf-8") + print(text) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/jyotishganit_mismatch_attribution_queue.py b/scripts/jyotishganit_mismatch_attribution_queue.py new file mode 100644 index 00000000..5bf5fbe6 --- /dev/null +++ b/scripts/jyotishganit_mismatch_attribution_queue.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Classify local vs jyotishganit comparison mismatches without resolving truth.""" +from __future__ import annotations + +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +DEFAULT = ROOT / "references/oracle/jyotishganit_vs_local_field_comparison_steve_jobs_2026_07_19.json" + + +def classify(row: dict) -> dict: + section = row["section"] + body = row["body"] + reason = "needs_formula_variant_review" + owner = "varga_formula_attribution" + if section == "D4": + reason = "schema_alias_or_formula_variant" + owner = "D4_Turyamsa_Chaturthamsa_alias_and_formula" + if section == "D10" and body in {"Rahu", "Ketu"}: + reason = "node_mode_or_shadow_planet_handling" + owner = "node_mode_mapping" + return { + **row, + "attribution_status": "queued", + "probable_reason": reason, + "next_evidence_owner": owner, + "claim_boundary": "Do not tune local formula to jyotishganit until source formula, ayanamsa, node mode, and schema aliases are pinned.", + } + + +def build(path: Path = DEFAULT) -> dict: + data = json.loads(path.read_text(encoding="utf-8")) + mismatches = [classify(r) for r in data["rows"] if r["status"] == "mismatch"] + return { + "scope": "jyotishganit_mismatch_attribution_queue", + "created_at": "2026-07-19", + "status": "queue_ready", + "claim_status": "partial", + "production_tuning_allowed": False, + "truth_matrix_allowed": False, + "source_comparison": str(path.relative_to(ROOT)), + "summary": { + "mismatch_count": len(mismatches), + "by_reason": { + reason: sum(1 for r in mismatches if r["probable_reason"] == reason) + for reason in sorted({r["probable_reason"] for r in mismatches}) + }, + }, + "rows": mismatches, + "boundary": "This queue classifies mismatch work; it does not settle formula truth.", + } + + +def main() -> int: + print(json.dumps(build(), ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/prepare_vedicastro_kp_tmp_env.py b/scripts/prepare_vedicastro_kp_tmp_env.py new file mode 100644 index 00000000..9cc0737f --- /dev/null +++ b/scripts/prepare_vedicastro_kp_tmp_env.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Prepare an isolated temporary dependency path for VedicAstro KP probes. + +Installs only under /tmp/vedicastro_flatlib_probe. Never mutates project +requirements, venvs, package-locks, or runtime dependencies. +""" +from __future__ import annotations + +import argparse +import hashlib +import importlib.metadata +import json +import shutil +import subprocess +import sys +from pathlib import Path +from typing import Any + + +TARGET = Path("/tmp/vedicastro_flatlib_probe") +REQUIRED_PACKAGES = { + "flatlib": "git+https://github.com/diliprk/flatlib.git@sidereal#egg=flatlib", + "polars": "polars", + "timezonefinder": "timezonefinder", + "pyswisseph": "pyswisseph", +} + + +def stable(data: Any) -> str: + return json.dumps(data, ensure_ascii=False, sort_keys=True, separators=(",", ":"), default=str) + + +def digest_tree(path: Path) -> str | None: + if not path.exists(): + return None + h = hashlib.sha256() + for file in sorted(p for p in path.rglob("*") if p.is_file()): + rel = file.relative_to(path).as_posix() + h.update(rel.encode("utf-8")) + try: + h.update(file.read_bytes()) + except OSError: + continue + return h.hexdigest() + + +def package_versions(target: Path) -> dict[str, str | None]: + sys.path.insert(0, str(target)) + versions: dict[str, str | None] = {} + for name in REQUIRED_PACKAGES: + try: + versions[name] = importlib.metadata.version(name) + except importlib.metadata.PackageNotFoundError: + versions[name] = None + return versions + + +def install(target: Path) -> dict[str, Any]: + target.mkdir(parents=True, exist_ok=True) + cmd = [ + sys.executable, + "-m", + "pip", + "install", + "--upgrade", + "--target", + str(target), + *REQUIRED_PACKAGES.values(), + ] + proc = subprocess.run(cmd, text=True, capture_output=True, timeout=180) + return { + "command": cmd, + "returncode": proc.returncode, + "stdout_tail": proc.stdout[-4000:], + "stderr_tail": proc.stderr[-4000:], + } + + +def build_payload(target: Path, install_result: dict[str, Any] | None = None) -> dict[str, Any]: + versions = package_versions(target) if target.exists() else {name: None for name in REQUIRED_PACKAGES} + ready = all(versions.values()) + payload: dict[str, Any] = { + "scope": "vedicastro_kp_tmp_env_preparer", + "created_at": "2026-07-21", + "target": str(target), + "project_dependency_mutation_allowed": False, + "required_packages": REQUIRED_PACKAGES, + "package_versions": versions, + "target_tree_hash": digest_tree(target), + "claim_status": "runtime_dependency_ready" if ready else "blocked_runtime_dependency", + "production_tuning_allowed": False, + "truth_matrix_allowed": False, + "boundary": "dependency_preparation_only_no_kp_oracle_truth", + } + if install_result is not None: + payload["install_result"] = install_result + return payload + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--target", default=str(TARGET)) + ap.add_argument("--report-only", action="store_true") + ap.add_argument("--clean", action="store_true") + args = ap.parse_args() + target = Path(args.target) + if args.clean and target.exists() and str(target).startswith("/tmp/"): + shutil.rmtree(target) + install_result = None if args.report_only else install(target) + payload = build_payload(target, install_result) + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 if args.report_only or payload["claim_status"] == "runtime_dependency_ready" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/technique_promotion_audit_kp_gochara_muhurta.py b/scripts/technique_promotion_audit_kp_gochara_muhurta.py new file mode 100644 index 00000000..5a947fb9 --- /dev/null +++ b/scripts/technique_promotion_audit_kp_gochara_muhurta.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""Audit KP/Gochara/Muhurta/Panchanga fragments and runtime entrypoints.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +def _text(path: Path) -> str: + return path.read_text(encoding="utf-8", errors="ignore") if path.exists() else "" + + +def build_audit(root: Path) -> dict: + api = _text(root / "scripts/jyotish_api_server.py") + research_main_js = _text(root / "jyotish-app/main.js") + commercial_page = _text(root / "frontend/src/app/page.tsx") + dashaflow_muhurtha = root / "references/open_source_sources/dashaflow/muhurtha.py" + panchanga_license = _text(root / "references/open_source_sources/panchanga_api/LICENSE").splitlines() + kp_reference = root / "/Users/wuyongnaren/.workbuddy/backups/jyotish-vedic-astrology-20260711-154109/references/kp-astrology-complete-system.md" + gochara_template = Path("/tmp/jyotisha-optimize/assets/event_timing_template.md") + panchanga_called = ( + "/api/panchanga_range" in api + and ( + ("panchanga-range" in research_main_js and "panchanga-csv" in research_main_js) + or ("panchanga-range" in commercial_page and "panchanga-csv" in commercial_page) + ) + ) + + items = [ + { + "technique_id": "panchanga_calendar", + "current_call_status": "formally_called_in_api_and_web" if panchanga_called else "partial", + "main_artifacts": [ + "scripts/jyotish_api_server.py", + "frontend/src/app/page.tsx", + "jyotish-app/main.js (research static UI only, absent in commercial repo)", + ], + "external_or_reference_artifacts": ["references/open_source_sources/panchanga_api"], + "reuse_decision": "do_not_duplicate_runtime", + "source_or_license_boundary": "Existing runtime/UI present; panchanga_api license observed as " + + (panchanga_license[0] if panchanga_license else "unknown") + + ". Treat external panchanga_api as reference unless license/API contract is separately audited.", + "next_action": "add panchanga claim/display contract and source/oracle packet for tithi/nakshatra/yoga/karana/rahu-kalam outputs", + "claim_boundary": "Panchanga is runtime-visible but still needs field-level external oracle examples for high-rigor claims.", + }, + { + "technique_id": "muhurta_dashaflow_candidate", + "current_call_status": "oss_reference_not_main_runtime", + "main_artifacts": [], + "external_or_reference_artifacts": [str(dashaflow_muhurtha)], + "reuse_decision": "license_audit_before_reuse", + "source_or_license_boundary": "dashaflow/muhurtha.py exists under references/open_source_sources; verify license and formula sources before adapting.", + "next_action": "audit dashaflow license, extract formula surface, then compare Tarabala/Chandrabala/Rahu Kalam against local Panchanga.", + "claim_boundary": "Muhurta remains reference-only until license, formula, and worked examples close.", + }, + { + "technique_id": "kp_astrology", + "current_call_status": "reference_only_not_main_runtime", + "main_artifacts": [], + "external_or_reference_artifacts": [str(kp_reference)], + "reuse_decision": "reference_only", + "source_or_license_boundary": "KP backup/reference may contain useful notes but must pass privacy/license/source audit; do not copy blindly.", + "next_action": "create KP separate track: cusp system, ayanamsa, star lord/sub lord, ruling planets, public oracle examples.", + "claim_boundary": "KP is not part of current main Jyotish runtime truth.", + }, + { + "technique_id": "gochara_event_timing_template", + "current_call_status": "template_reference_not_main_runtime", + "main_artifacts": [], + "external_or_reference_artifacts": [str(gochara_template)], + "reuse_decision": "reference_only", + "source_or_license_boundary": "Template in /tmp must be privacy/source reviewed before promotion.", + "next_action": "turn Gochara template into scoring contract only after Dasha+Varga+Transit features and negative holdout are ready.", + "claim_boundary": "Transit template is not a calibrated timing engine.", + }, + ] + return { + "scope": "technique_promotion_audit_kp_gochara_muhurta", + "created_at": "2026-07-19", + "truth_policy": "runtime_presence_not_oracle_closure", + "production_tuning_allowed": False, + "summary": { + "items_checked": len(items), + "formally_called_count": sum("formally_called" in item["current_call_status"] for item in items), + "reference_only_count": sum("not_main_runtime" in item["current_call_status"] or "template_reference" in item["current_call_status"] for item in items), + }, + "items": items, + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=Path(".")) + parser.add_argument("--output", type=Path) + args = parser.parse_args() + audit = build_audit(args.root) + text = json.dumps(audit, ensure_ascii=False, indent=2, sort_keys=True) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(text, encoding="utf-8") + print(text, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/three_engine_high_rigor_parity.py b/scripts/three_engine_high_rigor_parity.py index e8b637d0..e0d2c58f 100644 --- a/scripts/three_engine_high_rigor_parity.py +++ b/scripts/three_engine_high_rigor_parity.py @@ -13,9 +13,6 @@ from typing import Any ROOT = Path(__file__).resolve().parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) -SCRIPTS = ROOT / "scripts" -if str(SCRIPTS) not in sys.path: - sys.path.insert(0, str(SCRIPTS)) from benchmarks.jyotish.scripts.run_pyjhora_compare import build_pyjhora_sample from benchmarks.jyotish.scripts.run_skill_baseline import run_sample diff --git a/scripts/three_engine_mismatch_arbitrator.py b/scripts/three_engine_mismatch_arbitrator.py index cdace7d1..f187d22b 100644 --- a/scripts/three_engine_mismatch_arbitrator.py +++ b/scripts/three_engine_mismatch_arbitrator.py @@ -82,16 +82,52 @@ def arbitrate_manifest(path: str | Path) -> dict[str, Any]: } +def render_markdown_report(report: dict[str, Any]) -> str: + lines = [ + "# Three-engine mismatch arbitration", + "", + f"manifest: `{report['manifest_path']}`", + f"status: `{report['status']}`", + f"truth_policy: `{report['truth_policy']}`", + "commercial_sync: `status_and_claim_boundary_only`", + f"mismatch_count: `{report['mismatch_count']}`", + f"classified_count: `{report['classified_count']}`", + f"unclassified_count: `{report['unclassified_count']}`", + "", + "Do not copy raw research debt into commercial runtime. Commercial receives readiness, claim boundary, and user-safe status only.", + "", + "## Category counts", + "", + "| category | count |", + "|---|---:|", + ] + for category, count in report["category_counts"].items(): + lines.append(f"| `{category}` | {count} |") + lines.extend(["", "## Closure requirements", ""]) + seen: set[str] = set() + for row in report["rows"]: + category = row["category"] + if category in seen: + continue + seen.add(category) + lines.append(f"- `{category}`: {row['closure_requirement']}") + return "\n".join(lines) + "\n" + + def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("manifest", nargs="?", default="references/oracle/three_engine_parity_replay_manifest.json") parser.add_argument("--output", type=Path) + parser.add_argument("--markdown-output", type=Path) args = parser.parse_args() report = arbitrate_manifest(args.manifest) text = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + "\n" if args.output: args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(text, encoding="utf-8") + if args.markdown_output: + args.markdown_output.parent.mkdir(parents=True, exist_ok=True) + args.markdown_output.write_text(render_markdown_report(report), encoding="utf-8") print(text, end="") return 0 diff --git a/scripts/three_engine_mismatch_closure_queue.py b/scripts/three_engine_mismatch_closure_queue.py new file mode 100644 index 00000000..e4b5f08e --- /dev/null +++ b/scripts/three_engine_mismatch_closure_queue.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Create actionable closure tickets for three-engine mismatch rows.""" +from __future__ import annotations + +import argparse +import json +from collections import Counter +from pathlib import Path +from typing import Any + + +POLICY = { + "endpoint_or_varga_semantics": ("P0", "endpoint_contract", "identified endpoint/method contract with ayanamsa, node mode, varga, timezone semantics"), + "shadbala_formula_variant": ("P0", "formula_source", "public formula source + unit/cap/floor evidence for the component"), + "derived_total_from_component_variants": ("P1", "unit_schema", "component closure before total recomputation; explicit Rupa/Virupa total rule"), + "ashtakavarga_table_or_contributor_variant": ("P1", "worked_example", "public worked BAV/SAV table with contributor set, shodhana state, and Lagna inclusion"), +} + + +def build_queue(arbitration_path: str | Path) -> dict[str, Any]: + path = Path(arbitration_path) + report = json.loads(path.read_text(encoding="utf-8")) + tickets = [] + for index, row in enumerate(report.get("rows") or [], start=1): + priority, owner_track, required = POLICY.get( + row["category"], + ("P2", "worked_example", "manual source review and worked example required"), + ) + tickets.append({ + "ticket_id": f"TEMCQ-{index:03d}", + "priority": priority, + "owner_track": owner_track, + "section": row.get("section"), + "field": row.get("field"), + "category": row.get("category"), + "differing_engines": row.get("differing_engines") or [], + "required_evidence": required, + "closure_status": "open", + "commercial_visibility": "do_not_expose_raw", + }) + return { + "scope": "three_engine_mismatch_closure_queue", + "source_arbitration": str(path), + "status": "open" if tickets else "empty", + "truth_policy": "no_majority_vote", + "production_tuning_allowed": False, + "summary": { + "source_mismatch_count": report.get("mismatch_count", 0), + "queue_count": len(tickets), + "priority_counts": dict(Counter(ticket["priority"] for ticket in tickets)), + "owner_track_counts": dict(Counter(ticket["owner_track"] for ticket in tickets)), + }, + "queue": tickets, + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("arbitration", nargs="?", default="references/oracle/three_engine_mismatch_arbitration_2026_07_19.json") + parser.add_argument("--output", type=Path) + args = parser.parse_args() + queue = build_queue(args.arbitration) + text = json.dumps(queue, ensure_ascii=False, indent=2, sort_keys=True) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(text, encoding="utf-8") + print(text, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_commercial_startup_self_check_contract.py b/tests/test_commercial_startup_self_check_contract.py new file mode 100644 index 00000000..30dbe945 --- /dev/null +++ b/tests/test_commercial_startup_self_check_contract.py @@ -0,0 +1,44 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +CONTRACT = ROOT / "references/oracle/commercial_startup_self_check_contract_2026_07_21.json" +GOLDEN = ROOT / "references/oracle/commercial_golden_oss_case_ci_contract_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_commercial_startup_self_check_contract_requires_truth_source_identity(): + contract = json.loads(CONTRACT.read_text(encoding="utf-8")) + + assert contract["scope"] == "commercial_startup_self_check_contract" + required = set(contract["required_fields"]) + assert { + "truth_source_path", + "truth_source_git_commit", + "skill_version", + "evidence_packet_count", + "artifact_gate_status", + "privacy_artifact_status", + "oracle_ready_summary", + }.issubset(required) + assert contract["reject_if"]["truth_source_path_contains"] == ["/WorkBuddy/", "/.workbuddy/"] + assert contract["claim_boundary"] == "startup_identity_gate_only_not_business_runtime" + + +def test_commercial_golden_oss_ci_contract_uses_pyjhora_sphuta_probe(): + contract = json.loads(GOLDEN.read_text(encoding="utf-8")) + + assert contract["scope"] == "commercial_golden_oss_case_ci_contract" + assert contract["golden_case"]["probe"] == "scripts/prashna_sphuta_oss_case_probe.py" + assert contract["golden_case"]["expected_raw_hash"] == "f0705d205440ea8d7f39116042a0723d971f4ab79c94f7d448fc42d683d52326" + assert contract["gate_policy"]["truth_upgrade_allowed"] is False + assert contract["gate_policy"]["fail_if_probe_missing"] is True + + +def test_commercial_contracts_are_registered_in_evidence_index(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + ids = {row["packet_id"] for row in index["packets"]} + + assert "commercial_startup_self_check_contract_2026_07_21" in ids + assert "commercial_golden_oss_case_ci_contract_2026_07_21" in ids diff --git a/tests/test_jyotishganit_and_vedicastro_probes.py b/tests/test_jyotishganit_and_vedicastro_probes.py new file mode 100644 index 00000000..87406a3e --- /dev/null +++ b/tests/test_jyotishganit_and_vedicastro_probes.py @@ -0,0 +1,56 @@ +import json +import subprocess +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_jyotishganit_field_probe_outputs_raw_hash_and_required_sections(): + out = subprocess.check_output(["python3", "scripts/jyotishganit_field_probe.py"], cwd=ROOT, text=True) + data = json.loads(out) + assert data["scope"] == "jyotishganit_field_probe" + assert data["claim_status"] == "observation_only" + assert data["production_tuning_allowed"] is False + assert data["truth_matrix_allowed"] is False + assert data["raw_hash"] + assert data["selected_hash"] + for key in ["panchanga", "D2", "D4", "D9", "D10", "BAV_SAV", "Shadbala"]: + assert key in data["coverage"] + assert data["coverage"]["D2"] is True + assert data["coverage"]["D4"] is True + assert data["coverage"]["D9"] is True + assert data["coverage"]["D10"] is True + assert data["coverage"]["panchanga"] is True + assert data["coverage"]["BAV_SAV"] is True + assert data["coverage"]["Shadbala"] is False + + +def test_vedicastro_kp_api_probe_is_observation_only_even_when_import_blocks(): + out = subprocess.check_output(["python3", "scripts/vedicastro_kp_api_probe.py"], cwd=ROOT, text=True) + data = json.loads(out) + assert data["scope"] == "vedicastro_kp_api_probe" + assert data["claim_status"] == "observation_only" + assert data["source_sha256"] + assert "get_rl_nl_sl_data" in data["api_surface"]["methods"] + assert data["runtime_probe"]["attempted"] is True + + +def test_public_worked_example_queue_keeps_sources_unverified(): + data = json.loads((ROOT / "references/oracle/public_worked_example_queue_2026_07_19.json").read_text(encoding="utf-8")) + assert data["claim_status"] == "open_queue" + assert data["production_tuning_allowed"] is False + assert data["truth_matrix_allowed"] is False + topics = {row["topic"] for row in data["queues"]} + assert "KP exact cusp star/sub/sub-sub lord" in topics + assert "Muhurta Tarabala/Chandrabala/Rahu Kalam/Abhijit" in topics + assert "Shadbala Virupa and Ashtakavarga component" in topics + + +def test_evidence_index_registers_new_probes(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + packets = {row["packet_id"]: row for row in index["packets"]} + for packet_id in ["jyotishganit_field_probe", "vedicastro_kp_api_probe", "public_worked_example_queue"]: + assert packet_id in packets + assert packets[packet_id]["claim_status"] in {"observation_only", "open_queue"} diff --git a/tests/test_jyotishganit_field_closure_rows.py b/tests/test_jyotishganit_field_closure_rows.py new file mode 100644 index 00000000..2009badf --- /dev/null +++ b/tests/test_jyotishganit_field_closure_rows.py @@ -0,0 +1,37 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/jyotishganit_field_closure_rows_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_jyotishganit_field_closure_rows_are_observation_only(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["scope"] == "jyotishganit_field_closure_rows" + assert data["claim_status"] == "observation_only" + assert data["truth_matrix_allowed"] is False + assert data["source_selected_hash"] == "4709b8ade84efdea4d0a67c15f3e32cea516a5aa2e8abe3885578feda20cb3f4" + assert len(data["rows"]) == 7 + + +def test_jyotishganit_field_rows_mark_shadbala_as_gap(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + rows = {row["field"]: row for row in data["rows"]} + + for field in ["D2", "D4", "D9", "D10", "Panchanga", "BAV", "SAV"]: + assert rows[field]["closure_status"] == "ready_for_field_comparison" + assert data["explicit_gaps"] == [{"field": "Shadbala", "reason": "jyotishganit probe did not expose shadbala/strengths in selected raw"}] + + +def test_jyotishganit_field_rows_are_registered(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "jyotishganit_field_closure_rows_2026_07_21" + ) + + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "observation_only" diff --git a/tests/test_jyotishganit_node_source_attribution.py b/tests/test_jyotishganit_node_source_attribution.py new file mode 100644 index 00000000..66ee87fd --- /dev/null +++ b/tests/test_jyotishganit_node_source_attribution.py @@ -0,0 +1,24 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_node_source_attribution_identifies_boundary_crossing_not_d10_formula(): + data = json.loads((ROOT / "references/oracle/jyotishganit_node_source_attribution_2026_07_19.json").read_text(encoding="utf-8")) + assert data["scope"] == "jyotishganit_node_source_attribution" + assert data["claim_status"] == "partial" + assert data["production_tuning_allowed"] is False + assert data["attribution"]["formula_delta"] == "not_d10_formula_shape" + assert data["attribution"]["primary_delta"] == "node_longitude_source_plus_ayanamsa_boundary_crossing" + assert data["local_engine"]["Rahu"]["d10_part_index_zero_based"] == 3 + assert data["jyotishganit_engine"]["Rahu"]["d10_part_index_zero_based"] == 2 + assert data["local_true_node_control"]["control_result"].startswith("true node moves farther") + + +def test_evidence_index_registers_node_source_attribution(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + packets = {row["packet_id"]: row for row in index["packets"]} + assert packets["jyotishganit_node_source_attribution"]["claim_status"] == "partial" diff --git a/tests/test_jyotishganit_three_engine_closure_bridge.py b/tests/test_jyotishganit_three_engine_closure_bridge.py new file mode 100644 index 00000000..504a0e38 --- /dev/null +++ b/tests/test_jyotishganit_three_engine_closure_bridge.py @@ -0,0 +1,37 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +BRIDGE = ROOT / "references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_jyotishganit_bridge_routes_only_available_fields_to_closure_queue(): + data = json.loads(BRIDGE.read_text(encoding="utf-8")) + + assert data["scope"] == "jyotishganit_three_engine_closure_bridge" + assert data["claim_status"] == "observation_only" + assert data["truth_matrix_allowed"] is False + assert data["source_probe"] == "scripts/jyotishganit_field_probe.py" + assert data["source_raw_hash"] == "19fa9ea862b68c4cffb6756fa6cbf0466daf47a8b008b3fe8809e9fc6a1ed30c" + assert set(data["field_routes"]["ready_for_field_comparison"]) == { + "D2", + "D4", + "D9", + "D10", + "Panchanga", + "BAV_SAV", + } + assert data["field_routes"]["explicit_gaps"] == ["Shadbala"] + + +def test_jyotishganit_bridge_is_registered(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "jyotishganit_three_engine_closure_bridge_2026_07_21" + ) + + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "observation_only" diff --git a/tests/test_kp_public_worked_example_candidate_queue.py b/tests/test_kp_public_worked_example_candidate_queue.py new file mode 100644 index 00000000..be3bd315 --- /dev/null +++ b/tests/test_kp_public_worked_example_candidate_queue.py @@ -0,0 +1,39 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +QUEUE = ROOT / "references/oracle/kp_public_worked_example_candidate_queue_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_kp_public_queue_has_sources_but_no_numeric_oracle_upgrade(): + data = json.loads(QUEUE.read_text(encoding="utf-8")) + + assert data["scope"] == "kp_public_worked_example_candidate_queue" + assert data["claim_status"] == "open_queue" + assert data["numeric_oracle_ready_count"] == 0 + assert data["truth_matrix_allowed"] is False + assert data["boundary"] == "public_source_candidate_queue_only_no_kp_numeric_oracle" + + +def test_kp_public_queue_records_formula_and_runtime_cusp_candidates(): + data = json.loads(QUEUE.read_text(encoding="utf-8")) + rows = {row["id"]: row for row in data["candidates"]} + + assert rows["astrosage_kp_chapter_2"]["candidate_type"] == "formula_and_text_worked_example" + assert rows["astrosage_kp_chapter_2"]["has_numeric_cusp_table"] is False + assert rows["astrosage_kp_sign_star_sub_table"]["candidate_type"] == "reference_table" + assert rows["onlinejyotish_kp_horoscope"]["candidate_type"] == "runtime_form_candidate" + assert rows["astrobix_kp_houses"]["candidate_type"] == "runtime_form_candidate" + + +def test_kp_public_queue_is_registered(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "kp_public_worked_example_candidate_queue_2026_07_21" + ) + + assert entry["domain"] == "kp_precision_timing" + assert entry["claim_status"] == "open_queue" diff --git a/tests/test_source_runtime_closure_queue_2026_07_21.py b/tests/test_source_runtime_closure_queue_2026_07_21.py new file mode 100644 index 00000000..c5c36554 --- /dev/null +++ b/tests/test_source_runtime_closure_queue_2026_07_21.py @@ -0,0 +1,60 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +QUEUE = ROOT / "references/oracle/source_runtime_closure_queue_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" +KP_RAW = ROOT / "references/oracle/vedicastro_kp_house_cusp_raw_2026_07_21.json" +KP_ENV = ROOT / "references/oracle/vedicastro_kp_tmp_env_identity_2026_07_21.json" + + +def test_queue_prioritizes_jyotishganit_and_vedicastro_without_truth_upgrade(): + data = json.loads(QUEUE.read_text(encoding="utf-8")) + rows = {row["id"]: row for row in data["rows"]} + + assert data["scope"] == "source_runtime_closure_queue" + assert data["claim_status"] == "open_queue" + assert data["truth_matrix_allowed"] is False + assert rows["jyotishganit_field_closure"]["current_status"] == "probe_runs_observation_only" + assert rows["vedicastro_kp_house_cusp_closure"]["current_status"] == "raw_ready_observation_only" + assert rows["vedicastro_kp_house_cusp_closure"]["latest_observed_raw_hash"] == "2e7a6b17eb2965a60846f625f6bd8bc03555216d6225983647bcbfa23d0e345b" + assert rows["vedicastro_kp_house_cusp_closure"]["coverage"]["sub_sub_lord"] is True + + +def test_queue_marks_existing_assets_as_not_fully_invoked(): + data = json.loads(QUEUE.read_text(encoding="utf-8")) + not_closed = data["not_fully_closed_reference_layers"] + + assert "references/open_source_sources/jyotishganit" in not_closed + assert "references/open_source_sources/VedicAstro" in not_closed + assert "references/open_source_sources/rishi-ai-mcp" in not_closed + assert "references/open_source_sources/vedic-astro-skills" in not_closed + assert data["boundary"] == "queue_only_no_adapter_or_truth_upgrade" + + +def test_queue_is_registered_in_evidence_index(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "source_runtime_closure_queue_2026_07_21" + ) + + assert entry["domain"] == "source_runtime_closure" + assert entry["claim_status"] == "open_queue" + + +def test_vedicastro_kp_raw_and_tmp_env_are_observation_only_packets(): + raw = json.loads(KP_RAW.read_text(encoding="utf-8")) + env = json.loads(KP_ENV.read_text(encoding="utf-8")) + + assert raw["scope"] == "vedicastro_kp_house_cusp_probe" + assert raw["status"] == "complete" + assert raw["claim_status"] == "observation_only" + assert raw["truth_matrix_allowed"] is False + assert raw["raw_hash"] == "2e7a6b17eb2965a60846f625f6bd8bc03555216d6225983647bcbfa23d0e345b" + assert raw["schema_fingerprint"]["house_count"] == 12 + assert "SubLord" in raw["schema_fingerprint"]["fields"] + assert "SubSubLord" in raw["schema_fingerprint"]["fields"] + assert env["claim_status"] == "runtime_dependency_ready" + assert env["project_dependency_mutation_allowed"] is False diff --git a/tests/test_technique_promotion_audit_kp_gochara_muhurta.py b/tests/test_technique_promotion_audit_kp_gochara_muhurta.py new file mode 100644 index 00000000..e0993091 --- /dev/null +++ b/tests/test_technique_promotion_audit_kp_gochara_muhurta.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import json +from pathlib import Path + +from scripts.technique_promotion_audit_kp_gochara_muhurta import build_audit + +ROOT = Path(__file__).resolve().parents[1] + + +def test_kp_gochara_muhurta_audit_splits_runtime_presence() -> None: + audit = build_audit(ROOT) + statuses = {item["technique_id"]: item["current_call_status"] for item in audit["items"]} + assert audit["scope"] == "technique_promotion_audit_kp_gochara_muhurta" + assert audit["truth_policy"] == "runtime_presence_not_oracle_closure" + assert statuses["panchanga_calendar"] == "partial" + assert statuses["muhurta_dashaflow_candidate"] == "oss_reference_not_main_runtime" + assert statuses["kp_astrology"] == "reference_only_not_main_runtime" + assert statuses["gochara_event_timing_template"] == "template_reference_not_main_runtime" + + +def test_kp_gochara_muhurta_audit_records_reuse_boundaries() -> None: + audit = build_audit(ROOT) + for item in audit["items"]: + assert item["reuse_decision"] in {"do_not_duplicate_runtime", "license_audit_before_reuse", "reference_only"} + assert item["next_action"] + assert item["claim_boundary"] + assert item["source_or_license_boundary"] + + +def test_kp_gochara_muhurta_audit_artifact_exists() -> None: + artifact = ROOT / "references/oracle/technique_promotion_audit_kp_gochara_muhurta_2026_07_19.json" + data = json.loads(artifact.read_text(encoding="utf-8")) + assert data["summary"] == { + "items_checked": 4, + "formally_called_count": 0, + "reference_only_count": 3, + } diff --git a/tests/test_three_engine_high_rigor_parity.py b/tests/test_three_engine_high_rigor_parity.py index ba5c5a11..e8ca896a 100644 --- a/tests/test_three_engine_high_rigor_parity.py +++ b/tests/test_three_engine_high_rigor_parity.py @@ -1,51 +1,6 @@ -import os -from pathlib import Path -import subprocess -import sys - -from benchmarks.jyotish.scripts.run_skill_baseline import run_sample -from benchmarks.jyotish.scripts.run_pyjhora_compare import build_pyjhora_sample, compare_one from scripts import three_engine_high_rigor_parity as parity -ROOT = Path(__file__).resolve().parents[1] - - -def test_runner_imports_from_commercial_root() -> None: - env = os.environ.copy() - env["PYTHONPATH"] = str(ROOT) - result = subprocess.run( - [sys.executable, "-c", "import scripts.three_engine_high_rigor_parity"], - cwd=ROOT, - env=env, - capture_output=True, - text=True, - check=False, - ) - assert result.returncode == 0, result.stderr - - -def test_public_sample_exposes_shadbala_components() -> None: - result = run_sample(parity.SAMPLE) - assert result["ok"] is True - components = result["canonical"]["shadbala_components"] - assert set(components["Sun"]) == set(parity.VED_COMPONENT_FIELDS) - - -def test_pyjhora_sample_exposes_shadbala_components() -> None: - sample = build_pyjhora_sample(parity.SAMPLE) - assert set(sample["shadbala_components"]["Sun"]) == set(parity.VED_COMPONENT_FIELDS) - - -def test_pyjhora_matrix_includes_shadbala_components() -> None: - baseline = { - "ascendant": {}, "planets": {}, "varga": {}, "ashtakavarga": {}, "shadbala": {}, "dasha": {}, - "shadbala_components": {"Sun": {name: 1.0 for name in parity.VED_COMPONENT_FIELDS}}, - } - rows = compare_one("sample", baseline, baseline) - assert {row["field"] for row in rows if row["section"] == "Shadbala_Component" and row["body"] == "Sun"} == set(parity.VED_COMPONENT_FIELDS) - - def test_jyotishganit_planet_map_reads_divisional_occupants() -> None: chart = {"houses": [{"occupants": [{"celestialBody": "Sun", "sign": "Leo"}]}]} assert parity._jyotish_planet_signs(chart) == {"Sun": "Leo"} diff --git a/tests/test_three_engine_jyotishganit_bridge_applied.py b/tests/test_three_engine_jyotishganit_bridge_applied.py new file mode 100644 index 00000000..de508d02 --- /dev/null +++ b/tests/test_three_engine_jyotishganit_bridge_applied.py @@ -0,0 +1,46 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/three_engine_jyotishganit_bridge_applied_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_applied_bridge_maps_jyotishganit_fields_to_existing_temcq_tickets(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["scope"] == "three_engine_jyotishganit_bridge_applied" + assert data["claim_status"] == "observation_only" + assert data["truth_matrix_allowed"] is False + assert data["source_bridge"] == "references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json" + assert data["summary"]["existing_ticket_rows"] == 18 + assert data["summary"]["no_existing_ticket_rows"] == 1 + assert data["summary"]["truth_upgrades"] == 0 + + +def test_applied_bridge_keeps_all_rows_open_or_comparison_ready(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + rows = data["rows"] + ticket_rows = [row for row in rows if row["existing_ticket_id"]] + panchanga = next(row for row in rows if row["field"] == "Panchanga") + + assert len(ticket_rows) == 18 + assert {row["closure_status"] for row in rows} == { + "ready_for_field_comparison", + "no_existing_ticket_create_next", + } + assert panchanga["existing_ticket_id"] is None + assert panchanga["closure_status"] == "no_existing_ticket_create_next" + assert all(row["claim_boundary"] == "field_comparison_only_no_formula_truth" for row in rows) + + +def test_applied_bridge_is_registered_in_evidence_index(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "three_engine_jyotishganit_bridge_applied_2026_07_21" + ) + + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "observation_only" diff --git a/tests/test_three_engine_mismatch_arbitrator.py b/tests/test_three_engine_mismatch_arbitrator.py index be3ac9d0..f6ca637e 100644 --- a/tests/test_three_engine_mismatch_arbitrator.py +++ b/tests/test_three_engine_mismatch_arbitrator.py @@ -1,7 +1,7 @@ import json from pathlib import Path -from scripts.three_engine_mismatch_arbitrator import arbitrate_manifest +from scripts.three_engine_mismatch_arbitrator import arbitrate_manifest, render_markdown_report ROOT = Path(__file__).resolve().parents[1] @@ -35,13 +35,16 @@ def test_vedastro_only_d2_difference_is_endpoint_semantics(tmp_path: Path) -> No assert row["differing_engines"] == ["VedAstro"] -def test_commercial_receives_mismatch_status_not_raw_truth_upgrade() -> None: - report = json.loads((ROOT / "references/oracle/three_engine_mismatch_arbitration_2026_07_19.json").read_text(encoding="utf-8")) - markdown = (ROOT / "docs/research/three_engine_mismatch_arbitration_2026_07_19.md").read_text(encoding="utf-8") +def test_markdown_report_summarizes_policy_and_category_counts() -> None: + report = arbitrate_manifest(ROOT / "references" / "oracle" / "three_engine_parity_replay_manifest.json") + markdown = render_markdown_report(report) - assert report["mismatch_count"] == 60 - assert report["classified_count"] == 60 - assert report["unclassified_count"] == 0 - assert report["truth_policy"] == "no_majority_vote" + assert "Three-engine mismatch arbitration" in markdown + assert "mismatch_count: `60`" in markdown + assert "truth_policy: `no_majority_vote`" in markdown assert "commercial_sync: `status_and_claim_boundary_only`" in markdown + assert "endpoint_or_varga_semantics" in markdown + assert "shadbala_formula_variant" in markdown + assert "ashtakavarga_table_or_contributor_variant" in markdown + assert "derived_total_from_component_variants" in markdown assert "Do not copy raw research debt into commercial runtime" in markdown diff --git a/tests/test_three_engine_parity_artifact_contract.py b/tests/test_three_engine_parity_artifact_contract.py index c55e5013..3eb7cb65 100644 --- a/tests/test_three_engine_parity_artifact_contract.py +++ b/tests/test_three_engine_parity_artifact_contract.py @@ -19,3 +19,69 @@ def test_verified_oracle_requires_raw_artifact_hash_and_settings(tmp_path): assert result["status"] == "invalid" assert any(error["error"] == "required_for_verified_status" for error in result["errors"]) + + +def test_high_rigor_parity_requires_non_d1_and_shadbala_component_rows(tmp_path): + manifest = { + "engines": { + "VedAstro": {"status": "blocked"}, + "PyJHora_JHora": {"status": "blocked"}, + "jyotishganit": {"status": "blocked"}, + }, + "comparison_rows": [ + { + "section": "D1", + "field": "Sun.sign", + "local_value": "Aquarius", + "oracle_values": {}, + "status": "match", + } + ], + } + path = tmp_path / "manifest.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + + result = validate_manifest(path) + + assert result["status"] == "partial" + assert result["blocked_reason"] == "missing_high_rigor_sections" + assert "D2" in result["missing_high_rigor_sections"] + assert "shadbala_components" in result["missing_high_rigor_sections"] + + +def test_high_rigor_parity_passes_only_when_required_sections_are_present(tmp_path): + rows = [ + { + "section": section, + "field": "sample", + "local_value": 1, + "oracle_values": {"PyJHora_JHora": 1}, + "status": "match", + } + for section in [ + "D1", + "D2", + "D4", + "D9", + "D10", + "ashtakavarga_bav", + "ashtakavarga_sav", + "shadbala_total", + "shadbala_components", + ] + ] + manifest = { + "engines": { + "VedAstro": {"status": "blocked"}, + "PyJHora_JHora": {"status": "blocked"}, + "jyotishganit": {"status": "blocked"}, + }, + "comparison_rows": rows, + } + path = tmp_path / "manifest.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + + result = validate_manifest(path) + + assert result["status"] == "pass" + assert result["missing_high_rigor_sections"] == [] diff --git a/tests/test_truth_source_runtime_identity.py b/tests/test_truth_source_runtime_identity.py new file mode 100644 index 00000000..8df0b28d --- /dev/null +++ b/tests/test_truth_source_runtime_identity.py @@ -0,0 +1,45 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/truth_source_runtime_identity_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_truth_source_runtime_identity_packet_is_governance_only(): + packet = json.loads(PACKET.read_text(encoding="utf-8")) + + assert packet["scope"] == "truth_source_runtime_identity" + assert packet["truth_source"]["path"] == "/Users/wuyongnaren/Documents/印度占星" + assert packet["truth_source"]["role"] == "sole_main_research_truth_source" + assert len(packet["truth_source"]["git_commit"]) == 40 + assert packet["oracle_summary"]["truth_matrix_allowed"] is False + assert packet["oracle_summary"]["production_tuning_allowed"] is False + assert packet["claim_boundary"] == "identity_ready_governance_only_not_oracle_truth" + + +def test_truth_source_runtime_identity_is_registered_in_evidence_index(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "truth_source_runtime_identity" + ) + + assert entry["path"] == "references/oracle/truth_source_runtime_identity_2026_07_21.json" + assert entry["domain"] == "truth_source_governance" + assert entry["claim_status"] == "ready_contract" + + +def test_workbuddy_old_copy_is_quarantined_as_fragment_only(): + packet = json.loads(PACKET.read_text(encoding="utf-8")) + quarantine = packet["fragment_quarantine"]["workbuddy_old_copies"] + + assert quarantine["status"] == "quarantined" + assert set(quarantine["labels"]) == { + "not_for_truth_source", + "privacy_review_required", + "artifact_incomplete", + "historical_fragment_only", + } + assert all("/WorkBuddy/" in path or "/.workbuddy/" in path for path in quarantine["paths"]) diff --git a/tests/test_vedicastro_kp_house_cusp_probe.py b/tests/test_vedicastro_kp_house_cusp_probe.py new file mode 100644 index 00000000..2ae01fd5 --- /dev/null +++ b/tests/test_vedicastro_kp_house_cusp_probe.py @@ -0,0 +1,49 @@ +import json +import os +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +RUNTIME = Path("/tmp/vedicastro_sidereal_flatlib_probe.Nt8ANZ") +ARTIFACT = ROOT / "references/oracle/vedicastro_kp_house_cusp_probe_steve_jobs_2026_07_19.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_vedicastro_kp_house_cusp_artifact_has_12_cusps_and_hash(): + data = json.loads(ARTIFACT.read_text(encoding="utf-8")) + assert data["scope"] == "vedicastro_kp_house_cusp_probe" + assert data["claim_status"] == "observation_only" + assert data["production_tuning_allowed"] is False + assert data["truth_matrix_allowed"] is False + assert data["raw_hash"] + assert data["schema_fingerprint"]["house_count"] == 12 + fields = set(data["schema_fingerprint"]["fields"]) + assert {"LonDecDeg", "NakshatraLord", "SubLord", "SubSubLord"} <= fields + assert data["raw"]["houses"][0]["Object"] == "I" + + +def test_vedicastro_kp_house_cusp_probe_runtime_when_temp_env_exists(): + if not RUNTIME.exists(): + return + env = { + **os.environ, + "PYTHONPATH": f"{RUNTIME}:{ROOT / 'references/open_source_sources/VedicAstro'}", + } + out = subprocess.check_output( + [sys.executable, "scripts/vedicastro_kp_house_cusp_probe.py"], + cwd=ROOT, + text=True, + env=env, + ) + data = json.loads(out) + assert data["schema_fingerprint"]["house_count"] == 12 + assert data["raw"]["houses"][0]["SubLord"] + assert data["dependency_identity"]["observed_pinned_flatlib_commit"] == "2618c348ce1ab2588548f935ff65f031630b4872" + + +def test_evidence_index_registers_house_cusp_probe(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + packets = {row["packet_id"]: row for row in index["packets"]} + assert packets["vedicastro_kp_house_cusp_probe"]["claim_status"] == "observation_only" diff --git a/tests/test_vedicastro_kp_runtime_tmp_probe.py b/tests/test_vedicastro_kp_runtime_tmp_probe.py index 92010012..edcae641 100644 --- a/tests/test_vedicastro_kp_runtime_tmp_probe.py +++ b/tests/test_vedicastro_kp_runtime_tmp_probe.py @@ -26,5 +26,6 @@ def test_vedicastro_probe_reports_dependency_chain_without_truth_upgrade(): assert data["runtime_probe"]["method_present"] is True assert "SubLord" in data["runtime_probe"]["sample_rl_nl_sl"] assert "AY_KRISHNAMURTI" in data["runtime_probe"]["sidereal_ayanamsa_constants_present"] + assert data["dependency_identity"]["observed_pinned_flatlib_commit"] == "2618c348ce1ab2588548f935ff65f031630b4872" else: assert data["status"] == "blocked_runtime_import" diff --git a/tests/test_vedicastro_kp_tmp_env_preparer.py b/tests/test_vedicastro_kp_tmp_env_preparer.py new file mode 100644 index 00000000..14fe1812 --- /dev/null +++ b/tests/test_vedicastro_kp_tmp_env_preparer.py @@ -0,0 +1,36 @@ +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +SCRIPT = ROOT / "scripts/prepare_vedicastro_kp_tmp_env.py" + + +def test_preparer_supports_report_only_mode_without_project_install(): + out = subprocess.check_output( + [sys.executable, str(SCRIPT), "--report-only"], + cwd=ROOT, + text=True, + ) + data = json.loads(out) + + assert data["scope"] == "vedicastro_kp_tmp_env_preparer" + assert data["target"] == "/tmp/vedicastro_flatlib_probe" + assert data["project_dependency_mutation_allowed"] is False + assert data["required_packages"]["flatlib"].startswith("git+https://github.com/diliprk/flatlib.git@sidereal") + assert data["claim_status"] in {"blocked_runtime_dependency", "runtime_dependency_ready"} + + +def test_preparer_result_never_claims_oracle_truth(): + out = subprocess.check_output( + [sys.executable, str(SCRIPT), "--report-only"], + cwd=ROOT, + text=True, + ) + data = json.loads(out) + + assert data["truth_matrix_allowed"] is False + assert data["production_tuning_allowed"] is False + assert data["boundary"] == "dependency_preparation_only_no_kp_oracle_truth" From 3d4fc40dae7c97b8d395e2c03c9c072edf5d9130 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 12:55:10 +0800 Subject: [PATCH 04/25] fix: recover interrupted chat and candidate birth time adoption --- frontend/src/app/page.tsx | 6 +++--- tests/test_supabase_user_data_contract.py | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index fb1dc796..f1023f29 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -2172,16 +2172,16 @@ export default function Home() { await persistSession(interruptedSession); setRequestError({ sessionId, - message: "回答中途断开,已保留生成内容;本次已开始生成并计费。", + message: "回答中途断开,已保留生成内容;请复制现有内容或继续追问,系统正在以账户记录为准同步点数。", }); } catch (persistError) { setRequestError({ sessionId, - message: `${persistError instanceof Error ? persistError.message : "云端同步失败"} 已计费的部分回答仍保留在当前页面,请复制保存。`, + message: `${persistError instanceof Error ? persistError.message : "云端同步失败"} 部分回答仍保留在当前页面,请复制保存后继续追问。`, }); } if (activeSessionIdRef.current === sessionId) { - setComposerNotice("回答中途断开,已保留现有内容,本次已计费。"); + setComposerNotice("回答中途断开,已保留现有内容;请继续追问或复制保存。"); } } } finally { diff --git a/tests/test_supabase_user_data_contract.py b/tests/test_supabase_user_data_contract.py index 43e7000b..3e634750 100644 --- a/tests/test_supabase_user_data_contract.py +++ b/tests/test_supabase_user_data_contract.py @@ -112,6 +112,9 @@ def test_chat_page_uses_authenticated_cloud_persistence() -> None: assert 'await persistence' in source assert "pendingSessionId || cancellationInFlight.current || pendingConsultation.current" in source assert "setCancellationPending(true)" in source + assert "系统正在以账户记录为准同步点数" in source + assert "回答中途断开,已保留现有内容,本次已计费。" not in source + assert "本次已开始生成并计费" not in source assert 'localStorage.setItem(chartLibraryStorageKey(accountId)' in source assert 'localStorage.setItem("chat_sessions"' not in source From 62e0f98c9a9de711fbaef70d4ffc5c4393fa5aaa Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 13:27:08 +0800 Subject: [PATCH 05/25] sync: import latest three-engine closure packets --- ...bav_sav_field_closure_packet_2026_07_21.md | 20 + ...gine_d2_field_closure_packet_2026_07_21.md | 26 + ..._d9_d10_field_closure_packet_2026_07_21.md | 18 + ...ee_engine_field_status_batch_2026_07_21.md | 18 + .../evidence_packet_index_2026_07_19.json | 26 +- ...v_sav_field_closure_packet_2026_07_21.json | 615 ++++ ...ne_d2_field_closure_packet_2026_07_21.json | 132 + ...9_d10_field_closure_packet_2026_07_21.json | 68 + ..._engine_field_status_batch_2026_07_21.json | 207 ++ ...hine_jyotish_fragment_scan_2026_07_21.json | 2707 +++++++++++++++++ ...ree_engine_bav_sav_field_closure_packet.py | 49 + ...st_three_engine_d2_field_closure_packet.py | 55 + ...e_engine_d4_d9_d10_field_closure_packet.py | 51 + ...ee_engine_field_status_batch_2026_07_21.py | 44 + ...est_whole_machine_jyotish_fragment_scan.py | 55 + 15 files changed, 4090 insertions(+), 1 deletion(-) create mode 100644 docs/research/three_engine_bav_sav_field_closure_packet_2026_07_21.md create mode 100644 docs/research/three_engine_d2_field_closure_packet_2026_07_21.md create mode 100644 docs/research/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.md create mode 100644 docs/research/three_engine_field_status_batch_2026_07_21.md create mode 100644 references/oracle/three_engine_bav_sav_field_closure_packet_2026_07_21.json create mode 100644 references/oracle/three_engine_d2_field_closure_packet_2026_07_21.json create mode 100644 references/oracle/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.json create mode 100644 references/oracle/three_engine_field_status_batch_2026_07_21.json create mode 100644 references/oracle/whole_machine_jyotish_fragment_scan_2026_07_21.json create mode 100644 tests/test_three_engine_bav_sav_field_closure_packet.py create mode 100644 tests/test_three_engine_d2_field_closure_packet.py create mode 100644 tests/test_three_engine_d4_d9_d10_field_closure_packet.py create mode 100644 tests/test_three_engine_field_status_batch_2026_07_21.py create mode 100644 tests/test_whole_machine_jyotish_fragment_scan.py diff --git a/docs/research/three_engine_bav_sav_field_closure_packet_2026_07_21.md b/docs/research/three_engine_bav_sav_field_closure_packet_2026_07_21.md new file mode 100644 index 00000000..a3e0d9fc --- /dev/null +++ b/docs/research/three_engine_bav_sav_field_closure_packet_2026_07_21.md @@ -0,0 +1,20 @@ +# Three-engine BAV/SAV field closure packet — 2026-07-21 + +## Result + +- Rows: `8` +- Local / PyJHora-JHora / jyotishganit agreement: `5/8` +- Multi-engine variants: `3/8` +- Truth upgrades: `0` + +## Required evidence before upgrade + +- Public worked BAV/SAV table +- Contributor set +- Lagna inclusion policy +- Shodhana state +- Rashi order / orientation + +## Boundary + +This packet reuses existing arbitration/probe output. It does not implement or copy a new Ashtakavarga algorithm and does not upgrade formula truth. diff --git a/docs/research/three_engine_d2_field_closure_packet_2026_07_21.md b/docs/research/three_engine_d2_field_closure_packet_2026_07_21.md new file mode 100644 index 00000000..343062cb --- /dev/null +++ b/docs/research/three_engine_d2_field_closure_packet_2026_07_21.md @@ -0,0 +1,26 @@ +# Three-engine D2 field closure packet — 2026-07-21 + +## Result + +- Rows: `7` +- Local / PyJHora-JHora / jyotishganit agreement: `7/7` +- VedAstro differing endpoint result: `7/7` +- Truth upgrades: `0` + +## Interpretation + +For the Steve Jobs public same-case D2 rows, local, PyJHora/JHora and +jyotishganit agree on seven planet signs. VedAstro differs on all seven rows. + +This is therefore classified as: + +`partial_consensus_vedastro_endpoint_blocked` + +The remaining blocker is not local formula evidence. It is VedAstro hosted +endpoint/method semantics: same ayanamsa, node mode, varga and timezone contract +must be pinned before these rows can become global truth. + +## Boundary + +This packet supports local/PyJHora/jyotishganit D2 confidence only. It does not +upgrade global oracle truth or commercial production tuning. diff --git a/docs/research/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.md b/docs/research/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.md new file mode 100644 index 00000000..fcb71536 --- /dev/null +++ b/docs/research/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.md @@ -0,0 +1,18 @@ +# Three-engine D4/D9/D10 field closure packet — 2026-07-21 + +## Result + +- Rows: `3` +- Local / PyJHora-JHora / jyotishganit agreement: `3/3` +- VedAstro differing endpoint result: `3/3` +- Truth upgrades: `0` + +## Classification + +D4, D9 and D10 each have one Moon.sign mismatch row. In all three rows, local, PyJHora/JHora and jyotishganit agree; VedAstro differs. + +Status: `partial_consensus_vedastro_endpoint_blocked` + +## Boundary + +This packet supports partial same-case confidence for these three varga rows only. It does not upgrade global oracle truth or commercial production tuning. diff --git a/docs/research/three_engine_field_status_batch_2026_07_21.md b/docs/research/three_engine_field_status_batch_2026_07_21.md new file mode 100644 index 00000000..8b27cf8e --- /dev/null +++ b/docs/research/three_engine_field_status_batch_2026_07_21.md @@ -0,0 +1,18 @@ +# Three-engine field status batch — 2026-07-21 + +## Result + +- Existing TEMCQ rows classified: `18` +- New Panchanga ticket placeholder: `TEMCQ-061` +- Truth upgrades: `0` + +## Classification + +- `10` D2/D4/D9/D10 rows are ready for endpoint/method semantics checks. +- `8` BAV/SAV rows are ready for public worked-example comparison. +- Panchanga has jyotishganit raw but no existing mismatch ticket, so it is queued separately. + +## Boundary + +This is a closure-status packet only. It does not mark any field as numeric +truth, formula parity, or production timing evidence. diff --git a/references/oracle/evidence_packet_index_2026_07_19.json b/references/oracle/evidence_packet_index_2026_07_19.json index 996c1dd2..e46ebf8f 100644 --- a/references/oracle/evidence_packet_index_2026_07_19.json +++ b/references/oracle/evidence_packet_index_2026_07_19.json @@ -5,7 +5,7 @@ "production_tuning_allowed": false, "boundary": "Index of current governance packets only. Raw oracle artifacts remain in references/oracle/artifacts and are not all duplicated here.", "summary": { - "packet_count": 115, + "packet_count": 118, "blocked_or_partial_count": 51, "human_review_required_count": 3 }, @@ -929,6 +929,30 @@ "claim_status": "observation_only", "consumer_policy": "research_observation_only", "claim_boundary": "Classifies 18 jyotishganit-applied TEMCQ rows and creates a Panchanga ticket placeholder; status only, no numeric truth upgrade." + }, + { + "packet_id": "three_engine_d2_field_closure_packet_2026_07_21", + "path": "references/oracle/three_engine_d2_field_closure_packet_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "partial", + "consumer_policy": "research_observation_only", + "claim_boundary": "D2 rows show local/PyJHora/jyotishganit agreement while VedAstro endpoint semantics remain blocked; no global truth upgrade." + }, + { + "packet_id": "three_engine_d4_d9_d10_field_closure_packet_2026_07_21", + "path": "references/oracle/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "partial", + "consumer_policy": "research_observation_only", + "claim_boundary": "D4/D9/D10 Moon.sign rows show local/PyJHora/jyotishganit agreement while VedAstro endpoint semantics remain blocked; no global truth upgrade." + }, + { + "packet_id": "three_engine_bav_sav_field_closure_packet_2026_07_21", + "path": "references/oracle/three_engine_bav_sav_field_closure_packet_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "partial", + "consumer_policy": "research_observation_only", + "claim_boundary": "Classifies BAV/SAV rows into partial consensus or multi-engine variant; public worked table and method metadata still required." } ] } diff --git a/references/oracle/three_engine_bav_sav_field_closure_packet_2026_07_21.json b/references/oracle/three_engine_bav_sav_field_closure_packet_2026_07_21.json new file mode 100644 index 00000000..805d09c7 --- /dev/null +++ b/references/oracle/three_engine_bav_sav_field_closure_packet_2026_07_21.json @@ -0,0 +1,615 @@ +{ + "scope": "three_engine_bav_sav_field_closure_packet", + "created_at": "2026-07-21", + "claim_status": "partial", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_arbitration": "references/oracle/three_engine_mismatch_arbitration_2026_07_19.json", + "source_queue": "references/oracle/three_engine_mismatch_closure_queue_2026_07_19.json", + "source_jyotishganit_bridge": "references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json", + "summary": { + "rows_total": 8, + "local_pyjhora_jyotishganit_agree": 5, + "multi_engine_variant": 3, + "truth_upgrades": 0 + }, + "rows": [ + { + "ticket_id": "TEMCQ-011", + "section": "ashtakavarga_bav", + "field": "Sun", + "local_value": [ + 3, + 7, + 4, + 3, + 3, + 2, + 6, + 7, + 4, + 4, + 3, + 2 + ], + "pyjhora_jhora_value": [ + 3, + 7, + 4, + 3, + 3, + 2, + 6, + 7, + 4, + 4, + 3, + 2 + ], + "jyotishganit_value": [ + 3, + 7, + 4, + 3, + 3, + 2, + 6, + 7, + 4, + 4, + 3, + 2 + ], + "vedastro_value": [ + 3, + 7, + 3, + 2, + 3, + 3, + 6, + 7, + 4, + 3, + 4, + 3 + ], + "local_pyjhora_jyotishganit_agree": true, + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_variant_blocked", + "required_evidence": [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ], + "claim_boundary": "ashtakavarga_table_comparison_only_no_formula_truth" + }, + { + "ticket_id": "TEMCQ-012", + "section": "ashtakavarga_bav", + "field": "Moon", + "local_value": [ + 4, + 4, + 4, + 3, + 6, + 5, + 3, + 3, + 4, + 5, + 3, + 5 + ], + "pyjhora_jhora_value": [ + 4, + 4, + 4, + 3, + 6, + 5, + 3, + 3, + 4, + 5, + 3, + 5 + ], + "jyotishganit_value": [ + 4, + 5, + 4, + 2, + 6, + 5, + 3, + 2, + 5, + 5, + 3, + 5 + ], + "vedastro_value": [ + 4, + 5, + 3, + 2, + 6, + 6, + 3, + 2, + 5, + 4, + 4, + 5 + ], + "local_pyjhora_jyotishganit_agree": false, + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "closure_status": "multi_engine_variant_worked_example_required", + "required_evidence": [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ], + "claim_boundary": "ashtakavarga_table_comparison_only_no_formula_truth" + }, + { + "ticket_id": "TEMCQ-013", + "section": "ashtakavarga_bav", + "field": "Mars", + "local_value": [ + 4, + 7, + 4, + 4, + 3, + 0, + 4, + 5, + 1, + 4, + 1, + 2 + ], + "pyjhora_jhora_value": [ + 4, + 7, + 4, + 4, + 3, + 0, + 4, + 5, + 1, + 4, + 1, + 2 + ], + "jyotishganit_value": [ + 4, + 7, + 4, + 4, + 3, + 0, + 4, + 5, + 1, + 4, + 1, + 2 + ], + "vedastro_value": [ + 4, + 7, + 3, + 4, + 2, + 1, + 4, + 5, + 2, + 3, + 2, + 2 + ], + "local_pyjhora_jyotishganit_agree": true, + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_variant_blocked", + "required_evidence": [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ], + "claim_boundary": "ashtakavarga_table_comparison_only_no_formula_truth" + }, + { + "ticket_id": "TEMCQ-014", + "section": "ashtakavarga_bav", + "field": "Mercury", + "local_value": [ + 5, + 5, + 5, + 4, + 4, + 2, + 6, + 5, + 5, + 8, + 2, + 3 + ], + "pyjhora_jhora_value": [ + 5, + 5, + 5, + 4, + 4, + 2, + 6, + 5, + 5, + 8, + 2, + 3 + ], + "jyotishganit_value": [ + 5, + 5, + 5, + 4, + 4, + 2, + 6, + 5, + 5, + 8, + 2, + 3 + ], + "vedastro_value": [ + 5, + 5, + 4, + 5, + 3, + 2, + 7, + 4, + 6, + 8, + 2, + 3 + ], + "local_pyjhora_jyotishganit_agree": true, + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_variant_blocked", + "required_evidence": [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ], + "claim_boundary": "ashtakavarga_table_comparison_only_no_formula_truth" + }, + { + "ticket_id": "TEMCQ-015", + "section": "ashtakavarga_bav", + "field": "Jupiter", + "local_value": [ + 7, + 5, + 3, + 3, + 4, + 7, + 4, + 5, + 4, + 6, + 5, + 3 + ], + "pyjhora_jhora_value": [ + 7, + 5, + 3, + 3, + 4, + 7, + 4, + 5, + 4, + 6, + 5, + 3 + ], + "jyotishganit_value": [ + 7, + 5, + 3, + 3, + 4, + 7, + 4, + 5, + 4, + 6, + 5, + 3 + ], + "vedastro_value": [ + 7, + 5, + 3, + 3, + 4, + 7, + 5, + 4, + 4, + 6, + 4, + 4 + ], + "local_pyjhora_jyotishganit_agree": true, + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_variant_blocked", + "required_evidence": [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ], + "claim_boundary": "ashtakavarga_table_comparison_only_no_formula_truth" + }, + { + "ticket_id": "TEMCQ-016", + "section": "ashtakavarga_bav", + "field": "Venus", + "local_value": [ + 4, + 3, + 5, + 4, + 3, + 5, + 4, + 3, + 5, + 5, + 5, + 6 + ], + "pyjhora_jhora_value": [ + 4, + 3, + 5, + 4, + 3, + 5, + 4, + 3, + 5, + 5, + 5, + 6 + ], + "jyotishganit_value": [ + 4, + 3, + 5, + 3, + 4, + 5, + 4, + 3, + 5, + 5, + 5, + 6 + ], + "vedastro_value": [ + 4, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 5, + 6, + 6, + 6 + ], + "local_pyjhora_jyotishganit_agree": false, + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "closure_status": "multi_engine_variant_worked_example_required", + "required_evidence": [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ], + "claim_boundary": "ashtakavarga_table_comparison_only_no_formula_truth" + }, + { + "ticket_id": "TEMCQ-017", + "section": "ashtakavarga_bav", + "field": "Saturn", + "local_value": [ + 1, + 5, + 3, + 0, + 6, + 3, + 4, + 5, + 3, + 3, + 3, + 3 + ], + "pyjhora_jhora_value": [ + 1, + 5, + 3, + 0, + 6, + 3, + 4, + 5, + 3, + 3, + 3, + 3 + ], + "jyotishganit_value": [ + 1, + 5, + 3, + 0, + 6, + 3, + 4, + 5, + 3, + 3, + 3, + 3 + ], + "vedastro_value": [ + 1, + 5, + 2, + 0, + 5, + 4, + 4, + 4, + 4, + 2, + 4, + 4 + ], + "local_pyjhora_jyotishganit_agree": true, + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_variant_blocked", + "required_evidence": [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ], + "claim_boundary": "ashtakavarga_table_comparison_only_no_formula_truth" + }, + { + "ticket_id": "TEMCQ-018", + "section": "ashtakavarga_sav", + "field": "12_sign_scores", + "local_value": [ + 28, + 36, + 28, + 21, + 29, + 24, + 31, + 33, + 26, + 35, + 22, + 24 + ], + "pyjhora_jhora_value": [ + 28, + 36, + 28, + 21, + 29, + 24, + 31, + 33, + 26, + 35, + 22, + 24 + ], + "jyotishganit_value": [ + 28, + 37, + 28, + 19, + 30, + 24, + 31, + 32, + 27, + 35, + 22, + 24 + ], + "vedastro_value": [ + 24, + 28, + 37, + 28, + 19, + 30, + 24, + 31, + 32, + 27, + 35, + 22 + ], + "local_pyjhora_jyotishganit_agree": false, + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "closure_status": "multi_engine_variant_worked_example_required", + "required_evidence": [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ], + "claim_boundary": "ashtakavarga_table_comparison_only_no_formula_truth" + } + ], + "boundary": "BAV/SAV table comparison only; public worked table and method metadata required before truth upgrade.", + "human_report": "docs/research/three_engine_bav_sav_field_closure_packet_2026_07_21.md" +} diff --git a/references/oracle/three_engine_d2_field_closure_packet_2026_07_21.json b/references/oracle/three_engine_d2_field_closure_packet_2026_07_21.json new file mode 100644 index 00000000..633f0d9a --- /dev/null +++ b/references/oracle/three_engine_d2_field_closure_packet_2026_07_21.json @@ -0,0 +1,132 @@ +{ + "scope": "three_engine_d2_field_closure_packet", + "created_at": "2026-07-21", + "claim_status": "partial", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_arbitration": "references/oracle/three_engine_mismatch_arbitration_2026_07_19.json", + "source_queue": "references/oracle/three_engine_mismatch_closure_queue_2026_07_19.json", + "source_jyotishganit_bridge": "references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json", + "summary": { + "rows_total": 7, + "local_pyjhora_jyotishganit_agree": 7, + "vedastro_endpoint_semantics_blocked": 7, + "truth_upgrades": 0 + }, + "rows": [ + { + "ticket_id": "TEMCQ-001", + "section": "D2", + "field": "Sun.sign", + "local_value": "Leo", + "pyjhora_jhora_value": "Leo", + "jyotishganit_value": "Leo", + "vedastro_value": "Gemini", + "partial_consensus_value": "Leo", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same D2 request", + "claim_boundary": "three_engine_consensus_not_global_truth" + }, + { + "ticket_id": "TEMCQ-002", + "section": "D2", + "field": "Moon.sign", + "local_value": "Cancer", + "pyjhora_jhora_value": "Cancer", + "jyotishganit_value": "Cancer", + "vedastro_value": "Pisces", + "partial_consensus_value": "Cancer", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same D2 request", + "claim_boundary": "three_engine_consensus_not_global_truth" + }, + { + "ticket_id": "TEMCQ-003", + "section": "D2", + "field": "Mars.sign", + "local_value": "Leo", + "pyjhora_jhora_value": "Leo", + "jyotishganit_value": "Leo", + "vedastro_value": "Aries", + "partial_consensus_value": "Leo", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same D2 request", + "claim_boundary": "three_engine_consensus_not_global_truth" + }, + { + "ticket_id": "TEMCQ-004", + "section": "D2", + "field": "Mercury.sign", + "local_value": "Leo", + "pyjhora_jhora_value": "Leo", + "jyotishganit_value": "Leo", + "vedastro_value": "Virgo", + "partial_consensus_value": "Leo", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same D2 request", + "claim_boundary": "three_engine_consensus_not_global_truth" + }, + { + "ticket_id": "TEMCQ-005", + "section": "D2", + "field": "Jupiter.sign", + "local_value": "Cancer", + "pyjhora_jhora_value": "Cancer", + "jyotishganit_value": "Cancer", + "vedastro_value": "Aquarius", + "partial_consensus_value": "Cancer", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same D2 request", + "claim_boundary": "three_engine_consensus_not_global_truth" + }, + { + "ticket_id": "TEMCQ-006", + "section": "D2", + "field": "Venus.sign", + "local_value": "Cancer", + "pyjhora_jhora_value": "Cancer", + "jyotishganit_value": "Cancer", + "vedastro_value": "Leo", + "partial_consensus_value": "Cancer", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same D2 request", + "claim_boundary": "three_engine_consensus_not_global_truth" + }, + { + "ticket_id": "TEMCQ-007", + "section": "D2", + "field": "Saturn.sign", + "local_value": "Cancer", + "pyjhora_jhora_value": "Cancer", + "jyotishganit_value": "Cancer", + "vedastro_value": "Gemini", + "partial_consensus_value": "Cancer", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same D2 request", + "claim_boundary": "three_engine_consensus_not_global_truth" + } + ], + "boundary": "D2 partial consensus only; hosted VedAstro remains blocked, so no global truth or commercial tuning upgrade.", + "human_report": "docs/research/three_engine_d2_field_closure_packet_2026_07_21.md" +} diff --git a/references/oracle/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.json b/references/oracle/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.json new file mode 100644 index 00000000..4e7159d1 --- /dev/null +++ b/references/oracle/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.json @@ -0,0 +1,68 @@ +{ + "scope": "three_engine_d4_d9_d10_field_closure_packet", + "created_at": "2026-07-21", + "claim_status": "partial", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_arbitration": "references/oracle/three_engine_mismatch_arbitration_2026_07_19.json", + "source_queue": "references/oracle/three_engine_mismatch_closure_queue_2026_07_19.json", + "source_jyotishganit_bridge": "references/oracle/jyotishganit_three_engine_closure_bridge_2026_07_21.json", + "summary": { + "rows_total": 3, + "local_pyjhora_jyotishganit_agree": 3, + "vedastro_endpoint_semantics_blocked": 3, + "truth_upgrades": 0 + }, + "rows": [ + { + "ticket_id": "TEMCQ-008", + "section": "D4", + "field": "Moon.sign", + "local_value": "Gemini", + "pyjhora_jhora_value": "Gemini", + "jyotishganit_value": "Gemini", + "vedastro_value": "Pisces", + "partial_consensus_value": "Gemini", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same request", + "claim_boundary": "three_engine_consensus_not_global_truth" + }, + { + "ticket_id": "TEMCQ-009", + "section": "D9", + "field": "Moon.sign", + "local_value": "Scorpio", + "pyjhora_jhora_value": "Scorpio", + "jyotishganit_value": "Scorpio", + "vedastro_value": "Leo", + "partial_consensus_value": "Scorpio", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same request", + "claim_boundary": "three_engine_consensus_not_global_truth" + }, + { + "ticket_id": "TEMCQ-010", + "section": "D10", + "field": "Moon.sign", + "local_value": "Pisces", + "pyjhora_jhora_value": "Pisces", + "jyotishganit_value": "Pisces", + "vedastro_value": "Sagittarius", + "partial_consensus_value": "Pisces", + "differing_engines": [ + "VedAstro" + ], + "closure_status": "partial_consensus_vedastro_endpoint_blocked", + "remaining_evidence_needed": "VedAstro endpoint/method contract with ayanamsa, node mode, varga and timezone semantics, plus raw replay pinned to same request", + "claim_boundary": "three_engine_consensus_not_global_truth" + } + ], + "boundary": "D4/D9/D10 partial consensus only; hosted VedAstro remains blocked, so no global truth or commercial tuning upgrade.", + "human_report": "docs/research/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.md" +} diff --git a/references/oracle/three_engine_field_status_batch_2026_07_21.json b/references/oracle/three_engine_field_status_batch_2026_07_21.json new file mode 100644 index 00000000..e9ee01a4 --- /dev/null +++ b/references/oracle/three_engine_field_status_batch_2026_07_21.json @@ -0,0 +1,207 @@ +{ + "scope": "three_engine_field_status_batch", + "created_at": "2026-07-21", + "claim_status": "observation_only", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_applied_bridge": "references/oracle/three_engine_jyotishganit_bridge_applied_2026_07_21.json", + "panchanga_ticket": { + "ticket_id": "TEMCQ-061", + "section": "panchanga", + "field": "schema_and_named_fields", + "category": "schema_or_tradition_semantics", + "owner_track": "new_ticket_required", + "priority": "P1", + "closure_status": "open", + "required_evidence": "same-case local/jyotishganit/VedAstro panchanga schema mapping with weekday/tithi/nakshatra/yoga/karana calculation contract" + }, + "summary": { + "rows_total": 18, + "endpoint_semantics": 10, + "worked_example_required": 8, + "truth_upgrades": 0 + }, + "rows": [ + { + "ticket_id": "TEMCQ-001", + "section": "D2", + "field": "Sun.sign", + "source_field": "D2", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-002", + "section": "D2", + "field": "Moon.sign", + "source_field": "D2", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-003", + "section": "D2", + "field": "Mars.sign", + "source_field": "D2", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-004", + "section": "D2", + "field": "Mercury.sign", + "source_field": "D2", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-005", + "section": "D2", + "field": "Jupiter.sign", + "source_field": "D2", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-006", + "section": "D2", + "field": "Venus.sign", + "source_field": "D2", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-007", + "section": "D2", + "field": "Saturn.sign", + "source_field": "D2", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-008", + "section": "D4", + "field": "Moon.sign", + "source_field": "D4", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-009", + "section": "D9", + "field": "Moon.sign", + "source_field": "D9", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-010", + "section": "D10", + "field": "Moon.sign", + "source_field": "D10", + "owner_track": "endpoint_contract", + "status_after_bridge": "ready_for_endpoint_semantics_check", + "remaining_evidence_needed": "VedAstro endpoint/method semantics and same-unit local/jyotishganit raw comparison", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-011", + "section": "ashtakavarga_bav", + "field": "Sun", + "source_field": "BAV", + "owner_track": "worked_example", + "status_after_bridge": "ready_for_worked_example_comparison", + "remaining_evidence_needed": "public worked BAV/SAV table with contributor set and shodhana policy", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-012", + "section": "ashtakavarga_bav", + "field": "Moon", + "source_field": "BAV", + "owner_track": "worked_example", + "status_after_bridge": "ready_for_worked_example_comparison", + "remaining_evidence_needed": "public worked BAV/SAV table with contributor set and shodhana policy", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-013", + "section": "ashtakavarga_bav", + "field": "Mars", + "source_field": "BAV", + "owner_track": "worked_example", + "status_after_bridge": "ready_for_worked_example_comparison", + "remaining_evidence_needed": "public worked BAV/SAV table with contributor set and shodhana policy", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-014", + "section": "ashtakavarga_bav", + "field": "Mercury", + "source_field": "BAV", + "owner_track": "worked_example", + "status_after_bridge": "ready_for_worked_example_comparison", + "remaining_evidence_needed": "public worked BAV/SAV table with contributor set and shodhana policy", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-015", + "section": "ashtakavarga_bav", + "field": "Jupiter", + "source_field": "BAV", + "owner_track": "worked_example", + "status_after_bridge": "ready_for_worked_example_comparison", + "remaining_evidence_needed": "public worked BAV/SAV table with contributor set and shodhana policy", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-016", + "section": "ashtakavarga_bav", + "field": "Venus", + "source_field": "BAV", + "owner_track": "worked_example", + "status_after_bridge": "ready_for_worked_example_comparison", + "remaining_evidence_needed": "public worked BAV/SAV table with contributor set and shodhana policy", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-017", + "section": "ashtakavarga_bav", + "field": "Saturn", + "source_field": "BAV", + "owner_track": "worked_example", + "status_after_bridge": "ready_for_worked_example_comparison", + "remaining_evidence_needed": "public worked BAV/SAV table with contributor set and shodhana policy", + "claim_boundary": "status_classification_only_no_numeric_truth" + }, + { + "ticket_id": "TEMCQ-018", + "section": "ashtakavarga_sav", + "field": "12_sign_scores", + "source_field": "SAV", + "owner_track": "worked_example", + "status_after_bridge": "ready_for_worked_example_comparison", + "remaining_evidence_needed": "public worked BAV/SAV table with contributor set and shodhana policy", + "claim_boundary": "status_classification_only_no_numeric_truth" + } + ], + "boundary": "status_batch_only_no_truth_upgrade" +} diff --git a/references/oracle/whole_machine_jyotish_fragment_scan_2026_07_21.json b/references/oracle/whole_machine_jyotish_fragment_scan_2026_07_21.json new file mode 100644 index 00000000..5478b8dc --- /dev/null +++ b/references/oracle/whole_machine_jyotish_fragment_scan_2026_07_21.json @@ -0,0 +1,2707 @@ +{ + "scope": "whole_machine_jyotish_fragment_scan_2026_07_21", + "created_at": "2026-07-21", + "scan_bases": [ + "/Users/wuyongnaren/Documents", + "/Users/wuyongnaren/WorkBuddy", + "/Users/wuyongnaren/.workbuddy", + "/Users/wuyongnaren/Downloads", + "/Users/wuyongnaren/Desktop", + "/private/tmp", + "/tmp", + "/Users/wuyongnaren/Projects" + ], + "summary": { + "files_with_signals": 2096, + "main_repo_signal_files": 564, + "external_signal_files": 1532, + "privacy_blocked_files": 983, + "reviewable_external_files": 742 + }, + "decision_policy": { + "main_repo": "check invocation coverage before new work", + "workbuddy_fragment": "reference only; never truth source", + "privacy_review_required": "forbidden until human privacy review", + "tmp_fragment": "must pin license/version/hash before reuse" + }, + "reviewable_external_candidates": [ + { + "path": "/Users/wuyongnaren/Documents/Codex/2026-06-20/732642856-talk-https-github-com-732642856/work/talk-active/engines-repo/jyotish/jyotishganit-adapter.js", + "size": 7588, + "category": "external_candidate", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/10_个人知识管理/09_术数与玄学/紫微风水与印度占星的本土术数谱系.md", + "size": 3600, + "category": "external_candidate", + "signals": [ + "dasha" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/05_文件仓库索引/电子书资料库/分类-术数占星玄学.md", + "size": 23971, + "category": "external_candidate", + "signals": [ + "tajika", + "dasha" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/05_文件仓库索引/电子书资料库/书目卡片/Yogini Dasha VPGOEL 0000 Yogini001.png 1.png.md", + "size": 1922, + "category": "external_candidate", + "signals": [ + "dasha" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/05_文件仓库索引/电子书资料库/书目卡片/Tajika占星术是印度占星术体系之一.md", + "size": 2185, + "category": "external_candidate", + "signals": [ + "tajika" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/05_文件仓库索引/电子书资料库/书目卡片/Predicting Through Shasti Hayani Dasha (V.P.Goel) (Z-Library.md", + "size": 1980, + "category": "external_candidate", + "signals": [ + "dasha" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/05_文件仓库索引/电子书资料库/书目卡片/Predicting through Jaiminis Chara Dasha An Original Research Hindu Astrology Series (K. N..md", + "size": 2364, + "category": "external_candidate", + "signals": [ + "dasha" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/03_研究_术数占星/一楠 · 印度占星完整解盘报告 v2.md", + "size": 1001, + "category": "external_candidate", + "signals": [ + "dasha" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/03_研究_术数占星/印度占星研究结论 v3.md", + "size": 5264, + "category": "external_candidate", + "signals": [ + "dasha" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/ObsidianVault/03_研究_术数占星/印度占星 Jyotish.md", + "size": 1988, + "category": "external_candidate", + "signals": [ + "prashna", + "tajika", + "saham", + "kp", + "dasha" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/engines-repo/verify-local-jyotish-reference-audit.js", + "size": 2388, + "category": "external_candidate", + "signals": [ + "oracle", + "benchmark", + "test" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/engines-repo/jyotish-prashna-bridge.js", + "size": 9568, + "category": "external_candidate", + "signals": [ + "prashna", + "kp", + "test" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/engines-repo/verify-jyotish-prashna-bridge.js", + "size": 5628, + "category": "external_candidate", + "signals": [ + "prashna", + "gulika", + "kp", + "registry", + "test" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/engines-repo/jyotish/jyotishganit-adapter.js", + "size": 7588, + "category": "external_candidate", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/docs/jyotish-gap-migration-plan.md", + "size": 3107, + "category": "external_candidate", + "signals": [ + "saham", + "dasha", + "benchmark", + "test" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/reports/local-jyotish-reference-audit.md", + "size": 2176, + "category": "external_candidate", + "signals": [ + "dasha", + "oracle", + "benchmark", + "test" + ], + "risk_labels": [], + "decision": "candidate_review_required" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/sahams-execution-guide.md", + "size": 1867, + "category": "workbuddy_fragment", + "signals": [ + "prashna", + "tajika", + "saham", + "sphuta", + "kp", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/chara-dasha-calibration-roadmap.md", + "size": 2690, + "category": "workbuddy_fragment", + "signals": [ + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/dasha-transit-method.md", + "size": 6575, + "category": "workbuddy_fragment", + "signals": [ + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/ashtakavarga-complete-system.md", + "size": 8651, + "category": "workbuddy_fragment", + "signals": [ + "ashtakavarga", + "varga" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/chara-dasha-v6910-test-report.md", + "size": 3119, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/kp-practical-event-timing.md", + "size": 303, + "category": "workbuddy_fragment", + "signals": [ + "kp" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/tajika-yoga-complete-guide.md", + "size": 11850, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/muhurta-complete-guide.md", + "size": 1348, + "category": "workbuddy_fragment", + "signals": [ + "muhurta", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/multi-dasha-convergence-protocol.md", + "size": 6189, + "category": "workbuddy_fragment", + "signals": [ + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/bphs-ch48-narayana-dasha.md", + "size": 1085, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/vp-goel-jaimini-dasha-systems.md", + "size": 4955, + "category": "workbuddy_fragment", + "signals": [ + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/bhrigu-pada-dasha-marriage-counting.md", + "size": 7474, + "category": "workbuddy_fragment", + "signals": [ + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/relationship-astrology-guide.md", + "size": 16418, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "varga" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/jyotishganit_benchmark.md", + "size": 2333, + "category": "workbuddy_fragment", + "signals": [ + "ashtakavarga", + "varga", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/real_case_studies/印度占星修正版研究结论v3-高压基建后的反转兑现模型.md", + "size": 32938, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/oracle/artifacts/pyjhora_historical_epoch_dasha_stdout_20260627.txt", + "size": 6188, + "category": "workbuddy_fragment", + "signals": [ + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/oracle/artifacts/pyjhora_steve_jobs_shadbala_lahiri_stdout_20260627.txt", + "size": 2980, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/oracle/artifacts/pyjhora_extreme_latitude_kp_shadbala_stdout_20260627.txt", + "size": 2973, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "kp" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/oracle/artifacts/pyjhora_marilyn_monroe_varshaphala_1962_lahiri_20260629.txt", + "size": 1190, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/oracle/artifacts/pyjhora_historical_dst_london_varshaphala_1943_lahiri_20260629.txt", + "size": 1140, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/oracle/artifacts/pyjhora_steve_jobs_dasha_stdout_20260627.txt", + "size": 5765, + "category": "workbuddy_fragment", + "signals": [ + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/oracle/artifacts/pyjhora_synthetic_extreme_latitude_varshaphala_kp_20260629.txt", + "size": 1182, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "kp", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/oracle/artifacts/pyjhora_einstein_varshaphala_1905_lahiri_partial_20260629.txt", + "size": 1030, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/jyotishganit/tests/test_astronomical.py", + "size": 538, + "category": "workbuddy_fragment", + "signals": [ + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/jyotishganit/tests/test_ashtakavarga.py", + "size": 11682, + "category": "workbuddy_fragment", + "signals": [ + "ashtakavarga", + "kp", + "varga", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/VedicAstro/VedicAstroAPI.py", + "size": 5043, + "category": "workbuddy_fragment", + "signals": [ + "kp", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/VedicAstro/vedicastro/VedicAstro.py", + "size": 33217, + "category": "workbuddy_fragment", + "signals": [ + "kp" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/VedicAstro/vedicastro/horary_chart.py", + "size": 7325, + "category": "workbuddy_fragment", + "signals": [ + "kp" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/VedicAstro/test_suite/horary_functions_test.py", + "size": 3617, + "category": "workbuddy_fragment", + "signals": [ + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/dashaflow/ashtakavarga.py", + "size": 4770, + "category": "workbuddy_fragment", + "signals": [ + "ashtakavarga", + "varga" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/vedic-astro-skills/claude-code/.claude/commands/vedic-love.md", + "size": 10118, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/references/open_source_sources/vedic-astro-skills/claude-code/.claude/commands/vedic-career.md", + "size": 13323, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_ashtakavarga_invariants.py", + "size": 3548, + "category": "workbuddy_fragment", + "signals": [ + "ashtakavarga", + "varga", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_shadbala_oracle_closure_status.py", + "size": 2254, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_nakshatra.py", + "size": 10401, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_tajika_einstein_1905_fill_map.py", + "size": 1100, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_fast_path_checklist.py", + "size": 2038, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_external_technique_evidence.py", + "size": 18178, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "rectification", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_dasha_first_packet_operator_card.py", + "size": 1590, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_chara_dasha_dignity.py", + "size": 8430, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_muhurta.py", + "size": 18035, + "category": "workbuddy_fragment", + "signals": [ + "gulika", + "muhurta", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_pyjhora_oracle_artifact_manifest.py", + "size": 1592, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "tajika", + "saham", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_final_jhora_evidence_packet_acceptance.py", + "size": 5294, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_jyotishganit_adapter_diagnostics.py", + "size": 1216, + "category": "workbuddy_fragment", + "signals": [ + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_shadbala_sthana_targeted_audit.py", + "size": 851, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_dasha_oracle_evidence_validator.py", + "size": 3539, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_deep_varga_avastha.py", + "size": 1477, + "category": "workbuddy_fragment", + "signals": [ + "varga", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_runtime_mode_diagnostics.py", + "size": 3026, + "category": "workbuddy_fragment", + "signals": [ + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_varga_vedastro_mode.py", + "size": 4640, + "category": "workbuddy_fragment", + "signals": [ + "varga", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_tajika_annual_oracle_queue.py", + "size": 6504, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_method_catalog_sync.py", + "size": 4206, + "category": "workbuddy_fragment", + "signals": [ + "registry", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_tajika_first_packet_operator_card.py", + "size": 2098, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_evidence_orchestrator.py", + "size": 10937, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_pyjhora_adapter_diagnostics.py", + "size": 1690, + "category": "workbuddy_fragment", + "signals": [ + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_chara_dasha_precision_v6910.py", + "size": 13446, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_narayana_dasha.py", + "size": 1357, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_adapter_candidate_guard.py", + "size": 1746, + "category": "workbuddy_fragment", + "signals": [ + "probe", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_varga_bphs.py", + "size": 5225, + "category": "workbuddy_fragment", + "signals": [ + "varga", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_shadbala_sapta_layer_hotspots.py", + "size": 936, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "varga", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_shadbala_d3_mapping_audit.py", + "size": 750, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_shadbala_oracle_comparison.py", + "size": 1077, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_shadbala_sapta_dignity_whitelist.py", + "size": 794, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_parity_matrix.py", + "size": 5001, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "ashtakavarga", + "prashna", + "tajika", + "dasha", + "varga", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_shadbala_oracle_component_cluster_summary.py", + "size": 868, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_shadbala_dig_source_of_truth_audit.py", + "size": 758, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_ingestion_closure_pack.py", + "size": 859, + "category": "workbuddy_fragment", + "signals": [ + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_vedastro_gateway.py", + "size": 3063, + "category": "workbuddy_fragment", + "signals": [ + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_dasha_oracle_closure_status.py", + "size": 3829, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_tajika_annual_closure_status.py", + "size": 4097, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_tajika_annual_benchmark_dashboard.py", + "size": 1983, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "oracle", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/tests/test_pyjhora_oracle_artifacts_presence.py", + "size": 1290, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/first_tajika_oracle_packet_assistant.md", + "size": 2590, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/tajika_einstein_1905_fill_map.md", + "size": 3139, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/public_jyotish_benchmark_dashboard.json", + "size": 2258, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/tajika_einstein_1905_copy_ready_checklist.md", + "size": 2871, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/tajika_einstein_1905_packet_paste_instructions.md", + "size": 1669, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/first_dasha_oracle_packet_assistant.md", + "size": 1210, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/shadbala_external_absolute_value_closure_status.md", + "size": 699, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "tajika", + "saham", + "dasha", + "oracle" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/tajika_einstein_1905_field_copy_template.md", + "size": 5627, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/tajika_einstein_1905_packet_paste_block.jsonc", + "size": 1280, + "category": "workbuddy_fragment", + "signals": [ + "tajika" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/public_jyotish_benchmark_dashboard.md", + "size": 1266, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "benchmark", + "registry" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/tajika_sahams_annual_benchmark_dashboard.json", + "size": 1393, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/jyotish_external_oracle_closure_master_dashboard.md", + "size": 967, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "tajika", + "saham", + "dasha", + "oracle" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/dasha_external_oracle_closure_status.json", + "size": 663, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/first_tajika_oracle_packet_assistant.json", + "size": 3275, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/dasha_external_oracle_closure_status.md", + "size": 661, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/shadbala_external_absolute_value_closure_status.json", + "size": 994, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "tajika", + "saham", + "dasha", + "oracle" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/first_dasha_oracle_packet_assistant.json", + "size": 1517, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/jyotish_external_oracle_closure_master_dashboard.json", + "size": 3916, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "tajika", + "saham", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/tajika_sahams_annual_closure_status.md", + "size": 3120, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/tajika_sahams_annual_benchmark_dashboard.md", + "size": 1229, + "category": "workbuddy_fragment", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/benchmark/legacy-marriage-v6.1/印度占星实战案例综合验证报告-v6.1-2026-05-03.md", + "size": 8142, + "category": "workbuddy_fragment", + "signals": [ + "saham", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/shadbala_component_cap_all_routes_2026_06_28.md", + "size": 1445, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/whole_project_fragment_sweep_and_vedastro_ledger_link_2026_06_28.md", + "size": 1935, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "benchmark", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/antigravity_round25_shadbala_validator_phase2_acceptance_2026_06_25.md", + "size": 1325, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "oracle" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/antigravity_round26_shadbala_validator_phase2_tickets_2026_06_25.md", + "size": 2027, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "oracle" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/antigravity_round28_pyjhora_jhora_breadth_gap_matrix_2026_06_26.md", + "size": 1792, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "ashtakavarga", + "tajika", + "dasha", + "varga", + "benchmark" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/vedastro_required_high_frequency_radar_contract_2026_06_28.md", + "size": 4023, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/antigravity_round24_open_source_jyotish_landscape_2026_06_25.md", + "size": 2552, + "category": "workbuddy_fragment", + "signals": [ + "dasha", + "oracle" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/antigravity_round28_jaimini_kp_prashna_depth_plan_2026_06_26.md", + "size": 1129, + "category": "workbuddy_fragment", + "signals": [ + "prashna", + "tajika", + "muhurta", + "kp" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/raman_dasha_boundary_series_oracle_seed_2026_06_28.md", + "size": 1473, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "dasha", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/shadbala_component_confidence_cap_v1_2026_06_28.md", + "size": 1490, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "oracle" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/antigravity_round29_jaimini_kp_prashna_gap_matrix_2026_06_26.md", + "size": 1188, + "category": "workbuddy_fragment", + "signals": [ + "prashna", + "kp", + "dasha" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + }, + { + "path": "/Users/wuyongnaren/WorkBuddy/2026-07-05-19-03-49/yinduzhanxing/docs/research/antigravity_round20_shadbala_units_totals_design_2026_06_25.md", + "size": 1514, + "category": "workbuddy_fragment", + "signals": [ + "shadbala", + "oracle", + "test" + ], + "risk_labels": [ + "historical_fragment_only", + "not_for_truth_source" + ], + "decision": "reference_only_candidate_not_truth_source" + } + ], + "privacy_blocked_sample": [ + { + "path": "/Users/wuyongnaren/Documents/Codex/2026-06-18/new-chat-4/work/starcanvas-active/apps/web/src/app/canvas/utils/characterAstrologyService.ts", + "size": 13673, + "category": "external_candidate", + "signals": [ + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/Codex/2026-06-20/732642856-yinduzhanxing-https-github-com-732642856/work/audit_tmp/jyotish-engine-modules/scripts/special_lagnas.py", + "size": 12121, + "category": "external_candidate", + "signals": [ + "prashna" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/Codex/2026-06-20/732642856-talk-https-github-com-732642856/work/talk-active/engines-repo/jyotish/jyotish-adapter.js", + "size": 10913, + "category": "external_candidate", + "signals": [ + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/Codex/2026-06-20/732642856-talk-https-github-com-732642856/work/talk-active/engines-repo/jyotish/vedic-calc-runner.py", + "size": 13523, + "category": "external_candidate", + "signals": [ + "prashna", + "tajika", + "gulika", + "kp", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/Codex/2026-06-20/732642856-talk-https-github-com-732642856/work/talk-active/engines-repo/jyotish/jyotishganit-runner.py", + "size": 9754, + "category": "external_candidate", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/Codex/2026-06-21/starcanvas-https-github-com-732642856-starcanvas/work/starcanvas/apps/web/src/app/canvas/utils/characterAstrologyService.ts", + "size": 13673, + "category": "external_candidate", + "signals": [ + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/engines-repo/local-jyotish-reference-audit.js", + "size": 16997, + "category": "external_candidate", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "oracle", + "benchmark", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/engines-repo/jyotish/jyotish-adapter.js", + "size": 10913, + "category": "external_candidate", + "signals": [ + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/engines-repo/jyotish/vedic-calc-runner.py", + "size": 13523, + "category": "external_candidate", + "signals": [ + "prashna", + "tajika", + "gulika", + "kp", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/星轨talk/engines-repo/jyotish/jyotishganit-runner.py", + "size": 9754, + "category": "external_candidate", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "forbidden_until_privacy_review" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/indastro-case-studies.md", + "size": 26174, + "category": "main_repo", + "signals": [ + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/condition-dasha-complete.md", + "size": 5844, + "category": "main_repo", + "signals": [ + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/global-astrologer-reflections.md", + "size": 17184, + "category": "main_repo", + "signals": [ + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/dasha-calculation-tool.md", + "size": 11706, + "category": "main_repo", + "signals": [ + "shadbala", + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/varga-system-quick-reference.md", + "size": 10468, + "category": "main_repo", + "signals": [ + "kp", + "dasha", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/open-source-jyotish-scan-2026.md", + "size": 16821, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "prashna", + "tajika", + "gulika", + "muhurta", + "kp", + "dasha", + "varga", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/varga-divisional-charts-quick-reference.md", + "size": 14961, + "category": "main_repo", + "signals": [ + "dasha", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/global-astrologer-practical-methodology.md", + "size": 14244, + "category": "main_repo", + "signals": [ + "shadbala", + "kp", + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/pancha-pakshi-nakshatra-systems.md", + "size": 11280, + "category": "main_repo", + "signals": [ + "muhurta", + "kp", + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/yoga-and-dasha.md", + "size": 9968, + "category": "main_repo", + "signals": [ + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/analysis-natal-full-part3-Ashtakavarga.md", + "size": 681, + "category": "main_repo", + "signals": [ + "ashtakavarga", + "varga", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/shasti-hayani-dasha-guide.md", + "size": 3734, + "category": "main_repo", + "signals": [ + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/vimshottari_dasha_guide.md", + "size": 18384, + "category": "main_repo", + "signals": [ + "shadbala", + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/deep-varga-avastha-execution-guide.md", + "size": 2170, + "category": "main_repo", + "signals": [ + "dasha", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/alternative-dasha-systems.md", + "size": 7628, + "category": "main_repo", + "signals": [ + "ashtakavarga", + "prashna", + "tajika", + "kp", + "dasha", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/shadbala-complete-methodology.md", + "size": 10552, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "benchmark" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/saham_rules.json", + "size": 2065, + "category": "main_repo", + "signals": [ + "tajika", + "saham" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/ashwini-abhijit-ketu-nakshatra-freeze-guide.md", + "size": 8520, + "category": "main_repo", + "signals": [ + "shadbala", + "muhurta", + "dasha", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/shadbala-interpretation-methodology.md", + "size": 7935, + "category": "main_repo", + "signals": [ + "shadbala", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/prashna-complete-guide.md", + "size": 17864, + "category": "main_repo", + "signals": [ + "prashna", + "tajika", + "saham", + "gulika", + "sphuta", + "kp", + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/kp-astrology-complete-system.md", + "size": 10337, + "category": "main_repo", + "signals": [ + "prashna", + "kp", + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/shodasavarga-complete-guide.md", + "size": 9252, + "category": "main_repo", + "signals": [ + "shadbala", + "dasha", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/cross_project_contract/commercial_astrology_e2e_context_capture_manifest_2026_07_19.json", + "size": 4030, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "rectification" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/cross_project_contract/commercial_astrology_e2e_acceptance_questions_2026_07_19.json", + "size": 4162, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "rectification" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/shadbala_component_closure_queue_v2_2026_07_19.json", + "size": 58478, + "category": "main_repo", + "signals": [ + "shadbala", + "varga", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/pyjhora_same_chart_parity_public_smoke_manifest.json", + "size": 755, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "varga", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/vp_jain_shadbala_component_benchmark_2026_07_17.json", + "size": 11770, + "category": "main_repo", + "signals": [ + "shadbala", + "benchmark", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/xalen_public_case_batch_2026_07_17.json", + "size": 41935, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/kp_muhurta_shadbala_numeric_packet_queue_2026_07_19.json", + "size": 2693, + "category": "main_repo", + "signals": [ + "shadbala", + "muhurta", + "kp", + "oracle", + "probe" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/jyotishyamitra_pinned_adapter_probe_2026_07_18.json", + "size": 2520, + "category": "main_repo", + "signals": [ + "dasha", + "oracle", + "probe" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/xalen_multi_public_case_component_coverage_2026_07_19.json", + "size": 7587, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "varga", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/jyotishyamitra_steve_jobs_probe_2026_07_18.json", + "size": 615069, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga", + "oracle", + "probe" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/dasha_shadbala_oracle_cases.json", + "size": 9600, + "category": "main_repo", + "signals": [ + "shadbala", + "kp", + "dasha", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/pyjhora_extended_parity_public_smoke_manifest.json", + "size": 777, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "varga", + "test" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/tajika_annual_oracle_cases.json", + "size": 21232, + "category": "main_repo", + "signals": [ + "tajika", + "saham", + "kp", + "dasha", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/authoritative_oss_jyotish_source_intake_2026_07_19.json", + "size": 4220, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "muhurta", + "kp", + "dasha", + "varga", + "oracle", + "benchmark", + "probe" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/prashna_sphuta_pyjhora_public_smoke.json", + "size": 1817, + "category": "main_repo", + "signals": [ + "prashna", + "gulika", + "sphuta", + "benchmark" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/xalen_multi_public_case_independent_ephemeris_2026_07_17.json", + "size": 42430, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pyjhora_steve_jobs_varshaphala_1984_lahiri_stdout_20260627.txt", + "size": 3455, + "category": "main_repo", + "signals": [ + "tajika", + "saham", + "dasha" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/xalen_multi_public_case_shared_input_2026_07_17.json", + "size": 41935, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/jyotishganit_steve_jobs_high_rigor_raw.json", + "size": 212469, + "category": "main_repo", + "signals": [ + "shadbala", + "ashtakavarga", + "dasha", + "varga" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pending_packets/external_template_historical_epoch_lahiri_pyjhora_20260627.json", + "size": 1552, + "category": "main_repo", + "signals": [ + "shadbala", + "dasha", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pending_packets/external_template_steve_jobs_dasha_lahiri_pyjhora_20260627.json", + "size": 1549, + "category": "main_repo", + "signals": [ + "shadbala", + "dasha", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pending_packets/external_template_synthetic_north_china_shadbala_raman.json", + "size": 2595, + "category": "main_repo", + "signals": [ + "shadbala", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pending_packets/external_template_steve_jobs_varshaphala_1984_lahiri_pyjhora_20260627.json", + "size": 3249, + "category": "main_repo", + "signals": [ + "tajika", + "saham", + "dasha", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pending_packets/external_template_extreme_latitude_kp_pyjhora_20260627.json", + "size": 3054, + "category": "main_repo", + "signals": [ + "shadbala", + "kp", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pending_packets/external_template_synthetic_north_china_shadbala_raman_pyjhora_20260627.json", + "size": 3220, + "category": "main_repo", + "signals": [ + "shadbala", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pending_packets/external_template_steve_jobs_shadbala_lahiri_pyjhora_20260627.json", + "size": 3036, + "category": "main_repo", + "signals": [ + "shadbala", + "dasha", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/artifacts/pending_packets/external_template_steve_jobs_dasha_lahiri.json", + "size": 2570, + "category": "main_repo", + "signals": [ + "shadbala", + "dasha", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + }, + { + "path": "/Users/wuyongnaren/Documents/印度占星/references/oracle/evidence_packet_templates/jhora_steve_jobs_lahiri_first_packet.json", + "size": 2803, + "category": "main_repo", + "signals": [ + "shadbala", + "dasha", + "oracle" + ], + "risk_labels": [ + "privacy_review_required" + ], + "decision": "already_in_truth_source_check_invocation" + } + ], + "runtime_invocation_findings": { + "interpretation_source_runtime_coverage_status": "partial", + "proven_runtime_markers": [ + "dasha_timing_layer_used", + "varga_strength_layer_used", + "annual_special_layer_context", + "modifier_obstacle_layer_used" + ], + "not_fully_closed_reference_layers": [ + "references/open_source_sources/jyotishganit", + "references/open_source_sources/jaimini-tropical", + "references/open_source_sources/VedicAstro", + "references/open_source_sources/rishi-ai-mcp", + "references/open_source_sources/vedic-astro-skills", + "references/open_source_sources/dashaflow" + ], + "inventory_gate_status": "pass", + "boundary": "runtime invocation is proven for surfaced strict-workflow markers, not every source asset; remaining layers require adapter/API/UI/skill call proof before claiming capability." + }, + "priority_follow_up": [ + { + "id": "jyotishganit_field_closure", + "reason": "already one of the comparison engines; still not fully closed as source asset/runtime parity layer", + "action": "reuse existing adapters/probes; add field-level closure rows only, no truth upgrade" + }, + { + "id": "vedicastro_kp_runtime_surface", + "reason": "KP/cusp/sub-lord source exists but exact public oracle remains partial", + "action": "keep isolated runtime probe and source hash; add worked-example numeric packets when fields are public" + }, + { + "id": "jaimini_tropical_dashaflow_reference", + "reason": "reference source present but not truth source", + "action": "registry/reference only unless license, input contract, raw hash and replay tests are pinned" + }, + { + "id": "rishi_ai_mcp_vedic_astro_skills", + "reason": "prompt/product-flow source, not numeric oracle", + "action": "reuse as skill wording/process reference only; no formula copying or truth matrix" + }, + { + "id": "workbuddy_reference_docs", + "reason": "many docs are already mirrored or represented in main repo; old checkout remains artifact incomplete and privacy risky", + "action": "only migrate via tests/registry after file-by-file ledger review" + } + ] +} diff --git a/tests/test_three_engine_bav_sav_field_closure_packet.py b/tests/test_three_engine_bav_sav_field_closure_packet.py new file mode 100644 index 00000000..f0727d96 --- /dev/null +++ b/tests/test_three_engine_bav_sav_field_closure_packet.py @@ -0,0 +1,49 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/three_engine_bav_sav_field_closure_packet_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_bav_sav_packet_classifies_without_truth_upgrade(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["scope"] == "three_engine_bav_sav_field_closure_packet" + assert data["claim_status"] == "partial" + assert data["truth_matrix_allowed"] is False + assert data["summary"]["rows_total"] == 8 + assert data["summary"]["local_pyjhora_jyotishganit_agree"] == 5 + assert data["summary"]["multi_engine_variant"] == 3 + assert data["summary"]["truth_upgrades"] == 0 + + +def test_bav_sav_rows_require_worked_examples_and_method_metadata(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + rows = {row["ticket_id"]: row for row in data["rows"]} + + assert rows["TEMCQ-011"]["closure_status"] == "partial_consensus_vedastro_variant_blocked" + assert rows["TEMCQ-012"]["closure_status"] == "multi_engine_variant_worked_example_required" + assert rows["TEMCQ-016"]["closure_status"] == "multi_engine_variant_worked_example_required" + assert rows["TEMCQ-018"]["closure_status"] == "multi_engine_variant_worked_example_required" + for row in rows.values(): + assert row["required_evidence"] == [ + "public worked BAV/SAV table", + "contributor set", + "Lagna inclusion policy", + "shodhana state", + "rashi order/orientation" + ] + assert row["claim_boundary"] == "ashtakavarga_table_comparison_only_no_formula_truth" + + +def test_bav_sav_packet_is_registered(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "three_engine_bav_sav_field_closure_packet_2026_07_21" + ) + + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "partial" diff --git a/tests/test_three_engine_d2_field_closure_packet.py b/tests/test_three_engine_d2_field_closure_packet.py new file mode 100644 index 00000000..eee94d88 --- /dev/null +++ b/tests/test_three_engine_d2_field_closure_packet.py @@ -0,0 +1,55 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/three_engine_d2_field_closure_packet_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_d2_packet_closes_local_pyjhora_jyotishganit_agreement_only(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["scope"] == "three_engine_d2_field_closure_packet" + assert data["claim_status"] == "partial" + assert data["truth_matrix_allowed"] is False + assert data["summary"]["rows_total"] == 7 + assert data["summary"]["local_pyjhora_jyotishganit_agree"] == 7 + assert data["summary"]["vedastro_endpoint_semantics_blocked"] == 7 + assert data["summary"]["truth_upgrades"] == 0 + + +def test_d2_rows_keep_vedastro_as_endpoint_semantics_not_formula_error(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + expected = { + "Sun.sign": ("Leo", "Gemini"), + "Moon.sign": ("Cancer", "Pisces"), + "Mars.sign": ("Leo", "Aries"), + "Mercury.sign": ("Leo", "Virgo"), + "Jupiter.sign": ("Cancer", "Aquarius"), + "Venus.sign": ("Cancer", "Leo"), + "Saturn.sign": ("Cancer", "Gemini"), + } + rows = {row["field"]: row for row in data["rows"]} + assert set(rows) == set(expected) + for field, (consensus, vedastro) in expected.items(): + row = rows[field] + assert row["ticket_id"].startswith("TEMCQ-00") + assert row["local_value"] == consensus + assert row["pyjhora_jhora_value"] == consensus + assert row["jyotishganit_value"] == consensus + assert row["vedastro_value"] == vedastro + assert row["closure_status"] == "partial_consensus_vedastro_endpoint_blocked" + assert row["claim_boundary"] == "three_engine_consensus_not_global_truth" + + +def test_d2_packet_is_registered_in_evidence_index(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "three_engine_d2_field_closure_packet_2026_07_21" + ) + + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "partial" diff --git a/tests/test_three_engine_d4_d9_d10_field_closure_packet.py b/tests/test_three_engine_d4_d9_d10_field_closure_packet.py new file mode 100644 index 00000000..45292a2c --- /dev/null +++ b/tests/test_three_engine_d4_d9_d10_field_closure_packet.py @@ -0,0 +1,51 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/three_engine_d4_d9_d10_field_closure_packet_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_d4_d9_d10_packet_closes_partial_consensus_only(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["scope"] == "three_engine_d4_d9_d10_field_closure_packet" + assert data["claim_status"] == "partial" + assert data["truth_matrix_allowed"] is False + assert data["summary"]["rows_total"] == 3 + assert data["summary"]["local_pyjhora_jyotishganit_agree"] == 3 + assert data["summary"]["vedastro_endpoint_semantics_blocked"] == 3 + assert data["summary"]["truth_upgrades"] == 0 + + +def test_d4_d9_d10_rows_preserve_exact_values_and_boundaries(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + rows = {(row["section"], row["field"]): row for row in data["rows"]} + + expected = { + ("D4", "Moon.sign"): ("TEMCQ-008", "Gemini", "Pisces"), + ("D9", "Moon.sign"): ("TEMCQ-009", "Scorpio", "Leo"), + ("D10", "Moon.sign"): ("TEMCQ-010", "Pisces", "Sagittarius"), + } + assert set(rows) == set(expected) + for key, (ticket, consensus, vedastro) in expected.items(): + row = rows[key] + assert row["ticket_id"] == ticket + assert row["local_value"] == consensus + assert row["pyjhora_jhora_value"] == consensus + assert row["jyotishganit_value"] == consensus + assert row["vedastro_value"] == vedastro + assert row["closure_status"] == "partial_consensus_vedastro_endpoint_blocked" + assert row["claim_boundary"] == "three_engine_consensus_not_global_truth" + + +def test_d4_d9_d10_packet_is_registered(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "three_engine_d4_d9_d10_field_closure_packet_2026_07_21" + ) + + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "partial" diff --git a/tests/test_three_engine_field_status_batch_2026_07_21.py b/tests/test_three_engine_field_status_batch_2026_07_21.py new file mode 100644 index 00000000..10e292e6 --- /dev/null +++ b/tests/test_three_engine_field_status_batch_2026_07_21.py @@ -0,0 +1,44 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/three_engine_field_status_batch_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_field_status_batch_adds_panchanga_ticket_and_keeps_truth_closed(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["scope"] == "three_engine_field_status_batch" + assert data["claim_status"] == "observation_only" + assert data["truth_matrix_allowed"] is False + assert data["summary"]["truth_upgrades"] == 0 + assert data["panchanga_ticket"]["ticket_id"] == "TEMCQ-061" + assert data["panchanga_ticket"]["closure_status"] == "open" + + +def test_field_status_batch_classifies_all_mapped_rows(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + rows = data["rows"] + + assert len(rows) == 18 + assert data["summary"]["rows_total"] == 18 + assert data["summary"]["endpoint_semantics"] == 10 + assert data["summary"]["worked_example_required"] == 8 + assert {row["status_after_bridge"] for row in rows} == { + "ready_for_endpoint_semantics_check", + "ready_for_worked_example_comparison", + } + assert all(row["claim_boundary"] == "status_classification_only_no_numeric_truth" for row in rows) + + +def test_field_status_batch_is_registered(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "three_engine_field_status_batch_2026_07_21" + ) + + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "observation_only" diff --git a/tests/test_whole_machine_jyotish_fragment_scan.py b/tests/test_whole_machine_jyotish_fragment_scan.py new file mode 100644 index 00000000..952fc46e --- /dev/null +++ b/tests/test_whole_machine_jyotish_fragment_scan.py @@ -0,0 +1,55 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +LEDGER = ROOT / "references/oracle/whole_machine_jyotish_fragment_scan_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_whole_machine_fragment_scan_records_external_and_privacy_buckets(): + data = json.loads(LEDGER.read_text(encoding="utf-8")) + summary = data["summary"] + + assert data["scope"] == "whole_machine_jyotish_fragment_scan_2026_07_21" + assert summary["files_with_signals"] >= 2000 + assert summary["external_signal_files"] >= 1500 + assert summary["privacy_blocked_files"] >= 900 + assert summary["reviewable_external_files"] >= 700 + + +def test_scan_keeps_old_workbuddy_out_of_truth_source(): + data = json.loads(LEDGER.read_text(encoding="utf-8")) + policy = data["decision_policy"] + + assert policy["workbuddy_fragment"] == "reference only; never truth source" + assert policy["privacy_review_required"] == "forbidden until human privacy review" + + workbuddy_rows = [ + row for row in data["reviewable_external_candidates"] + if row["category"] == "workbuddy_fragment" + ] + assert workbuddy_rows + assert all(row["decision"] == "reference_only_candidate_not_truth_source" for row in workbuddy_rows) + + +def test_scan_records_reference_layers_that_exist_but_are_not_fully_called(): + data = json.loads(LEDGER.read_text(encoding="utf-8")) + runtime = data["runtime_invocation_findings"] + + assert runtime["interpretation_source_runtime_coverage_status"] == "partial" + assert "references/open_source_sources/jyotishganit" in runtime["not_fully_closed_reference_layers"] + assert "references/open_source_sources/VedicAstro" in runtime["not_fully_closed_reference_layers"] + assert runtime["inventory_gate_status"] == "pass" + + +def test_fragment_scan_is_registered_in_evidence_index(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "whole_machine_jyotish_fragment_scan_2026_07_21" + ) + + assert entry["domain"] == "fragment_governance" + assert entry["claim_status"] == "open_queue" + assert "not truth source" in entry["claim_boundary"] From a9d756691a878b25275578bb932be8c331d3835f Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 14:00:26 +0800 Subject: [PATCH 06/25] docs: plan AI Jyotish consultation workbench alignment --- ...yotish-consultation-workbench-alignment.md | 311 ++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-21-ai-jyotish-consultation-workbench-alignment.md diff --git a/docs/superpowers/plans/2026-07-21-ai-jyotish-consultation-workbench-alignment.md b/docs/superpowers/plans/2026-07-21-ai-jyotish-consultation-workbench-alignment.md new file mode 100644 index 00000000..cceaf323 --- /dev/null +++ b/docs/superpowers/plans/2026-07-21-ai-jyotish-consultation-workbench-alignment.md @@ -0,0 +1,311 @@ +# AI Jyotish consultation workbench alignment + +Date: 2026-07-21 + +Commercial repo: `/Users/wuyongnaren/Documents/Jyotisha-commercial` + +Research truth source: `/Users/wuyongnaren/Documents/印度占星` + +Current commercial branch observed: `codex/fix-chat-session-deletion` + +## Product target + +Upgrade the commercial Jyotish website from a form/report/chat surface into an AI astrology consultation workbench: + +- guided topics when users do not know what to ask; +- strict workflow routing for career, marriage, wealth, timing, rectification and general readings; +- visible evidence chain, parameter freeze, confidence and claim boundary; +- follow-up prompts after every answer; +- privacy-safe commercial runtime without writing user birth data or private cases into public artifacts. + +This is not a UI-only optimization. UI work is downstream of runtime identity, evidence gates and workflow routing. + +## Current architecture observed + +### Frontend + +- Main page: `frontend/src/app/page.tsx` +- Global styles: `frontend/src/app/globals.css` +- Sidebar/session UI: + - `frontend/src/components/app-sidebar.tsx` + - `frontend/src/components/sidebar-session-row.tsx` +- Message rendering: + - `frontend/src/components/chat-message-row.tsx` + - `frontend/src/components/chat-message-content.tsx` +- Birth-time journey UI: + - `frontend/src/components/birth-time-intake.tsx` + - `frontend/src/components/birth-time-guide-turn.tsx` + - `frontend/src/components/birth-time-choice-question.tsx` + - `frontend/src/components/birth-time-candidate-result.tsx` + - `frontend/src/components/birth-time-evidence-draft-card.tsx` + +### API routes + +- Chat/consultation: `frontend/src/app/api/consult/route.ts` +- Health check: `frontend/src/app/api/health/route.ts` +- Daily star language: `frontend/src/app/api/daily-starlanguage/route.ts` +- Chart profiles: `frontend/src/app/api/chart-profiles/route.ts` +- Birth-time guide: `frontend/src/app/api/birth-time-guide/route.ts` +- Birth-time journey: `frontend/src/app/api/birth-time-journey/route.ts` +- Birth rectification: `frontend/src/app/api/birth-rectification/route.ts` + +### Workflow and safety layer + +- Workflow projection: `frontend/src/lib/consultation-workflow-request.ts` +- Entrypoint question resolver: `frontend/src/lib/consultation-entrypoint.ts` +- Safety guard: `frontend/src/lib/consult-safety.ts` +- Timing boundary guard: `frontend/src/lib/timing-output-guard.ts` +- Agent reply and streaming: + - `frontend/src/lib/agent-reply.ts` + - `frontend/src/lib/stream-text-response.ts` + +### Existing tests already relevant + +- `frontend/tests/consultation-workflow-contract.test.ts` +- `frontend/tests/consultation-workflow-request.test.ts` +- `frontend/tests/starter-questions.test.ts` +- `frontend/tests/chat-stream-layout.test.ts` +- `frontend/tests/birth-time-mobile-scroll-contract.test.ts` +- `frontend/tests/birth-time-journey-*.test.ts` +- `frontend/tests/timing-output-guard.test.ts` +- `frontend/tests/jyotish-api-reachability-contract.test.ts` + +## Gap summary + +| Layer | Current state | Gap | +|---|---|---| +| Truth source identity | `/api/health` reports web/env/model/Jyotish API status and git commit | Does not expose research truth source path, research commit, evidence packet count, oracle summary or claim gate status | +| Guided topics | Starter questions and private entrypoints exist | Topics are not yet generated from profile completeness + question intent + strict workflow evidence requirements | +| Strict workflow routing | `consultation-workflow-request.ts` maps broad themes | Timing currently maps to career; missing explicit route taxonomy for rectification, health, annual/monthly timing, Prashna, compatibility | +| Evidence display | Birth-time evidence components exist | No general consultation evidence panel/audit table for D1/D9/D10/Dasha/Narayana/Transit/Shadbala/AV/Jaimini/UL/DK/A10 | +| Parameter freeze | Some backend headers expose workflow status | UI does not consistently show Ayanamsa, node mode, timezone, coordinates, birth-time precision and calculation source | +| Claim boundary | Timing guard exists | Need runtime gate that prevents blocked/partial/oracle-missing claims from rendering as definitive predictions | +| Layout maturity | Chat shell exists | Composer can visually drift/overlay content; workbench should reserve bottom space and use stable scroll container | +| Privacy | Commercial repo has Supabase/business flow | Need explicit artifact filter and CI guard to prevent private birth data/events from entering public fixtures | + +## P0 implementation plan + +### P0-1 — Runtime identity and capability status + +Goal: the site must show what capability source it is using. + +Files: + +- Add `frontend/src/lib/truth-source-runtime-identity.ts` +- Add or extend `frontend/src/app/api/health/route.ts` +- Add `frontend/tests/truth-source-runtime-identity.test.ts` +- Add `frontend/tests/health-deployment.test.ts` assertions + +Contract: + +```json +{ + "truthSource": { + "path": "/Users/wuyongnaren/Documents/印度占星", + "commit": "...", + "skillVersion": "...", + "evidencePacketCount": 120, + "oracleSummary": { + "ready": [], + "partial": [], + "blocked": [] + }, + "claimGateStatus": "partial_or_blocked_present" + } +} +``` + +Rules: + +- Do not read private user artifacts. +- Do not require the production container to mount the local research repo; if absent, report `not_mounted`, not `ok`. +- Do not claim synced when git/packet metadata cannot be read. + +### P0-2 — Workbench shell layout fix + +Goal: stop the composer from floating over forms/content and make the page feel like a stable consultation tool. + +Files: + +- `frontend/src/app/page.tsx` +- `frontend/src/app/globals.css` +- `frontend/tests/chat-stream-layout.test.ts` +- `frontend/tests/birth-time-mobile-scroll-contract.test.ts` + +Contract: + +- One scroll container for conversation/workbench content. +- Composer pinned inside the main column, not viewport-floating across sidebar. +- Content bottom padding equals composer height. +- Onboarding/profile forms cannot appear underneath the composer. + +### P0-3 — Guided topic cards + +Goal: when user lacks a question, show 3-5 useful consultation cards with evidence preview. + +Files: + +- Add `frontend/src/lib/guided-jyotish-topics.ts` +- Extend `frontend/src/lib/consultation-entrypoint.ts` +- Extend `frontend/src/app/page.tsx` +- Extend `frontend/tests/starter-questions.test.ts` + +Initial topics: + +- Career phase and next leverage point +- Relationship pattern and partnership timing boundary +- Wealth structure and risk point +- Current year/month broad timing window +- Birth-time confidence check + +Each topic must carry: + +- `theme` +- `visibleQuestion` +- `strictWorkflowRoute` +- `evidencePreview` +- `confidenceCap` +- `claimBoundary` + +### P0-4 — Strict workflow route contract + +Goal: every user question must route to an explicit Jyotish workflow before the agent speaks. + +Files: + +- Extend `frontend/src/lib/consultation-workflow-request.ts` +- Extend `frontend/src/app/api/consult/route.ts` +- Extend `frontend/tests/consultation-workflow-contract.test.ts` +- Extend `frontend/tests/consultation-workflow-request.test.ts` + +Routes: + +- `career`: D1, D10, 10th house/lord, A10, AmK, Vimshottari, Narayana, transit +- `marriage`: D1, D9, 7th house/lord, Venus/Jupiter, DK, UL, A7, Vimshottari, Narayana, transit +- `wealth`: D1, D2, D11, 2nd/11th/9th/5th, wealth yogas, AV, Dasha +- `timing`: Dasha + Narayana + transit + varga; day/month remains candidate unless holdout passes +- `rectification`: D1 boundary, D9/D10/D12/D60 sensitivity, event backtest, candidate not truth +- `prashna`: question time/place/timezone/ayanamsa/node mode; observation only until oracle packets close +- `general`: broad multi-domain reading with clear missing layers + +### P0-5 — Claim gate display + +Goal: users can still receive dates/windows, but the UI must not package exploratory candidates as verified prediction. + +Files: + +- `frontend/src/lib/timing-output-guard.ts` +- `frontend/src/components/chat-message-content.tsx` +- Add `frontend/src/components/claim-boundary-badge.tsx` +- Tests: + - `frontend/tests/timing-output-guard.test.ts` + - `frontend/tests/consultation-workflow-contract.test.ts` + +Statuses: + +- `verified_window` +- `candidate_day_window` +- `exploratory_unvalidated` +- `observation_only` +- `blocked_until_oracle` +- `blocked_until_human_labels` + +## P1 implementation plan + +### P1-1 — Evidence panel / Technique Audit Table + +Files: + +- Add `frontend/src/components/evidence-audit-panel.tsx` +- Extend `frontend/src/components/chat-message-content.tsx` +- Add `frontend/tests/evidence-audit-panel.test.ts` + +Rows: + +- D1 +- D9 / D10 / D2 / D11 as applicable +- Vimshottari Dasha +- Narayana Dasha +- Transit / Gochara +- Shadbala +- Ashtakavarga +- Jaimini: DK, AmK, UL, A7, A10 +- Functional Benefic/Malefic +- MEVG / Global Web Evidence +- Real Case Calibration + +### P1-2 — Report export + +Files: + +- Add `frontend/src/lib/consultation-report-export.ts` +- Add export button in message/report view +- Add `frontend/tests/consultation-report-export.test.ts` + +Export must include: + +- frozen parameters; +- conclusion; +- evidence table; +- conflict points; +- claim boundary; +- follow-up questions. + +### P1-3 — Privacy artifact filter + +Files: + +- Add `scripts/commercial_privacy_artifact_scan.py` +- Add `tests/test_commercial_privacy_artifact_scan.py` +- Wire into commercial CI. + +Rules: + +- Reject real user names, exact birth data, private location/event text in fixtures. +- Allow public-person examples only with source tag. +- Mark imported research oracle packets as non-user artifacts. + +## P2 implementation plan + +### P2-1 — Visual polish after P0/P1 + +Files: + +- `frontend/src/app/page.tsx` +- `frontend/src/app/globals.css` +- `frontend/src/components/app-sidebar.tsx` + +Direction: + +- Keep original warm color palette. +- Use commercial site layout language, but make the homepage cards smaller, lower, and easier to click. +- Do not put birth-time rectification as an oversized floating block. + +### P2-2 — Commercial sync discipline + +Files: + +- Add `docs/research_sync_contract.md` +- Add `frontend/tests/research-truth-source-contract.test.ts` + +Rules: + +- Commercial repo consumes mature research contracts. +- Commercial repo does not copy private WorkBuddy fragments. +- WorkBuddy backups remain `historical_fragment_only`. + +## First landing sequence + +1. Implement P0-1 runtime identity endpoint and test. +2. Implement P0-2 layout contract to fix composer drift. +3. Implement P0-3 guided topics as deterministic cards. +4. Implement P0-4 strict workflow route taxonomy. +5. Implement P0-5 claim boundary badge. +6. Run focused frontend tests. +7. Only then start P1 evidence panel. + +## Current blocker / caution + +`/private/tmp/jyotisha-optimize` from the pasted request does not exist on this machine. The active commercial candidate is `/Users/wuyongnaren/Documents/Jyotisha-commercial`. + +The commercial worktree currently has untracked `hip_main.dat` and `hip_main.dat.download`. They look like local ephemeris/runtime assets and should not be committed unless explicitly reviewed. From c60b3af7c343c99f41c02c6dc52e8471b80824eb Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 14:00:40 +0800 Subject: [PATCH 07/25] sync: import panchanga closure packets --- ...ocal_jyotishganit_comparison_2026_07_21.md | 29 ++++++ ...anga_temcq_061_schema_packet_2026_07_21.md | 26 ++++++ ..._jyotishganit_bridge_applied_2026_07_21.md | 27 ++++++ .../evidence_packet_index_2026_07_19.json | 20 ++++- ...al_jyotishganit_comparison_2026_07_21.json | 88 +++++++++++++++++++ ...ga_temcq_061_schema_packet_2026_07_21.json | 53 +++++++++++ ...ga_local_jyotishganit_comparison_packet.py | 66 ++++++++++++++ .../test_panchanga_temcq_061_schema_packet.py | 61 +++++++++++++ 8 files changed, 368 insertions(+), 2 deletions(-) create mode 100644 docs/research/panchanga_local_jyotishganit_comparison_2026_07_21.md create mode 100644 docs/research/panchanga_temcq_061_schema_packet_2026_07_21.md create mode 100644 docs/research/three_engine_jyotishganit_bridge_applied_2026_07_21.md create mode 100644 references/oracle/panchanga_local_jyotishganit_comparison_2026_07_21.json create mode 100644 references/oracle/panchanga_temcq_061_schema_packet_2026_07_21.json create mode 100644 tests/test_panchanga_local_jyotishganit_comparison_packet.py create mode 100644 tests/test_panchanga_temcq_061_schema_packet.py diff --git a/docs/research/panchanga_local_jyotishganit_comparison_2026_07_21.md b/docs/research/panchanga_local_jyotishganit_comparison_2026_07_21.md new file mode 100644 index 00000000..65b4f1c7 --- /dev/null +++ b/docs/research/panchanga_local_jyotishganit_comparison_2026_07_21.md @@ -0,0 +1,29 @@ +# Panchanga local ↔ jyotishganit comparison — TEMCQ-061 + +Date: 2026-07-21 + +This packet closes the first local comparison step for the Panchanga schema ticket without writing a new Panchanga algorithm. + +## Result + +- Local method: `scripts/muhurta.py::calc_panchanga` +- Case: Steve Jobs +- Compared fields: `vaara`, `tithi`, `nakshatra`, `yoga`, `karana` +- Result: 4 exact matches + 1 naming alias +- Truth upgrade: 0 + +## Field comparison + +| Field | Local | jyotishganit | Status | +|---|---|---|---| +| vaara | Thursday | Thursday | within_tolerance | +| tithi | Shukla Tritiya | Shukla Tritiya | within_tolerance | +| nakshatra | Uttara Bhadrapada | Uttara Bhadrapada | within_tolerance | +| yoga | Shubha | Shubha | within_tolerance | +| karana | Garija | Gara | alias_match | + +`Gara` and `Garija` are recorded as a naming alias, not a formula mismatch. + +## Boundary + +This is still `research_observation_only`. It does not upgrade Panchanga to global truth because VedAstro and PyJHora/JHora five-field normalized packets are not pinned for this same comparison, and sunrise-relative semantics still need multi-case closure. diff --git a/docs/research/panchanga_temcq_061_schema_packet_2026_07_21.md b/docs/research/panchanga_temcq_061_schema_packet_2026_07_21.md new file mode 100644 index 00000000..9d30054c --- /dev/null +++ b/docs/research/panchanga_temcq_061_schema_packet_2026_07_21.md @@ -0,0 +1,26 @@ +# Panchanga TEMCQ-061 schema packet — 2026-07-21 + +## Result + +- Status: `schema_mapping_required` +- Normalized fields ready: `0` +- Truth upgrades: `0` + +## Observed jyotishganit raw + +- Vaara: Thursday +- Tithi: Shukla Tritiya +- Nakshatra: Uttara Bhadrapada +- Yoga: Shubha +- Karana: Gara + +## Blockers + +- Local archived high-rigor raw lacks a normalized Panchanga field. +- PyJHora/JHora normalized Panchanga raw is not archived. +- VedAstro shared Panchanga endpoint/method is not pinned. +- Naming and calculation conventions must be fixed before field comparison. + +## Boundary + +This is schema mapping only. It does not prove Panchanga parity or production timing readiness. diff --git a/docs/research/three_engine_jyotishganit_bridge_applied_2026_07_21.md b/docs/research/three_engine_jyotishganit_bridge_applied_2026_07_21.md new file mode 100644 index 00000000..624f261d --- /dev/null +++ b/docs/research/three_engine_jyotishganit_bridge_applied_2026_07_21.md @@ -0,0 +1,27 @@ +# Three-engine jyotishganit bridge applied — 2026-07-21 + +## Status + +- Claim status: `observation_only` +- Truth upgrades: `0` +- Source selected hash: `4709b8ade84efdea4d0a67c15f3e32cea516a5aa2e8abe3885578feda20cb3f4` + +## Applied rows + +- Existing TEMCQ tickets mapped: `18` +- No existing ticket: `1` + +Mapped groups: + +- D2/D4/D9/D10 sign rows → existing `endpoint_or_varga_semantics` tickets. +- BAV/SAV rows → existing `ashtakavarga_table_or_contributor_variant` tickets. +- Panchanga → no old mismatch ticket; create a separate closure row before comparison. + +## Boundary + +This packet only proves that jyotishganit selected raw can be routed into the +existing closure queue. It does not prove formula truth, timing readiness, or +commercial production tuning. + +Shadbala remains an explicit gap because the current jyotishganit probe does not +expose `shadbala` or `strengths` in selected raw. diff --git a/references/oracle/evidence_packet_index_2026_07_19.json b/references/oracle/evidence_packet_index_2026_07_19.json index e46ebf8f..41011109 100644 --- a/references/oracle/evidence_packet_index_2026_07_19.json +++ b/references/oracle/evidence_packet_index_2026_07_19.json @@ -5,8 +5,8 @@ "production_tuning_allowed": false, "boundary": "Index of current governance packets only. Raw oracle artifacts remain in references/oracle/artifacts and are not all duplicated here.", "summary": { - "packet_count": 118, - "blocked_or_partial_count": 51, + "packet_count": 120, + "blocked_or_partial_count": 52, "human_review_required_count": 3 }, "packets": [ @@ -953,6 +953,22 @@ "claim_status": "partial", "consumer_policy": "research_observation_only", "claim_boundary": "Classifies BAV/SAV rows into partial consensus or multi-engine variant; public worked table and method metadata still required." + }, + { + "packet_id": "panchanga_temcq_061_schema_packet_2026_07_21", + "path": "references/oracle/panchanga_temcq_061_schema_packet_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "open_queue", + "consumer_policy": "research_observation_only", + "claim_boundary": "Creates TEMCQ-061 Panchanga schema mapping queue from jyotishganit raw; local/PyJHora/VedAstro normalized fields remain missing or unpinned." + }, + { + "packet_id": "panchanga_local_jyotishganit_comparison_2026_07_21", + "path": "references/oracle/panchanga_local_jyotishganit_comparison_2026_07_21.json", + "domain": "three_engine_parity", + "claim_status": "partial", + "consumer_policy": "research_observation_only", + "claim_boundary": "Compares local calc_panchanga against jyotishganit for five Panchanga labels; one Karana alias remains naming-only and no global truth upgrade is made." } ] } diff --git a/references/oracle/panchanga_local_jyotishganit_comparison_2026_07_21.json b/references/oracle/panchanga_local_jyotishganit_comparison_2026_07_21.json new file mode 100644 index 00000000..c6c1e5df --- /dev/null +++ b/references/oracle/panchanga_local_jyotishganit_comparison_2026_07_21.json @@ -0,0 +1,88 @@ +{ + "scope": "panchanga_local_jyotishganit_comparison", + "ticket_id": "TEMCQ-061", + "created_at": "2026-07-21", + "claim_status": "partial", + "truth_matrix_allowed": false, + "production_tuning_allowed": false, + "algorithm_reuse_policy": "reuse_existing_local_muhurta_calc_panchanga_no_new_algorithm", + "source_local_method": "scripts/muhurta.py::calc_panchanga", + "source_local_input": { + "case": "Steve Jobs", + "source_artifact": "references/oracle/artifacts/local_steve_jobs_high_rigor_raw.json", + "sun_lon_reconstructed_from_sign_degree": 312.5175, + "moon_lon_reconstructed_from_sign_degree": 344.5165, + "weekday_convention": "0=Sun ... 6=Sat", + "weekday_used": 4, + "hour_from_sunrise_used": 6.0 + }, + "local_normalized_raw": { + "vaara": "Thursday", + "tithi": "Shukla Tritiya", + "nakshatra": "Uttara Bhadrapada", + "yoga": "Shubha", + "karana": "Garija" + }, + "jyotishganit_observed_raw": { + "@type": "Panchanga", + "karana": "Gara", + "nakshatra": "Uttara Bhadrapada", + "tithi": "Shukla Tritiya", + "vaara": "Thursday", + "yoga": "Shubha" + }, + "field_comparison": [ + { + "field": "vaara", + "local_value": "Thursday", + "jyotishganit_value": "Thursday", + "status": "within_tolerance", + "reason": "same weekday name" + }, + { + "field": "tithi", + "local_value": "Shukla Tritiya", + "jyotishganit_value": "Shukla Tritiya", + "status": "within_tolerance", + "reason": "same paksha+tithi label" + }, + { + "field": "nakshatra", + "local_value": "Uttara Bhadrapada", + "jyotishganit_value": "Uttara Bhadrapada", + "status": "within_tolerance", + "reason": "same nakshatra label" + }, + { + "field": "yoga", + "local_value": "Shubha", + "jyotishganit_value": "Shubha", + "status": "within_tolerance", + "reason": "same yoga label" + }, + { + "field": "karana", + "local_value": "Garija", + "jyotishganit_value": "Gara", + "status": "alias_match", + "alias_rule": "Gara == Garija", + "reason": "naming alias only; not treated as formula mismatch" + } + ], + "summary": { + "fields_total": 5, + "within_tolerance": 4, + "alias_match": 1, + "formula_mismatch": 0, + "unit_mismatch": 0, + "truth_upgrades": 0 + }, + "still_blocked": [ + "VedAstro shared Panchanga endpoint/version not pinned", + "PyJHora/JHora Panchanga normalized packet not archived for this five-field comparison", + "single-case agreement cannot establish global Panchanga oracle truth", + "sunrise-relative semantics and local civil-time convention still require explicit multi-case contract" + ], + "supersedes_schema_gap": "references/oracle/panchanga_temcq_061_schema_packet_2026_07_21.json", + "boundary": "local_jyotishganit_panchanga_field_observation_not_global_truth" +} diff --git a/references/oracle/panchanga_temcq_061_schema_packet_2026_07_21.json b/references/oracle/panchanga_temcq_061_schema_packet_2026_07_21.json new file mode 100644 index 00000000..1a34dc7e --- /dev/null +++ b/references/oracle/panchanga_temcq_061_schema_packet_2026_07_21.json @@ -0,0 +1,53 @@ +{ + "scope": "panchanga_temcq_061_schema_packet", + "created_at": "2026-07-21", + "ticket_id": "TEMCQ-061", + "claim_status": "open_queue", + "production_tuning_allowed": false, + "truth_matrix_allowed": false, + "source_ticket_placeholder": "references/oracle/three_engine_field_status_batch_2026_07_21.json", + "source_jyotishganit_probe": "references/oracle/jyotishganit_field_probe_steve_jobs_2026_07_19.json", + "closure_status": "schema_mapping_required", + "summary": { + "normalized_fields_ready": 0, + "truth_upgrades": 0, + "observed_jyotishganit_field_count": 6 + }, + "jyotishganit_observed_raw": { + "@type": "Panchanga", + "karana": "Gara", + "nakshatra": "Uttara Bhadrapada", + "tithi": "Shukla Tritiya", + "vaara": "Thursday", + "yoga": "Shubha" + }, + "engine_field_status": { + "local": "archived_panchanga_field_missing", + "jyotishganit": "raw_available_not_normalized", + "VedAstro": "shared_panchanga_endpoint_not_pinned", + "PyJHora_JHora": "not_archived_as_normalized_panchanga" + }, + "required_schema_fields": [ + "vaara", + "tithi", + "nakshatra", + "yoga", + "karana" + ], + "required_contract": [ + "weekday convention", + "tithi naming and paksha convention", + "nakshatra spelling/diacritic alias table", + "yoga calculation convention", + "karana naming convention", + "sunrise-relative vs birth-moment rule" + ], + "next_actions": [ + "archive local normalized Panchanga for the same public case", + "locate or generate legal PyJHora/JHora normalized Panchanga raw", + "pin VedAstro Panchanga endpoint or mark not available", + "then compare exact named fields after alias normalization" + ], + "boundary": "panchanga_schema_mapping_only_no_numeric_truth", + "human_report": "docs/research/panchanga_temcq_061_schema_packet_2026_07_21.md" +} diff --git a/tests/test_panchanga_local_jyotishganit_comparison_packet.py b/tests/test_panchanga_local_jyotishganit_comparison_packet.py new file mode 100644 index 00000000..6b3f3d3b --- /dev/null +++ b/tests/test_panchanga_local_jyotishganit_comparison_packet.py @@ -0,0 +1,66 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/panchanga_local_jyotishganit_comparison_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_panchanga_local_jyotishganit_packet_scope_and_boundary(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + assert data["scope"] == "panchanga_local_jyotishganit_comparison" + assert data["ticket_id"] == "TEMCQ-061" + assert data["claim_status"] == "partial" + assert data["truth_matrix_allowed"] is False + assert data["production_tuning_allowed"] is False + assert data["algorithm_reuse_policy"] == "reuse_existing_local_muhurta_calc_panchanga_no_new_algorithm" + assert data["boundary"] == "local_jyotishganit_panchanga_field_observation_not_global_truth" + + +def test_panchanga_local_values_reuse_existing_muhurta_engine(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + assert data["source_local_method"] == "scripts/muhurta.py::calc_panchanga" + assert data["local_normalized_raw"] == { + "vaara": "Thursday", + "tithi": "Shukla Tritiya", + "nakshatra": "Uttara Bhadrapada", + "yoga": "Shubha", + "karana": "Garija", + } + assert data["jyotishganit_observed_raw"] == { + "@type": "Panchanga", + "karana": "Gara", + "nakshatra": "Uttara Bhadrapada", + "tithi": "Shukla Tritiya", + "vaara": "Thursday", + "yoga": "Shubha", + } + + +def test_panchanga_field_comparison_distinguishes_alias_from_truth(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + rows = {row["field"]: row for row in data["field_comparison"]} + assert set(rows) == {"vaara", "tithi", "nakshatra", "yoga", "karana"} + assert [row["status"] for row in rows.values()].count("within_tolerance") == 4 + assert rows["karana"]["status"] == "alias_match" + assert rows["karana"]["alias_rule"] == "Gara == Garija" + assert data["summary"] == { + "fields_total": 5, + "within_tolerance": 4, + "alias_match": 1, + "formula_mismatch": 0, + "unit_mismatch": 0, + "truth_upgrades": 0, + } + + +def test_panchanga_local_comparison_registered_in_evidence_index(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "panchanga_local_jyotishganit_comparison_2026_07_21" + ) + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "partial" + assert entry["consumer_policy"] == "research_observation_only" diff --git a/tests/test_panchanga_temcq_061_schema_packet.py b/tests/test_panchanga_temcq_061_schema_packet.py new file mode 100644 index 00000000..bf59fa59 --- /dev/null +++ b/tests/test_panchanga_temcq_061_schema_packet.py @@ -0,0 +1,61 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PACKET = ROOT / "references/oracle/panchanga_temcq_061_schema_packet_2026_07_21.json" +INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json" + + +def test_panchanga_temcq_061_records_not_comparable_status(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["scope"] == "panchanga_temcq_061_schema_packet" + assert data["ticket_id"] == "TEMCQ-061" + assert data["claim_status"] == "open_queue" + assert data["truth_matrix_allowed"] is False + assert data["closure_status"] == "schema_mapping_required" + assert data["summary"]["normalized_fields_ready"] == 0 + assert data["summary"]["truth_upgrades"] == 0 + + +def test_panchanga_temcq_061_preserves_jyotishganit_raw_fields(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["jyotishganit_observed_raw"] == { + "@type": "Panchanga", + "karana": "Gara", + "nakshatra": "Uttara Bhadrapada", + "tithi": "Shukla Tritiya", + "vaara": "Thursday", + "yoga": "Shubha", + } + assert data["engine_field_status"]["local"] == "archived_panchanga_field_missing" + assert data["engine_field_status"]["VedAstro"] == "shared_panchanga_endpoint_not_pinned" + assert data["engine_field_status"]["PyJHora_JHora"] == "not_archived_as_normalized_panchanga" + + +def test_panchanga_temcq_061_requires_exact_schema_before_comparison(): + data = json.loads(PACKET.read_text(encoding="utf-8")) + + assert data["required_schema_fields"] == ["vaara", "tithi", "nakshatra", "yoga", "karana"] + assert data["required_contract"] == [ + "weekday convention", + "tithi naming and paksha convention", + "nakshatra spelling/diacritic alias table", + "yoga calculation convention", + "karana naming convention", + "sunrise-relative vs birth-moment rule" + ] + assert data["boundary"] == "panchanga_schema_mapping_only_no_numeric_truth" + + +def test_panchanga_temcq_061_is_registered(): + index = json.loads(INDEX.read_text(encoding="utf-8")) + entry = next( + row for row in index["packets"] + if row["packet_id"] == "panchanga_temcq_061_schema_packet_2026_07_21" + ) + + assert entry["domain"] == "three_engine_parity" + assert entry["claim_status"] == "open_queue" From a5a69a82b0ca454c8cff7de91eae52e202a26786 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 14:30:44 +0800 Subject: [PATCH 08/25] feat: expose research truth source health identity --- frontend/src/app/api/health/route.ts | 8 ++ .../src/lib/truth-source-runtime-identity.ts | 82 +++++++++++++++++++ .../truth-source-runtime-identity.test.ts | 28 +++++++ 3 files changed, 118 insertions(+) create mode 100644 frontend/src/lib/truth-source-runtime-identity.ts create mode 100644 frontend/tests/truth-source-runtime-identity.test.ts diff --git a/frontend/src/app/api/health/route.ts b/frontend/src/app/api/health/route.ts index e8cab627..fa8da833 100644 --- a/frontend/src/app/api/health/route.ts +++ b/frontend/src/app/api/health/route.ts @@ -1,5 +1,7 @@ import { NextResponse } from "next/server"; +import { getTruthSourceRuntimeIdentity } from "@/lib/truth-source-runtime-identity"; + type Check = { status: "ok" | "degraded" | "blocked"; message?: string; @@ -58,12 +60,17 @@ function aggregate(checks: Record) { } export async function GET() { + const truthSourceIdentity = getTruthSourceRuntimeIdentity(); const checks = { web: { status: "ok" } satisfies Check, supabasePublicConfig: envCheck(["NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY"]), supabaseServiceRole: envCheck(["SUPABASE_SERVICE_ROLE_KEY"]), modelProvider: anyEnvCheck(["LLM_MODELS_JSON", "OPENAI_API_KEY", "LLM_API_KEY", "DEEPSEEK_API_KEY"]), jyotishApi: await jyotishApiCheck(), + researchTruthSource: { + status: truthSourceIdentity.status, + message: truthSourceIdentity.mountStatus === "mounted" ? undefined : truthSourceIdentity.mountStatus, + } satisfies Check, }; const status = aggregate(checks); return NextResponse.json( @@ -73,6 +80,7 @@ export async function GET() { deployment: { gitCommit, }, + truthSource: truthSourceIdentity, checks, }, { status: status === "ok" ? 200 : 503 }, diff --git a/frontend/src/lib/truth-source-runtime-identity.ts b/frontend/src/lib/truth-source-runtime-identity.ts new file mode 100644 index 00000000..3297b130 --- /dev/null +++ b/frontend/src/lib/truth-source-runtime-identity.ts @@ -0,0 +1,82 @@ +import { existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; + +type OracleSummary = { + ready: string[]; + partial: string[]; + blocked: string[]; +}; + +export type TruthSourceRuntimeIdentity = { + status: "ok" | "blocked"; + path: string; + mountStatus: "mounted" | "not_mounted"; + commit: string; + skillVersion: string; + evidencePacketCount: number | null; + oracleSummary: OracleSummary; + claimGateStatus: "ready" | "partial_or_blocked_present" | "not_mounted"; +}; + +export const DEFAULT_RESEARCH_TRUTH_SOURCE_PATH = "/Users/wuyongnaren/Documents/印度占星"; + +function readJson(path: string): unknown { + return JSON.parse(readFileSync(path, "utf8")); +} + +function readEvidencePacketCount(researchPath: string): number | null { + const indexPath = join(researchPath, "references/oracle/evidence_packet_index_2026_07_19.json"); + if (!existsSync(indexPath)) return null; + const data = readJson(indexPath) as { summary?: { packet_count?: unknown } }; + return typeof data.summary?.packet_count === "number" ? data.summary.packet_count : null; +} + +function readSkillVersion(researchPath: string): string { + const skillPath = join(researchPath, "SKILL.md"); + if (!existsSync(skillPath)) return "unknown"; + const source = readFileSync(skillPath, "utf8"); + const version = source.match(/(?:version|Version)[::]\s*([^\n]+)/); + return version?.[1]?.trim() || "present_unversioned"; +} + +function readCommit(researchPath: string): string { + const headPath = join(researchPath, ".git/HEAD"); + if (!existsSync(headPath)) return "unknown"; + const head = readFileSync(headPath, "utf8").trim(); + if (!head.startsWith("ref: ")) return head.slice(0, 40); + const refPath = join(researchPath, ".git", head.slice(5)); + if (!existsSync(refPath)) return "unknown"; + return readFileSync(refPath, "utf8").trim().slice(0, 40); +} + +export function getTruthSourceRuntimeIdentity( + researchPath = process.env.JYOTISH_RESEARCH_TRUTH_SOURCE_PATH ?? DEFAULT_RESEARCH_TRUTH_SOURCE_PATH, +): TruthSourceRuntimeIdentity { + if (!existsSync(researchPath)) { + return { + status: "blocked", + path: researchPath, + mountStatus: "not_mounted", + commit: "unknown", + skillVersion: "unknown", + evidencePacketCount: null, + oracleSummary: { ready: [], partial: [], blocked: ["research_truth_source_not_mounted"] }, + claimGateStatus: "not_mounted", + }; + } + + const evidencePacketCount = readEvidencePacketCount(researchPath); + const blocked = evidencePacketCount === null ? ["evidence_packet_index_missing"] : []; + const partial = ["commercial_runtime_identity_only"]; + + return { + status: blocked.length ? "blocked" : "ok", + path: researchPath, + mountStatus: "mounted", + commit: readCommit(researchPath), + skillVersion: readSkillVersion(researchPath), + evidencePacketCount, + oracleSummary: { ready: [], partial, blocked }, + claimGateStatus: blocked.length ? "partial_or_blocked_present" : "partial_or_blocked_present", + }; +} diff --git a/frontend/tests/truth-source-runtime-identity.test.ts b/frontend/tests/truth-source-runtime-identity.test.ts new file mode 100644 index 00000000..d02a8933 --- /dev/null +++ b/frontend/tests/truth-source-runtime-identity.test.ts @@ -0,0 +1,28 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test from "node:test"; + +const identitySource = readFileSync( + new URL("../src/lib/truth-source-runtime-identity.ts", import.meta.url), + "utf8", +); +const healthSource = readFileSync( + new URL("../src/app/api/health/route.ts", import.meta.url), + "utf8", +); + +test("truth source identity records research source without pretending local mount is always present", () => { + assert.match(identitySource, /DEFAULT_RESEARCH_TRUTH_SOURCE_PATH/); + assert.ok(identitySource.includes("/Users/wuyongnaren/Documents/印度占星")); + assert.match(identitySource, /not_mounted/); + assert.match(identitySource, /claimGateStatus/); + assert.match(identitySource, /evidencePacketCount/); + assert.match(identitySource, /oracleSummary/); +}); + +test("health endpoint exposes truth source identity beside deployment identity", () => { + assert.match(healthSource, /getTruthSourceRuntimeIdentity/); + assert.match(healthSource, /truthSource/); + assert.match(healthSource, /truthSource:\s*truthSourceIdentity/); + assert.match(healthSource, /researchTruthSource/); +}); From 2ce83ec7e3af35980c009b12ee903f0590162775 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 14:34:39 +0800 Subject: [PATCH 09/25] feat: expose truth source identity and dock composer --- frontend/src/app/globals.css | 5 +++-- frontend/tests/chat-stream-layout.test.ts | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index c07a0eab..dd4ea9c4 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -84,6 +84,7 @@ --space-12: 48px; --space-16: 64px; --space-24: 96px; + --composer-reserve: 148px; --ease-out: cubic-bezier(.22, 1, .36, 1); --sidebar-background: var(--color-sidebar); --sidebar-solid: var(--color-sidebar-solid); @@ -328,7 +329,7 @@ button:disabled { cursor: default; opacity: .45; } .status-loading { background: var(--color-action); } .credit-button { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 11px; cursor: pointer; font-size: 13px; font-variant-numeric: tabular-nums; transition: background-color 120ms ease-out, transform 120ms ease-out; min-width: 64px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas-soft); color: var(--color-ink-secondary); font-weight: 500; } -.conversation { min-width: 0; min-height: 0; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; background: var(--color-canvas); } +.conversation { min-width: 0; min-height: 0; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; padding-bottom: var(--composer-reserve); background: var(--color-canvas); } .conversation.is-empty { display: grid; place-items: center; padding: var(--space-8); } .welcome { width: min(820px, 100%); padding: var(--space-10) 0 var(--space-16); } .welcome > .onboarding-message:first-child { padding-bottom: var(--space-8); } @@ -492,7 +493,7 @@ button:disabled { cursor: default; opacity: .45; } .thinking i { width: 5px; height: 5px; border-radius: 50%; animation: pulse 850ms ease-in-out infinite alternate; background: var(--color-action); } .error-message { margin: 12px 0 0; padding: 12px 14px; border-left: 3px solid var(--color-danger); background: var(--color-danger-muted); color: var(--color-danger); font-size: 13px; line-height: 1.6; border-color: var(--color-danger); border-radius: 0 var(--radius-md) var(--radius-md) 0; } -.composer-wrap { z-index: 2; min-width: 0; border-top: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); padding: var(--space-3) var(--space-6) var(--space-4); background: var(--color-frosted); backdrop-filter: saturate(130%) blur(20px); } +.composer-wrap { position: sticky; bottom: 0; z-index: 2; min-width: 0; border-top: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); padding: var(--space-3) var(--space-6) var(--space-4); background: var(--color-frosted); backdrop-filter: saturate(130%) blur(20px); } .composer-suggestions { width: min(680px, 100%); margin: 0 auto 9px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity; scrollbar-width: none; touch-action: pan-x; -webkit-overflow-scrolling: touch; display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: var(--space-2); margin-bottom: var(--space-3); overflow: visible; } .composer-suggestions button { flex: 0 0 clamp(176px, 56vw, 230px); overflow: hidden; border: 1px solid var(--color-border); color: var(--color-ink-secondary); cursor: pointer; scroll-snap-align: start; text-overflow: ellipsis; white-space: nowrap; transition: border-color 120ms ease-out, color 120ms ease-out, transform 120ms ease-out; width: 100%; min-width: 0; min-height: 44px; padding: 0 var(--space-2); border-color: var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas-soft); font-size: var(--type-caption); } .composer { width: min(760px, 100%); display: flex; align-items: flex-end; gap: 10px; margin: 0 auto; border: 1px solid var(--color-border); transition: border-color 140ms ease-out; min-height: 60px; padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4); border-color: var(--color-border-strong); border-radius: var(--radius-lg); background: var(--color-canvas); box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-border) 42%, transparent); } diff --git a/frontend/tests/chat-stream-layout.test.ts b/frontend/tests/chat-stream-layout.test.ts index 977a7ec5..00e8cc80 100644 --- a/frontend/tests/chat-stream-layout.test.ts +++ b/frontend/tests/chat-stream-layout.test.ts @@ -5,6 +5,7 @@ import test from "node:test"; import { chatMessageViews } from "../src/lib/chat-message-view.ts"; const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8"); +const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); const previousMessages = [ { role: "user", text: "问题" }, @@ -52,3 +53,12 @@ test("keeps the suggestion row height stable while an answer streams", () => { assert.doesNotMatch(suggestionBlock[0].split("
{ + assert.match(pageSource, /
/); + assert.match(globalStyles, /\.chat-panel[^}]*grid-template-rows:\s*68px minmax\(0,\s*1fr\) auto/); + assert.match(globalStyles, /\.conversation[^}]*padding-bottom:\s*var\(--composer-reserve\)/); + assert.match(globalStyles, /\.composer-wrap[^}]*position:\s*sticky/); + assert.match(globalStyles, /\.composer-wrap[^}]*bottom:\s*0/); + assert.doesNotMatch(globalStyles, /\.composer-wrap[^}]*position:\s*fixed/); +}); From 878ff96dc5e89c3ce7a57c68528e8729b8440e65 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 14:40:07 +0800 Subject: [PATCH 10/25] feat: add guided Jyotish topic evidence cards --- frontend/src/app/page.tsx | 9 ++-- frontend/src/lib/guided-jyotish-topics.ts | 50 +++++++++++++++++++++++ frontend/tests/starter-questions.test.ts | 15 +++++++ 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 frontend/src/lib/guided-jyotish-topics.ts diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index f1023f29..3867440e 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -41,6 +41,7 @@ import { isGuidedBirthTimePreview, previewRectificationJourney, } from "@/lib/birth-time-guided-preview"; +import { defaultGuidedJyotishTopics } from "@/lib/guided-jyotish-topics"; import { keepFocusWithin } from "@/lib/focus-trap"; import { chatMessageViews, type ChatMessage } from "@/lib/chat-message-view"; import { @@ -155,11 +156,7 @@ type PendingConsultation = { const undoWindowMs = 2_500; const china = chinaLocations.country; -const themes: Array<{ id: Exclude; label: string; prompt: string }> = [ - { id: "career", label: "事业", prompt: "未来一年,事业和收入该关注什么?" }, - { id: "marriage", label: "关系", prompt: "我的关系模式是什么?" }, - { id: "timing", label: "时运", prompt: "未来哪些阶段值得把握?" }, -]; +const themes = defaultGuidedJyotishTopics; const accountDialogTitles = { profile: "个人资料", @@ -2482,7 +2479,7 @@ export default function Home() { const theme = themes.find((candidate) => candidate.id === item.theme); return ( ); diff --git a/frontend/src/lib/guided-jyotish-topics.ts b/frontend/src/lib/guided-jyotish-topics.ts new file mode 100644 index 00000000..cde6ee58 --- /dev/null +++ b/frontend/src/lib/guided-jyotish-topics.ts @@ -0,0 +1,50 @@ +import type { ConsultationTheme } from "./consultation-workflow-request"; + +export type GuidedJyotishTopic = { + id: ConsultationTheme; + label: string; + prompt: string; + strictWorkflowRoute: string; + evidencePreview: string[]; + confidenceCap: "low" | "medium"; + claimBoundary: string; +}; + +export const defaultGuidedJyotishTopics: GuidedJyotishTopic[] = [ + { + id: "career", + label: "事业", + prompt: "未来一年,事业和收入该关注什么?", + strictWorkflowRoute: "career", + evidencePreview: ["D1", "D10", "A10", "Vimshottari", "Narayana", "Transit"], + confidenceCap: "medium", + claimBoundary: "输出职业结构与阶段判断;具体日/月只作为候选窗口。", + }, + { + id: "marriage", + label: "关系", + prompt: "我的关系模式是什么?", + strictWorkflowRoute: "marriage", + evidencePreview: ["D1", "D9", "7宫", "DK", "UL", "Vimshottari", "Narayana"], + confidenceCap: "medium", + claimBoundary: "输出关系模式与宽窗口;不承诺某日必然发生事件。", + }, + { + id: "wealth", + label: "财富", + prompt: "我的财富增长方式和风险点是什么?", + strictWorkflowRoute: "wealth", + evidencePreview: ["D1", "D2", "D11", "2/11宫", "财富 Yoga", "Ashtakavarga"], + confidenceCap: "medium", + claimBoundary: "输出财富结构和风险类型;不替代投资建议。", + }, + { + id: "timing", + label: "时运", + prompt: "未来哪些阶段值得把握?", + strictWorkflowRoute: "timing", + evidencePreview: ["Dasha", "Narayana", "Transit", "Varga"], + confidenceCap: "low", + claimBoundary: "精确月/日仍是探索性候选,未通过独立 holdout 前不升级。", + }, +]; diff --git a/frontend/tests/starter-questions.test.ts b/frontend/tests/starter-questions.test.ts index d317cd34..044d30bf 100644 --- a/frontend/tests/starter-questions.test.ts +++ b/frontend/tests/starter-questions.test.ts @@ -5,6 +5,7 @@ import test from "node:test"; const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8"); const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); const appSidebarSource = readFileSync(new URL("../src/components/app-sidebar.tsx", import.meta.url), "utf8"); +const guidedTopicsSource = readFileSync(new URL("../src/lib/guided-jyotish-topics.ts", import.meta.url), "utf8"); function sourceBetween(source: string, startMarker: string, endMarker: string) { const start = source.indexOf(startMarker); @@ -28,6 +29,20 @@ test("keeps starter questions visible while the user edits a draft", () => { assert.doesNotMatch(starterVisibilityGuard, /\bdraft\b/); }); +test("default starter questions are guided Jyotish topics with evidence and claim boundaries", () => { + assert.match(pageSource, /defaultGuidedJyotishTopics/); + assert.match(pageSource, /theme\.evidencePreview\.join/); + assert.match(pageSource, /theme\.claimBoundary/); + assert.match(guidedTopicsSource, /strictWorkflowRoute/); + assert.match(guidedTopicsSource, /evidencePreview/); + assert.match(guidedTopicsSource, /confidenceCap/); + assert.match(guidedTopicsSource, /claimBoundary/); + assert.match(guidedTopicsSource, /D10/); + assert.match(guidedTopicsSource, /D9/); + assert.match(guidedTopicsSource, /Ashtakavarga/); + assert.match(guidedTopicsSource, /独立 holdout/); +}); + test("keeps follow-up suggestions visible while the user edits a draft", () => { // Given: the follow-up suggestion block and its render guard. const suggestionGuard = sourceBetween( From 3427b30fd42e9746c65e4f0841791dd7f91d88db Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 15:04:07 +0800 Subject: [PATCH 11/25] feat: add strict workflow taxonomy and claim badges --- frontend/src/app/globals.css | 1 + frontend/src/components/chat-message-row.tsx | 3 +- .../src/components/claim-boundary-badge.tsx | 13 ++++ .../src/lib/consultation-workflow-request.ts | 69 ++++++++++++++++--- frontend/tests/claim-boundary-badge.test.ts | 16 +++++ .../consultation-workflow-contract.test.ts | 8 +++ .../consultation-workflow-request.test.ts | 12 ++-- 7 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 frontend/src/components/claim-boundary-badge.tsx create mode 100644 frontend/tests/claim-boundary-badge.test.ts diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index dd4ea9c4..d153c602 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -148,6 +148,7 @@ button:disabled { cursor: default; opacity: .45; } .message-markdown { color: var(--color-ink); font-size: 17px; line-height: 1.65; } .message-markdown > *:first-child { margin-top: 0; } .message-markdown > *:last-child { margin-bottom: 0; } +.claim-boundary-badge { width: fit-content; margin: 0 0 var(--space-2); padding: 4px 9px; border: 1px solid color-mix(in srgb, var(--color-action) 24%, transparent); border-radius: 999px; background: var(--color-action-soft); color: var(--color-action-hover); font-size: 12px; line-height: 1.45; } .message-markdown p { margin: 0 0 14px; } .message-markdown ul, .message-markdown ol { margin: 10px 0 16px; padding-left: 24px; } .message-markdown li { margin: 6px 0; } diff --git a/frontend/src/components/chat-message-row.tsx b/frontend/src/components/chat-message-row.tsx index 5abd93c3..2791a612 100644 --- a/frontend/src/components/chat-message-row.tsx +++ b/frontend/src/components/chat-message-row.tsx @@ -1,4 +1,5 @@ import { ChatMessageContent } from "@/components/chat-message-content"; +import { ClaimBoundaryBadge } from "@/components/claim-boundary-badge"; import type { ChatMessageView } from "@/lib/chat-message-view"; export function AgentAvatar() { @@ -23,7 +24,7 @@ export function ChatMessageRow({ message }: { readonly message: ChatMessageView {message.role === "assistant" ? ( message.state === "thinking" ?
- : + : <> ) :

{message.text}

}
diff --git a/frontend/src/components/claim-boundary-badge.tsx b/frontend/src/components/claim-boundary-badge.tsx new file mode 100644 index 00000000..e9dd5b28 --- /dev/null +++ b/frontend/src/components/claim-boundary-badge.tsx @@ -0,0 +1,13 @@ +const boundaryCopy: Record = { + unknown: "证据边界未知", + partial: "部分证据闭环", + observation_only: "仅观察", + blocked: "证据阻塞", + reference_only: "仅参考", +}; + +export function ClaimBoundaryBadge({ status }: { readonly status?: string }) { + const normalized = status?.trim() || "unknown"; + const label = boundaryCopy[normalized] ?? `证据状态:${normalized}`; + return

{label} · 不把未闭环内容包装成确定预测。

; +} diff --git a/frontend/src/lib/consultation-workflow-request.ts b/frontend/src/lib/consultation-workflow-request.ts index 1f4d08d8..0fe7b16e 100644 --- a/frontend/src/lib/consultation-workflow-request.ts +++ b/frontend/src/lib/consultation-workflow-request.ts @@ -2,15 +2,64 @@ export const consultationThemeValues = ["career", "marriage", "wealth", "timing" export type ConsultationTheme = typeof consultationThemeValues[number]; +export type ConsultationWorkflowRoute = + | "career" + | "marriage" + | "wealth" + | "timing" + | "rectification" + | "prashna" + | "general"; + +type WorkflowProjection = { + question: string; + themes: readonly ("career" | "marriage" | "wealth")[]; + strictWorkflowRoute: ConsultationWorkflowRoute; + requiredLayers: readonly string[]; + claimBoundary: string; +}; + +const routeRequirements: Record & { prefix?: string }> = { + career: { + themes: ["career"], + strictWorkflowRoute: "career", + requiredLayers: ["D1", "D10", "10th house/lord", "A10", "AmK", "Vimshottari", "Narayana", "Transit"], + claimBoundary: "career_direction_and_broad_timing_only", + }, + marriage: { + themes: ["marriage"], + strictWorkflowRoute: "marriage", + requiredLayers: ["D1", "D9", "7th house/lord", "Venus/Jupiter", "DK", "UL", "A7", "Vimshottari", "Narayana", "Transit"], + claimBoundary: "relationship_pattern_and_broad_window_only", + }, + wealth: { + themes: ["wealth"], + strictWorkflowRoute: "wealth", + requiredLayers: ["D1", "D2", "D11", "2nd/11th/9th/5th houses", "Wealth Yogas", "Ashtakavarga", "Dasha"], + claimBoundary: "wealth_structure_not_financial_advice", + }, + timing: { + themes: ["career"], + strictWorkflowRoute: "timing", + requiredLayers: ["Vimshottari", "Narayana", "Transit", "Varga", "negative holdout gate"], + claimBoundary: "candidate_day_month_window_only_until_holdout_passes", + prefix: "应期与阶段问题:", + }, + general: { + themes: ["career", "marriage", "wealth"], + strictWorkflowRoute: "general", + requiredLayers: ["D1", "D9", "D10", "D2", "Dasha", "Narayana", "Transit", "Functional Benefic/Malefic"], + claimBoundary: "multi_domain_summary_with_missing_layers_disclosed", + }, +}; + export function projectConsultationWorkflowRequest(question: string, theme: ConsultationTheme) { - switch (theme) { - case "career": - case "marriage": - case "wealth": - return { question, themes: [theme] } as const; - case "timing": - return { question: `应期与阶段问题:${question}`, themes: ["career"] } as const; - case "general": - return { question, themes: ["career", "marriage", "wealth"] } as const; - } + const requirement = routeRequirements[theme]; + return { + question: `${requirement.prefix ?? ""}${question}`, + themes: requirement.themes, + strictWorkflowRoute: requirement.strictWorkflowRoute, + requiredLayers: requirement.requiredLayers, + claimBoundary: requirement.claimBoundary, + } satisfies WorkflowProjection; } diff --git a/frontend/tests/claim-boundary-badge.test.ts b/frontend/tests/claim-boundary-badge.test.ts new file mode 100644 index 00000000..dd481914 --- /dev/null +++ b/frontend/tests/claim-boundary-badge.test.ts @@ -0,0 +1,16 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test from "node:test"; + +const rowSource = readFileSync(new URL("../src/components/chat-message-row.tsx", import.meta.url), "utf8"); +const badgeSource = readFileSync(new URL("../src/components/claim-boundary-badge.tsx", import.meta.url), "utf8"); +const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); + +test("assistant messages render a claim boundary badge from technique truth", () => { + assert.match(rowSource, /ClaimBoundaryBadge/); + assert.match(rowSource, /status=\{message\.techniqueTruth\}/); + assert.match(badgeSource, /不把未闭环内容包装成确定预测/); + assert.match(badgeSource, /observation_only/); + assert.match(badgeSource, /reference_only/); + assert.match(globalStyles, /\.claim-boundary-badge/); +}); diff --git a/frontend/tests/consultation-workflow-contract.test.ts b/frontend/tests/consultation-workflow-contract.test.ts index fb90af0f..447d840a 100644 --- a/frontend/tests/consultation-workflow-contract.test.ts +++ b/frontend/tests/consultation-workflow-contract.test.ts @@ -34,3 +34,11 @@ test("carries commercial technique truth into the model contract", () => { assert.match(mastra, /Do not use a restricted technique/); assert.match(route, /x-jyotish-technique-truth/); }); + +test("projects consultation themes through explicit strict workflow taxonomy", () => { + const projection = readFileSync(new URL("../src/lib/consultation-workflow-request.ts", import.meta.url), "utf8"); + assert.match(projection, /strictWorkflowRoute/); + assert.match(projection, /claimBoundary/); + assert.match(projection, /requiredLayers/); + assert.match(projection, /negative holdout gate/); +}); diff --git a/frontend/tests/consultation-workflow-request.test.ts b/frontend/tests/consultation-workflow-request.test.ts index 210aa518..2961f805 100644 --- a/frontend/tests/consultation-workflow-request.test.ts +++ b/frontend/tests/consultation-workflow-request.test.ts @@ -11,11 +11,13 @@ test("timing questions use a legal report theme and preserve a timing route hint // When: its workflow request is projected for the Python service. const request = projectConsultationWorkflowRequest(question, "timing"); - // Then: the illegal public theme is converted to a legal report theme with a route hint. - assert.deepEqual(request, { - question: "应期与阶段问题:未来哪些阶段值得把握?", - themes: ["career"], - }); + // Then: the illegal public theme is converted to a legal report theme with strict route metadata. + assert.equal(request.question, "应期与阶段问题:未来哪些阶段值得把握?"); + assert.deepEqual(request.themes, ["career"]); + assert.equal(request.strictWorkflowRoute, "timing"); + assert.ok(request.requiredLayers.includes("Narayana")); + assert.ok(request.requiredLayers.includes("negative holdout gate")); + assert.equal(request.claimBoundary, "candidate_day_month_window_only_until_holdout_passes"); }); test("timing input projects only legal private workflow fields", async () => { From 19b8491b186ca6ae714fd1f1e26e5f6237fd768b Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 15:11:21 +0800 Subject: [PATCH 12/25] feat: add evidence audit panel shell --- frontend/src/app/globals.css | 6 ++++ frontend/src/components/chat-message-row.tsx | 3 +- .../src/components/evidence-audit-panel.tsx | 33 +++++++++++++++++++ frontend/tests/evidence-audit-panel.test.ts | 19 +++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/evidence-audit-panel.tsx create mode 100644 frontend/tests/evidence-audit-panel.test.ts diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index d153c602..6df5d435 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -149,6 +149,12 @@ button:disabled { cursor: default; opacity: .45; } .message-markdown > *:first-child { margin-top: 0; } .message-markdown > *:last-child { margin-bottom: 0; } .claim-boundary-badge { width: fit-content; margin: 0 0 var(--space-2); padding: 4px 9px; border: 1px solid color-mix(in srgb, var(--color-action) 24%, transparent); border-radius: 999px; background: var(--color-action-soft); color: var(--color-action-hover); font-size: 12px; line-height: 1.45; } +.evidence-audit-panel { margin: 0 0 var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: color-mix(in srgb, var(--color-canvas-soft) 68%, transparent); } +.evidence-audit-panel summary { cursor: pointer; padding: var(--space-2) var(--space-3); color: var(--color-ink-secondary); font-size: 13px; } +.evidence-audit-table { display: grid; gap: 1px; padding: 0 var(--space-3) var(--space-3); } +.evidence-audit-row { display: grid; grid-template-columns: minmax(120px, 1fr) auto minmax(160px, 1.2fr); gap: var(--space-3); align-items: center; padding: var(--space-2) 0; border-top: 1px solid color-mix(in srgb, var(--color-border) 64%, transparent); font-size: 12px; } +.evidence-audit-row b { color: var(--color-action-hover); font-weight: 600; } +.evidence-audit-row small { color: var(--color-ink-tertiary); line-height: 1.45; } .message-markdown p { margin: 0 0 14px; } .message-markdown ul, .message-markdown ol { margin: 10px 0 16px; padding-left: 24px; } .message-markdown li { margin: 6px 0; } diff --git a/frontend/src/components/chat-message-row.tsx b/frontend/src/components/chat-message-row.tsx index 2791a612..c262b32c 100644 --- a/frontend/src/components/chat-message-row.tsx +++ b/frontend/src/components/chat-message-row.tsx @@ -1,5 +1,6 @@ import { ChatMessageContent } from "@/components/chat-message-content"; import { ClaimBoundaryBadge } from "@/components/claim-boundary-badge"; +import { EvidenceAuditPanel } from "@/components/evidence-audit-panel"; import type { ChatMessageView } from "@/lib/chat-message-view"; export function AgentAvatar() { @@ -24,7 +25,7 @@ export function ChatMessageRow({ message }: { readonly message: ChatMessageView {message.role === "assistant" ? ( message.state === "thinking" ?
- : <> + : <> ) :

{message.text}

}
diff --git a/frontend/src/components/evidence-audit-panel.tsx b/frontend/src/components/evidence-audit-panel.tsx new file mode 100644 index 00000000..5cdb328f --- /dev/null +++ b/frontend/src/components/evidence-audit-panel.tsx @@ -0,0 +1,33 @@ +type AuditRow = { + label: string; + status: "required" | "partial" | "blocked"; + boundary: string; +}; + +const defaultAuditRows: AuditRow[] = [ + { label: "D1 / Natal", status: "required", boundary: "基础命盘层" }, + { label: "Varga: D9 / D10 / D2 / D11", status: "required", boundary: "按问题域调用,不混用" }, + { label: "Vimshottari Dasha", status: "required", boundary: "阶段证据之一" }, + { label: "Narayana Dasha", status: "required", boundary: "应期/校时必须交叉" }, + { label: "Transit / Gochara", status: "partial", boundary: "精确日/月仍需 holdout" }, + { label: "Shadbala / Ashtakavarga", status: "partial", boundary: "组件 parity 未全闭环" }, + { label: "Functional Benefic/Malefic", status: "required", boundary: "不能只看自然吉凶" }, + { label: "MEVG / Real Case Calibration", status: "blocked", boundary: "外部校准不足时降级" }, +]; + +export function EvidenceAuditPanel({ claimStatus }: { readonly claimStatus?: string }) { + return ( +
+ 证据链摘要 · {claimStatus || "unknown"} +
+ {defaultAuditRows.map((row) => ( +
+ {row.label} + {row.status} + {row.boundary} +
+ ))} +
+
+ ); +} diff --git a/frontend/tests/evidence-audit-panel.test.ts b/frontend/tests/evidence-audit-panel.test.ts new file mode 100644 index 00000000..18bd5343 --- /dev/null +++ b/frontend/tests/evidence-audit-panel.test.ts @@ -0,0 +1,19 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test from "node:test"; + +const rowSource = readFileSync(new URL("../src/components/chat-message-row.tsx", import.meta.url), "utf8"); +const panelSource = readFileSync(new URL("../src/components/evidence-audit-panel.tsx", import.meta.url), "utf8"); +const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); + +test("assistant messages include a collapsible Technique Audit Table shell", () => { + assert.match(rowSource, /EvidenceAuditPanel/); + assert.match(rowSource, /claimStatus=\{message\.techniqueTruth\}/); + assert.match(panelSource, /Technique Audit Table/); + assert.match(panelSource, /D1 \/ Natal/); + assert.match(panelSource, /Narayana Dasha/); + assert.match(panelSource, /Functional Benefic\/Malefic/); + assert.match(panelSource, /MEVG \/ Real Case Calibration/); + assert.match(panelSource, /组件 parity 未全闭环/); + assert.match(globalStyles, /\.evidence-audit-panel/); +}); From d6726e849d903559d5c5472ac83b435507d0d452 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 15:17:22 +0800 Subject: [PATCH 13/25] feat: show parameter freeze panel --- frontend/src/app/globals.css | 7 ++++++ frontend/src/app/page.tsx | 12 ++++++++++ .../src/components/parameter-freeze-panel.tsx | 23 +++++++++++++++++++ frontend/tests/parameter-freeze-panel.test.ts | 18 +++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 frontend/src/components/parameter-freeze-panel.tsx create mode 100644 frontend/tests/parameter-freeze-panel.test.ts diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 6df5d435..5ac010bd 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -481,6 +481,13 @@ button:disabled { cursor: default; opacity: .45; } .product-entrypoint-action .starter-arrow { width: 15px; height: 15px; color: currentColor; } .starter-loading { color: var(--color-ink-secondary); margin-left: 0; padding: var(--space-5); border-radius: var(--radius-lg); background: var(--color-canvas-muted); font-size: 14px; } .starter-note { margin: 10px 0 0; color: var(--color-ink-secondary); line-height: 1.5; grid-column: 1 / -1; font-size: 13px; } +.parameter-freeze-panel { display: grid; grid-column: 1 / -1; gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-canvas); } +.parameter-freeze-panel > div { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-2); color: var(--color-ink-secondary); font-size: var(--type-caption); } +.parameter-freeze-panel b { color: var(--color-ink); font-size: var(--type-title-sm); } +.parameter-freeze-panel dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: var(--space-2); margin: 0; } +.parameter-freeze-panel dl > div { min-width: 0; padding: var(--space-2); border-radius: var(--radius-md); background: var(--color-canvas-soft); } +.parameter-freeze-panel dt { color: var(--color-ink-tertiary); font-size: 11px; } +.parameter-freeze-panel dd { overflow: hidden; margin: 2px 0 0; color: var(--color-ink); text-overflow: ellipsis; white-space: nowrap; font-size: 12px; } .message-list { margin: 0 auto; width: min(900px, 100%); padding: var(--space-8) var(--space-8) var(--space-16); } .message { display: flex; animation: message-enter 160ms var(--ease-out) both; padding: var(--space-2) 0; } diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 3867440e..c84fe4de 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -14,6 +14,7 @@ import { BirthTimeIntakeFields } from "@/components/birth-time-intake"; import { ChatMessageContent } from "@/components/chat-message-content"; import { AgentAvatar, ChatMessageRow } from "@/components/chat-message-row"; import { ModelSelector } from "@/components/model-selector"; +import { ParameterFreezePanel, type ParameterFreezeRow } from "@/components/parameter-freeze-panel"; import { Button } from "@/components/ui/button"; import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; import { Textarea } from "@/components/ui/textarea"; @@ -841,6 +842,16 @@ export default function Home() { const profileComplete = isProfileComplete(profile); const birthTimeDisplay = birthTimeDisplayState(profile); + const profileBirthPlace = selectedBirthPlace(profile); + const parameterFreezeRows: ParameterFreezeRow[] = profileComplete && profileBirthPlace ? [ + { label: "出生时间", value: `${profile.date} ${profile.time || "未定"}` }, + { label: "出生地点", value: profileBirthPlace.label }, + { label: "经纬度", value: `${profileBirthPlace.lat.toFixed(4)}, ${profileBirthPlace.lon.toFixed(4)}` }, + { label: "时区", value: `UTC+${profileBirthPlace.tz}` }, + { label: "Ayanamsa", value: "Lahiri / Sidereal" }, + { label: "Node mode", value: "True Node" }, + { label: "出生时间精度", value: birthTimeDisplay?.kind === "candidate" ? "候选时间" : birthTimeDisplay?.kind === "confirmed" ? "已确认" : "待校正" }, + ] : []; const dailyStarlanguage = dailyStarlanguageCard ?? (profileComplete ? buildDailyStarlanguageCard(profile) : null); const onboardingPending = profileComplete && !onboarding && !onboardingError; const currentOnboardingMessage = onboardingJustCompleted @@ -2417,6 +2428,7 @@ export default function Home() {
正在准备三个入门问题…
) : (
+ {parameterFreezeRows.length > 0 && }
diff --git a/frontend/src/components/evidence-audit-panel.tsx b/frontend/src/components/evidence-audit-panel.tsx index 5cdb328f..2d766a25 100644 --- a/frontend/src/components/evidence-audit-panel.tsx +++ b/frontend/src/components/evidence-audit-panel.tsx @@ -4,6 +4,13 @@ type AuditRow = { boundary: string; }; +type WorkflowReceipt = { + route: string; + status: string; + preciseTiming: string; + missingLayers: readonly string[]; +}; + const defaultAuditRows: AuditRow[] = [ { label: "D1 / Natal", status: "required", boundary: "基础命盘层" }, { label: "Varga: D9 / D10 / D2 / D11", status: "required", boundary: "按问题域调用,不混用" }, @@ -15,12 +22,27 @@ const defaultAuditRows: AuditRow[] = [ { label: "MEVG / Real Case Calibration", status: "blocked", boundary: "外部校准不足时降级" }, ]; -export function EvidenceAuditPanel({ claimStatus }: { readonly claimStatus?: string }) { +function workflowRows(receipt?: WorkflowReceipt): AuditRow[] { + if (!receipt) return defaultAuditRows; + return [ + { label: `Workflow route: ${receipt.route}`, status: receipt.status === "blocked" ? "blocked" : "required", boundary: "后端实际路由" }, + { label: `Precise timing: ${receipt.preciseTiming}`, status: receipt.preciseTiming === "allowed" ? "partial" : "blocked", boundary: "精确应期 claim gate" }, + { + label: "Missing route layers", + status: receipt.missingLayers.length ? "blocked" : "required", + boundary: receipt.missingLayers.length ? receipt.missingLayers.join(" / ") : "none", + }, + ...defaultAuditRows, + ]; +} + +export function EvidenceAuditPanel({ claimStatus, workflowReceipt }: { readonly claimStatus?: string; readonly workflowReceipt?: WorkflowReceipt }) { + const rows = workflowRows(workflowReceipt); return (
证据链摘要 · {claimStatus || "unknown"}
- {defaultAuditRows.map((row) => ( + {rows.map((row) => (
{row.label} {row.status} diff --git a/frontend/src/lib/chat-message-view.ts b/frontend/src/lib/chat-message-view.ts index 35825f56..e7f0d9ad 100644 --- a/frontend/src/lib/chat-message-view.ts +++ b/frontend/src/lib/chat-message-view.ts @@ -3,6 +3,12 @@ export type ChatMessage = { readonly text: string; readonly suggestions?: readonly string[]; readonly techniqueTruth?: string; + readonly workflowReceipt?: { + readonly route: string; + readonly status: string; + readonly preciseTiming: string; + readonly missingLayers: readonly string[]; + }; }; export type ChatMessageView = ChatMessage & { diff --git a/frontend/tests/evidence-audit-panel.test.ts b/frontend/tests/evidence-audit-panel.test.ts index 18bd5343..44ab6d6e 100644 --- a/frontend/tests/evidence-audit-panel.test.ts +++ b/frontend/tests/evidence-audit-panel.test.ts @@ -4,12 +4,17 @@ import test from "node:test"; const rowSource = readFileSync(new URL("../src/components/chat-message-row.tsx", import.meta.url), "utf8"); const panelSource = readFileSync(new URL("../src/components/evidence-audit-panel.tsx", import.meta.url), "utf8"); +const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8"); const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); test("assistant messages include a collapsible Technique Audit Table shell", () => { assert.match(rowSource, /EvidenceAuditPanel/); assert.match(rowSource, /claimStatus=\{message\.techniqueTruth\}/); + assert.match(rowSource, /workflowReceipt=\{message\.workflowReceipt\}/); assert.match(panelSource, /Technique Audit Table/); + assert.match(panelSource, /Workflow route:/); + assert.match(panelSource, /Precise timing:/); + assert.match(panelSource, /Missing route layers/); assert.match(panelSource, /D1 \/ Natal/); assert.match(panelSource, /Narayana Dasha/); assert.match(panelSource, /Functional Benefic\/Malefic/); @@ -17,3 +22,11 @@ test("assistant messages include a collapsible Technique Audit Table shell", () assert.match(panelSource, /组件 parity 未全闭环/); assert.match(globalStyles, /\.evidence-audit-panel/); }); + +test("consult responses persist workflow receipt headers for evidence rendering", () => { + assert.match(pageSource, /x-jyotish-workflow-route/); + assert.match(pageSource, /x-jyotish-workflow-status/); + assert.match(pageSource, /x-jyotish-precise-timing/); + assert.match(pageSource, /x-jyotish-missing-layers/); + assert.match(pageSource, /workflowReceipt/); +}); From b421883a511a80ebc4406ef2cbdab523b993880e Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 15:31:15 +0800 Subject: [PATCH 15/25] feat: include evidence boundaries in report export --- frontend/src/app/page.tsx | 5 ++++ .../src/lib/consultation-report-export.ts | 25 ++++++++++++++++ .../tests/consultation-report-export.test.ts | 30 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 frontend/src/lib/consultation-report-export.ts create mode 100644 frontend/tests/consultation-report-export.test.ts diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 353ccc8e..aeba5cb7 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -45,6 +45,7 @@ import { import { defaultGuidedJyotishTopics } from "@/lib/guided-jyotish-topics"; import { keepFocusWithin } from "@/lib/focus-trap"; import { chatMessageViews, type ChatMessage } from "@/lib/chat-message-view"; +import { consultationReportMarkdown } from "@/lib/consultation-report-export"; import { OnboardingAuthenticationError, type OnboardingContent, @@ -1292,11 +1293,15 @@ export default function Home() { message_count: session.messages.length, messages: session.messages.map((message) => ({ role: message.role, text: message.text })), }; + const reportMarkdown = consultationReportMarkdown({ title: session.title, messages: session.messages }); const transcript = [ `Jyotisha 对话:${session.title}`, "", ...session.messages.map((message) => `${message.role === "user" ? "我" : "Jyotisha"}:${message.text}`), "", + "---- Markdown 报告 ----", + reportMarkdown, + "", "---- JSON 分享包 ----", JSON.stringify(sharePayload, null, 2), ].join("\n"); diff --git a/frontend/src/lib/consultation-report-export.ts b/frontend/src/lib/consultation-report-export.ts new file mode 100644 index 00000000..88aee559 --- /dev/null +++ b/frontend/src/lib/consultation-report-export.ts @@ -0,0 +1,25 @@ +import type { ChatMessage } from "./chat-message-view"; + +export function consultationReportMarkdown(input: { + title: string; + messages: readonly ChatMessage[]; +}) { + const latestAssistant = [...input.messages].reverse().find((message) => message.role === "assistant"); + const evidence = latestAssistant?.workflowReceipt; + return [ + `# ${input.title}`, + "", + "## 最新回答", + latestAssistant?.text || "暂无回答。", + "", + "## Claim boundary", + `technique_truth: ${latestAssistant?.techniqueTruth || "unknown"}`, + evidence ? `workflow_route: ${evidence.route}` : "workflow_route: unknown", + evidence ? `workflow_status: ${evidence.status}` : "workflow_status: unknown", + evidence ? `precise_timing: ${evidence.preciseTiming}` : "precise_timing: unknown", + evidence ? `missing_layers: ${evidence.missingLayers.join(" / ") || "none"}` : "missing_layers: unknown", + "", + "## Boundary", + "本报告保留证据边界;未闭环内容不得包装成确定预测。", + ].join("\n"); +} diff --git a/frontend/tests/consultation-report-export.test.ts b/frontend/tests/consultation-report-export.test.ts new file mode 100644 index 00000000..90cb8ee6 --- /dev/null +++ b/frontend/tests/consultation-report-export.test.ts @@ -0,0 +1,30 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { consultationReportMarkdown } from "../src/lib/consultation-report-export.ts"; + +test("exports latest consultation answer with workflow receipt and claim boundary", () => { + const report = consultationReportMarkdown({ + title: "事业咨询", + messages: [ + { role: "user", text: "未来一年事业如何?" }, + { + role: "assistant", + text: "先看阶段,不承诺具体日期。", + techniqueTruth: "partial", + workflowReceipt: { + route: "career", + status: "ready", + preciseTiming: "blocked", + missingLayers: ["MEVG"], + }, + }, + ], + }); + assert.match(report, /# 事业咨询/); + assert.match(report, /先看阶段/); + assert.match(report, /technique_truth: partial/); + assert.match(report, /workflow_route: career/); + assert.match(report, /precise_timing: blocked/); + assert.match(report, /missing_layers: MEVG/); + assert.match(report, /未闭环内容不得包装成确定预测/); +}); From ebdd1a40e1eb9aa399dd9ad4005ec01b05cfc084 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 15:46:16 +0800 Subject: [PATCH 16/25] feat: add non-sensitive raw evidence receipt --- frontend/src/app/globals.css | 3 +++ frontend/src/components/evidence-audit-panel.tsx | 9 +++++++++ frontend/tests/evidence-audit-panel.test.ts | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 5ac010bd..b53f8a06 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -155,6 +155,9 @@ button:disabled { cursor: default; opacity: .45; } .evidence-audit-row { display: grid; grid-template-columns: minmax(120px, 1fr) auto minmax(160px, 1.2fr); gap: var(--space-3); align-items: center; padding: var(--space-2) 0; border-top: 1px solid color-mix(in srgb, var(--color-border) 64%, transparent); font-size: 12px; } .evidence-audit-row b { color: var(--color-action-hover); font-weight: 600; } .evidence-audit-row small { color: var(--color-ink-tertiary); line-height: 1.45; } +.raw-evidence-receipt { margin: 0 var(--space-3) var(--space-3); border-top: 1px solid color-mix(in srgb, var(--color-border) 64%, transparent); padding-top: var(--space-2); } +.raw-evidence-receipt summary { padding: var(--space-1) 0; color: var(--color-ink-tertiary); font-size: 12px; } +.raw-evidence-receipt pre { max-height: 180px; overflow: auto; margin: var(--space-2) 0 0; padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink-secondary); font-size: 11px; line-height: 1.5; } .message-markdown p { margin: 0 0 14px; } .message-markdown ul, .message-markdown ol { margin: 10px 0 16px; padding-left: 24px; } .message-markdown li { margin: 6px 0; } diff --git a/frontend/src/components/evidence-audit-panel.tsx b/frontend/src/components/evidence-audit-panel.tsx index 2d766a25..abb9e626 100644 --- a/frontend/src/components/evidence-audit-panel.tsx +++ b/frontend/src/components/evidence-audit-panel.tsx @@ -38,6 +38,11 @@ function workflowRows(receipt?: WorkflowReceipt): AuditRow[] { export function EvidenceAuditPanel({ claimStatus, workflowReceipt }: { readonly claimStatus?: string; readonly workflowReceipt?: WorkflowReceipt }) { const rows = workflowRows(workflowReceipt); + const rawReceipt = { + techniqueTruth: claimStatus || "unknown", + workflowReceipt: workflowReceipt ?? null, + privacyBoundary: "no_birth_data_or_private_case_raw", + }; return (
证据链摘要 · {claimStatus || "unknown"} @@ -50,6 +55,10 @@ export function EvidenceAuditPanel({ claimStatus, workflowReceipt }: { readonly
))}
+
+ 查看非敏感 raw receipt +
{JSON.stringify(rawReceipt, null, 2)}
+
); } diff --git a/frontend/tests/evidence-audit-panel.test.ts b/frontend/tests/evidence-audit-panel.test.ts index 44ab6d6e..11ba762b 100644 --- a/frontend/tests/evidence-audit-panel.test.ts +++ b/frontend/tests/evidence-audit-panel.test.ts @@ -20,7 +20,11 @@ test("assistant messages include a collapsible Technique Audit Table shell", () assert.match(panelSource, /Functional Benefic\/Malefic/); assert.match(panelSource, /MEVG \/ Real Case Calibration/); assert.match(panelSource, /组件 parity 未全闭环/); + assert.match(panelSource, /rawReceipt/); + assert.match(panelSource, /no_birth_data_or_private_case_raw/); + assert.match(panelSource, /查看非敏感 raw receipt/); assert.match(globalStyles, /\.evidence-audit-panel/); + assert.match(globalStyles, /\.raw-evidence-receipt/); }); test("consult responses persist workflow receipt headers for evidence rendering", () => { From e11444904dd94f6fb48a08791bb99adc55022ad1 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Tue, 21 Jul 2026 15:49:09 +0800 Subject: [PATCH 17/25] feat: add markdown consultation report download --- frontend/src/app/globals.css | 1 + frontend/src/components/chat-message-row.tsx | 3 ++- frontend/src/lib/consultation-report-export.ts | 12 ++++++++++++ frontend/tests/consultation-report-export.test.ts | 12 +++++++++++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index b53f8a06..f6f724d7 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -158,6 +158,7 @@ button:disabled { cursor: default; opacity: .45; } .raw-evidence-receipt { margin: 0 var(--space-3) var(--space-3); border-top: 1px solid color-mix(in srgb, var(--color-border) 64%, transparent); padding-top: var(--space-2); } .raw-evidence-receipt summary { padding: var(--space-1) 0; color: var(--color-ink-tertiary); font-size: 12px; } .raw-evidence-receipt pre { max-height: 180px; overflow: auto; margin: var(--space-2) 0 0; padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink-secondary); font-size: 11px; line-height: 1.5; } +.report-download-button { margin-top: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas-soft); color: var(--color-ink-secondary); cursor: pointer; padding: 7px 10px; font-size: 12px; } .message-markdown p { margin: 0 0 14px; } .message-markdown ul, .message-markdown ol { margin: 10px 0 16px; padding-left: 24px; } .message-markdown li { margin: 6px 0; } diff --git a/frontend/src/components/chat-message-row.tsx b/frontend/src/components/chat-message-row.tsx index 3aded28a..149b714e 100644 --- a/frontend/src/components/chat-message-row.tsx +++ b/frontend/src/components/chat-message-row.tsx @@ -2,6 +2,7 @@ import { ChatMessageContent } from "@/components/chat-message-content"; import { ClaimBoundaryBadge } from "@/components/claim-boundary-badge"; import { EvidenceAuditPanel } from "@/components/evidence-audit-panel"; import type { ChatMessageView } from "@/lib/chat-message-view"; +import { consultationReportMarkdown, downloadMarkdownReport } from "@/lib/consultation-report-export"; export function AgentAvatar() { return