Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/commands/spac.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,19 @@ export function registerSpacCommands(program: Command): void {
spacCmd
.command("backfill-redemptions")
.description("Re-process known-SPAC trigger-item 8-Ks to extract realized redemptions")
.action(async () => {
const out = (await withCli(new BackfillRedemptionsTask()).run({})) as {
.option("--force", "Re-process filings even when a successful run already exists", false)
.option("--dry-run", "Report selected filing count without reprocessing", false)
.action(async (opts: { force?: boolean; dryRun?: boolean }) => {
const out = (await withCli(new BackfillRedemptionsTask()).run({
force: opts.force === true,
dryRun: opts.dryRun === true,
} as never)) as {
selected: number;
processed: number;
skipped: number;
};
console.log(`selected ${out.selected} filing(s); processed ${out.processed}`);
console.log(
`selected ${out.selected} filing(s); processed ${out.processed}; skipped ${out.skipped}`
);
});
}
29 changes: 23 additions & 6 deletions src/sec/forms/miscellaneous-filings/redemption8k.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ describe("processForm8K — redemption e2e", () => {
expect(spacRow?.total_redemption_amount).toBe(8200000);
});

it("known SPAC with no deal yields no redemption rollup", async () => {
// Seed SPAC row but no deal milestone
it("known SPAC with no deal persists the orphan extraction and rolls up once the deal lands", async () => {
// Seed SPAC row but no deal milestone — the redemption is recorded
// anyway; deriveDeals correlates it as soon as a deal exists.
await new SpacReportWriter().recordRegistration({
cik: 21,
accession_number: "21-reg",
Expand Down Expand Up @@ -169,11 +170,27 @@ describe("processForm8K — redemption e2e", () => {
model: fakeS1Model(),
});

const spacRow = await new SpacRepo().getSpac(21);
expect(spacRow).toBeDefined();
expect(spacRow?.total_redemption_amount ?? null).toBeNull();
// No deal yet — nothing to roll up against.
expect((await new SpacRepo().getDeals(21)).length).toBe(0);
const orphanSpac = await new SpacRepo().getSpac(21);
expect(orphanSpac?.total_redemption_amount ?? null).toBeNull();

// Later, the definitive-agreement 8-K lands; the orphan extraction
// is correlated automatically.
await new SpacReportWriter().recordDealMilestones({
cik: 21,
accession_number: "21-da",
filing_date: "2026-01-10",
form: "8-K",
primary_document: null,
events: [{ event_type: "definitive_agreement", event_date: "2026-01-10" }],
});

const deals = await new SpacRepo().getDeals(21);
expect(deals).toHaveLength(0);
expect(deals).toHaveLength(1);
expect(deals[0].redemption_amount).toBe(8200000);

const spacRow = await new SpacRepo().getSpac(21);
expect(spacRow?.total_redemption_amount).toBe(8200000);
});
});
Loading