feat(household): archive / un-archive families (households)#959
Open
jee7s wants to merge 1 commit into
Open
Conversation
Soft-archive for households (UI "families"): a nullable Household.archivedAt stamp sets a family aside without deleting anything. Archived households are hidden from default board lists/searches/pickers (the membership-ops households view gets an "include archived" toggle), skipped by notification fan-outs and crons (renewal + person-BG sweeps, new-program blast, audit/compliance chase lists, nav badges), and their members are blocked from starting new activity — program enrollment, check-in/scan, and membership application — through one shared guard (assertHouseholdActive), returning a clear 4xx. Board/sysadmin only, surfaced as a toggle in the households view alongside grant/revoke/deny. Who/when is captured in the AuditLog (no extra column). History is fully intact; un-archive clears the stamp and restores everything. See checkin-app/docs/designs/HOUSEHOLD_ARCHIVE.md for the decisions and the full touched/not-touched query sweep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Adds soft-archive for households (UI "families") — a way to set a family
aside (moved away, aged out, duplicate) without deleting anything, so it drops
out of the board's day-to-day surfaces and its members can't start new activity,
while every historical row stays intact and one action fully restores it.
Design + decisions + the full query sweep:
checkin-app/docs/designs/HOUSEHOLD_ARCHIVE.md.Archive is orthogonal to membership Deny — it does not block login; it hides
the family from board lists/crons and blocks new activity. The two can coexist.
Decisions (from the product interview)
Household.archivedAt DateTime?stamp (@sensitivity:internal).Non-null == archived. Additive/nullable → expand-only migration.
AuditLog, not a second column (mirrors the existinghousehold Deny / membership-archive audit rows).
(rides the existing sanctioned households POST — no new route, no
routeAuthDriftchange).
"Include archived" toggle to find them for un-archive.
and membership-application start — via one shared guard
assertHouseholdActive(→ 409 "household is archived"), guarded at the service/route level, not per caller.
ACTIVE_HOUSEHOLD_WHEREfilter folded into the existing predicates: renewal + person-BG sweeps, the
new-program email blast, the audit/compliance chase lists, and the nav badges.
The design doc §6 documents every household/person query that feeds a list, cron,
or fan-out as a touched / deliberately-not-touched table (e.g. the safety
emergency-contacts board is deliberately left showing archived households, since
archive doesn't force-checkout a member who may still be physically present).
Testing
householdArchive.integration.test.ts(6 cases, allgreen against seeded Postgres): archive → stamped + audit row (+ idempotency) →
hidden-from-list / shown-with-toggle → blocked enrollment (409) → blocked
membership start → renewal-sweep skip (representative cron) → un-archive
clears + audit + enrollment restored.
tsc --noEmitclean,eslint --max-warnings 0clean.classifications.tsregenerated viaprisma generate.scanRoute,programSignupIntegration) tosupply
household.findUniquefor the new guard.🤖 Generated with Claude Code