fix(curator): preserve active sprint on reads
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
|||||||
decideCuratorSprintReconciliation,
|
decideCuratorSprintReconciliation,
|
||||||
resolveCuratorSprintAccess,
|
resolveCuratorSprintAccess,
|
||||||
shouldReconcileCuratorSprint,
|
shouldReconcileCuratorSprint,
|
||||||
|
shouldRebaseCuratorSprintForOnboardingContext,
|
||||||
} from "../src/v1/curator/curator-store.js";
|
} from "../src/v1/curator/curator-store.js";
|
||||||
import { templateSetFor, trialDaysFor } from "../src/v1/curator/task-registry.js";
|
import { templateSetFor, trialDaysFor } from "../src/v1/curator/task-registry.js";
|
||||||
|
|
||||||
@@ -51,6 +52,26 @@ const startDate = "2026-07-13";
|
|||||||
const studentFingerprint = curatorSprintPlanFingerprint({ startDate, variantId: "student_recent_grad", durationDays: 2, access: "trial" });
|
const studentFingerprint = curatorSprintPlanFingerprint({ startDate, variantId: "student_recent_grad", durationDays: 2, access: "trial" });
|
||||||
const experiencedFingerprint = curatorSprintPlanFingerprint({ startDate, variantId: "experienced_professional", durationDays: 2, access: "trial" });
|
const experiencedFingerprint = curatorSprintPlanFingerprint({ startDate, variantId: "experienced_professional", durationDays: 2, access: "trial" });
|
||||||
const experiencedPlan = curatorSprintTaskPlan({ startDate, variantId: "experienced_professional", durationDays: 2, access: "trial" });
|
const experiencedPlan = curatorSprintTaskPlan({ startDate, variantId: "experienced_professional", durationDays: 2, access: "trial" });
|
||||||
|
assert.equal(
|
||||||
|
shouldRebaseCuratorSprintForOnboardingContext({
|
||||||
|
existingStartDate: "2026-07-14",
|
||||||
|
focusDate: "2026-07-15",
|
||||||
|
onboardingContextProvided: true,
|
||||||
|
existingCompatible: true,
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
"a normal sprint read with onboarding context must preserve the active sprint across days",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
shouldRebaseCuratorSprintForOnboardingContext({
|
||||||
|
existingStartDate: "2026-07-14",
|
||||||
|
focusDate: "2026-07-15",
|
||||||
|
onboardingContextProvided: true,
|
||||||
|
existingCompatible: false,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
"changed onboarding compatibility may replace the existing sprint without resetting its calendar identity",
|
||||||
|
);
|
||||||
assert.notEqual(studentFingerprint, experiencedFingerprint, "ICP changes must change the compatibility fingerprint");
|
assert.notEqual(studentFingerprint, experiencedFingerprint, "ICP changes must change the compatibility fingerprint");
|
||||||
assert.equal(shouldReconcileCuratorSprint(
|
assert.equal(shouldReconcileCuratorSprint(
|
||||||
{ access: "trial", durationDays: 2, variantId: "student_recent_grad", planFingerprint: studentFingerprint },
|
{ access: "trial", durationDays: 2, variantId: "student_recent_grad", planFingerprint: studentFingerprint },
|
||||||
|
|||||||
@@ -141,6 +141,20 @@ export function decideCuratorSprintReconciliation(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function shouldRebaseCuratorSprintForOnboardingContext(input: {
|
||||||
|
existingStartDate?: string;
|
||||||
|
focusDate: string;
|
||||||
|
onboardingContextProvided: boolean;
|
||||||
|
existingCompatible: boolean;
|
||||||
|
}) {
|
||||||
|
return Boolean(
|
||||||
|
input.onboardingContextProvided
|
||||||
|
&& input.existingStartDate
|
||||||
|
&& input.existingStartDate !== input.focusDate
|
||||||
|
&& !input.existingCompatible,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function accessChoiceFromContext(context: Record<string, unknown> | undefined): "trial" | "full" | undefined {
|
function accessChoiceFromContext(context: Record<string, unknown> | undefined): "trial" | "full" | undefined {
|
||||||
const preferences = context && context.preferences && typeof context.preferences === "object" && !Array.isArray(context.preferences)
|
const preferences = context && context.preferences && typeof context.preferences === "object" && !Array.isArray(context.preferences)
|
||||||
? context.preferences as Record<string, unknown>
|
? context.preferences as Record<string, unknown>
|
||||||
@@ -751,7 +765,16 @@ async function loadSprintState(
|
|||||||
const existingDuration = latestPayload?.durationDays === 7 ? 7 : latestPayload?.durationDays === 2 ? 2 : undefined;
|
const existingDuration = latestPayload?.durationDays === 7 ? 7 : latestPayload?.durationDays === 2 ? 2 : undefined;
|
||||||
const existingAccess = latestPayload?.access === "full" ? "full" : latestPayload?.access === "trial" ? "trial" : undefined;
|
const existingAccess = latestPayload?.access === "full" ? "full" : latestPayload?.access === "trial" ? "trial" : undefined;
|
||||||
const existingFingerprint = typeof latestPayload?.planFingerprint === "string" ? latestPayload.planFingerprint : undefined;
|
const existingFingerprint = typeof latestPayload?.planFingerprint === "string" ? latestPayload.planFingerprint : undefined;
|
||||||
const rebaseExistingForCompletion = Boolean(onboardingContextOverride && existingStartDate && existingStartDate !== todayDate);
|
const existingCompatible = Boolean(
|
||||||
|
existingStartDate && existingVersion === CURATOR_PLAN_VERSION && existingVariant
|
||||||
|
&& existingDuration === desired.durationDays && existingAccess === desired.access,
|
||||||
|
);
|
||||||
|
const rebaseExistingForCompletion = shouldRebaseCuratorSprintForOnboardingContext({
|
||||||
|
existingStartDate,
|
||||||
|
focusDate: todayDate,
|
||||||
|
onboardingContextProvided: Boolean(onboardingContextOverride),
|
||||||
|
existingCompatible,
|
||||||
|
});
|
||||||
const reuseDecision = existingStartDate && existingVersion && existingDuration && existingAccess
|
const reuseDecision = existingStartDate && existingVersion && existingDuration && existingAccess
|
||||||
? resolveAgedTrialReuse({
|
? resolveAgedTrialReuse({
|
||||||
existing: { startDate: existingStartDate, version: existingVersion, durationDays: existingDuration, access: existingAccess },
|
existing: { startDate: existingStartDate, version: existingVersion, durationDays: existingDuration, access: existingAccess },
|
||||||
|
|||||||
Reference in New Issue
Block a user