Verify retained feature block after restart

This commit is contained in:
vorotamoroz
2026-09-27 10:15:46 +00:00
parent 6c50505096
commit 9a047d44f3
3 changed files with 80 additions and 8 deletions
@@ -32,6 +32,9 @@ const unknownFeature = "future-format-v7";
type FeatureState = {
version: number | null;
features: string[];
snapshotFeatures: string[];
snapshotDatabaseId: string | null;
databaseId: string | null;
hasActiveReplicator: boolean;
};
@@ -43,9 +46,14 @@ async function readFeatureState(cliBinary: string, env: NodeJS.ProcessEnv): Prom
"const core=app.plugins.plugins['obsidian-livesync'].core;",
`const id=${JSON.stringify(VERSIONING_DOCID)};`,
"const info=await core.localDatabase.getRaw(id).catch(()=>null);",
"const snapshot=await core.kvDB.get('replicationResultProcessorSnapshot');",
"const databaseId=await core.localDatabase.localDatabase.id();",
"return JSON.stringify({",
"version:typeof info?.version==='number'?info.version:null,",
"features:Array.isArray(info?.used_features)?info.used_features:[],",
"snapshotFeatures:Array.isArray(snapshot?.observedFeatures)?snapshot.observedFeatures:[],",
"snapshotDatabaseId:snapshot?.databaseId??null,",
"databaseId,",
"hasActiveReplicator:!!core.services.replicator.getActiveReplicator(),",
"});",
"})()",
@@ -145,8 +153,12 @@ async function main(): Promise<void> {
const observed = await waitForState(
cli.binary,
session.cliEnv,
(state) => state.version === 13 && state.features.includes(unknownFeature) && !state.hasActiveReplicator,
"the live feature change and Replicator retirement"
(state) =>
state.version === 13 &&
state.features.includes(unknownFeature) &&
state.snapshotFeatures.includes(unknownFeature) &&
!state.hasActiveReplicator,
"the live feature change, durable observation, and Replicator retirement"
);
const replicated = await evalObsidianJson<boolean>(
@@ -158,8 +170,63 @@ async function main(): Promise<void> {
const acceptedAfterStop = await readFile(fullPath, "utf-8");
if (acceptedAfterStop !== acceptedContent)
throw new Error("Previously accepted Vault content changed on stop.");
// Shorten both visible lists so only the snapshot can retain the observed requirement.
const shortenedRemoteVersion = await fetchCouchDbDocument(couchDb, dbName, VERSIONING_DOCID);
await putCouchDbDocument(couchDb, dbName, { ...shortenedRemoteVersion, used_features: [] });
const shortenedLocalFeatures = await evalObsidianJson<string[]>(
cli.binary,
[
"(async()=>{",
"const core=app.plugins.plugins['obsidian-livesync'].core;",
`const id=${JSON.stringify(VERSIONING_DOCID)};`,
"const info=await core.localDatabase.getRaw(id);",
"await core.localDatabase.putRaw({...info,used_features:[]});",
"return JSON.stringify((await core.localDatabase.getRaw(id)).used_features);",
"})()",
].join(""),
session.cliEnv
);
if (shortenedLocalFeatures.length !== 0)
throw new Error("The local control document did not shorten for the restart scenario.");
await session.app.stop();
session = undefined;
session = await startObsidianLiveSyncSession({ binary, cliBinary: cli.binary, vault });
await waitForLiveSyncCoreReady(cli.binary, session.cliEnv);
const afterRestart = await waitForState(
cli.binary,
session.cliEnv,
(state) =>
state.version === 13 && state.features.length === 0 && state.snapshotFeatures.includes(unknownFeature),
"the retained unknown feature after restart"
);
if (afterRestart.snapshotDatabaseId !== afterRestart.databaseId)
throw new Error("The retained feature observation belongs to a different physical database.");
const replicatedAfterRestart = await evalObsidianJson<boolean>(
cli.binary,
"(async()=>JSON.stringify(!!(await app.plugins.plugins['obsidian-livesync'].core.services.replication.replicate(true))))()",
session.cliEnv
);
const continuousAfterRestart = await evalObsidianJson<{ status: string }>(
cli.binary,
[
"(async()=>{",
"const core=app.plugins.plugins['obsidian-livesync'].core;",
"const result=await core.services.replication.startContinuous({trigger:'daemon',interaction:{kind:'forbidden'}});",
"return JSON.stringify(result);",
"})()",
].join(""),
session.cliEnv
);
if (replicatedAfterRestart || continuousAfterRestart.status !== "blocked")
throw new Error(
`Replication resumed after restart despite a previously observed unknown feature: ${JSON.stringify({ afterRestart, replicatedAfterRestart, continuousAfterRestart })}`
);
if ((await readFile(fullPath, "utf-8")) !== acceptedContent)
throw new Error("Previously accepted Vault content changed after restart.");
console.log(
`Active feature change retired the Replicator and kept accepted content: ${JSON.stringify(observed)}`
`Active feature change retired the Replicator; a shortened control document did not clear the block after restart: ${JSON.stringify({ observed, afterRestart })}`
);
} finally {
await session?.app.stop();