feat(ql3): rehearse legacy sqlite upgrade

This commit is contained in:
whyour
2026-08-21 00:52:58 +08:00
parent 4ea156f189
commit c9e41812cb
23 changed files with 1533 additions and 23 deletions
@@ -107,6 +107,7 @@ function verifyActivation(
'recoverySha256',
'schemaVersion',
'sourcePathDigest',
'sourceSha256',
'state',
'targetDevice',
'targetInode',
@@ -129,6 +130,8 @@ function verifyActivation(
if (
typeof activationDigest !== 'string' ||
!DIGEST_PATTERN.test(activationDigest) ||
typeof activation.sourceSha256 !== 'string' ||
!DIGEST_PATTERN.test(activation.sourceSha256) ||
digest(payload) !== activationDigest
) {
configurationError('activation digest does not match');
@@ -14,7 +14,7 @@ const HASH_BUFFER_BYTES = 64 * 1024;
export interface TargetDataReconciliationEvidence {
readonly disposition: LocalDeploymentTargetReconciliationDisposition;
readonly targetMatchesActivation: boolean | null;
readonly sourceMatchesRecovery: boolean | null;
readonly sourceMatchesActivation: boolean | null;
readonly targetSidecarsClear: boolean | null;
readonly sourceSidecarsClear: boolean | null;
readonly targetFileIdentityDigest: string;
@@ -194,6 +194,8 @@ export function readTargetDataReconciliationEvidenceForPaths(
!DIGEST_PATTERN.test(activation.targetSha256) ||
typeof activation.recoverySha256 !== 'string' ||
!DIGEST_PATTERN.test(activation.recoverySha256) ||
typeof activation.sourceSha256 !== 'string' ||
!DIGEST_PATTERN.test(activation.sourceSha256) ||
typeof activation.targetDevice !== 'string' ||
typeof activation.targetInode !== 'string' ||
cutoverDigest(payload) !== activationDigest
@@ -218,17 +220,17 @@ export function readTargetDataReconciliationEvidenceForPaths(
throw new Error('target database stable identity drifted');
}
const targetMatchesActivation = target.sha256 === activation.targetSha256;
const sourceMatchesRecovery = source.sha256 === activation.recoverySha256;
const sourceMatchesActivation = source.sha256 === activation.sourceSha256;
const disposition =
!targetMatchesActivation || !target.sidecarsClear
? ('reconciliation_required' as const)
: sourceMatchesRecovery && source.sidecarsClear
: sourceMatchesActivation && source.sidecarsClear
? ('rollback_candidate' as const)
: ('manual_review' as const);
return evidence({
disposition,
targetMatchesActivation,
sourceMatchesRecovery,
sourceMatchesActivation,
targetSidecarsClear: target.sidecarsClear,
sourceSidecarsClear: source.sidecarsClear,
targetFileIdentityDigest: target.identityDigest,
@@ -238,7 +240,7 @@ export function readTargetDataReconciliationEvidenceForPaths(
return evidence({
disposition: 'manual_review',
targetMatchesActivation: null,
sourceMatchesRecovery: null,
sourceMatchesActivation: null,
targetSidecarsClear: null,
sourceSidecarsClear: null,
targetFileIdentityDigest: UNKNOWN_DIGEST,
@@ -256,7 +258,7 @@ export function verifyTargetDataReconciliationEvidence(
'disposition',
'evidenceDigest',
'sourceFileIdentityDigest',
'sourceMatchesRecovery',
'sourceMatchesActivation',
'sourceSidecarsClear',
'targetFileIdentityDigest',
'targetMatchesActivation',
@@ -270,8 +272,8 @@ export function verifyTargetDataReconciliationEvidence(
candidate.disposition !== 'manual_review') ||
(candidate.targetMatchesActivation !== null &&
typeof candidate.targetMatchesActivation !== 'boolean') ||
(candidate.sourceMatchesRecovery !== null &&
typeof candidate.sourceMatchesRecovery !== 'boolean') ||
(candidate.sourceMatchesActivation !== null &&
typeof candidate.sourceMatchesActivation !== 'boolean') ||
(candidate.targetSidecarsClear !== null &&
typeof candidate.targetSidecarsClear !== 'boolean') ||
(candidate.sourceSidecarsClear !== null &&
@@ -147,6 +147,7 @@ export function verifyTargetRunActivation(
'recoverySha256',
'schemaVersion',
'sourcePathDigest',
'sourceSha256',
'state',
'targetDevice',
'targetInode',
@@ -166,6 +167,8 @@ export function verifyTargetRunActivation(
activationDigest !== command.request.expectedActivationDigest ||
typeof activationDigest !== 'string' ||
!DIGEST_PATTERN.test(activationDigest) ||
typeof activation.sourceSha256 !== 'string' ||
!DIGEST_PATTERN.test(activation.sourceSha256) ||
cutoverDigest(payload) !== activationDigest
) {
configurationError('activation does not match the target run request');
@@ -404,6 +404,8 @@ function verifyAdoptedEvidence(
activation.profile !== intent.profile ||
activation.sourcePathDigest !== textDigest(binding.sourcePath) ||
activation.targetPathDigest !== textDigest(binding.targetPath) ||
typeof activation.sourceSha256 !== 'string' ||
!DIGEST_PATTERN.test(activation.sourceSha256) ||
typeof activation.recoverySha256 !== 'string' ||
!DIGEST_PATTERN.test(activation.recoverySha256) ||
typeof activation.adoptionManifestDigest !== 'string' ||
@@ -451,7 +453,7 @@ function verifyAdoptedEvidence(
if (
activation.targetDevice !== target.device ||
activation.targetInode !== target.inode ||
sourceSha256 !== activation.recoverySha256 ||
sourceSha256 !== activation.sourceSha256 ||
recoverySha256 !== activation.recoverySha256
) {
configurationError('adopted data evidence drifted');