feat: add oracle evidence safeguards
This commit is contained in:
@@ -21,3 +21,5 @@ htmlcov/
|
||||
benchmarks/jyotish/outputs/
|
||||
output_report.txt
|
||||
results_extracted.md
|
||||
runtime-smoke-report-*.html
|
||||
jyotish-app/runtime-smoke-report-*.html
|
||||
|
||||
@@ -131,8 +131,22 @@ python3 scripts/oracle_collection_queue.py \
|
||||
|
||||
该 JSON 的 scope 是 `external_oracle_collection_queue`。当前队列有 5 个 `template_only` 任务、`ready_for_calibration: 0`、`production_tuning_allowed: false`,说明只能继续采集 JHora/PyJHora/VedAstro 等外部黑盒目标值;在模板字段未填充、状态未升为 `external_verified` 前,不能用这些样本做 Dasha/Shadbala 生产调参。
|
||||
|
||||
Ashtakoot 外部合婚 oracle 使用同一个队列生成器,但独立样本文件是 `references/oracle/ashtakoot_oracle_cases.json`:
|
||||
|
||||
```bash
|
||||
python3 scripts/oracle_collection_queue.py \
|
||||
--oracle-file references/oracle/ashtakoot_oracle_cases.json \
|
||||
--format json
|
||||
```
|
||||
|
||||
该队列同样保持 `ready_for_calibration: 0`,用于采集 `ashtakoot_36_point` 外部合婚目标值,而不是重写现有 `scripts/ashtakoot.py` 算法。每条样本要补齐 `target.total_score`、`target.varna`、`target.vashya`、`target.tara`、`target.yoni`、`target.graha_maitri`、`target.gana`、`target.bhakoot`、`target.nadi`、`target.kuja_status`,并保留 JHora/VedAstro/AstroSage 等外部截图或 API artifact。
|
||||
|
||||
每个队列任务还包含 `evidence_packet.capture_id` 草稿证据包。人工或副手录入外部真值时,必须至少填写 `tool_name`、`tool_version_or_url`、`capture_date`、`source_artifact`、`ayanamsa`、`node_mode`、`timezone`、`operator_note`,并保留截图、API 响应或 stdout 等外部 artifact;不得把本仓库本地计算输出当作 `source_artifact`。
|
||||
|
||||
外部截图和 stdout 片段统一存入 `references/oracle/artifacts/`,证据包里的 `source_artifact` 必须使用该目录下的 repo-relative 路径或明确标注的外部审阅位置。所有私人截图必须打码;不得提交私人 PDF 原件、不得提交完整出生报告,也不得提交浏览器 scratch 目录或含账号会话/cookie/token/桌面通知的截图。
|
||||
|
||||
第一条 JHora/PyJHora 黑盒证据采集按 `docs/user_jhora_capture_guide.md` 执行:优先使用 Steve Jobs 或合成样本,采集 Moon sidereal longitude、Vimshottari start date 与 Shadbala 七曜六分量,保存到 `references/oracle/artifacts/` 后再运行 evidence validator。
|
||||
|
||||
外部目标字段采用 `target_fields` + `target_placeholders` 双层结构:`target_fields` 固定记录该案例需要校验的目标,例如 `target.moon_sidereal_longitude_deg`、`target.vimshottari_start_date`、`target.shadbala_components`;当这些字段被真实外部来源填入并且证据包状态升为 `external_verified` 后,队列生成器会保留这些值,不会再把它们降级成 `draft`。这保证了“人工/JHora/PyJHora/VedAstro 采集 → JSON 填写 → 队列生成 → validator 复核”的路径可复验。
|
||||
|
||||
当外部证据包被填写回队列 JSON 后,用证据验证器做第二层防线:
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
# JHora/PyJHora External Evidence Capture Guide
|
||||
|
||||
This guide turns the first Dasha/Shadbala oracle sample from a draft into a
|
||||
reviewable external evidence packet. It is for manual black-box capture only:
|
||||
do not copy JHora/PyJHora implementation code, formulas, constants, or private
|
||||
reports into this repository.
|
||||
|
||||
## Safe Case Choice
|
||||
|
||||
Start with one of these public or synthetic tasks:
|
||||
|
||||
- `template_steve_jobs_dasha_lahiri`: public-figure Dasha boundary sample.
|
||||
- `template_user_REDACTED_YEAR_moon_longitude_lahiri`: user-template sample; use only if
|
||||
the screenshot is heavily redacted.
|
||||
|
||||
Avoid private client material for the first pass. If a private screenshot is
|
||||
unavoidable, it 必须打码 before it enters `references/oracle/artifacts/`.
|
||||
|
||||
## Privacy Rules
|
||||
|
||||
- Store evidence only under `references/oracle/artifacts/`.
|
||||
- `source_artifact` must be a repo-relative path such as
|
||||
`references/oracle/artifacts/jhora_steve_jobs_dasha_lahiri_v1.png`.
|
||||
- 必须打码 names, addresses, account names, notifications, file paths, browser
|
||||
tabs, and any personally identifying text.
|
||||
- 不得提交私人 PDF 原件.
|
||||
- 不得提交完整出生报告.
|
||||
- 不得提交浏览器 scratch folders, cookies, account sessions, tokens, desktop
|
||||
notifications, or downloaded temp folders.
|
||||
|
||||
## External Tool Setup
|
||||
|
||||
For JHora:
|
||||
|
||||
1. Open a clean chart in JHora.
|
||||
2. Enter the exact date, time, timezone, latitude, and longitude from
|
||||
`references/oracle/dasha_shadbala_oracle_cases.json`.
|
||||
3. Set ayanamsa explicitly. The first pass should use Lahiri; later samples
|
||||
should repeat with Raman and KP.
|
||||
4. Record whether the tool is using mean node or true node. The packet metadata
|
||||
must write this exactly as `mean node` or `true node`.
|
||||
5. Keep the tool version, build date, or download URL visible in notes if
|
||||
possible.
|
||||
|
||||
For PyJHora:
|
||||
|
||||
1. Use an isolated environment and treat output as a black-box external
|
||||
reference.
|
||||
2. Record the package version and command or script path in operator notes.
|
||||
3. Capture stdout as a text artifact only if it contains the requested target
|
||||
values and no private data.
|
||||
4. Because PyJHora license metadata can vary by source, use it as an external
|
||||
behavior oracle unless a separate license review approves direct reuse.
|
||||
|
||||
## Required Screenshots Or Stdout
|
||||
|
||||
Capture enough external evidence to fill every requested target field.
|
||||
|
||||
### Moon sidereal longitude
|
||||
|
||||
Required for `template_user_REDACTED_YEAR_moon_longitude_lahiri`.
|
||||
|
||||
- Capture Moon sidereal longitude in absolute 0-360 degrees if available.
|
||||
- Also capture sign-local degrees/minutes/seconds when the tool shows it.
|
||||
- Record ayanamsa, timezone, and node mode beside the value.
|
||||
|
||||
### Vimshottari start date
|
||||
|
||||
Required for `template_user_REDACTED_YEAR_moon_longitude_lahiri`,
|
||||
`template_steve_jobs_dasha_lahiri`, and
|
||||
`template_historical_epoch_lahiri`.
|
||||
|
||||
- Capture the Vimshottari start date or first Mahadasha boundary shown by the
|
||||
external tool.
|
||||
- Write it as ISO date in `target.vimshottari_start_date`, for example
|
||||
`1986-05-18`.
|
||||
- If the tool uses a visible year length, DST, or local sunrise policy, record
|
||||
that in `operator_note`.
|
||||
|
||||
### Shadbala 七曜六分量
|
||||
|
||||
Required for every task whose target includes `shadbala_components`.
|
||||
|
||||
The packet must include all seven planets:
|
||||
|
||||
- Sun
|
||||
- Moon
|
||||
- Mars
|
||||
- Mercury
|
||||
- Jupiter
|
||||
- Venus
|
||||
- Saturn
|
||||
|
||||
Each planet must include all six components:
|
||||
|
||||
- `sthana`
|
||||
- `dig`
|
||||
- `kala`
|
||||
- `chesta`
|
||||
- `naisargika`
|
||||
- `drik`
|
||||
|
||||
Do not fill only a total. Do not apply a global scaling factor. If the tool
|
||||
shows Rupa and Virupa, record the unit in `operator_note` and preserve the
|
||||
component numbers exactly as read from the screenshot or stdout.
|
||||
|
||||
## Artifact Naming
|
||||
|
||||
Use short, stable names:
|
||||
|
||||
- `references/oracle/artifacts/jhora_steve_jobs_lahiri_dasha_v1.png`
|
||||
- `references/oracle/artifacts/jhora_steve_jobs_lahiri_shadbala_v1.png`
|
||||
- `references/oracle/artifacts/pyjhora_steve_jobs_lahiri_stdout_v1.txt`
|
||||
- `references/oracle/artifacts/jhora_user_REDACTED_YEAR_lahiri_redacted_v1.png`
|
||||
|
||||
If a screenshot is corrected, keep the old artifact only if it is already
|
||||
referenced by a packet; otherwise replace the draft before review.
|
||||
|
||||
## Packet Fill Procedure
|
||||
|
||||
Generate the current queue:
|
||||
|
||||
```bash
|
||||
python3 scripts/oracle_collection_queue.py \
|
||||
--oracle-file references/oracle/dasha_shadbala_oracle_cases.json \
|
||||
--format json > /tmp/jyotish_oracle_queue_capture.json
|
||||
```
|
||||
|
||||
Choose the task matching the external tool output, then fill:
|
||||
|
||||
- `evidence_packet.status`: `external_verified`
|
||||
- `evidence_packet.metadata.tool_name`: `JHora` or `PyJHora`
|
||||
- `evidence_packet.metadata.tool_version_or_url`
|
||||
- `evidence_packet.metadata.capture_date`
|
||||
- `evidence_packet.metadata.source_artifact`
|
||||
- `evidence_packet.metadata.ayanamsa`: `Lahiri`, `Raman`, or `KP`
|
||||
- `evidence_packet.metadata.node_mode`: `mean node` or `true node`
|
||||
- `evidence_packet.metadata.timezone`
|
||||
- `evidence_packet.metadata.operator_note`
|
||||
- `evidence_packet.target_placeholders.target.moon_sidereal_longitude_deg`
|
||||
- `evidence_packet.target_placeholders.target.vimshottari_start_date`
|
||||
- `evidence_packet.target_placeholders.target.shadbala_components`
|
||||
|
||||
For a valid first Shadbala row, the component object must look like this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"Sun": {"sthana": 0, "dig": 0, "kala": 0, "chesta": 0, "naisargika": 0, "drik": 0},
|
||||
"Moon": {"sthana": 0, "dig": 0, "kala": 0, "chesta": 0, "naisargika": 0, "drik": 0},
|
||||
"Mars": {"sthana": 0, "dig": 0, "kala": 0, "chesta": 0, "naisargika": 0, "drik": 0},
|
||||
"Mercury": {"sthana": 0, "dig": 0, "kala": 0, "chesta": 0, "naisargika": 0, "drik": 0},
|
||||
"Jupiter": {"sthana": 0, "dig": 0, "kala": 0, "chesta": 0, "naisargika": 0, "drik": 0},
|
||||
"Venus": {"sthana": 0, "dig": 0, "kala": 0, "chesta": 0, "naisargika": 0, "drik": 0},
|
||||
"Saturn": {"sthana": 0, "dig": 0, "kala": 0, "chesta": 0, "naisargika": 0, "drik": 0}
|
||||
}
|
||||
```
|
||||
|
||||
Replace every `0` with the external value. A zero is valid only if the external
|
||||
tool literally shows zero.
|
||||
|
||||
## Validation
|
||||
|
||||
Run the validator against the filled queue:
|
||||
|
||||
```bash
|
||||
python3 scripts/oracle_evidence_validator.py \
|
||||
--queue-file /tmp/jyotish_oracle_queue_capture.json
|
||||
```
|
||||
|
||||
The first successful sample should move the summary from `valid_packets: 0` to
|
||||
`valid_packets: 1`, and from `ready_for_calibration: 0` to
|
||||
`ready_for_calibration: 1`. This does not allow production tuning by itself; it
|
||||
only means one packet is review-ready.
|
||||
|
||||
## Reject And Recapture Cases
|
||||
|
||||
Reject the packet and recapture if any of these occur:
|
||||
|
||||
- `missing_external_artifact`
|
||||
- `status_not_external_verified:draft`
|
||||
- `local_engine_artifact_rejected`
|
||||
- `missing_shadbala_component:all_planets`
|
||||
- `missing_shadbala_component:Sun.kala` or any other component-level error
|
||||
- Screenshot does not show ayanamsa.
|
||||
- Screenshot does not show whether mean node or true node was used.
|
||||
- Source artifact contains unredacted private data.
|
||||
- Source artifact is a private PDF original or complete birth report.
|
||||
- Values came from this repository's local engine instead of JHora/PyJHora or
|
||||
another external oracle.
|
||||
|
||||
## Minimal Recapture Checklist
|
||||
|
||||
If the screenshot is not enough, recapture only the missing evidence:
|
||||
|
||||
1. Tool/version and settings screen.
|
||||
2. Moon sidereal longitude screen.
|
||||
3. Vimshottari start date screen.
|
||||
4. Shadbala component table with all seven planets and six components.
|
||||
5. A short operator note explaining ayanamsa, node mode, timezone, and unit.
|
||||
+130
-3
@@ -894,8 +894,13 @@ function renderAIPromptPackPanel(cd) {
|
||||
<span>AI Prompt Pack</span>
|
||||
<strong>${escapeHtml(pack.mode || 'jyotish_structured_prompt_pack')}</strong>
|
||||
</div>
|
||||
<em>schema v${escapeHtml(String(pack.schema_version || 1))}</em>
|
||||
<div class="ai-prompt-pack-actions">
|
||||
<button type="button" data-ai-prompt-action="copy-prompt">复制 Prompt</button>
|
||||
<button type="button" data-ai-prompt-action="copy-evidence">复制 Evidence</button>
|
||||
<em>schema v${escapeHtml(String(pack.schema_version || 1))}</em>
|
||||
</div>
|
||||
</div>
|
||||
${renderAyanamsaRuntimeStatus(cd, pack)}
|
||||
<div class="ai-prompt-pack-grid">
|
||||
<div class="ai-prompt-pack-card">
|
||||
<span>Ayanamsa</span>
|
||||
@@ -945,6 +950,79 @@ function renderAIPromptPackPanel(cd) {
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
setupAIPromptPackActions(host, cd, pack);
|
||||
}
|
||||
|
||||
function renderAyanamsaRuntimeStatus(cd, pack) {
|
||||
const settings = readCalculationSettings();
|
||||
const boundary = cd?._calculation_boundary || {};
|
||||
const evidenceAyanamsa = pack?.evidence_snapshot?.ayanamsa || {};
|
||||
const requested = evidenceAyanamsa.name || settings.ayanamsa;
|
||||
const applied = boundary.appliedAyanamsa || evidenceAyanamsa.name || requested;
|
||||
const status = cd?._fallback ? 'browser_fallback' : 'backend_computed';
|
||||
const label = status === 'backend_computed' ? '后端实算' : '浏览器降级';
|
||||
const note = boundary.note || (
|
||||
status === 'backend_computed'
|
||||
? '当前 Ayanamsa 来自本地 API 服务返回,可随导出和 AI Prompt Pack 追溯。'
|
||||
: '浏览器 fallback 只能使用 Lahiri 兼容路径;Raman/KP 等需启动本地 API 服务。'
|
||||
);
|
||||
return `
|
||||
<div class="ayanamsa-runtime-status ayanamsa-runtime-status-${escapeAttr(status)}">
|
||||
<span>${escapeHtml(label)}</span>
|
||||
<strong>${escapeHtml(getCalculationSettingLabel('ayanamsa', requested))}</strong>
|
||||
<small>${escapeHtml(`applied=${applied} · status=${status}`)}</small>
|
||||
<em>${escapeHtml(note)}</em>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function setupAIPromptPackActions(host, cd, pack) {
|
||||
host.querySelectorAll('[data-ai-prompt-action]').forEach(button => {
|
||||
button.addEventListener('click', async () => {
|
||||
const action = button.getAttribute('data-ai-prompt-action');
|
||||
const copied = await copyAIPromptPackSection(action, cd, pack);
|
||||
button.textContent = copied ? '已复制' : '复制失败';
|
||||
setTimeout(() => {
|
||||
button.textContent = action === 'copy-evidence' ? '复制 Evidence' : '复制 Prompt';
|
||||
}, 1400);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function copyAIPromptPackSection(action, cd, pack) {
|
||||
const payload = action === 'copy-evidence'
|
||||
? {
|
||||
evidence_snapshot: pack?.evidence_snapshot || {},
|
||||
retrieval_plan: pack?.retrieval_plan || {},
|
||||
calculation_boundary: cd?._calculation_boundary || null,
|
||||
}
|
||||
: {
|
||||
prompt_zh: pack?.prompt_zh || '',
|
||||
mode: pack?.mode || 'jyotish_structured_prompt_pack',
|
||||
schema_version: pack?.schema_version || 1,
|
||||
};
|
||||
const text = [
|
||||
'AI Prompt Pack 审计上下文已复制',
|
||||
JSON.stringify(payload, null, 2),
|
||||
].join('\n');
|
||||
try {
|
||||
if (navigator.clipboard?.writeText) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
return true;
|
||||
}
|
||||
const area = document.createElement('textarea');
|
||||
area.value = text;
|
||||
area.setAttribute('readonly', 'true');
|
||||
area.style.position = 'fixed';
|
||||
area.style.left = '-9999px';
|
||||
document.body.appendChild(area);
|
||||
area.select();
|
||||
const ok = document.execCommand('copy');
|
||||
document.body.removeChild(area);
|
||||
return ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeAIPromptPack(cd = {}) {
|
||||
@@ -981,6 +1059,7 @@ function normalizeAIPromptPack(cd = {}) {
|
||||
strength: {
|
||||
shadbala_ranking: Object.entries(cd.shadbala || {}).map(([planet, pdata]) => ({ planet, rupas: pdata?.rupas, level: pdata?.level })),
|
||||
},
|
||||
oracle_progress: oracleProgressSnapshot(),
|
||||
},
|
||||
retrieval_plan: {
|
||||
local_reference_docs: [
|
||||
@@ -993,6 +1072,20 @@ function normalizeAIPromptPack(cd = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function oracleProgressSnapshot() {
|
||||
return {
|
||||
scope: 'external_oracle_evidence_validation',
|
||||
collection_queue: 'external_oracle_collection_queue',
|
||||
total_packets: 5,
|
||||
valid_packets: 0,
|
||||
ready_for_calibration: 0,
|
||||
production_tuning_allowed: false,
|
||||
artifact_policy: 'references/oracle/artifacts/',
|
||||
promotion_rule: 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
|
||||
boundary: 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.',
|
||||
};
|
||||
}
|
||||
|
||||
function formatCompactDegree(value) {
|
||||
const num = Number(value);
|
||||
return Number.isFinite(num) ? `${num.toFixed(2)}°` : '-';
|
||||
@@ -1827,6 +1920,7 @@ function renderOracleEvidenceIntakePanel() {
|
||||
<strong>Oracle Evidence Intake</strong>
|
||||
<span>下载 5 个外部真值空白包;只接受 JHora/PyJHora/VedAstro 黑盒证据,禁止本地输出冒充 external_verified。</span>
|
||||
</div>
|
||||
${renderOracleEvidenceProgressDashboard()}
|
||||
<div class="oracle-evidence-intake-grid">
|
||||
${ORACLE_EVIDENCE_INTAKE_TASKS.map(task => `
|
||||
<article class="oracle-evidence-card">
|
||||
@@ -1859,6 +1953,38 @@ function renderOracleEvidenceIntakePanel() {
|
||||
`;
|
||||
}
|
||||
|
||||
function renderOracleEvidenceProgressDashboard() {
|
||||
const totalPackets = ORACLE_EVIDENCE_INTAKE_TASKS.length;
|
||||
const validPackets = 0;
|
||||
const readyPackets = 0;
|
||||
const shadbalaTargets = ORACLE_EVIDENCE_INTAKE_TASKS
|
||||
.filter(task => task.targetFields.includes('shadbala_components')).length;
|
||||
const dashaTargets = ORACLE_EVIDENCE_INTAKE_TASKS
|
||||
.filter(task => task.targetFields.includes('vimshottari_start_date')).length;
|
||||
const progress = totalPackets ? Math.round((validPackets / totalPackets) * 100) : 0;
|
||||
return `
|
||||
<section class="oracle-evidence-progress-dashboard" aria-label="Dasha/Shadbala 真实进度">
|
||||
<div class="calculation-settings-head">
|
||||
<strong>Dasha/Shadbala 真实进度</strong>
|
||||
<span>${validPackets} / ${totalPackets} valid_packets · ${readyPackets} ready_for_calibration · production_tuning_allowed=false</span>
|
||||
</div>
|
||||
<div class="oracle-evidence-progress-bar" aria-label="Oracle evidence progress ${validPackets} / ${totalPackets}">
|
||||
<span style="width: ${progress}%"></span>
|
||||
</div>
|
||||
<div class="dasha-shadbala-calibration-grid">
|
||||
${renderValidationTransparencyMetric('Evidence', `${validPackets} / ${totalPackets}`, '0 / 5 外部证据包通过前,生产调参保持关闭。')}
|
||||
${renderValidationTransparencyMetric('Dasha', `${dashaTargets} targets`, 'vimshottari_start_date 需要 JHora/PyJHora 黑盒对照。')}
|
||||
${renderValidationTransparencyMetric('Shadbala', `${shadbalaTargets} targets`, 'missing_shadbala_component 会拦截缺 sthana/dig/kala/chesta/naisargika/drik 的七曜分量。')}
|
||||
${renderValidationTransparencyMetric('Artifacts', 'references/oracle/artifacts/', 'source_artifact 必须指向脱敏后的外部截图或 stdout。')}
|
||||
</div>
|
||||
<div class="dasha-shadbala-calibration-boundary">
|
||||
<strong>隐私</strong>
|
||||
<span>私人截图必须打码;不得上传私人 PDF 原件、完整出生报告或浏览器 scratch 会话文件。</span>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
function buildOracleEvidencePacket(task) {
|
||||
const target = {};
|
||||
task.targetFields.forEach(field => {
|
||||
@@ -1895,7 +2021,8 @@ function buildOracleEvidencePacket(task) {
|
||||
},
|
||||
operator_checklist: [
|
||||
'Fill tool_name and tool_version_or_url from the external product.',
|
||||
'Attach source_artifact as a screenshot/export path outside this local engine.',
|
||||
'Attach source_artifact as a repo-relative references/oracle/artifacts/... path after 必须打码.',
|
||||
'Do not attach private PDF originals, complete birth reports, browser scratch folders, account sessions, cookies, tokens, or desktop notifications.',
|
||||
'Fill every target value from black-box output before requesting review.',
|
||||
],
|
||||
};
|
||||
@@ -1911,7 +2038,7 @@ function downloadOracleEvidencePacket(caseId) {
|
||||
const packet = buildOracleEvidencePacket(task);
|
||||
downloadText(JSON.stringify(packet, null, 2), `jyotish-oracle-evidence-${task.caseId}.json`, 'application/json;charset=utf-8');
|
||||
if (status) {
|
||||
status.textContent = `已下载 ${task.caseId} 空白证据包;填入外部截图和数值后再运行 evidence validator。`;
|
||||
status.textContent = `已下载 ${task.caseId} 空白证据包;source_artifact 请指向 references/oracle/artifacts/ 下已打码文件,再运行 evidence validator。`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -172,6 +172,10 @@ async function generateReportArtifact(payload) {
|
||||
return postJson('/api/report_artifact', payload);
|
||||
}
|
||||
|
||||
async function validateOracleEvidence(payload) {
|
||||
return postJson('/api/oracle_evidence', payload);
|
||||
}
|
||||
|
||||
async function computeThematicReport(payload) {
|
||||
return postJson('/api/thematic_report', payload);
|
||||
}
|
||||
@@ -437,6 +441,7 @@ window.JyotishAPI = {
|
||||
computeRelationship,
|
||||
importChart,
|
||||
generateReportArtifact,
|
||||
validateOracleEvidence,
|
||||
computeThematicReport,
|
||||
getAPIHealth,
|
||||
getCapabilityAudit,
|
||||
|
||||
+81
-1
@@ -5217,6 +5217,64 @@ body { font-family: var(--font-body); background: var(--bg-page); color: var(--t
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
}
|
||||
.ai-prompt-pack-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
.ai-prompt-pack-actions button {
|
||||
min-height: 30px;
|
||||
padding: 5px 9px;
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--bg-surface);
|
||||
color: var(--text-body);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ai-prompt-pack-actions button:hover {
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
.ayanamsa-runtime-status {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 4px 10px;
|
||||
align-items: center;
|
||||
margin: 12px 0;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--bg-surface);
|
||||
}
|
||||
.ayanamsa-runtime-status span {
|
||||
padding: 3px 7px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: rgba(32, 111, 83, 0.12);
|
||||
color: var(--accent);
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
}
|
||||
.ayanamsa-runtime-status-browser_fallback span {
|
||||
background: rgba(160, 98, 0, 0.13);
|
||||
color: #8a5600;
|
||||
}
|
||||
.ayanamsa-runtime-status strong {
|
||||
min-width: 0;
|
||||
color: var(--text-heading);
|
||||
font-size: 13px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.ayanamsa-runtime-status small,
|
||||
.ayanamsa-runtime-status em {
|
||||
grid-column: 1 / -1;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.ai-prompt-pack-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
@@ -5884,6 +5942,28 @@ body { font-family: var(--font-body); background: var(--bg-page); color: var(--t
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--bg-surface);
|
||||
}
|
||||
.oracle-evidence-progress-dashboard {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin-top: 8px;
|
||||
padding: 10px;
|
||||
border: 1px solid rgba(21, 94, 117, 0.18);
|
||||
border-radius: var(--radius-sm);
|
||||
background: rgba(236, 254, 255, 0.74);
|
||||
}
|
||||
.oracle-evidence-progress-bar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(148, 163, 184, 0.22);
|
||||
}
|
||||
.oracle-evidence-progress-bar span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, var(--blue), var(--green));
|
||||
}
|
||||
.oracle-evidence-intake-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
@@ -6522,9 +6602,9 @@ body { font-family: var(--font-body); background: var(--bg-page); color: var(--t
|
||||
.first-use-grid,
|
||||
.runtime-health-grid,
|
||||
.trust-status-grid,
|
||||
.oracle-evidence-intake-grid,
|
||||
.terminology-mode-options,
|
||||
.case-workspace-controls { grid-template-columns: 1fr; }
|
||||
.oracle-evidence-intake-grid { grid-template-columns: 1fr; }
|
||||
.static-demo-boundary-grid { grid-template-columns: 1fr; }
|
||||
.validation-transparency-grid { grid-template-columns: 1fr; }
|
||||
.case-workspace-counts,
|
||||
|
||||
+22
@@ -356,3 +356,25 @@
|
||||
- 完成副手任务升级:新增 Round 13/14/15 工作单;Round 15 改为 6 个并行包,覆盖 Evidence 判卷闭环、Shadbala 六分量、Dasha 边界日期、高需求技法、外部截图存档规范与下一轮 Codex 任务建议。
|
||||
- 验证完成:`python3 -B -m pytest tests/test_frontend_productization.py::test_trust_center_exposes_oracle_evidence_intake_cards tests/test_frontend_productization.py::test_trust_center_and_ai_expose_dasha_shadbala_calibration_status tests/test_frontend_productization.py::test_provenance_panchanga_workspace_panel_is_productized tests/test_api_server_security.py::test_oracle_evidence_api_validates_uploaded_packets tests/test_oracle_collection_queue.py tests/test_oracle_evidence_validator.py -q` 通过;`python3 scripts/oracle_collection_queue.py ...` + `python3 scripts/oracle_evidence_validator.py ...` 仍显示 `valid_packets: 0` / `ready_for_calibration: 0`;`npm run build --prefix jyotish-app` 通过;`git diff --check` 通过。
|
||||
- 当前下一最高优先级:创建 `references/oracle/artifacts/` 存档规范和第一条真实 JHora/PyJHora 黑盒证据包,优先推动 Shadbala 六分量与 Vimshottari Dasha 边界日期从 0/5 进入至少 1/5 可人工复核状态。
|
||||
- 完成前端 Multi-Ayanamsa 与 AI Prompt Pack 可视化补强:完整解盘页 `AI Prompt Pack` 面板新增 Ayanamsa runtime status,区分 `backend_computed` 与 `browser_fallback`,显示 requested/applied ayanamsa 与降级说明;新增“复制 Prompt / 复制 Evidence”按钮,复制内容包含 prompt、evidence_snapshot、retrieval_plan 与 calculation_boundary。
|
||||
- TDD/验证完成:先让 `test_frontend_ayanamsa_settings_are_live_api_parameters` 和 `test_ai_prompt_pack_panel_exposes_copyable_audit_context` 红灯,随后实现转绿;`python3 -m pytest -q tests/test_frontend_productization.py -k "ayanamsa_settings or ai_prompt_pack_panel or ai_chat_prefers"` 通过;`npm run build --prefix jyotish-app` 通过;`git diff --check` 通过。
|
||||
- 当前下一最高优先级:继续 `references/oracle/artifacts/` 存档规范与第一条 JHora/PyJHora 黑盒证据包采集说明,和 Antigravity Round 16 的 artifact storage / Shadbala component validator / first JHora sample checklist 对齐。
|
||||
- 完成 Antigravity Round 18 重型副手任务单:新增 `docs/research/antigravity_sidecar_work_order_round18_2026_06_25.md`,要求副手至少产出 12 份报告,覆盖当前补丁复核、全网开源许可证矩阵、功能缺口重排、用户黑盒流程、隐私仓库卫生、测试债和 AI Prompt Pack/Skill 同步,明确只允许宽松许可证代码进入可复用候选。
|
||||
- 完成 Oracle artifact 存档规范:新增 `references/oracle/artifacts/.gitkeep` 与 `references/oracle/artifacts/README.md`,README 同步 `references/oracle/artifacts/`、`source_artifact`、`external_oracle_artifact`、必须打码、不得提交私人 PDF 原件、不得提交完整出生报告、不得提交浏览器 scratch 的规则;前端 Evidence Packet 下载文案同步相对路径与脱敏要求。
|
||||
- 完成 Shadbala evidence 强校验:`scripts/oracle_evidence_validator.py` 新增 `SHADBALA_REQUIRED_PLANETS` 与 `SHADBALA_REQUIRED_COMPONENTS`,对 Sun/Moon/Mars/Mercury/Jupiter/Venus/Saturn 七曜的 sthana/dig/kala/chesta/naisargika/drik 六分量逐项拦截;测试覆盖空 dict、只填 Sun、完整七曜六分量和 API 上传复用 validator。
|
||||
- 完成 Trust Center 真实采集进度面板:`renderOracleEvidenceProgressDashboard()` 在 Oracle Evidence Intake 顶部展示 `Dasha/Shadbala 真实进度`、`0 / 5`、`valid_packets`、`ready_for_calibration`、`production_tuning_allowed=false`、`references/oracle/artifacts/` 与 `missing_shadbala_component` 边界,避免用户误以为 Dasha/Shadbala 已完成外部绝对值校准。
|
||||
- 修复 Round 17 暴露的门禁失败:移动端布局测试失败根因是 CSS selector 组被新增 `.oracle-evidence-intake-grid` 打断,已恢复原断言组并单独声明 oracle intake 移动单列;`jyotish-app/public/api-bridge.js` 已同步 `validateOracleEvidence`,确保 public/static bridge 与源码 bridge 一致。
|
||||
- 验证完成:`python3 -m pytest -q tests/test_frontend_productization.py::test_mobile_layout_keeps_dense_sections_single_column tests/test_frontend_productization.py::test_trust_center_exposes_oracle_evidence_intake_cards tests/test_frontend_productization.py::test_oracle_artifact_storage_policy_is_documented` 通过;`python3 -m pytest -q tests/test_api_server_security.py::test_oracle_evidence_api_validates_uploaded_packets tests/test_oracle_evidence_validator.py` 通过;`python3 -m pytest tests/test_frontend_productization.py tests/test_cli_smoke.py tests/test_api_server_security.py tests/test_jaimini.py tests/test_shadbala_complete.py tests/test_transit_trigger.py tests/test_oracle_collection_queue.py tests/test_oracle_evidence_validator.py -q` 238 项通过;`npm run build --prefix jyotish-app` 通过;`python3 scripts/run_quality_gate.py --profile quick --skip-yoga-logic` 通过,包含 runtime smoke。
|
||||
- 当前下一最高优先级:把第一条 JHora/PyJHora 黑盒 evidence packet 教程/模板落成可交给人工执行的文档,并等待外部截图或 stdout 后把 `valid_packets` 从 0/5 推到 1/5;同时接收 Round 18 副手报告,优先吸收宽松许可证可复用候选和真实用户流程缺口。
|
||||
- 完成 Antigravity Round 19 副手任务单:新增 `docs/research/antigravity_sidecar_work_order_round19_2026_06_25.md`,要求副手以当前工作树重新复核 Round 18 过期结论、JHora/PyJHora 教程、第一条 evidence packet 模板、1/5 人工 runbook、runtime artifact 卫生、progress dashboard DOM/UX、Top 30 开源许可证复筛、Ashtakoot 破冰、Shadbala 单位校验、AI Prompt Pack oracle progress 和 Git tracking 策略。
|
||||
- 完成第一条外部证据包执行资产:新增 `docs/user_jhora_capture_guide.md` 与 `references/oracle/evidence_packet_templates/jhora_steve_jobs_lahiri_first_packet.json`;模板保持 `status=draft`,只预留 Steve Jobs Lahiri/true node 的 metadata、Vimshottari start date 和 Shadbala 七曜六分量空位,不伪造任何外部数值。
|
||||
- 验证完成:`python3 -m pytest -q tests/test_oracle_collection_queue.py::test_first_jhora_evidence_packet_template_is_safe_and_fillable tests/test_frontend_productization.py::test_first_jhora_capture_guide_is_actionable` 通过;`python3 -m json.tool references/oracle/evidence_packet_templates/jhora_steve_jobs_lahiri_first_packet.json` 通过。
|
||||
- 当前下一最高优先级:接收 Round 19 副手报告并继续修正其发现;并在没有人工 JHora/PyJHora 截图前,优先补 `.gitignore` runtime-smoke HTML 卫生、Shadbala 数值单位/类型校验和 AI Prompt Pack 的 oracle progress 摘要。
|
||||
- 完成 Round 19 反馈的无需人工 P0/P1 修复:`.gitignore` 新增 `runtime-smoke-report-*.html` 与 `jyotish-app/runtime-smoke-report-*.html`,避免 runtime smoke HTML 上云;`oracle_evidence_validator.py` 对 Shadbala 七曜六分量增加 int/float 且非 bool、非负校验,拒绝字符串数字和负数;`jyotish_engine.py`、`jyotish_api_server.py` 与前端 fallback `normalizeAIPromptPack()` 的 `evidence_snapshot.oracle_progress` 均携带 `valid_packets: 0`、`ready_for_calibration: 0`、`production_tuning_allowed: false`、`references/oracle/artifacts/` 与 `external_verified` 晋级规则。
|
||||
- TDD/验证完成:新增红灯测试 `test_runtime_smoke_html_artifacts_are_ignored`、`test_validator_rejects_non_numeric_or_negative_shadbala_components`、CLI prompt pack `oracle_progress` 断言和前端 fallback 静态断言;修复后 `python3 -m pytest -q tests/test_frontend_productization.py::test_frontend_branded_avatar_and_prompt_pack_are_productized tests/test_frontend_productization.py::test_api_bridge_variants_prefer_backend_prompt_pack_context tests/test_frontend_productization.py::test_runtime_smoke_html_artifacts_are_ignored` 通过;`python3 -m pytest -q tests/test_cli_smoke.py::test_full_reading_reports_ayanamsa_metadata_and_ai_prompt_pack tests/test_oracle_evidence_validator.py tests/test_api_server_security.py::test_oracle_evidence_api_validates_uploaded_packets` 通过;`npm run build --prefix jyotish-app` 通过;`python3 scripts/run_quality_gate.py --profile quick --skip-yoga-logic` 通过,当前 quick 集合 242 项 pytest、build 与 runtime smoke 均通过。
|
||||
- 当前下一最高优先级:在无法本机生成真实 JHora/PyJHora 外部截图的前提下,推进 Ashtakoot 合婚破冰与外部 oracle template 设计;同时准备把 Round 16-19 副手报告、work orders、artifact policy、capture guide 与 first packet template 纳入 Git 跟踪,避免不同窗口再次遗漏。
|
||||
- 完成 Antigravity Round 20 副手任务单:新增 `docs/research/antigravity_sidecar_work_order_round20_2026_06_25.md`,要求副手纠正 Round 19 “Ashtakoot 完全缺失”的过期结论,复核已有 `scripts/ashtakoot.py`、`tests/test_ashtakoot.py`、`jyotish_engine.py ashtakoot`、`/api/synastry` 与前端合盘入口;同时继续审计 Round 19 修复、Ashtakoot oracle case 设计、UI/E2E 流程、JHora 1/5 人工监督、Git 纳入策略、开源 Ashtakoot 许可证矩阵、Shadbala 单位/总分设计和 AI Prompt Pack oracle progress。
|
||||
- 当前下一最高优先级:先不重写 Ashtakoot 已有算法,转为补 Ashtakoot 外部 oracle cases、E2E 用户流程和 Git tracking 收口;真实 JHora/PyJHora 1/5 仍等待人工外部工具。
|
||||
- 完成 Antigravity Round 21 重型副手任务单:新增 `docs/research/antigravity_sidecar_work_order_round21_2026_06_25.md`,把副手工作量扩到 18 个报告包、至少 20 个联网开源/产品复核对象、Top 40 ROI 任务,并明确 Ashtakoot oracle、VedAstro/RaviKarrii/pyhora2 许可证深挖、Trust Center UX、Git 入库、Shadbala 二期校验和 Round 22 执行计划。
|
||||
- 完成 Ashtakoot 外部 oracle 第一层队列与 validator 强化:新增 `references/oracle/ashtakoot_oracle_cases.json` 5 条 draft cases,`oracle_collection_queue.py` 可输出 `target_modules=["ashtakoot"]`,README 记录 `Ashtakoot 外部合婚 oracle` 命令;`oracle_evidence_validator.py` 增加 36 分制范围、8 Kuta 分项范围和总分求和一致性拦截,拒绝 99 分、字符串/非法范围与 sum mismatch 的假外部证据包。
|
||||
- 验证完成:`python3 -m pytest -q tests/test_oracle_evidence_validator.py tests/test_oracle_collection_queue.py` 14 项通过;`python3 -m pytest -q tests/test_ashtakoot.py tests/test_frontend_productization.py::test_dasha_reference_audit_is_documented_and_gated` 53 项通过;`npm run build --prefix jyotish-app` 通过;Ashtakoot queue 输出 5 条 template-only 样本且 `production_tuning_allowed=false`。
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# External Oracle Artifacts
|
||||
|
||||
This directory stores sanitized evidence artifacts referenced by `source_artifact`
|
||||
inside external oracle evidence packets.
|
||||
|
||||
## Required Path Policy
|
||||
|
||||
- Use repo-relative paths such as `references/oracle/artifacts/jhora_REDACTED_YEAR_moon_lahiri_v1.png`.
|
||||
- `source_artifact` must point to a file in `references/oracle/artifacts/` or to a documented external review location.
|
||||
- Mark the artifact kind as `external_oracle_artifact` in operator notes when possible.
|
||||
|
||||
## Privacy Rules
|
||||
|
||||
- 必须打码 any real person's full name, contact details, exact street address, hospital, account name, or other identifying text.
|
||||
- 不得提交私人 PDF 原件;only sanitized screenshots or typed stdout snippets belong here.
|
||||
- 不得提交完整出生报告 or full private reading transcripts.
|
||||
- 不得提交浏览器 scratch folders, screenshots containing account sessions, cookies, tokens, or desktop notifications.
|
||||
- Prefer synthetic fixtures, public figures, or heavily redacted screenshots.
|
||||
|
||||
## Allowed Examples
|
||||
|
||||
- Redacted JHora screenshot showing settings, Moon longitude, Dasha boundary, or Shadbala component table.
|
||||
- Redacted PyJHora stdout captured in a plain text file with command metadata.
|
||||
- Public-figure sample screenshot where source settings are visible and no private account data appears.
|
||||
|
||||
## Not Allowed
|
||||
|
||||
- Unredacted volunteer/client birth data.
|
||||
- `output_report.txt`, `results_extracted.md`, private PDFs, or full generated readings.
|
||||
- Local engine output from this repository pretending to be an external oracle.
|
||||
|
||||
Evidence packets remain `draft` until their target values are filled, their
|
||||
artifact is reviewed, and their status is explicitly promoted to
|
||||
`external_verified`.
|
||||
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"scope": "external_ashtakoot_oracle_cases",
|
||||
"notes": [
|
||||
"These rows are external compatibility evidence templates, not production tuning constants.",
|
||||
"Do not promote any row to external_verified until all target values come from JHora/VedAstro/AstroSage or another documented external source.",
|
||||
"Do not copy AGPL/GPL/commercial implementation code or proprietary screenshots beyond redacted evidence artifacts."
|
||||
],
|
||||
"template_cases": [
|
||||
{
|
||||
"id": "ashtakoot_public_couple_lahiri_01",
|
||||
"status": "template_only",
|
||||
"source": "JHora/VedAstro/AstroSage compatibility screenshot",
|
||||
"privacy": "public_or_synthetic_relationship_template",
|
||||
"birth": {
|
||||
"male": {
|
||||
"year": 1955,
|
||||
"month": 2,
|
||||
"day": 24,
|
||||
"hour": 19,
|
||||
"minute": 15,
|
||||
"second": 0,
|
||||
"lat": 37.7749,
|
||||
"lon": -122.4194,
|
||||
"tz": -8
|
||||
},
|
||||
"female": {
|
||||
"year": 1958,
|
||||
"month": 8,
|
||||
"day": 14,
|
||||
"hour": 12,
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
"lat": 34.0522,
|
||||
"lon": -118.2437,
|
||||
"tz": -8
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "true",
|
||||
"matching_system": "ashtakoot_36_point"
|
||||
},
|
||||
"target": {
|
||||
"total_score": null,
|
||||
"varna": null,
|
||||
"vashya": null,
|
||||
"tara": null,
|
||||
"yoni": null,
|
||||
"graha_maitri": null,
|
||||
"gana": null,
|
||||
"bhakoot": null,
|
||||
"nadi": null,
|
||||
"kuja_status": null
|
||||
},
|
||||
"verification_note": "Use public/synthetic relationship data first; fill targets only from external compatibility output."
|
||||
},
|
||||
{
|
||||
"id": "ashtakoot_synthetic_high_score_lahiri_02",
|
||||
"status": "template_only",
|
||||
"source": "VedAstro/JHora compatibility screenshot",
|
||||
"privacy": "synthetic_relationship_template",
|
||||
"birth": {
|
||||
"male_moon_longitude_deg": 45.0,
|
||||
"female_moon_longitude_deg": 48.0
|
||||
},
|
||||
"settings": {
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "mean",
|
||||
"matching_system": "ashtakoot_36_point",
|
||||
"input_mode": "moon_longitude_only"
|
||||
},
|
||||
"target": {
|
||||
"total_score": null,
|
||||
"varna": null,
|
||||
"vashya": null,
|
||||
"tara": null,
|
||||
"yoni": null,
|
||||
"graha_maitri": null,
|
||||
"gana": null,
|
||||
"bhakoot": null,
|
||||
"nadi": null,
|
||||
"kuja_status": null
|
||||
},
|
||||
"verification_note": "Synthetic high-score style case; use an external tool that accepts Moon longitude or matching birth data."
|
||||
},
|
||||
{
|
||||
"id": "ashtakoot_synthetic_nadi_dosha_03",
|
||||
"status": "template_only",
|
||||
"source": "JHora/AstroSage compatibility screenshot",
|
||||
"privacy": "synthetic_relationship_template",
|
||||
"birth": {
|
||||
"male_moon_longitude_deg": 0.0,
|
||||
"female_moon_longitude_deg": 180.0
|
||||
},
|
||||
"settings": {
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "mean",
|
||||
"matching_system": "ashtakoot_36_point",
|
||||
"input_mode": "moon_longitude_only"
|
||||
},
|
||||
"target": {
|
||||
"total_score": null,
|
||||
"varna": null,
|
||||
"vashya": null,
|
||||
"tara": null,
|
||||
"yoni": null,
|
||||
"graha_maitri": null,
|
||||
"gana": null,
|
||||
"bhakoot": null,
|
||||
"nadi": null,
|
||||
"kuja_status": null
|
||||
},
|
||||
"verification_note": "Nadi-dosha boundary case; capture any exception or mitigation note shown by the external source."
|
||||
},
|
||||
{
|
||||
"id": "ashtakoot_synthetic_bhakoot_boundary_04",
|
||||
"status": "template_only",
|
||||
"source": "VedAstro/JHora compatibility screenshot",
|
||||
"privacy": "synthetic_relationship_template",
|
||||
"birth": {
|
||||
"male_moon_longitude_deg": 15.5,
|
||||
"female_moon_longitude_deg": 120.0
|
||||
},
|
||||
"settings": {
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "mean",
|
||||
"matching_system": "ashtakoot_36_point",
|
||||
"input_mode": "moon_longitude_only"
|
||||
},
|
||||
"target": {
|
||||
"total_score": null,
|
||||
"varna": null,
|
||||
"vashya": null,
|
||||
"tara": null,
|
||||
"yoni": null,
|
||||
"graha_maitri": null,
|
||||
"gana": null,
|
||||
"bhakoot": null,
|
||||
"nadi": null,
|
||||
"kuja_status": null
|
||||
},
|
||||
"verification_note": "Bhakoot/rashi distance boundary case; capture component score and external explanation."
|
||||
},
|
||||
{
|
||||
"id": "ashtakoot_kuja_dosha_chart_05",
|
||||
"status": "template_only",
|
||||
"source": "JHora/AstroSage compatibility screenshot",
|
||||
"privacy": "synthetic_relationship_template",
|
||||
"birth": {
|
||||
"male": {
|
||||
"year": 1990,
|
||||
"month": 1,
|
||||
"day": 15,
|
||||
"hour": 6,
|
||||
"minute": 30,
|
||||
"second": 0,
|
||||
"lat": 28.6139,
|
||||
"lon": 77.209,
|
||||
"tz": 5.5
|
||||
},
|
||||
"female": {
|
||||
"year": 1992,
|
||||
"month": 7,
|
||||
"day": 20,
|
||||
"hour": 18,
|
||||
"minute": 45,
|
||||
"second": 0,
|
||||
"lat": 19.076,
|
||||
"lon": 72.8777,
|
||||
"tz": 5.5
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "mean",
|
||||
"matching_system": "ashtakoot_36_point",
|
||||
"requires_full_chart": true
|
||||
},
|
||||
"target": {
|
||||
"total_score": null,
|
||||
"varna": null,
|
||||
"vashya": null,
|
||||
"tara": null,
|
||||
"yoni": null,
|
||||
"graha_maitri": null,
|
||||
"gana": null,
|
||||
"bhakoot": null,
|
||||
"nadi": null,
|
||||
"kuja_status": null
|
||||
},
|
||||
"verification_note": "Full chart compatibility case; external artifact must show Kuja/Manglik status or its absence."
|
||||
}
|
||||
]
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"capture_id": "external_template_steve_jobs_dasha_lahiri",
|
||||
"status": "draft",
|
||||
"case_id": "template_steve_jobs_dasha_lahiri",
|
||||
"birth": {
|
||||
"year": 1955,
|
||||
"month": 2,
|
||||
"day": 24,
|
||||
"hour": 19,
|
||||
"minute": 15,
|
||||
"second": 0,
|
||||
"lat": 37.7749,
|
||||
"lon": -122.4194,
|
||||
"tz": -8
|
||||
},
|
||||
"settings": {
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "true"
|
||||
},
|
||||
"required_metadata_fields": [
|
||||
"tool_name",
|
||||
"tool_version_or_url",
|
||||
"capture_date",
|
||||
"source_artifact",
|
||||
"ayanamsa",
|
||||
"node_mode",
|
||||
"timezone",
|
||||
"operator_note"
|
||||
],
|
||||
"metadata": {
|
||||
"tool_name": "",
|
||||
"tool_version_or_url": "",
|
||||
"capture_date": "",
|
||||
"source_artifact": "references/oracle/artifacts/",
|
||||
"ayanamsa": "Lahiri",
|
||||
"node_mode": "true node",
|
||||
"timezone": "UTC-08:00",
|
||||
"operator_note": ""
|
||||
},
|
||||
"target_placeholders": {
|
||||
"target.vimshottari_start_date": null,
|
||||
"target.shadbala_components": {
|
||||
"Sun": {
|
||||
"sthana": null,
|
||||
"dig": null,
|
||||
"kala": null,
|
||||
"chesta": null,
|
||||
"naisargika": null,
|
||||
"drik": null
|
||||
},
|
||||
"Moon": {
|
||||
"sthana": null,
|
||||
"dig": null,
|
||||
"kala": null,
|
||||
"chesta": null,
|
||||
"naisargika": null,
|
||||
"drik": null
|
||||
},
|
||||
"Mars": {
|
||||
"sthana": null,
|
||||
"dig": null,
|
||||
"kala": null,
|
||||
"chesta": null,
|
||||
"naisargika": null,
|
||||
"drik": null
|
||||
},
|
||||
"Mercury": {
|
||||
"sthana": null,
|
||||
"dig": null,
|
||||
"kala": null,
|
||||
"chesta": null,
|
||||
"naisargika": null,
|
||||
"drik": null
|
||||
},
|
||||
"Jupiter": {
|
||||
"sthana": null,
|
||||
"dig": null,
|
||||
"kala": null,
|
||||
"chesta": null,
|
||||
"naisargika": null,
|
||||
"drik": null
|
||||
},
|
||||
"Venus": {
|
||||
"sthana": null,
|
||||
"dig": null,
|
||||
"kala": null,
|
||||
"chesta": null,
|
||||
"naisargika": null,
|
||||
"drik": null
|
||||
},
|
||||
"Saturn": {
|
||||
"sthana": null,
|
||||
"dig": null,
|
||||
"kala": null,
|
||||
"chesta": null,
|
||||
"naisargika": null,
|
||||
"drik": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"integrity_checks": {
|
||||
"must_not_come_from_local_engine": true,
|
||||
"requires_external_artifact": true,
|
||||
"requires_status_external_verified_before_calibration": true,
|
||||
"reject_global_shadbala_scaling": true
|
||||
},
|
||||
"promotion_status_after_fill": "external_verified",
|
||||
"operator_checklist": [
|
||||
"Open JHora or PyJHora as an external black-box oracle.",
|
||||
"Capture a redacted source artifact under references/oracle/artifacts/.",
|
||||
"Fill Vimshottari start date from the external tool.",
|
||||
"Fill all seven Shadbala planets and all six components from the external tool.",
|
||||
"Promote status to external_verified only after source_artifact and target values are filled."
|
||||
]
|
||||
}
|
||||
@@ -1817,6 +1817,17 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'不要仅凭单一配置下结论;核心判断至少交叉 D1、D9、Dasha、Shadbala/Ashtakavarga 或 Transit 中的两个证据层。',
|
||||
'必须显式标注置信度和边界:Dasha/PDF 起点差异、Shadbala 外部绝对值 oracle 尚未完成时,不得声称已经完全校准。',
|
||||
]
|
||||
oracle_progress = {
|
||||
'scope': 'external_oracle_evidence_validation',
|
||||
'collection_queue': 'external_oracle_collection_queue',
|
||||
'total_packets': 5,
|
||||
'valid_packets': 0,
|
||||
'ready_for_calibration': 0,
|
||||
'production_tuning_allowed': False,
|
||||
'artifact_policy': 'references/oracle/artifacts/',
|
||||
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
|
||||
'boundary': 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.',
|
||||
}
|
||||
return {
|
||||
'schema_version': 1,
|
||||
'mode': 'jyotish_structured_prompt_pack',
|
||||
@@ -1844,6 +1855,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'quality_boundary': {
|
||||
'external_oracle_status': 'D1/D9/VedAstro longitude boundary covered; Dasha/Shadbala external absolute calibration still requires multi-source oracle expansion.',
|
||||
},
|
||||
'oracle_progress': oracle_progress,
|
||||
},
|
||||
'retrieval_plan': {
|
||||
'local_reference_docs': [
|
||||
@@ -1858,6 +1870,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'no_single_factor_conclusion',
|
||||
'd1_d9_dasha_cross_validation',
|
||||
'oracle_boundary_visible',
|
||||
'external_oracle_evidence_validation',
|
||||
'confidence_labeled_reading',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -567,6 +567,20 @@ def _planet_snapshot(planets, planet_name):
|
||||
}
|
||||
|
||||
|
||||
def _oracle_progress_snapshot():
|
||||
return {
|
||||
'scope': 'external_oracle_evidence_validation',
|
||||
'collection_queue': 'external_oracle_collection_queue',
|
||||
'total_packets': 5,
|
||||
'valid_packets': 0,
|
||||
'ready_for_calibration': 0,
|
||||
'production_tuning_allowed': False,
|
||||
'artifact_policy': 'references/oracle/artifacts/',
|
||||
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
|
||||
'boundary': 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.',
|
||||
}
|
||||
|
||||
|
||||
def _build_ai_prompt_pack(report):
|
||||
"""Build a compact, evidence-first prompt pack for downstream AI/RAG reading."""
|
||||
modules = report.get('modules', {}) if isinstance(report, dict) else {}
|
||||
@@ -640,6 +654,7 @@ def _build_ai_prompt_pack(report):
|
||||
'warnings': report.get('warnings', []),
|
||||
'external_oracle_status': 'D1/D9/VedAstro longitude boundary covered; Dasha/Shadbala external absolute calibration still requires multi-source oracle expansion.',
|
||||
},
|
||||
'oracle_progress': _oracle_progress_snapshot(),
|
||||
}
|
||||
|
||||
prompt_lines = [
|
||||
@@ -670,6 +685,7 @@ def _build_ai_prompt_pack(report):
|
||||
'no_single_factor_conclusion',
|
||||
'd1_d9_dasha_cross_validation',
|
||||
'oracle_boundary_visible',
|
||||
'external_oracle_evidence_validation',
|
||||
'confidence_labeled_reading',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -58,6 +58,19 @@ SOURCE_GUIDANCE = {
|
||||
"The row is not derived from this repository's local Shadbala output.",
|
||||
],
|
||||
},
|
||||
"ashtakoot": {
|
||||
"preferred_sources": ["JHora compatibility screenshot", "VedAstro HTTP/API output", "AstroSage public compatibility screen"],
|
||||
"steps": [
|
||||
"Set both birth records or both Moon longitudes in the external compatibility tool.",
|
||||
"Record the 36-point total score and all eight Kuta component scores.",
|
||||
"Capture Kuja/Manglik status and any visible ayanamsa, node mode or matching settings.",
|
||||
],
|
||||
"promotion_criteria": [
|
||||
"All eight Ashtakoot component targets are filled from an external compatibility source.",
|
||||
"The 36-point total is traceable to component scores.",
|
||||
"The row is not derived from this repository's local synastry/ashtakoot output.",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +80,16 @@ FIELD_TO_MODULE = {
|
||||
"ascendant_longitude_deg": "longitude",
|
||||
"vimshottari_start_date": "dasha",
|
||||
"shadbala_components": "shadbala",
|
||||
"total_score": "ashtakoot",
|
||||
"varna": "ashtakoot",
|
||||
"vashya": "ashtakoot",
|
||||
"tara": "ashtakoot",
|
||||
"yoni": "ashtakoot",
|
||||
"graha_maitri": "ashtakoot",
|
||||
"gana": "ashtakoot",
|
||||
"bhakoot": "ashtakoot",
|
||||
"nadi": "ashtakoot",
|
||||
"kuja_status": "ashtakoot",
|
||||
}
|
||||
|
||||
REQUIRED_EVIDENCE_METADATA_FIELDS = [
|
||||
|
||||
@@ -24,6 +24,29 @@ LOCAL_ENGINE_MARKERS = [
|
||||
"oracle_collection_queue.py",
|
||||
"oracle_boundary_audit.py",
|
||||
]
|
||||
SHADBALA_REQUIRED_PLANETS = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
|
||||
SHADBALA_REQUIRED_COMPONENTS = ["sthana", "dig", "kala", "chesta", "naisargika", "drik"]
|
||||
ASHTAKOOT_SCORE_RANGES = {
|
||||
"target.total_score": (0.0, 36.0),
|
||||
"target.varna": (0.0, 1.0),
|
||||
"target.vashya": (0.0, 2.0),
|
||||
"target.tara": (0.0, 3.0),
|
||||
"target.yoni": (0.0, 4.0),
|
||||
"target.graha_maitri": (0.0, 5.0),
|
||||
"target.gana": (0.0, 6.0),
|
||||
"target.bhakoot": (0.0, 7.0),
|
||||
"target.nadi": (0.0, 8.0),
|
||||
}
|
||||
ASHTAKOOT_COMPONENT_FIELDS = [
|
||||
"target.varna",
|
||||
"target.vashya",
|
||||
"target.tara",
|
||||
"target.yoni",
|
||||
"target.graha_maitri",
|
||||
"target.gana",
|
||||
"target.bhakoot",
|
||||
"target.nadi",
|
||||
]
|
||||
|
||||
|
||||
def _resolve_path(path: str) -> str:
|
||||
@@ -72,6 +95,13 @@ def _validate_packet(task: dict[str, Any]) -> dict[str, Any]:
|
||||
if _is_blank(value):
|
||||
problems.append(f"placeholder_unfilled:{field}")
|
||||
|
||||
shadbala_components = target_placeholders.get("target.shadbala_components")
|
||||
if "target.shadbala_components" in expected_fields:
|
||||
problems.extend(_validate_shadbala_components(shadbala_components))
|
||||
|
||||
if "target.total_score" in expected_fields:
|
||||
problems.extend(_validate_ashtakoot_scores(target_placeholders))
|
||||
|
||||
integrity = packet.get("integrity_checks", {})
|
||||
if integrity.get("must_not_come_from_local_engine") and _is_local_engine_artifact(packet):
|
||||
problems.append("local_engine_artifact_rejected")
|
||||
@@ -95,6 +125,58 @@ def _validate_packet(task: dict[str, Any]) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _validate_shadbala_components(value: Any) -> list[str]:
|
||||
problems: list[str] = []
|
||||
if _is_blank(value) or not isinstance(value, dict):
|
||||
return ["missing_shadbala_component:all_planets"]
|
||||
for planet in SHADBALA_REQUIRED_PLANETS:
|
||||
row = value.get(planet)
|
||||
if not isinstance(row, dict) or _is_blank(row):
|
||||
problems.append(f"missing_shadbala_component:{planet}")
|
||||
continue
|
||||
for component in SHADBALA_REQUIRED_COMPONENTS:
|
||||
component_value = row.get(component)
|
||||
if _is_blank(component_value):
|
||||
problems.append(f"missing_shadbala_component:{planet}.{component}")
|
||||
continue
|
||||
if not isinstance(component_value, (int, float)) or isinstance(component_value, bool):
|
||||
problems.append(f"invalid_shadbala_component_type:{planet}.{component}")
|
||||
continue
|
||||
if component_value < 0:
|
||||
problems.append(f"invalid_shadbala_component_negative:{planet}.{component}")
|
||||
return problems
|
||||
|
||||
|
||||
def _is_number(value: Any) -> bool:
|
||||
return isinstance(value, (int, float)) and not isinstance(value, bool)
|
||||
|
||||
|
||||
def _validate_ashtakoot_scores(target_placeholders: dict[str, Any]) -> list[str]:
|
||||
problems: list[str] = []
|
||||
numeric_values: dict[str, float] = {}
|
||||
for field, (minimum, maximum) in ASHTAKOOT_SCORE_RANGES.items():
|
||||
if field not in target_placeholders:
|
||||
continue
|
||||
value = target_placeholders.get(field)
|
||||
if _is_blank(value):
|
||||
continue
|
||||
if not _is_number(value):
|
||||
problems.append(f"invalid_ashtakoot_score_type:{field}")
|
||||
continue
|
||||
numeric_value = float(value)
|
||||
numeric_values[field] = numeric_value
|
||||
if numeric_value < minimum or numeric_value > maximum:
|
||||
problems.append(f"invalid_ashtakoot_score_range:{field}")
|
||||
|
||||
if "target.total_score" in numeric_values and all(
|
||||
field in numeric_values for field in ASHTAKOOT_COMPONENT_FIELDS
|
||||
):
|
||||
component_sum = sum(numeric_values[field] for field in ASHTAKOOT_COMPONENT_FIELDS)
|
||||
if abs(component_sum - numeric_values["target.total_score"]) > 0.01:
|
||||
problems.append("ashtakoot_score_sum_mismatch")
|
||||
return problems
|
||||
|
||||
|
||||
def build_report(queue: dict[str, Any]) -> dict[str, Any]:
|
||||
packets = [_validate_packet(task) for task in queue.get("tasks", [])]
|
||||
ready_for_calibration = sum(1 for packet in packets if packet["ready_for_calibration"])
|
||||
|
||||
+5
-1
@@ -112,4 +112,8 @@
|
||||
- [x] AI Native Prompt/RAG 承载层第一步:`full-reading.ai_prompt_pack` 输出证据快照、检索文档、边界约束和结构化中文提示词,供网页/app 或 skill 后端 AI 代理生成高阶解读。
|
||||
- [x] Antigravity AI 副手工作单:新增 `docs/research/antigravity_sidecar_work_order_2026_06_25.md`,把 Antigravity 限定为外部 oracle 样本采集、网页/app 审计、skill 同步审计和浏览器用户流验证,避免与核心计算修改冲突。
|
||||
- [x] Standalone ayanamsa 全局状态修复:新增 `scripts/ayanamsa_utils.py`,让 Transit、Solar Return、Muhurta、cmd_muhurta、Yoga 验证脚本在 `FLG_SIDEREAL` 前显式设置 ayanamsa;默认 Lahiri,调用方可显式传入 Raman/KP 等。
|
||||
- [ ] 下一步:继续扩充多来源 oracle 样本,补 JHora/PyJHora 的 Moon sidereal longitude、ayanamsa、Vimshottari 起点和 Shadbala 六分量目标值;同时把前端 Multi-Ayanamsa 设置与 `ai_prompt_pack` 可视化。
|
||||
- [x] 前端 Multi-Ayanamsa 设置与 `ai_prompt_pack` 可视化:完整解盘页显示 Ayanamsa 后端实算/浏览器降级状态,AI Prompt Pack 支持复制 Prompt 与 Evidence 审计上下文。
|
||||
- [x] Oracle artifact 存档规范、Shadbala evidence 强校验与真实进度面板:`references/oracle/artifacts/` 已有脱敏规范与占位,Trust Center 显示 `0 / 5` 真实采集进度,validator 强制 Shadbala 七曜六分量,quick quality gate 已通过。
|
||||
- [x] Ashtakoot 外部 oracle 样本队列与第一层 validator:新增 5 条外部合婚 oracle draft cases,README/队列生成器/测试同步;validator 拦截 36 分制范围、8 Kuta 分项范围和总分不一致,防止假外部证据污染。
|
||||
- [x] Antigravity Round 21 重型副手任务单:把副手任务扩展到 18 个报告包、至少 20 个联网对标对象、Top 40 ROI 拆解,继续承接 Ashtakoot、开源复用、Trust Center UX、Git 入库和 Round 22 规划。
|
||||
- [ ] 第一条 JHora/PyJHora 黑盒证据包:等待人工外部工具截图/输出,补 Moon sidereal longitude、ayanamsa、Vimshottari 起点和 Shadbala 七曜六分量目标值,把 `valid_packets` 从 0/5 推到 1/5。
|
||||
|
||||
@@ -186,6 +186,13 @@ def test_full_reading_reports_ayanamsa_metadata_and_ai_prompt_pack() -> None:
|
||||
assert "references/ai-reading-workflow-prompt.md" in prompt_pack["retrieval_plan"]["local_reference_docs"]
|
||||
assert prompt_pack["evidence_snapshot"]["ayanamsa"]["name"] == "raman"
|
||||
assert prompt_pack["evidence_snapshot"]["core"]["ascendant"]["sign"] == result["chart"]["ascendant"]["sign"]
|
||||
oracle_progress = prompt_pack["evidence_snapshot"]["oracle_progress"]
|
||||
assert oracle_progress["scope"] == "external_oracle_evidence_validation"
|
||||
assert oracle_progress["valid_packets"] == 0
|
||||
assert oracle_progress["ready_for_calibration"] == 0
|
||||
assert oracle_progress["production_tuning_allowed"] is False
|
||||
assert oracle_progress["artifact_policy"] == "references/oracle/artifacts/"
|
||||
assert "external_verified" in oracle_progress["promotion_rule"]
|
||||
|
||||
|
||||
def test_varga_cli_outputs_d9_and_d10() -> None:
|
||||
|
||||
@@ -253,6 +253,8 @@ def test_frontend_branded_avatar_and_prompt_pack_are_productized() -> None:
|
||||
assert "function renderAIPromptPackPanel" in main
|
||||
assert "ai-prompt-pack-panel" in main
|
||||
assert "evidence_snapshot" in main
|
||||
assert "oracle_progress" in main
|
||||
assert "artifact_policy: 'references/oracle/artifacts/'" in main
|
||||
assert "retrieval_plan" in main
|
||||
assert ".logo-avatar" in style
|
||||
assert ".ai-prompt-pack-panel" in style
|
||||
@@ -273,6 +275,11 @@ def test_frontend_ayanamsa_settings_are_live_api_parameters() -> None:
|
||||
assert "computeChart({ year, month, day, hour, minute, second, lat, lon, tz })" in main
|
||||
assert "fallbackChart._calculation_boundary" in main
|
||||
assert "payload.ayanamsa" in main
|
||||
assert "renderAyanamsaRuntimeStatus(cd, pack)" in main
|
||||
assert "function renderAyanamsaRuntimeStatus" in main
|
||||
assert "backend_computed" in main
|
||||
assert "browser_fallback" in main
|
||||
assert "ayanamsa-runtime-status" in main
|
||||
|
||||
|
||||
def test_ai_chat_prefers_backend_prompt_pack_context() -> None:
|
||||
@@ -285,6 +292,20 @@ def test_ai_chat_prefers_backend_prompt_pack_context() -> None:
|
||||
assert "AI Prompt Pack 已作为上下文入口" in ai_chat
|
||||
|
||||
|
||||
def test_ai_prompt_pack_panel_exposes_copyable_audit_context() -> None:
|
||||
main = read("main.js")
|
||||
style = read("style.css")
|
||||
|
||||
assert "ai-prompt-pack-actions" in main
|
||||
assert "data-ai-prompt-action=\"copy-prompt\"" in main
|
||||
assert "data-ai-prompt-action=\"copy-evidence\"" in main
|
||||
assert "setupAIPromptPackActions" in main
|
||||
assert "copyAIPromptPackSection" in main
|
||||
assert "AI Prompt Pack 审计上下文已复制" in main
|
||||
assert ".ai-prompt-pack-actions" in style
|
||||
assert ".ayanamsa-runtime-status" in style
|
||||
|
||||
|
||||
def test_api_bridge_variants_prefer_backend_prompt_pack_context() -> None:
|
||||
for rel_path in ["api-bridge.js", "public/api-bridge.js"]:
|
||||
bridge = read(rel_path)
|
||||
@@ -295,6 +316,7 @@ def test_api_bridge_variants_prefer_backend_prompt_pack_context() -> None:
|
||||
assert "【evidence_snapshot】" in bridge
|
||||
assert "JSON.stringify(chartData.ai_prompt_pack.evidence_snapshot" in bridge
|
||||
assert "JSON.stringify(chartData.ai_prompt_pack.retrieval_plan" in bridge
|
||||
assert "external_oracle_evidence_validation: valid_packets: 0" in bridge
|
||||
|
||||
|
||||
def test_first_use_onboarding_is_actionable() -> None:
|
||||
@@ -607,6 +629,12 @@ def test_trust_center_exposes_oracle_evidence_intake_cards() -> None:
|
||||
"reject_global_shadbala_scaling",
|
||||
"status_not_external_verified",
|
||||
"local_engine_artifact_rejected",
|
||||
"renderOracleEvidenceProgressDashboard",
|
||||
"Dasha/Shadbala 真实进度",
|
||||
"0 / 5",
|
||||
"references/oracle/artifacts/",
|
||||
"必须打码",
|
||||
"missing_shadbala_component",
|
||||
"template_user_REDACTED_YEAR_moon_longitude_lahiri",
|
||||
"template_steve_jobs_dasha_lahiri",
|
||||
"template_redacted_place_shadbala_raman",
|
||||
@@ -637,10 +665,94 @@ def test_trust_center_exposes_oracle_evidence_intake_cards() -> None:
|
||||
".oracle-evidence-fields",
|
||||
".oracle-evidence-validation-result",
|
||||
".oracle-evidence-card button",
|
||||
".oracle-evidence-progress-dashboard",
|
||||
".oracle-evidence-progress-bar",
|
||||
]:
|
||||
assert token in style
|
||||
|
||||
|
||||
def test_oracle_artifact_storage_policy_is_documented() -> None:
|
||||
artifact_readme = ROOT / "references" / "oracle" / "artifacts" / "README.md"
|
||||
assert artifact_readme.exists()
|
||||
text = artifact_readme.read_text(encoding="utf-8")
|
||||
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
|
||||
for token in [
|
||||
"references/oracle/artifacts/",
|
||||
"source_artifact",
|
||||
"必须打码",
|
||||
"不得提交私人 PDF 原件",
|
||||
"不得提交完整出生报告",
|
||||
"浏览器 scratch",
|
||||
"external_oracle_artifact",
|
||||
]:
|
||||
assert token in text
|
||||
|
||||
for token in [
|
||||
"references/oracle/artifacts/",
|
||||
"source_artifact",
|
||||
"必须打码",
|
||||
"不得提交私人 PDF 原件",
|
||||
]:
|
||||
assert token in readme
|
||||
|
||||
|
||||
def test_runtime_smoke_html_artifacts_are_ignored() -> None:
|
||||
gitignore = (ROOT / ".gitignore").read_text(encoding="utf-8")
|
||||
for token in [
|
||||
"runtime-smoke-report-*.html",
|
||||
"jyotish-app/runtime-smoke-report-*.html",
|
||||
]:
|
||||
assert token in gitignore
|
||||
|
||||
|
||||
def test_first_jhora_capture_guide_is_actionable() -> None:
|
||||
guide = ROOT / "docs" / "user_jhora_capture_guide.md"
|
||||
assert guide.exists()
|
||||
text = guide.read_text(encoding="utf-8")
|
||||
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
|
||||
for token in [
|
||||
"template_user_REDACTED_YEAR_moon_longitude_lahiri",
|
||||
"template_steve_jobs_dasha_lahiri",
|
||||
"Lahiri",
|
||||
"Raman",
|
||||
"KP",
|
||||
"mean node",
|
||||
"true node",
|
||||
"Moon sidereal longitude",
|
||||
"Vimshottari start date",
|
||||
"Shadbala 七曜六分量",
|
||||
"Sun",
|
||||
"Moon",
|
||||
"Mars",
|
||||
"Mercury",
|
||||
"Jupiter",
|
||||
"Venus",
|
||||
"Saturn",
|
||||
"sthana",
|
||||
"dig",
|
||||
"kala",
|
||||
"chesta",
|
||||
"naisargika",
|
||||
"drik",
|
||||
"references/oracle/artifacts/",
|
||||
"source_artifact",
|
||||
"external_verified",
|
||||
"python3 scripts/oracle_collection_queue.py",
|
||||
"python3 scripts/oracle_evidence_validator.py",
|
||||
"valid_packets: 1",
|
||||
"ready_for_calibration: 1",
|
||||
"必须打码",
|
||||
"不得提交私人 PDF 原件",
|
||||
"不得提交完整出生报告",
|
||||
"浏览器 scratch",
|
||||
]:
|
||||
assert token in text
|
||||
|
||||
assert "docs/user_jhora_capture_guide.md" in readme
|
||||
|
||||
|
||||
def test_trust_center_exposes_real_case_revalidation_to_users() -> None:
|
||||
main = read("main.js")
|
||||
style = read("style.css")
|
||||
@@ -2079,6 +2191,7 @@ def test_dasha_reference_audit_is_documented_and_gated() -> None:
|
||||
queue_script = ROOT / "scripts" / "oracle_collection_queue.py"
|
||||
evidence_validator = ROOT / "scripts" / "oracle_evidence_validator.py"
|
||||
oracle_fixture = ROOT / "references" / "oracle" / "dasha_shadbala_oracle_cases.json"
|
||||
ashtakoot_oracle_fixture = ROOT / "references" / "oracle" / "ashtakoot_oracle_cases.json"
|
||||
quality_gate_module = load_quality_gate_module()
|
||||
quality_gate = (ROOT / "scripts" / "run_quality_gate.py").read_text(encoding="utf-8")
|
||||
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
@@ -2088,6 +2201,7 @@ def test_dasha_reference_audit_is_documented_and_gated() -> None:
|
||||
assert queue_script.exists()
|
||||
assert evidence_validator.exists()
|
||||
assert oracle_fixture.exists()
|
||||
assert ashtakoot_oracle_fixture.exists()
|
||||
assert "tests/test_oracle_collection_queue.py" in quality_gate_module.CORE_PYTEST_TARGETS
|
||||
assert "tests/test_oracle_evidence_validator.py" in quality_gate_module.CORE_PYTEST_TARGETS
|
||||
for token in [
|
||||
@@ -2119,7 +2233,20 @@ def test_dasha_reference_audit_is_documented_and_gated() -> None:
|
||||
"python3 scripts/oracle_collection_queue.py",
|
||||
"python3 scripts/oracle_evidence_validator.py",
|
||||
"references/oracle/dasha_shadbala_oracle_cases.json",
|
||||
"references/oracle/ashtakoot_oracle_cases.json",
|
||||
"--target-start-date 1986-05-18",
|
||||
"Ashtakoot 外部合婚 oracle",
|
||||
"ashtakoot_36_point",
|
||||
"target.total_score",
|
||||
"target.varna",
|
||||
"target.vashya",
|
||||
"target.tara",
|
||||
"target.yoni",
|
||||
"target.graha_maitri",
|
||||
"target.gana",
|
||||
"target.bhakoot",
|
||||
"target.nadi",
|
||||
"target.kuja_status",
|
||||
"不要为单份 PDF 直接调生产常数",
|
||||
"Moon sidereal longitude",
|
||||
"production_tuning_recommended: false",
|
||||
|
||||
@@ -102,6 +102,67 @@ def test_oracle_collection_queue_outputs_markdown_table() -> None:
|
||||
assert "capture_id" in markdown
|
||||
|
||||
|
||||
def test_first_jhora_evidence_packet_template_is_safe_and_fillable() -> None:
|
||||
template = ROOT / "references" / "oracle" / "evidence_packet_templates" / "jhora_steve_jobs_lahiri_first_packet.json"
|
||||
assert template.exists()
|
||||
packet = json.loads(template.read_text(encoding="utf-8"))
|
||||
|
||||
assert packet["capture_id"] == "external_template_steve_jobs_dasha_lahiri"
|
||||
assert packet["status"] == "draft"
|
||||
assert packet["case_id"] == "template_steve_jobs_dasha_lahiri"
|
||||
assert packet["metadata"]["tool_name"] == ""
|
||||
assert packet["metadata"]["source_artifact"] == "references/oracle/artifacts/"
|
||||
assert packet["metadata"]["ayanamsa"] == "Lahiri"
|
||||
assert packet["metadata"]["node_mode"] == "true node"
|
||||
assert packet["integrity_checks"]["must_not_come_from_local_engine"] is True
|
||||
assert packet["integrity_checks"]["requires_external_artifact"] is True
|
||||
assert packet["integrity_checks"]["reject_global_shadbala_scaling"] is True
|
||||
assert packet["target_placeholders"]["target.vimshottari_start_date"] is None
|
||||
|
||||
shadbala = packet["target_placeholders"]["target.shadbala_components"]
|
||||
assert set(shadbala) == {"Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"}
|
||||
for row in shadbala.values():
|
||||
assert set(row) == {"sthana", "dig", "kala", "chesta", "naisargika", "drik"}
|
||||
assert all(value is None for value in row.values())
|
||||
|
||||
|
||||
def test_ashtakoot_oracle_cases_generate_collection_tasks() -> None:
|
||||
oracle_file = ROOT / "references" / "oracle" / "ashtakoot_oracle_cases.json"
|
||||
assert oracle_file.exists()
|
||||
|
||||
completed = run_queue_for_file(oracle_file, "--format", "json")
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
assert report["scope"] == "external_oracle_collection_queue"
|
||||
assert report["summary"]["total_tasks"] == 5
|
||||
assert report["summary"]["ready_for_calibration"] == 0
|
||||
assert report["summary"]["production_tuning_allowed"] is False
|
||||
|
||||
first = report["tasks"][0]
|
||||
assert first["case_id"] == "ashtakoot_public_couple_lahiri_01"
|
||||
assert first["privacy"] == "public_or_synthetic_relationship_template"
|
||||
assert "ashtakoot" in first["target_modules"]
|
||||
assert any("JHora" in source for source in first["preferred_sources"])
|
||||
assert any("VedAstro" in source for source in first["preferred_sources"])
|
||||
for field in [
|
||||
"target.total_score",
|
||||
"target.varna",
|
||||
"target.vashya",
|
||||
"target.tara",
|
||||
"target.yoni",
|
||||
"target.graha_maitri",
|
||||
"target.gana",
|
||||
"target.bhakoot",
|
||||
"target.nadi",
|
||||
"target.kuja_status",
|
||||
]:
|
||||
assert field in first["target_fields"]
|
||||
assert field in first["evidence_packet"]["target_placeholders"]
|
||||
assert first["evidence_packet"]["integrity_checks"]["must_not_come_from_local_engine"] is True
|
||||
assert first["do_not_tune_production"] is True
|
||||
|
||||
|
||||
def test_oracle_collection_queue_preserves_external_verified_evidence(tmp_path: Path) -> None:
|
||||
oracle = json.loads((ROOT / "references/oracle/dasha_shadbala_oracle_cases.json").read_text(encoding="utf-8"))
|
||||
case = oracle["template_cases"][0]
|
||||
|
||||
@@ -10,6 +10,18 @@ from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SHADBALA_PLANETS = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
|
||||
SHADBALA_COMPONENTS = ["sthana", "dig", "kala", "chesta", "naisargika", "drik"]
|
||||
|
||||
|
||||
def complete_shadbala_components() -> dict[str, dict[str, float]]:
|
||||
return {
|
||||
planet: {
|
||||
component: float(40 + planet_index * 3 + component_index)
|
||||
for component_index, component in enumerate(SHADBALA_COMPONENTS)
|
||||
}
|
||||
for planet_index, planet in enumerate(SHADBALA_PLANETS)
|
||||
}
|
||||
|
||||
|
||||
def build_queue() -> dict:
|
||||
@@ -104,16 +116,7 @@ def test_validator_accepts_filled_external_packet_but_not_whole_queue(tmp_path:
|
||||
packet["target_placeholders"] = {
|
||||
"target.moon_sidereal_longitude_deg": 311.7897,
|
||||
"target.vimshottari_start_date": "1986-05-18",
|
||||
"target.shadbala_components": {
|
||||
"Sun": {
|
||||
"sthana": 100.0,
|
||||
"dig": 50.0,
|
||||
"kala": 100.0,
|
||||
"chesta": 40.0,
|
||||
"naisargika": 60.0,
|
||||
"drik": 30.0,
|
||||
}
|
||||
},
|
||||
"target.shadbala_components": complete_shadbala_components(),
|
||||
}
|
||||
packet["status"] = "external_verified"
|
||||
queue_path = tmp_path / "queue.json"
|
||||
@@ -131,6 +134,197 @@ def test_validator_accepts_filled_external_packet_but_not_whole_queue(tmp_path:
|
||||
assert first["problems"] == []
|
||||
|
||||
|
||||
def test_validator_rejects_incomplete_shadbala_component_rows(tmp_path: Path) -> None:
|
||||
queue = build_queue()
|
||||
packet = queue["tasks"][0]["evidence_packet"]
|
||||
packet["metadata"] = {
|
||||
"tool_name": "JHora",
|
||||
"tool_version_or_url": "manual-screenshot-v1",
|
||||
"capture_date": "2026-06-25",
|
||||
"source_artifact": "references/oracle/artifacts/manual_jhora_user_REDACTED_YEAR.png",
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "mean",
|
||||
"timezone": "UTC+08:00",
|
||||
"operator_note": "Manual external screenshot; values typed from JHora screen.",
|
||||
}
|
||||
packet["target_placeholders"] = {
|
||||
"target.moon_sidereal_longitude_deg": 311.7897,
|
||||
"target.vimshottari_start_date": "1986-05-18",
|
||||
"target.shadbala_components": {
|
||||
"Sun": {
|
||||
"sthana": 100.0,
|
||||
"dig": 50.0,
|
||||
}
|
||||
},
|
||||
}
|
||||
packet["status"] = "external_verified"
|
||||
queue_path = tmp_path / "queue.json"
|
||||
queue_path.write_text(json.dumps(queue, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
completed = run_validator(queue_path)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
first = report["packets"][0]
|
||||
assert first["valid"] is False
|
||||
assert "missing_shadbala_component:Sun.kala" in first["problems"]
|
||||
assert "missing_shadbala_component:Sun.chesta" in first["problems"]
|
||||
assert "missing_shadbala_component:Sun.naisargika" in first["problems"]
|
||||
assert "missing_shadbala_component:Sun.drik" in first["problems"]
|
||||
assert "missing_shadbala_component:Moon" in first["problems"]
|
||||
|
||||
|
||||
def test_validator_rejects_non_numeric_or_negative_shadbala_components(tmp_path: Path) -> None:
|
||||
queue = build_queue()
|
||||
packet = queue["tasks"][0]["evidence_packet"]
|
||||
shadbala = complete_shadbala_components()
|
||||
shadbala["Sun"]["sthana"] = "100"
|
||||
shadbala["Moon"]["dig"] = -1.0
|
||||
packet["metadata"] = {
|
||||
"tool_name": "JHora",
|
||||
"tool_version_or_url": "manual-screenshot-v1",
|
||||
"capture_date": "2026-06-25",
|
||||
"source_artifact": "references/oracle/artifacts/manual_jhora_user_REDACTED_YEAR.png",
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "mean",
|
||||
"timezone": "UTC+08:00",
|
||||
"operator_note": "Manual external screenshot; values typed from JHora screen.",
|
||||
}
|
||||
packet["target_placeholders"] = {
|
||||
"target.moon_sidereal_longitude_deg": 311.7897,
|
||||
"target.vimshottari_start_date": "1986-05-18",
|
||||
"target.shadbala_components": shadbala,
|
||||
}
|
||||
packet["status"] = "external_verified"
|
||||
queue_path = tmp_path / "queue.json"
|
||||
queue_path.write_text(json.dumps(queue, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
completed = run_validator(queue_path)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
first = report["packets"][0]
|
||||
assert first["valid"] is False
|
||||
assert "invalid_shadbala_component_type:Sun.sthana" in first["problems"]
|
||||
assert "invalid_shadbala_component_negative:Moon.dig" in first["problems"]
|
||||
|
||||
|
||||
def test_validator_rejects_invalid_ashtakoot_score_ranges(tmp_path: Path) -> None:
|
||||
queue = build_queue_from_file(ROOT / "references/oracle/ashtakoot_oracle_cases.json")
|
||||
packet = queue["tasks"][0]["evidence_packet"]
|
||||
packet["metadata"] = {
|
||||
"tool_name": "VedAstro",
|
||||
"tool_version_or_url": "https://vedastro.org/API",
|
||||
"capture_date": "2026-06-25",
|
||||
"source_artifact": "references/oracle/artifacts/manual_ashtakoot_case_01.png",
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "true",
|
||||
"timezone": "UTC-08:00",
|
||||
"operator_note": "External compatibility calculator screenshot.",
|
||||
}
|
||||
packet["target_placeholders"] = {
|
||||
"target.total_score": 99.0,
|
||||
"target.varna": 1.0,
|
||||
"target.vashya": 2.0,
|
||||
"target.tara": 3.0,
|
||||
"target.yoni": 4.0,
|
||||
"target.graha_maitri": 5.0,
|
||||
"target.gana": 6.0,
|
||||
"target.bhakoot": 7.0,
|
||||
"target.nadi": 99.0,
|
||||
"target.kuja_status": "",
|
||||
}
|
||||
packet["status"] = "external_verified"
|
||||
queue_path = tmp_path / "queue.json"
|
||||
queue_path.write_text(json.dumps(queue, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
completed = run_validator(queue_path)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
first = report["packets"][0]
|
||||
assert first["valid"] is False
|
||||
assert "invalid_ashtakoot_score_range:target.total_score" in first["problems"]
|
||||
assert "invalid_ashtakoot_score_range:target.nadi" in first["problems"]
|
||||
assert "placeholder_unfilled:target.kuja_status" in first["problems"]
|
||||
|
||||
|
||||
def test_validator_rejects_ashtakoot_score_sum_mismatch(tmp_path: Path) -> None:
|
||||
queue = build_queue_from_file(ROOT / "references/oracle/ashtakoot_oracle_cases.json")
|
||||
packet = queue["tasks"][0]["evidence_packet"]
|
||||
packet["metadata"] = {
|
||||
"tool_name": "VedAstro",
|
||||
"tool_version_or_url": "https://vedastro.org/API",
|
||||
"capture_date": "2026-06-25",
|
||||
"source_artifact": "references/oracle/artifacts/manual_ashtakoot_case_01.png",
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "true",
|
||||
"timezone": "UTC-08:00",
|
||||
"operator_note": "External compatibility calculator screenshot.",
|
||||
}
|
||||
packet["target_placeholders"] = {
|
||||
"target.total_score": 20.0,
|
||||
"target.varna": 1.0,
|
||||
"target.vashya": 2.0,
|
||||
"target.tara": 3.0,
|
||||
"target.yoni": 4.0,
|
||||
"target.graha_maitri": 5.0,
|
||||
"target.gana": 6.0,
|
||||
"target.bhakoot": 7.0,
|
||||
"target.nadi": 8.0,
|
||||
"target.kuja_status": "no_dosha",
|
||||
}
|
||||
packet["status"] = "external_verified"
|
||||
queue_path = tmp_path / "queue.json"
|
||||
queue_path.write_text(json.dumps(queue, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
completed = run_validator(queue_path)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
first = report["packets"][0]
|
||||
assert first["valid"] is False
|
||||
assert "ashtakoot_score_sum_mismatch" in first["problems"]
|
||||
|
||||
|
||||
def test_validator_accepts_valid_ashtakoot_external_packet(tmp_path: Path) -> None:
|
||||
queue = build_queue_from_file(ROOT / "references/oracle/ashtakoot_oracle_cases.json")
|
||||
packet = queue["tasks"][0]["evidence_packet"]
|
||||
packet["metadata"] = {
|
||||
"tool_name": "VedAstro",
|
||||
"tool_version_or_url": "https://vedastro.org/API",
|
||||
"capture_date": "2026-06-25",
|
||||
"source_artifact": "references/oracle/artifacts/manual_ashtakoot_case_01.png",
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "true",
|
||||
"timezone": "UTC-08:00",
|
||||
"operator_note": "External compatibility calculator screenshot.",
|
||||
}
|
||||
packet["target_placeholders"] = {
|
||||
"target.total_score": 18.0,
|
||||
"target.varna": 1.0,
|
||||
"target.vashya": 1.0,
|
||||
"target.tara": 2.0,
|
||||
"target.yoni": 3.0,
|
||||
"target.graha_maitri": 4.0,
|
||||
"target.gana": 2.0,
|
||||
"target.bhakoot": 3.0,
|
||||
"target.nadi": 2.0,
|
||||
"target.kuja_status": "no_dosha",
|
||||
}
|
||||
packet["status"] = "external_verified"
|
||||
queue_path = tmp_path / "queue.json"
|
||||
queue_path.write_text(json.dumps(queue, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
completed = run_validator(queue_path)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
first = report["packets"][0]
|
||||
assert first["valid"] is True
|
||||
assert first["problems"] == []
|
||||
|
||||
|
||||
def test_validator_rejects_local_engine_artifact(tmp_path: Path) -> None:
|
||||
queue = build_queue()
|
||||
packet = queue["tasks"][0]["evidence_packet"]
|
||||
@@ -167,16 +361,7 @@ def test_validator_accepts_external_verified_packet_generated_from_oracle_file(t
|
||||
case["target"] = {
|
||||
"moon_sidereal_longitude_deg": 311.7897,
|
||||
"vimshottari_start_date": "1986-05-18",
|
||||
"shadbala_components": {
|
||||
"Sun": {
|
||||
"sthana": 100.0,
|
||||
"dig": 50.0,
|
||||
"kala": 100.0,
|
||||
"chesta": 40.0,
|
||||
"naisargika": 60.0,
|
||||
"drik": 30.0,
|
||||
}
|
||||
},
|
||||
"shadbala_components": complete_shadbala_components(),
|
||||
}
|
||||
case["evidence_packet"] = {
|
||||
"status": "external_verified",
|
||||
|
||||
Reference in New Issue
Block a user