fix: bootstrap registry on first workspace list

This commit is contained in:
2026-08-05 16:49:56 +02:00
parent b93f13e39e
commit 840db9bd4f
3 changed files with 49 additions and 0 deletions
+6
View File
@@ -155,6 +155,12 @@ export class WorkspaceRegistry {
}
async list(): Promise<WorkspaceRevision[]> {
const active = await this.tryActiveState();
if (active) return active.revisions;
// A clean installation has no active snapshot until the first registry operation. Keep
// this lazy so health/startup remain available when Git is temporarily unreachable, while
// still refusing corrupted existing state (tryActiveState throws instead of returning none).
await this.bootstrap();
return (await this.activeState()).revisions;
}
+15
View File
@@ -254,6 +254,21 @@ test("bootstraps a checkout and activates a validated immutable snapshot", async
});
});
test("first list lazily bootstraps a clean registry", async () => {
const remote = await fixture();
const root = join(remote.root, "registry");
const registry = new WorkspaceRegistry(config(root, remote.remote));
await expect(registry.list()).resolves.toEqual([
expect.objectContaining({
id: "psd-clinical",
commit: remote.initialCommit,
state: "operational",
}),
]);
expect(existsSync(join(root, "state", "active.json"))).toBe(true);
});
test("publishes create, update, and delete with the configured Git author identity", async () => {
const remote = await fixture();
const registry = new WorkspaceRegistry(config(join(remote.root, "registry"), remote.remote, {
+28
View File
@@ -630,6 +630,27 @@ task13_report_server_workspace_failure() {
else
printf '%s\n' '(empty response)' >&2
fi
printf '%s\n' '--- sanitized registry integrity probe ---' >&2
{
task13_compose exec -T core node --input-type=module -e '
const { loadConfig } = await import("/app/backend/dist/config.js");
const { WorkspaceRegistry } = await import("/app/backend/dist/workspaces/registry.js");
const registry = new WorkspaceRegistry(loadConfig(process.env).workspaceRegistry);
try {
const revisions = await registry.list();
for (const revision of revisions) await registry.read(revision.id);
console.log(JSON.stringify({ ok: true, revisions: revisions.length }));
} catch (error) {
console.log(JSON.stringify({
ok: false,
name: error instanceof Error ? error.name : "UnknownError",
code: error && typeof error === "object" && "code" in error ? error.code : "unknown",
message: error instanceof Error ? error.message : "Unknown registry failure",
}));
process.exitCode = 1;
}
' 2>&1 || printf '%s\n' '(registry integrity probe unavailable)'
} | tail -n 20 | task13_sanitize >&2
printf '%s\n' '--- sanitized core logs (last 100 lines) ---' >&2
{
task13_compose logs --no-color --tail 100 core 2>&1 \
@@ -1063,6 +1084,11 @@ task13_self_test_server_workspace_diagnostics() {
printf '%s\n' \
'{"error":"workspace_invalid","detail":"password=fixture-known-secret"}' >"$response"
task13_compose() {
if [[ "$*" == "exec -T core node --input-type=module -e "* ]]; then
printf '%s\n' \
'{"name":"WorkspaceRegistryError","code":"workspace_invalid","message":"Workspace snapshot integrity check failed"}'
return 0
fi
[[ "$*" == "logs --no-color --tail 100 core" ]] \
|| task13_fail "server diagnostics requested an unexpected Compose command"
while [[ "$i" -le 150 ]]; do
@@ -1083,6 +1109,8 @@ task13_self_test_server_workspace_diagnostics() {
|| task13_fail "server diagnostics omit the unexpected HTTP status"
[[ "$output" == *'workspace_invalid'* ]] \
|| task13_fail "server diagnostics omit the generic response"
[[ "$output" == *'Workspace snapshot integrity check failed'* ]] \
|| task13_fail "server diagnostics omit the bounded internal registry reason"
[[ "$output" == *'core-log-051'* && "$output" != *'core-log-050'* ]] \
|| task13_fail "server diagnostics do not bound core logs to the last 100 lines"
count="$(grep -Ec '^core-log-[0-9]{3}' <<<"$output")"