feat(workspaces): verify seeds and add managed repair

Replace binary readiness markers with an evidence-bearing seed manifest and enforce a shared validation contract for minimal and full clones. Add an exclusive audited database repair path with recoverable backup, bounded phase diagnostics, and healthy restart validation.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-18 23:20:22 +00:00
1 parent 4b968d8c05
commit 53a0aa3020
21 files changed
+1568 -154

No files matched your search

+44 -3
View File
@@ -7,6 +7,7 @@ paperclip_home="${PAPERCLIP_HOME:-$HOME/.paperclip}"
paperclip_instance_id="${PAPERCLIP_INSTANCE_ID:-default}"
paperclip_dir="$worktree_cwd/.paperclip"
worktree_config_path="$paperclip_dir/config.json"
seed_manifest_path="$paperclip_dir/seed-manifest.json"
seed_pending_marker_path="$paperclip_dir/seed-pending"
seed_complete_marker_path="$paperclip_dir/seed-complete"
@@ -20,8 +21,37 @@ if [[ ! -d "$worktree_cwd" ]]; then
exit 1
fi
if [[ -e "$seed_complete_marker_path" || ! -e "$seed_pending_marker_path" ]]; then
echo "Worktree database is already seeded; skipping runtime provisioning." >&2
if [[ -e "$seed_manifest_path" ]]; then
seed_manifest_state="$(SEED_MANIFEST_PATH="$seed_manifest_path" node <<'EOF'
const fs = require("node:fs");
try {
const value = JSON.parse(fs.readFileSync(process.env.SEED_MANIFEST_PATH, "utf8"));
const complete = value?.version === 2
&& value?.state === "verified"
&& value?.phase === "complete"
&& typeof value?.source?.instanceId === "string" && value.source.instanceId.length > 0
&& typeof value?.source?.configPath === "string" && value.source.configPath.length > 0
&& (value?.seedMode === "minimal" || value?.seedMode === "full")
&& typeof value?.snapshotAt === "string" && value.snapshotAt.length > 0
&& typeof value?.migrationRevision === "string" && value.migrationRevision.length > 0
&& typeof value?.targetInstanceId === "string" && value.targetInstanceId.length > 0
&& typeof value?.attemptId === "string" && value.attemptId.length > 0
&& typeof value?.startedAt === "string"
&& typeof value?.finishedAt === "string"
&& Array.isArray(value?.diagnostics)
&& value.diagnostics.some((entry) => entry?.phase === "complete" && entry?.status === "succeeded" && typeof entry?.at === "string");
process.stdout.write(complete ? "verified" : "incomplete");
} catch {
process.stdout.write("invalid");
}
EOF
)"
if [[ "$seed_manifest_state" == "verified" ]]; then
echo "Worktree database has a verified seed manifest; skipping runtime provisioning." >&2
exit 0
fi
elif [[ -e "$seed_complete_marker_path" || ! -e "$seed_pending_marker_path" ]]; then
echo "Worktree database is already seeded by a legacy marker; skipping runtime provisioning." >&2
exit 0
fi
@@ -31,6 +61,17 @@ if [[ ! -f "$worktree_config_path" ]]; then
fi
source_config_path="${PAPERCLIP_CONFIG:-}"
if [[ -e "$seed_manifest_path" ]]; then
manifest_source_config_path="$(SEED_MANIFEST_PATH="$seed_manifest_path" node <<'EOF'
const fs = require("node:fs");
const value = JSON.parse(fs.readFileSync(process.env.SEED_MANIFEST_PATH, "utf8"));
process.stdout.write(typeof value?.source?.configPath === "string" ? value.source.configPath : "");
EOF
)"
if [[ -n "$manifest_source_config_path" ]]; then
source_config_path="$manifest_source_config_path"
fi
fi
if [[ -z "$source_config_path" && ( -e "$base_cwd/.paperclip/config.json" || -L "$base_cwd/.paperclip/config.json" ) ]]; then
source_config_path="$base_cwd/.paperclip/config.json"
fi
@@ -41,7 +82,7 @@ source_config_args=(--from-config "$source_config_path")
if [[ "$source_config_path" == "$worktree_config_path" ]]; then
# A human may invoke this after sourcing `worktree env`, which points
# PAPERCLIP_CONFIG at the target. In that case the CLI reads the original
# source config from the seed-pending marker instead.
# source config from the seed manifest or legacy pending marker instead.
source_config_args=()
fi