All checks were successful
deploy / deploy (push) Successful in 57s
Proof: DB-backed quote lifecycle retention policy, rollup-before-prune maintenance, dashboard storage visibility, and regression tests for preservation and fail-closed behavior. Assumptions: Completed quote outcome attribution remains the current durable successful-trade evidence; venue-native terminal fill ids and fee-complete PnL remain unavailable. Still fake: Historical non-success detail already pruned cannot be recovered; rollups are aggregate analytics, not per-quote settled trade proof.
86 lines
2.5 KiB
JavaScript
Executable file
86 lines
2.5 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
import process from 'node:process';
|
|
|
|
import { loadConfig } from '../../src/lib/config.mjs';
|
|
import {
|
|
countQuoteLifecyclePruneCandidates,
|
|
createPostgresPool,
|
|
ensureHistorySchema,
|
|
loadQuoteLifecycleRetentionPolicy,
|
|
loadQuoteLifecycleRetentionSummary,
|
|
runQuoteLifecycleRetentionMaintenance,
|
|
} from '../../src/lib/postgres.mjs';
|
|
|
|
function parseArgs(argv) {
|
|
const args = new Set(argv);
|
|
const reasonIndex = argv.indexOf('--reason');
|
|
return {
|
|
execute: args.has('--execute'),
|
|
emergency: args.has('--emergency'),
|
|
reason: reasonIndex >= 0 ? argv[reasonIndex + 1] || '' : '',
|
|
};
|
|
}
|
|
|
|
const args = parseArgs(process.argv.slice(2));
|
|
const config = loadConfig();
|
|
const pool = createPostgresPool({
|
|
connectionString: config.postgresUrl,
|
|
});
|
|
|
|
try {
|
|
await ensureHistorySchema(pool);
|
|
const now = new Date().toISOString();
|
|
const policy = await loadQuoteLifecycleRetentionPolicy(pool);
|
|
if (!policy.ok) {
|
|
console.error(JSON.stringify({
|
|
ok: false,
|
|
dry_run: !args.execute,
|
|
refused: true,
|
|
reason: policy.block_reason,
|
|
}, null, 2));
|
|
process.exitCode = 2;
|
|
} else {
|
|
const summary = await loadQuoteLifecycleRetentionSummary(pool, { now });
|
|
const mode = summary.retention_mode === 'pressure' ? 'pressure' : 'normal';
|
|
const candidates = await countQuoteLifecyclePruneCandidates(pool, {
|
|
policy: policy.policy,
|
|
mode,
|
|
now,
|
|
});
|
|
const preflight = {
|
|
ok: true,
|
|
dry_run: !args.execute,
|
|
emergency: args.emergency,
|
|
emergency_reason: args.reason || null,
|
|
retention_mode: summary.retention_mode,
|
|
policy: policy.policy,
|
|
preserved_successful_quote_count: summary.preserved_successful_quote_count,
|
|
rollup_watermark: summary.rollup_watermark,
|
|
rollup_required_until: summary.rollup_required_until,
|
|
candidate_counts: candidates,
|
|
};
|
|
|
|
if (!args.execute) {
|
|
console.log(JSON.stringify(preflight, null, 2));
|
|
} else if (args.emergency && !args.reason) {
|
|
console.error(JSON.stringify({
|
|
...preflight,
|
|
ok: false,
|
|
refused: true,
|
|
reason: 'emergency execution requires --reason',
|
|
}, null, 2));
|
|
process.exitCode = 2;
|
|
} else {
|
|
const result = await runQuoteLifecycleRetentionMaintenance(pool, { now });
|
|
console.log(JSON.stringify({
|
|
...preflight,
|
|
dry_run: false,
|
|
result,
|
|
}, null, 2));
|
|
if (result.status !== 'success') process.exitCode = 1;
|
|
}
|
|
}
|
|
} finally {
|
|
await pool.end().catch(() => {});
|
|
}
|