"""Seed local proposals with varied risk, type, state, merge conditions, and commits. Run once to populate the GUI with representative test fixtures: python3 scripts/seed_local_proposals.py Connects directly to the local Postgres instance on port 5434. """ from __future__ import annotations import hashlib import json import sys from datetime import datetime, timedelta, timezone import psycopg2 DSN = "host=localhost port=5434 dbname=musehub user=musehub password=musehub" REPO_ID = "sha256:05e85038d2e0a48f33d9f0f9ad8802c8857e36d6a4bc72c7c97fec83207b8102" # ── Proposals ──────────────────────────────────────────────────────────────── # merge_conditions spread across all 9 condition types; mix of pass/fail/unknown PROPOSALS = [ { "proposal_id": "sha256:89cde8275f5e4b6248765f3e9c2a51cc0edefa6f54bed34771ac9f5c5de8a798", "number": 1, "title": "Cryptographic identity layer: Ed25519 key rotation, MSign auth v2, and SLIP-0010 HD derivation", "proposal_type": "identity_transition", "merge_strategy": "overlay", "state": "open", "from_branch": "feat/identity-v2", "risk_score": 0.82, "dimensional_risk": {"code": 0.82}, "breakage_count": 3, "test_gap_count": 7, # Exercises: require_approvals (fail), max_risk_score (fail), # require_no_breakage (fail), require_test_coverage (fail), # require_signed_commits (fail — one unsigned commit), # require_domains_approved (fail — missing identity domain), # require_payment_settled (unknown) "merge_conditions": { "require_approvals": 2, "max_risk_score": 0.7, "require_signed_commits": True, "require_no_breakage": True, "require_test_coverage": True, "require_domains_approved": ["code", "identity"], "require_payment_settled": True, }, }, { "proposal_id": "sha256:d3642e390bfe558a23c04eda37a1d08964c63f49965e58b8b76e93c0301f23b2", "number": 2, "title": "Proposal type badges, 7-state lifecycle tabs, row redesign, and ghost object integrity fix", "proposal_type": "state_merge", "merge_strategy": "overlay", "state": "in_review", "from_branch": "task/proposal-models-v2", "risk_score": 0.45, "dimensional_risk": {"code": 0.45}, "breakage_count": 0, "test_gap_count": 4, # Exercises: require_approvals (pass — 1 of 1), # max_risk_score (pass — 0.45 ≤ 0.5), # require_test_coverage (fail — gap=4), # require_dependency_merged (fail — blocked by #1), # max_agent_commit_ratio (pass — 1 of 4 = 25% ≤ 50%) "merge_conditions": { "require_approvals": 1, "max_risk_score": 0.5, "require_test_coverage": True, "require_dependency_merged": True, "max_agent_commit_ratio": 0.5, }, }, { "proposal_id": "sha256:71957a1fa4631f58e7b9259272bc80da8ec26e3b364e372bd5f1b2f1dfe8fe26", "number": 3, "title": "Increase push stream timeout to prevent drops on large repos with deep commit history", "proposal_type": "canonical_release", "merge_strategy": "replay", "state": "approved", "from_branch": "fix/push-timeout", "risk_score": 0.18, "dimensional_risk": {"code": 0.18}, "breakage_count": 0, "test_gap_count": 0, # Exercises: require_approvals (pass — 2 of 2), # max_risk_score (pass — 0.18 ≤ 0.3), # require_no_breakage (pass), # require_test_coverage (pass), # require_dependency_merged (fail — still blocked by #2), # max_agent_commit_ratio (pass — 3 of 5 = 60% ≤ 0.8) "merge_conditions": { "require_approvals": 2, "max_risk_score": 0.3, "require_no_breakage": True, "require_test_coverage": True, "require_dependency_merged": True, "max_agent_commit_ratio": 0.8, }, }, ] # Dependency edges: #2 blocked by #1, #3 blocked by #2 DEPS = [ (PROPOSALS[1]["proposal_id"], PROPOSALS[0]["proposal_id"]), (PROPOSALS[2]["proposal_id"], PROPOSALS[1]["proposal_id"]), ] # ── Commits to seed per proposal branch ────────────────────────────────────── # Gives the gate real data for require_signed_commits and max_agent_commit_ratio. def _commit_id(branch: str, n: int) -> str: raw = f"{branch}:{n}".encode() return "sha256:" + hashlib.sha256(raw).hexdigest() def _snap_id(branch: str, n: int) -> str: raw = f"snap:{branch}:{n}".encode() return "sha256:" + hashlib.sha256(raw).hexdigest() COMMITS: list[dict] = [] now = datetime.now(tz=timezone.utc) # Proposal #1 — feat/identity-v2: 3 signed human + 1 unsigned human + 1 agent for i, (signed, agent_id, model_id, msg) in enumerate([ (True, "", "", "feat: add Ed25519 keygen"), (True, "", "", "feat: SLIP-0010 HD derivation"), (False, "", "", "wip: key rotation draft"), # unsigned → gate fails (True, "claude-code", "claude-sonnet-4-6", "feat: MSign auth v2"), (True, "", "", "test: identity key round-trip"), ]): COMMITS.append({ "commit_id": _commit_id("feat/identity-v2", i), "repo_id": REPO_ID, "branch": "feat/identity-v2", "message": msg, "author": "gabriel", "timestamp": now - timedelta(hours=10 - i), "snapshot_id": _snap_id("feat/identity-v2", i), "agent_id": agent_id, "model_id": model_id, "signature": "ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" if signed else "", }) # Proposal #2 — task/proposal-models-v2: 3 human signed + 1 agent signed for i, (agent_id, model_id, msg) in enumerate([ ("", "", "feat: proposal type badges"), ("", "", "feat: 7-state lifecycle tabs"), ("", "", "fix: ghost object integrity"), ("claude-code", "claude-sonnet-4-6", "feat: row redesign"), ]): COMMITS.append({ "commit_id": _commit_id("task/proposal-models-v2", i), "repo_id": REPO_ID, "branch": "task/proposal-models-v2", "message": msg, "author": "gabriel" if not agent_id else "claude-code", "timestamp": now - timedelta(hours=6 - i), "snapshot_id": _snap_id("task/proposal-models-v2", i), "agent_id": agent_id, "model_id": model_id, "signature": "ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", }) # Proposal #3 — fix/push-timeout: 2 human signed + 3 agent signed (60% agent ≤ 80%) for i, (agent_id, model_id, msg) in enumerate([ ("", "", "fix: increase push stream timeout"), ("", "", "test: large-repo push timeout regression"), ("claude-code", "claude-sonnet-4-6", "feat: configurable timeout per remote"), ("claude-code", "claude-sonnet-4-6", "refactor: extract timeout config"), ("claude-code", "claude-sonnet-4-6", "docs: push timeout configuration"), ]): COMMITS.append({ "commit_id": _commit_id("fix/push-timeout", i), "repo_id": REPO_ID, "branch": "fix/push-timeout", "message": msg, "author": "gabriel" if not agent_id else "claude-code", "timestamp": now - timedelta(hours=3 - i * 0.5), "snapshot_id": _snap_id("fix/push-timeout", i), "agent_id": agent_id, "model_id": model_id, "signature": "ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", }) def _dep_id(dependent: str, dependency: str) -> str: raw = (dependent + dependency).encode() return "sha256:" + hashlib.sha256(raw).hexdigest() def main() -> None: conn = psycopg2.connect(DSN) conn.autocommit = False cur = conn.cursor() # ── Update proposals ───────────────────────────────────────────────────── for p in PROPOSALS: cur.execute( """ UPDATE musehub_proposals SET title = %s, proposal_type = %s, merge_strategy = %s, state = %s, from_branch = %s, risk_score = %s, dimensional_risk = %s, breakage_count = %s, test_gap_count = %s, merge_conditions = %s, updated_at = %s WHERE proposal_id = %s """, ( p["title"], p["proposal_type"], p["merge_strategy"], p["state"], p["from_branch"], p["risk_score"], json.dumps(p["dimensional_risk"]), p["breakage_count"], p["test_gap_count"], json.dumps(p["merge_conditions"]), datetime.now(tz=timezone.utc), p["proposal_id"], ), ) rows = cur.rowcount conditions_list = ", ".join(p["merge_conditions"].keys()) print(f" proposal #{p['number']}: {rows} row(s) → {p['state']} / risk={p['risk_score']} / conditions=[{conditions_list}]") # ── Upsert commits ─────────────────────────────────────────────────────── for c in COMMITS: cur.execute( """ INSERT INTO musehub_commits (commit_id, repo_id, branch, parent_ids, message, author, timestamp, snapshot_id, agent_id, model_id, signature, created_at) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ON CONFLICT (commit_id) DO UPDATE SET agent_id = EXCLUDED.agent_id, model_id = EXCLUDED.model_id, signature = EXCLUDED.signature, message = EXCLUDED.message """, ( c["commit_id"], c["repo_id"], c["branch"], [], c["message"], c["author"], c["timestamp"], c["snapshot_id"], c["agent_id"], c["model_id"], c["signature"], now, ), ) print(f" commits: {len(COMMITS)} upserted") # ── Insert dependency edges ─────────────────────────────────────────────── seed_now = datetime.now(tz=timezone.utc) for dependent_id, dependency_id in DEPS: dep_id = _dep_id(dependent_id, dependency_id) cur.execute( """ INSERT INTO musehub_proposal_dependencies (dep_id, dependent_proposal_id, dependency_proposal_id, created_at) VALUES (%s, %s, %s, %s) ON CONFLICT (dependent_proposal_id, dependency_proposal_id) DO NOTHING """, (dep_id, dependent_id, dependency_id, seed_now), ) dep_num = next(p["number"] for p in PROPOSALS if p["proposal_id"] == dependent_id) dep_on_num = next(p["number"] for p in PROPOSALS if p["proposal_id"] == dependency_id) print(f" dependency: #{dep_num} blocked by #{dep_on_num}") conn.commit() cur.close() conn.close() print("\nDone. Refresh https://localhost:1337/gabriel/musehub/proposals") if __name__ == "__main__": main()