refactor: remove workspace revision state

This commit is contained in:
2026-08-10 20:54:40 +02:00
parent 512b0261b1
commit f102629cee
8 changed files with 292 additions and 314 deletions
+181 -64
View File
@@ -312,37 +312,47 @@ async function pushInvalidWorkspace(source: string): Promise<void> {
await git(source, ["push", "origin", "main"]);
}
function legacyDigest(contents: string): string {
return createHash("sha256").update(contents).digest("hex");
type HistoricalRevisionState = "absent" | "operational" | "migration_required" | "unknown";
function revisionWithHistoricalState(
revision: Record<string, unknown>,
encoding: HistoricalRevisionState,
): Record<string, unknown> {
const { state: _state, ...stateFree } = revision;
return encoding === "absent" ? stateFree : {
...stateFree,
state: encoding === "unknown" ? "retired" : encoding,
};
}
function persistPreStateManifest(root: string, commit: string): void {
const snapshotDirectory = join(root, "snapshots", commit);
function rewritePersistedRevisionStates(
root: string,
commit: string,
activeEncoding: HistoricalRevisionState,
manifestEncoding: HistoricalRevisionState,
): void {
const activePath = join(root, "state", "active.json");
const snapshotPath = join(snapshotDirectory, "snapshot.json");
const snapshotPath = join(root, "snapshots", commit, "snapshot.json");
const active = JSON.parse(readFileSync(activePath, "utf8"));
const manifest = JSON.parse(readFileSync(snapshotPath, "utf8"));
const envName = "psd-clinical.env.example";
const docsName = "psd-clinical.md";
const envExample = "# Legacy registry artifact\n";
const markdown = "# Legacy registry artifact\n";
chmodSync(join(snapshotDirectory, envName), 0o600);
chmodSync(join(snapshotDirectory, docsName), 0o600);
writeFileSync(join(snapshotDirectory, envName), envExample);
writeFileSync(join(snapshotDirectory, docsName), markdown);
active.revisions = active.revisions.map(({ state: _state, ...revision }: Record<string, unknown>) => revision);
manifest.revisions = manifest.revisions.map(({ state: _state, ...revision }: Record<string, unknown>) => revision);
manifest.files = {
"psd-clinical.yaml": manifest.files["psd-clinical.yaml"],
[envName]: legacyDigest(envExample),
[docsName]: legacyDigest(markdown),
};
active.revisions = active.revisions.map((revision: Record<string, unknown>) => (
revisionWithHistoricalState(revision, activeEncoding)
));
manifest.revisions = manifest.revisions.map((revision: Record<string, unknown>) => (
revisionWithHistoricalState(revision, manifestEncoding)
));
writeFileSync(activePath, JSON.stringify(active));
chmodSync(snapshotPath, 0o600);
writeFileSync(snapshotPath, JSON.stringify(manifest));
}
function persistedState(root: string, commit: string): { active: any; manifest: any } {
return {
active: JSON.parse(readFileSync(join(root, "state", "active.json"), "utf8")),
manifest: JSON.parse(readFileSync(join(root, "snapshots", commit, "snapshot.json"), "utf8")),
};
}
test("allows first API publication and delete-last from a content-only registry base", async () => {
const remote = await contentOnlyFixture();
const registry = new WorkspaceRegistry(config(join(remote.root, "registry"), remote.remote));
@@ -390,7 +400,6 @@ test("concurrent first lists lazily bootstrap a clean registry once safely", asy
expect.objectContaining({
id: "psd-clinical",
commit: remote.initialCommit,
state: "operational",
}),
]);
}
@@ -706,63 +715,171 @@ test.each([
expect(existsSync(join(remote.root, "registry", "state", "active.json"))).toBe(false);
});
test("migrates a validated pre-state manifest while preserving its v3 operational state", async () => {
test("writes only state-free revisions and never exposes revision state", async () => {
const remote = await fixture();
const root = join(remote.root, "registry");
const firstRegistry = new WorkspaceRegistry(config(root, remote.remote));
await firstRegistry.bootstrap();
persistPreStateManifest(root, remote.initialCommit);
const registry = new WorkspaceRegistry(config(root, remote.remote));
await registry.bootstrap();
const restoredRegistry = new WorkspaceRegistry(config(root, remote.remote));
await expect(restoredRegistry.bootstrap()).resolves.toMatchObject({
head: remote.initialCommit,
degraded: false,
const initial = persistedState(root, remote.initialCommit);
expect(Object.keys(initial.active).sort()).toEqual(["head", "revisions"]);
expect(Object.keys(initial.manifest).sort()).toEqual(["files", "head", "revisions"]);
expect(Object.keys(initial.active.revisions[0]).sort()).toEqual(["blob", "commit", "id", "snapshotPath"]);
expect(Object.keys(initial.manifest.revisions[0]).sort()).toEqual(["blob", "commit", "id", "snapshotPath"]);
const listed = await registry.list();
const read = await registry.read("psd-clinical");
expect(listed[0]).not.toHaveProperty("state");
expect(read.revision).not.toHaveProperty("state");
const published = await registry.publish({
action: "update",
workspace: workspaceWith("psd-clinical", { name: "State-free revision" }),
baseCommit: remote.initialCommit,
baseBlob: listed[0]!.blob,
});
await expect(restoredRegistry.list()).resolves.toMatchObject([
{ id: "psd-clinical", state: "operational" },
]);
const active = JSON.parse(readFileSync(join(root, "state", "active.json"), "utf8"));
const manifest = JSON.parse(readFileSync(join(root, "snapshots", remote.initialCommit, "snapshot.json"), "utf8"));
expect(active.revisions[0].state).toBe("operational");
expect(manifest.revisions[0].state).toBe("operational");
expect(Object.keys(manifest.files).sort()).toEqual([
"psd-clinical.env.example", "psd-clinical.md", "psd-clinical.yaml",
]);
const updated = persistedState(root, published!.commit);
expect(updated.active.revisions[0]).not.toHaveProperty("state");
expect(updated.manifest.revisions[0]).not.toHaveProperty("state");
expect(published).not.toHaveProperty("state");
});
test("finishes a pre-state active manifest migration after its v3 snapshot was atomically updated", async () => {
test("accepts historical operational state without leaking it or rewriting the immutable snapshot", async () => {
const remote = await fixture();
const root = join(remote.root, "registry");
await new WorkspaceRegistry(config(root, remote.remote)).bootstrap();
rewritePersistedRevisionStates(root, remote.initialCommit, "operational", "operational");
const snapshotPath = join(root, "snapshots", remote.initialCommit, "snapshot.json");
const historicalManifest = readFileSync(snapshotPath, "utf8");
const restored = new WorkspaceRegistry(config(root, remote.remote));
const listed = await restored.list();
const read = await restored.read("psd-clinical");
expect(listed[0]).not.toHaveProperty("state");
expect(read.revision).not.toHaveProperty("state");
expect(readFileSync(snapshotPath, "utf8")).toBe(historicalManifest);
await restored.bootstrap();
const rewrittenActive = persistedState(root, remote.initialCommit).active;
expect(rewrittenActive.revisions[0]).not.toHaveProperty("state");
expect(readFileSync(snapshotPath, "utf8")).toBe(historicalManifest);
});
test.each([
["historical active and state-free snapshot", "operational", "absent"],
["state-free active and historical snapshot", "absent", "operational"],
] as const)("normalizes mixed persisted revision encodings: %s", async (_name, activeState, manifestState) => {
const remote = await fixture();
const root = join(remote.root, "registry");
await new WorkspaceRegistry(config(root, remote.remote)).bootstrap();
rewritePersistedRevisionStates(root, remote.initialCommit, activeState, manifestState);
const snapshotPath = join(root, "snapshots", remote.initialCommit, "snapshot.json");
const historicalManifest = readFileSync(snapshotPath, "utf8");
const revisions = await new WorkspaceRegistry(config(root, remote.remote)).list();
expect(revisions[0]).not.toHaveProperty("state");
expect(readFileSync(snapshotPath, "utf8")).toBe(historicalManifest);
});
test.each([
["migration_required in active state", "migration_required", "absent"],
["migration_required in snapshot manifest", "absent", "migration_required"],
["unknown state in active state", "unknown", "operational"],
["unknown state in snapshot manifest", "operational", "unknown"],
] as const)("rejects %s", async (_name, activeState, manifestState) => {
const remote = await fixture();
const root = join(remote.root, "registry");
await new WorkspaceRegistry(config(root, remote.remote)).bootstrap();
rewritePersistedRevisionStates(root, remote.initialCommit, activeState, manifestState);
await expect(new WorkspaceRegistry(config(root, remote.remote)).list()).rejects.toMatchObject({
code: "workspace_invalid",
});
});
test.each([
["active top level", "active", "top"],
["active revision", "active", "revision"],
["snapshot top level", "manifest", "top"],
["snapshot revision", "manifest", "revision"],
] as const)("rejects unknown fields in %s", async (_name, component, location) => {
const remote = await fixture();
const root = join(remote.root, "registry");
await new WorkspaceRegistry(config(root, remote.remote)).bootstrap();
const path = component === "active"
? join(root, "state", "active.json")
: join(root, "snapshots", remote.initialCommit, "snapshot.json");
const persisted = JSON.parse(readFileSync(path, "utf8"));
if (location === "top") persisted.unexpected = true;
else persisted.revisions[0].unexpected = true;
if (component === "manifest") chmodSync(path, 0o600);
writeFileSync(path, JSON.stringify(persisted));
await expect(new WorkspaceRegistry(config(root, remote.remote)).list()).rejects.toMatchObject({
code: "workspace_invalid",
});
});
test("normalizes operational state in retained historical snapshots without rewriting them", async () => {
const remote = await fixture();
const root = join(remote.root, "registry");
const registry = new WorkspaceRegistry(config(root, remote.remote));
await registry.bootstrap();
persistPreStateManifest(root, remote.initialCommit);
writeFileSync(join(remote.source, "workspaces", "psd-clinical.yaml"), validYaml.replace(
"name: Policlinico San Donato", "name: Current workspace",
));
await git(remote.source, ["add", "workspaces/psd-clinical.yaml"]);
await git(remote.source, ["commit", "-m", "Update active workspace"]);
await git(remote.source, ["push", "origin", "main"]);
await registry.pull();
rewritePersistedRevisionStates(root, remote.initialCommit, "absent", "operational");
const snapshotPath = join(root, "snapshots", remote.initialCommit, "snapshot.json");
const manifest = JSON.parse(readFileSync(snapshotPath, "utf8"));
manifest.revisions[0].state = "operational";
writeFileSync(snapshotPath, JSON.stringify(manifest));
const historicalManifest = readFileSync(snapshotPath, "utf8");
const restoredRegistry = new WorkspaceRegistry(config(root, remote.remote));
await expect(restoredRegistry.list()).resolves.toMatchObject([
{ id: "psd-clinical", state: "operational" },
]);
const retained = await new WorkspaceRegistry(config(root, remote.remote)).listRetainedSnapshots();
expect(retained).toEqual(expect.arrayContaining([
expect.objectContaining({ id: "psd-clinical", commit: remote.initialCommit }),
]));
expect(retained.every((revision) => !("state" in revision))).toBe(true);
expect(readFileSync(snapshotPath, "utf8")).toBe(historicalManifest);
});
test("rejects a corrupt pre-state manifest rather than accepting it during migration", async () => {
test("normalizes historical operational state during offline fallback after restart", async () => {
const remote = await fixture();
const root = join(remote.root, "registry");
const registry = new WorkspaceRegistry(config(root, remote.remote));
await registry.bootstrap();
persistPreStateManifest(root, remote.initialCommit);
const snapshotPath = join(root, "snapshots", remote.initialCommit, "snapshot.json");
const manifest = JSON.parse(readFileSync(snapshotPath, "utf8"));
manifest.files["psd-clinical.yaml"] = "0".repeat(64);
writeFileSync(snapshotPath, JSON.stringify(manifest));
await new WorkspaceRegistry(config(root, remote.remote)).bootstrap();
rewritePersistedRevisionStates(root, remote.initialCommit, "operational", "operational");
rmSync(remote.remote, { recursive: true, force: true });
const restoredRegistry = new WorkspaceRegistry(config(root, remote.remote));
await expect(restoredRegistry.bootstrap()).rejects.toMatchObject({ code: "workspace_invalid" });
await expect(restoredRegistry.list()).rejects.toMatchObject({ code: "workspace_invalid" });
const restored = new WorkspaceRegistry(config(root, remote.remote));
await expect(restored.pull()).resolves.toMatchObject({ degraded: true, head: remote.initialCommit });
const listed = await restored.list();
const read = await restored.read("psd-clinical");
expect(listed[0]).not.toHaveProperty("state");
expect(read.revision).not.toHaveProperty("state");
});
test("fails closed when a retained snapshot descriptor is not schema v3", async () => {
const remote = await fixture();
const root = join(remote.root, "registry");
await new WorkspaceRegistry(config(root, remote.remote)).bootstrap();
const snapshotDirectory = join(root, "snapshots", remote.initialCommit);
const yamlPath = join(snapshotDirectory, "psd-clinical.yaml");
chmodSync(yamlPath, 0o600);
const legacy = legacyV2Yaml();
writeFileSync(yamlPath, legacy);
const manifestPath = join(snapshotDirectory, "snapshot.json");
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
manifest.files["psd-clinical.yaml"] = createHash("sha256").update(legacy).digest("hex");
chmodSync(manifestPath, 0o600);
writeFileSync(manifestPath, JSON.stringify(manifest));
await expect(new WorkspaceRegistry(config(root, remote.remote)).list()).rejects.toMatchObject({
code: "workspace_invalid",
});
});
test("keeps the last valid snapshot when a pulled commit has invalid YAML", async () => {
@@ -889,8 +1006,8 @@ test("lists operational descriptors retained after their workspace was removed f
const retained = await registry.listRetainedSnapshots();
expect(retained).toEqual(expect.arrayContaining([
expect.objectContaining({ id: "psd-clinical", commit: remote.initialCommit, state: "operational" }),
expect.objectContaining({ id: "archive-only", state: "operational" }),
expect.objectContaining({ id: "psd-clinical", commit: remote.initialCommit }),
expect.objectContaining({ id: "archive-only" }),
]));
});