fix(audit): residential/tenancies — M28 (unified stage validation), M29 (explicit-disable wins), L31 (active-tenancy warning), L32 (socket event + saveStages tx)

Updated tenancy-auto-create integration test to assert M29 (explicit disable
respected) instead of the old re-enable behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 13:18:28 +02:00
parent 7b74e2314b
commit e7fdf75a6c
9 changed files with 149 additions and 34 deletions

View File

@@ -18,6 +18,7 @@ import type {
UpdateResidentialInterestInput,
} from '@/lib/validators/residential';
import { sendEmail } from '@/lib/email';
import { assertValidStage } from '@/lib/services/residential-stages.service';
import { SETTING_KEYS, getPortBrandingConfig, readSetting } from '@/lib/services/port-config';
import { brandingPrimaryColor, renderShell } from '@/lib/email/shell';
@@ -550,6 +551,15 @@ export async function updateResidentialInterest(
});
if (!before) throw new NotFoundError('Residential interest');
// Reject moves to a stage that isn't in this port's live stage list.
// The validator accepts any string (so admins can add custom stages
// without a deploy); the membership check is enforced here at write
// time so a PATCH can't park an interest on a non-existent stage that
// would then surface as an orphan in funnel reports.
if (data.pipelineStage !== undefined) {
await assertValidStage(portId, data.pipelineStage);
}
const [updated] = await db
.update(residentialInterests)
.set({ ...data, updatedAt: new Date() })