Follow-up to #237, which registered coverage for sponsor-family / underwriter-family but deliberately left dropPrevious unregistered, so sec version drop-previous resolver sponsor-family still errors.
Why the purge was held back
On the person/company tier a version purge is safe because identity links are derived. The observation rows outlive the purge, so sec resolve --kind person --all rebuilds every link that was removed. Purge-then-rebuild is a closed loop.
The family tier has no such backstop:
- there is no observation → identity-link table — the per-filing link row is the attribution (
spac_sponsor_link, underwriter_link), not a projection of something that survives it; and
- batch
sec resolve refuses family kinds (person|company allow-list), so nothing can rebuild what a purge deletes.
Recovery from a family purge today means re-extracting every affected S-1/424 through the AI pipeline and re-paying that cost. And the trap is quiet: drop-previous is symmetric in shape across all four resolver kinds, so at the call site it reads like the same routine cleanup whichever kind it is handed.
What this issue asks for
- Implement a family-tier
resolve so family attribution is rebuildable from data that survives a purge, restoring the invariant person/company rely on.
- Then register the
dropPrevious closures for both family kinds, re-enabling the ceremony.
The hard part
A family is keyed off the sponsor/underwriter common name the AI extractor emitted. Only the legal name reaches the observation row, and the canonical family retains just one variant's display name — so a naive batch pass cannot faithfully re-partition families. A normalizer change that splits a family would reassign every member from that single stored name.
So this is not just wiring: it needs a decision about where the common name lives durably. Options worth weighing:
- persist the extractor's common name on the observation row (or a sibling table), so re-partitioning has the original input to work from;
- store the per-filing family assignment inputs alongside the link row, making the link reconstructable without re-extraction;
- accept re-extraction as the only rebuild path and keep
drop-previous refusing family kinds permanently — in which case close this and document the asymmetry as deliberate rather than temporary.
Already in place
SpacSponsorLinkRepo.deleteForResolverVersion and UnderwriterLinkRepo.deleteForResolverVersion landed in #237 with tests, and are intentionally unreferenced by any command. They are the building block the purge needs — no command reaches them until the closures are registered.
registerResolvers.family.test.ts currently asserts the refusal and that every family row survives it on both tiers at both versions; those tests invert when the purge is re-enabled.
🤖 Generated with Claude Code
https://claude.ai/code/session_01KB8s4qR5goCoxjE94YDFUV
Follow-up to #237, which registered
coverageforsponsor-family/underwriter-familybut deliberately leftdropPreviousunregistered, sosec version drop-previous resolver sponsor-familystill errors.Why the purge was held back
On the person/company tier a version purge is safe because identity links are derived. The observation rows outlive the purge, so
sec resolve --kind person --allrebuilds every link that was removed. Purge-then-rebuild is a closed loop.The family tier has no such backstop:
spac_sponsor_link,underwriter_link), not a projection of something that survives it; andsec resolverefuses family kinds (person|companyallow-list), so nothing can rebuild what a purge deletes.Recovery from a family purge today means re-extracting every affected S-1/424 through the AI pipeline and re-paying that cost. And the trap is quiet:
drop-previousis symmetric in shape across all four resolver kinds, so at the call site it reads like the same routine cleanup whichever kind it is handed.What this issue asks for
resolveso family attribution is rebuildable from data that survives a purge, restoring the invariant person/company rely on.dropPreviousclosures for both family kinds, re-enabling the ceremony.The hard part
A family is keyed off the sponsor/underwriter common name the AI extractor emitted. Only the legal name reaches the observation row, and the canonical family retains just one variant's display name — so a naive batch pass cannot faithfully re-partition families. A normalizer change that splits a family would reassign every member from that single stored name.
So this is not just wiring: it needs a decision about where the common name lives durably. Options worth weighing:
drop-previousrefusing family kinds permanently — in which case close this and document the asymmetry as deliberate rather than temporary.Already in place
SpacSponsorLinkRepo.deleteForResolverVersionandUnderwriterLinkRepo.deleteForResolverVersionlanded in #237 with tests, and are intentionally unreferenced by any command. They are the building block the purge needs — no command reaches them until the closures are registered.registerResolvers.family.test.tscurrently asserts the refusal and that every family row survives it on both tiers at both versions; those tests invert when the purge is re-enabled.🤖 Generated with Claude Code
https://claude.ai/code/session_01KB8s4qR5goCoxjE94YDFUV