新增市場情報 AI summary persistence run package preview
All checks were successful
CD Pipeline / deploy (push) Successful in 1m3s

This commit is contained in:
OoO
2026-05-20 00:09:17 +08:00
parent 124ddc7376
commit 89c2bc96e4
11 changed files with 1156 additions and 97 deletions

View File

@@ -694,6 +694,9 @@
<button class="market-intel-icon-button" type="button" title="檢查 queue review AI summary persistence writer preflight" data-market-intel-sample-candidate-queue-review-ai-summary-persistence-writer-preflight>
<i class="fas fa-database" aria-hidden="true"></i>
</button>
<button class="market-intel-icon-button" type="button" title="產生 queue review AI summary persistence run package" data-market-intel-sample-candidate-queue-review-ai-summary-persistence-run-package>
<i class="fas fa-box-archive" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
@@ -1053,6 +1056,7 @@
const sampleCandidateQueueReviewAiSummaryPersistencePreflight = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-review-ai-summary-persistence-preflight]') : null;
const sampleCandidateQueueReviewAiSummaryPersistenceTransaction = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-review-ai-summary-persistence-transaction]') : null;
const sampleCandidateQueueReviewAiSummaryPersistenceWriterPreflight = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-review-ai-summary-persistence-writer-preflight]') : null;
const sampleCandidateQueueReviewAiSummaryPersistenceRunPackage = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-review-ai-summary-persistence-run-package]') : null;
const sampleReviewEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_review') }}";
const sampleReviewEvaluateEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_review_evaluate') }}";
const sampleCandidateHandoffEndpoint = "{{ url_for('market_intel.market_intel_manual_sample_candidate_handoff') }}";
@@ -1089,6 +1093,7 @@
const sampleCandidateQueueReviewAiSummaryPersistencePreflightEndpoint = "{{ url_for('market_intel_review.market_intel_manual_sample_candidate_queue_review_ai_summary_persistence_preflight') }}";
const sampleCandidateQueueReviewAiSummaryPersistenceTransactionEndpoint = "{{ url_for('market_intel_review.market_intel_manual_sample_candidate_queue_review_ai_summary_persistence_transaction') }}";
const sampleCandidateQueueReviewAiSummaryPersistenceWriterPreflightEndpoint = "{{ url_for('market_intel_review.market_intel_manual_sample_candidate_queue_review_ai_summary_persistence_writer_preflight') }}";
const sampleCandidateQueueReviewAiSummaryPersistenceRunPackageEndpoint = "{{ url_for('market_intel_review.market_intel_manual_sample_candidate_queue_review_ai_summary_persistence_run_package') }}";
const schedulerMeta = schedulerRoot ? schedulerRoot.querySelector('[data-market-intel-scheduler-meta]') : null;
const schedulerBody = schedulerRoot ? schedulerRoot.querySelector('[data-market-intel-scheduler-body]') : null;
const schedulerRefresh = schedulerRoot ? schedulerRoot.querySelector('[data-market-intel-scheduler-refresh]') : null;
@@ -5417,6 +5422,136 @@
}
};
const renderCandidateQueueReviewAiSummaryPersistenceRunPackage = data => {
const blockers = (data.blocked_reasons || []).join(' / ');
const gates = data.package_gates || [];
const manifest = data.payload_manifest || {};
const artifacts = data.required_artifacts || [];
const commands = data.command_bundle || [];
sampleReviewMeta.innerHTML = [
`mode=${data.mode || 'unknown'}`,
`package=${data.package_ready ? 'ready' : 'blocked'}`,
`readiness=${data.ready_for_summary_persistence_run_readiness ? 'ready' : 'blocked'}`,
`payloads=${manifest.payload_count || 0}`,
`db=${data.database_write_executed ? 'written' : 'blocked'}`
].map(item => `<span class="market-intel-pill">${escapeHtml(item)}</span>`).join('');
sampleReviewBody.innerHTML = `
<div class="market-intel-empty mb-3">此卡只產生 AI summary persistence run package preview正式 metadata_json 寫入仍需後續 operator readiness 與 CLI receipt。API/UI 不讀 token、不執行 CLI、不開 DB、不寫檔、不派送 Telegram。${blockers ? `阻擋:${escapeHtml(blockers)}` : ''}</div>
<div class="market-intel-deploy-grid">
<div>
<p class="market-intel-deploy-section-title">PACKAGE GATES</p>
<div class="market-intel-check-list">${
gates.map(gate => `
<div class="market-intel-check">
<div>
<strong>${escapeHtml(gate.key)}</strong>
<small>${escapeHtml(gate.label)}</small>
</div>
<span>${gate.passed ? 'PASS' : 'BLOCK'}</span>
</div>
`).join('') || '<div class="market-intel-empty">尚未提供 run package gate。</div>'
}</div>
</div>
<div>
<p class="market-intel-deploy-section-title">PAYLOAD MANIFEST</p>
<div class="market-intel-check-list">
${[
['payload_count', manifest.payload_count || 0],
['manifest_hash', manifest.manifest_hash || 'missing'],
['summary_hash', manifest.summary_payload_hash || 'missing'],
['dedupe_keys', (manifest.dedupe_keys || []).join(', ') || 'none']
].map(([key, value]) => `
<div class="market-intel-check">
<div><strong>${escapeHtml(key)}</strong></div>
<span>${escapeHtml(String(value))}</span>
</div>
`).join('')}
</div>
</div>
<div>
<p class="market-intel-deploy-section-title">REQUIRED ARTIFACTS</p>
<div class="market-intel-check-list">${
artifacts.map(item => `
<div class="market-intel-check">
<div>
<strong>${escapeHtml(item.key)}</strong>
<small>${escapeHtml(item.path_shape || '')}</small>
</div>
<span>${item.created_by_api ? 'API' : 'OPERATOR'}</span>
</div>
`).join('') || '<div class="market-intel-empty">尚未列出 artifacts。</div>'
}</div>
</div>
<div>
<p class="market-intel-deploy-section-title">COMMAND BUNDLE</p>
<div class="market-intel-check-list">${
commands.map(item => `
<div class="market-intel-check">
<div>
<strong>${escapeHtml(item.key)}</strong>
<small>${escapeHtml(item.command_shape || '')}</small>
</div>
<span>${item.executed ? 'EXECUTED' : item.executes_database ? 'CLI DB' : 'PREP'}</span>
</div>
`).join('') || '<div class="market-intel-empty">尚未提供 command bundle。</div>'
}</div>
</div>
<div>
<p class="market-intel-deploy-section-title">NO-SIDE-EFFECTS</p>
<div class="market-intel-check-list">
${[
['api_executes_cli', data.api_executes_cli],
['api_reads_approval_token', data.api_reads_approval_token],
['api_writes_database', data.api_writes_database],
['package_artifact_created', data.package_artifact_created],
['summary_persistence_run_package_file_written', data.summary_persistence_run_package_file_written],
['database_connection_opened', data.database_connection_opened],
['database_write_executed', data.database_write_executed],
['telegram_dispatched', data.telegram_dispatched],
['scheduler_attached', data.scheduler_attached]
].map(([key, value]) => `
<div class="market-intel-check">
<div><strong>${escapeHtml(key)}</strong></div>
<span>${escapeHtml(String(value))}</span>
</div>
`).join('')}
</div>
</div>
</div>
`;
};
const loadCandidateQueueReviewAiSummaryPersistenceRunPackage = async () => {
if (!sampleReviewMeta || !sampleReviewBody || !sampleReviewInput) return;
let parsed;
try {
parsed = JSON.parse(sampleReviewInput.value || '{}');
} catch (error) {
sampleReviewMeta.innerHTML = '<span class="market-intel-pill">json_error</span>';
sampleReviewBody.innerHTML = `<div class="market-intel-empty">JSON 格式錯誤:${escapeHtml(error.message)}</div>`;
return;
}
const body = parsed && parsed.sample_result ? parsed : { sample_result: parsed };
sampleReviewBody.innerHTML = '<div class="market-intel-empty">產生 queue review AI summary persistence run package 中...</div>';
try {
const response = await fetch(sampleCandidateQueueReviewAiSummaryPersistenceRunPackageEndpoint, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify(body)
});
const data = await response.json();
if (!response.ok && !data.mode) throw new Error(`HTTP ${response.status}`);
renderCandidateQueueReviewAiSummaryPersistenceRunPackage(data);
} catch (error) {
sampleReviewMeta.innerHTML = '<span class="market-intel-pill">error</span>';
sampleReviewBody.innerHTML = `<div class="market-intel-empty">queue review AI summary persistence run package 失敗:${escapeHtml(error.message)}</div>`;
}
};
const renderCandidateQueueReviewDecisionWriter = data => {
const blockers = (data.blocked_reasons || []).join(' / ');
const summary = data.statement_summary || {};
@@ -7126,6 +7261,9 @@
if (sampleCandidateQueueReviewAiSummaryPersistenceWriterPreflight) {
sampleCandidateQueueReviewAiSummaryPersistenceWriterPreflight.addEventListener('click', loadCandidateQueueReviewAiSummaryPersistenceWriterPreflight);
}
if (sampleCandidateQueueReviewAiSummaryPersistenceRunPackage) {
sampleCandidateQueueReviewAiSummaryPersistenceRunPackage.addEventListener('click', loadCandidateQueueReviewAiSummaryPersistenceRunPackage);
}
if (schedulerRefresh) {
schedulerRefresh.addEventListener('click', loadScheduler);
}