mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-14 02:40:45 +08:00
209 lines
6.4 KiB
JavaScript
209 lines
6.4 KiB
JavaScript
// ST-BME: legacy persistence repair policy
|
|
// Pure helpers only: no IO, no runtime mutation.
|
|
|
|
const ACCEPTED_GRAPH_TIERS = new Set([
|
|
"authority-sql",
|
|
"opfs",
|
|
"indexeddb",
|
|
"chat-state",
|
|
"luker-chat-state",
|
|
]);
|
|
|
|
const RECOVERY_ONLY_GRAPH_TIERS = new Set([
|
|
"shadow",
|
|
"metadata-full",
|
|
"authority-blob",
|
|
"authority-blob-checkpoint",
|
|
"runtime-recovery",
|
|
]);
|
|
|
|
const REPLICA_ONLY_GRAPH_TIERS = new Set([
|
|
"trivium",
|
|
"authority-trivium",
|
|
"vector",
|
|
]);
|
|
|
|
export function normalizeLegacyPersistenceTier(storageTier = "none") {
|
|
return String(storageTier || "none").trim().toLowerCase() || "none";
|
|
}
|
|
|
|
export function classifyLegacyPersistenceTier(storageTier = "none") {
|
|
const tier = normalizeLegacyPersistenceTier(storageTier);
|
|
if (ACCEPTED_GRAPH_TIERS.has(tier)) {
|
|
return { tier, role: "accepted", accepted: true, recoverable: true };
|
|
}
|
|
if (RECOVERY_ONLY_GRAPH_TIERS.has(tier)) {
|
|
return { tier, role: "recovery-only", accepted: false, recoverable: true };
|
|
}
|
|
if (REPLICA_ONLY_GRAPH_TIERS.has(tier)) {
|
|
return { tier, role: "replica-only", accepted: false, recoverable: false };
|
|
}
|
|
return { tier, role: "unknown", accepted: false, recoverable: false };
|
|
}
|
|
|
|
export function isAcceptedLegacyPersistenceTier(storageTier = "none") {
|
|
return classifyLegacyPersistenceTier(storageTier).accepted === true;
|
|
}
|
|
|
|
export function isRecoveryOnlyLegacyPersistenceTier(storageTier = "none") {
|
|
const classified = classifyLegacyPersistenceTier(storageTier);
|
|
return classified.role === "recovery-only";
|
|
}
|
|
|
|
function firstMeaningfulTier(...values) {
|
|
for (const value of values) {
|
|
const tier = normalizeLegacyPersistenceTier(value);
|
|
if (tier && tier !== "none") return tier;
|
|
}
|
|
return "none";
|
|
}
|
|
|
|
export function getPendingPersistenceTargetRevision({
|
|
batchPersistence = null,
|
|
persistenceState = null,
|
|
} = {}) {
|
|
const persistenceRevision = Number(batchPersistence?.revision || 0);
|
|
const queuedRevision = Number(persistenceState?.queuedPersistRevision || 0);
|
|
const targetRevision = Math.max(
|
|
Number.isFinite(persistenceRevision) ? persistenceRevision : 0,
|
|
Number.isFinite(queuedRevision) ? queuedRevision : 0,
|
|
);
|
|
return Number.isFinite(targetRevision) && targetRevision > 0 ? targetRevision : 0;
|
|
}
|
|
|
|
export function getAcceptedCommitMarkerRevision(marker = null) {
|
|
if (!marker || typeof marker !== "object") return 0;
|
|
if (marker.accepted !== true) return 0;
|
|
const revision = Number(marker.revision || marker.acceptedRevision || 0);
|
|
return Number.isFinite(revision) && revision > 0 ? Math.floor(revision) : 0;
|
|
}
|
|
|
|
export function planAcceptedPendingPersistenceRepair({
|
|
batchPersistence = null,
|
|
persistenceState = null,
|
|
commitMarker = null,
|
|
activeChatId = "",
|
|
queuedChatId = "",
|
|
markerChatMatchesQueued = false,
|
|
} = {}) {
|
|
const targetRevision = getPendingPersistenceTargetRevision({
|
|
batchPersistence,
|
|
persistenceState,
|
|
});
|
|
if (persistenceState?.pendingPersist !== true) {
|
|
return { action: "keep", reason: "not-pending", targetRevision };
|
|
}
|
|
if (targetRevision <= 0) {
|
|
return { action: "keep", reason: "missing-target-revision", targetRevision };
|
|
}
|
|
const normalizedActiveChatId = String(activeChatId || "").trim();
|
|
const normalizedQueuedChatId = String(queuedChatId || "").trim();
|
|
if (!normalizedActiveChatId || !normalizedQueuedChatId) {
|
|
return { action: "keep", reason: "missing-chat-id", targetRevision };
|
|
}
|
|
if (normalizedActiveChatId !== normalizedQueuedChatId) {
|
|
return { action: "keep", reason: "queued-chat-mismatch", targetRevision };
|
|
}
|
|
|
|
const stateAcceptedRevision = Number(persistenceState?.lastAcceptedRevision || 0);
|
|
const markerAcceptedRevision = markerChatMatchesQueued
|
|
? getAcceptedCommitMarkerRevision(commitMarker)
|
|
: 0;
|
|
const acceptedRevision = Math.max(
|
|
Number.isFinite(stateAcceptedRevision) ? stateAcceptedRevision : 0,
|
|
Number.isFinite(markerAcceptedRevision) ? markerAcceptedRevision : 0,
|
|
);
|
|
if (acceptedRevision < targetRevision) {
|
|
return {
|
|
action: "keep",
|
|
reason: "accepted-revision-behind",
|
|
targetRevision,
|
|
acceptedRevision,
|
|
};
|
|
}
|
|
|
|
const tier = firstMeaningfulTier(
|
|
persistenceState?.acceptedStorageTier,
|
|
commitMarker?.storageTier,
|
|
batchPersistence?.storageTier,
|
|
);
|
|
if (!isAcceptedLegacyPersistenceTier(tier)) {
|
|
return {
|
|
action: "keep",
|
|
reason: "accepted-tier-not-canonical",
|
|
targetRevision,
|
|
acceptedRevision,
|
|
tier,
|
|
};
|
|
}
|
|
|
|
return {
|
|
action: "clear-stale-pending",
|
|
reason: "accepted-revision-covers-pending",
|
|
targetRevision,
|
|
acceptedRevision,
|
|
tier,
|
|
};
|
|
}
|
|
|
|
export function repairLegacyLastBatchPersistenceStatus({
|
|
batchStatus = null,
|
|
persistenceState = null,
|
|
commitMarker = null,
|
|
activeChatId = "",
|
|
commitMarkerChatMatchesActive = false,
|
|
} = {}) {
|
|
const persistence = batchStatus?.persistence;
|
|
if (!batchStatus || !persistence || typeof persistence !== "object") {
|
|
return { repaired: false, batchStatus };
|
|
}
|
|
const targetRevision = getPendingPersistenceTargetRevision({
|
|
batchPersistence: persistence,
|
|
persistenceState,
|
|
});
|
|
const normalizedActiveChatId = String(activeChatId || "").trim();
|
|
const stateChatId = String(persistenceState?.chatId || "").trim();
|
|
const stateMatchesActive =
|
|
!normalizedActiveChatId || !stateChatId || normalizedActiveChatId === stateChatId;
|
|
const acceptedRevision = Math.max(
|
|
stateMatchesActive ? Number(persistenceState?.lastAcceptedRevision || 0) : 0,
|
|
commitMarkerChatMatchesActive ? getAcceptedCommitMarkerRevision(commitMarker) : 0,
|
|
);
|
|
const tier = firstMeaningfulTier(
|
|
stateMatchesActive ? persistenceState?.acceptedStorageTier : "",
|
|
commitMarkerChatMatchesActive ? commitMarker?.storageTier : "",
|
|
persistence.storageTier,
|
|
);
|
|
if (
|
|
targetRevision <= 0 ||
|
|
acceptedRevision < targetRevision ||
|
|
!isAcceptedLegacyPersistenceTier(tier)
|
|
) {
|
|
return { repaired: false, batchStatus };
|
|
}
|
|
return {
|
|
repaired: true,
|
|
batchStatus: {
|
|
...batchStatus,
|
|
historyAdvanceAllowed: true,
|
|
historyAdvanced: batchStatus.historyAdvanced === true,
|
|
persistence: {
|
|
...persistence,
|
|
outcome: "accepted",
|
|
accepted: true,
|
|
saved: true,
|
|
queued: false,
|
|
blocked: false,
|
|
recoverable: false,
|
|
attempted: true,
|
|
storageTier: tier,
|
|
reason: "legacy-accepted-revision-repair",
|
|
revision: targetRevision,
|
|
},
|
|
},
|
|
targetRevision,
|
|
acceptedRevision,
|
|
tier,
|
|
};
|
|
}
|