unrip/scripts/ops/quote_lifecycle_retention.mjs
philipp 2d329c9b2f
All checks were successful
deploy / deploy (push) Successful in 57s
Add quote lifecycle retention rollups
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.
2026-06-06 15:50:29 +02:00

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(() => {});
}