新增市場情報 AI summary persistence run closeout
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
This commit is contained in:
@@ -703,6 +703,9 @@
|
||||
<button class="market-intel-icon-button" type="button" title="審核 queue review AI summary persistence run receipt" data-market-intel-sample-candidate-queue-review-ai-summary-persistence-run-receipt>
|
||||
<i class="fas fa-receipt" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button class="market-intel-icon-button" type="button" title="收尾 queue review AI summary persistence run closeout" data-market-intel-sample-candidate-queue-review-ai-summary-persistence-run-closeout>
|
||||
<i class="fas fa-flag-checkered" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1065,6 +1068,7 @@
|
||||
const sampleCandidateQueueReviewAiSummaryPersistenceRunPackage = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-review-ai-summary-persistence-run-package]') : null;
|
||||
const sampleCandidateQueueReviewAiSummaryPersistenceRunReadiness = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-review-ai-summary-persistence-run-readiness]') : null;
|
||||
const sampleCandidateQueueReviewAiSummaryPersistenceRunReceipt = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-review-ai-summary-persistence-run-receipt]') : null;
|
||||
const sampleCandidateQueueReviewAiSummaryPersistenceRunCloseout = sampleReviewRoot ? sampleReviewRoot.querySelector('[data-market-intel-sample-candidate-queue-review-ai-summary-persistence-run-closeout]') : 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') }}";
|
||||
@@ -1104,6 +1108,7 @@
|
||||
const sampleCandidateQueueReviewAiSummaryPersistenceRunPackageEndpoint = "{{ url_for('market_intel_review.market_intel_manual_sample_candidate_queue_review_ai_summary_persistence_run_package') }}";
|
||||
const sampleCandidateQueueReviewAiSummaryPersistenceRunReadinessEndpoint = "{{ url_for('market_intel_review.market_intel_manual_sample_candidate_queue_review_ai_summary_persistence_run_readiness') }}";
|
||||
const sampleCandidateQueueReviewAiSummaryPersistenceRunReceiptEndpoint = "{{ url_for('market_intel_review.market_intel_manual_sample_candidate_queue_review_ai_summary_persistence_run_receipt') }}";
|
||||
const sampleCandidateQueueReviewAiSummaryPersistenceRunCloseoutEndpoint = "{{ url_for('market_intel_review.market_intel_manual_sample_candidate_queue_review_ai_summary_persistence_run_closeout') }}";
|
||||
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;
|
||||
@@ -5818,6 +5823,144 @@
|
||||
}
|
||||
};
|
||||
|
||||
const renderCandidateQueueReviewAiSummaryPersistenceRunCloseout = data => {
|
||||
const blockers = (data.blocked_reasons || []).join(' / ');
|
||||
const gates = data.gates || [];
|
||||
const receipt = data.receipt_summary || {};
|
||||
const operator = data.operator_closeout_summary || {};
|
||||
const promotion = data.promotion_gate || {};
|
||||
sampleReviewMeta.innerHTML = [
|
||||
`mode=${data.mode || 'unknown'}`,
|
||||
`closeout=${data.closeout_passed ? 'passed' : 'blocked'}`,
|
||||
`telegram_gate=${data.ready_for_summary_persistence_telegram_dispatch_gate ? 'ready' : 'blocked'}`,
|
||||
`statements=${data.statement_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 receipt;後續 Telegram dispatch 必須另開 gate。API/UI 不讀 token、不執行 CLI、不開 DB、不寫 metadata_json、不派送 Telegram。${blockers ? `阻擋:${escapeHtml(blockers)}` : ''}</div>
|
||||
<div class="market-intel-deploy-grid">
|
||||
<div>
|
||||
<p class="market-intel-deploy-section-title">CLOSEOUT 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">尚未提供 closeout gate。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="market-intel-deploy-section-title">RECEIPT SUMMARY</p>
|
||||
<div class="market-intel-check-list">
|
||||
${[
|
||||
['receipt_passed', receipt.receipt_passed],
|
||||
['closeout_ready', receipt.ready_for_summary_persistence_closeout],
|
||||
['expected_keys', receipt.expected_dedupe_key_count || 0],
|
||||
['writer_metadata', receipt.writer_metadata_json_update_executed],
|
||||
['smoke_verified', receipt.postwrite_smoke_metadata_json_verified],
|
||||
['safe_boundaries', receipt.safe_boundaries_complete]
|
||||
].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">OPERATOR CLOSEOUT</p>
|
||||
<div class="market-intel-check-list">
|
||||
${[
|
||||
['closeout_artifact', operator.closeout_artifact_path_recorded],
|
||||
['closeout_confirmed', operator.operator_confirmed_summary_persistence_closeout],
|
||||
['telegram_separate_gate', operator.operator_confirmed_telegram_dispatch_requires_separate_gate],
|
||||
['no_api_db_write', operator.operator_confirmed_no_api_db_write],
|
||||
['no_telegram_dispatch', operator.operator_confirmed_no_telegram_dispatch],
|
||||
['token_submitted', operator.approval_token_submitted_to_api]
|
||||
].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">PROMOTION</p>
|
||||
<div class="market-intel-check-list">
|
||||
${[
|
||||
['allowed', promotion.allowed],
|
||||
['next_phase', promotion.next_manual_phase || 'blocked'],
|
||||
['requires_real_db_write', promotion.requires_real_db_write],
|
||||
['requires_scheduler_attach', promotion.requires_scheduler_attach],
|
||||
['telegram_separate_gate', promotion.telegram_dispatch_requires_separate_gate]
|
||||
].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">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],
|
||||
['closeout_file_written', data.closeout_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 loadCandidateQueueReviewAiSummaryPersistenceRunCloseout = 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 closeout 中...</div>';
|
||||
try {
|
||||
const response = await fetch(`${sampleCandidateQueueReviewAiSummaryPersistenceRunCloseoutEndpoint}?execute=false&apply_real_write=false`, {
|
||||
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}`);
|
||||
renderCandidateQueueReviewAiSummaryPersistenceRunCloseout(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 closeout 失敗:${escapeHtml(error.message)}</div>`;
|
||||
}
|
||||
};
|
||||
|
||||
const renderCandidateQueueReviewDecisionWriter = data => {
|
||||
const blockers = (data.blocked_reasons || []).join(' / ');
|
||||
const summary = data.statement_summary || {};
|
||||
@@ -7536,6 +7679,9 @@
|
||||
if (sampleCandidateQueueReviewAiSummaryPersistenceRunReceipt) {
|
||||
sampleCandidateQueueReviewAiSummaryPersistenceRunReceipt.addEventListener('click', loadCandidateQueueReviewAiSummaryPersistenceRunReceipt);
|
||||
}
|
||||
if (sampleCandidateQueueReviewAiSummaryPersistenceRunCloseout) {
|
||||
sampleCandidateQueueReviewAiSummaryPersistenceRunCloseout.addEventListener('click', loadCandidateQueueReviewAiSummaryPersistenceRunCloseout);
|
||||
}
|
||||
if (schedulerRefresh) {
|
||||
schedulerRefresh.addEventListener('click', loadScheduler);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user