gabriel / musehub public
seed_local_proposals.py python
282 lines 12.0 KB
Raw
sha256:94ef169c149a452bff7c604ded8b280b19bd477c2dabcb56972780b0b784c7aa Merge 'fix/assignee-sigil-inline' into 'dev' — proposal: As… Human 1 day ago
1 """Seed local proposals with varied risk, type, state, merge conditions, and commits.
2
3 Run once to populate the GUI with representative test fixtures:
4
5 python3 scripts/seed_local_proposals.py
6
7 Connects directly to the local Postgres instance on port 5434.
8 """
9 from __future__ import annotations
10
11 import hashlib
12 import json
13 import sys
14 from datetime import datetime, timedelta, timezone
15
16 import psycopg2
17
18 DSN = "host=localhost port=5434 dbname=musehub user=musehub password=musehub"
19
20 REPO_ID = "sha256:05e85038d2e0a48f33d9f0f9ad8802c8857e36d6a4bc72c7c97fec83207b8102"
21
22 # ── Proposals ────────────────────────────────────────────────────────────────
23 # merge_conditions spread across all 9 condition types; mix of pass/fail/unknown
24 PROPOSALS = [
25 {
26 "proposal_id": "sha256:89cde8275f5e4b6248765f3e9c2a51cc0edefa6f54bed34771ac9f5c5de8a798",
27 "number": 1,
28 "title": "Cryptographic identity layer: Ed25519 key rotation, MSign auth v2, and SLIP-0010 HD derivation",
29 "proposal_type": "identity_transition",
30 "merge_strategy": "overlay",
31 "state": "open",
32 "from_branch": "feat/identity-v2",
33 "risk_score": 0.82,
34 "dimensional_risk": {"code": 0.82},
35 "breakage_count": 3,
36 "test_gap_count": 7,
37 # Exercises: require_approvals (fail), max_risk_score (fail),
38 # require_no_breakage (fail), require_test_coverage (fail),
39 # require_signed_commits (fail — one unsigned commit),
40 # require_domains_approved (fail — missing identity domain),
41 # require_payment_settled (unknown)
42 "merge_conditions": {
43 "require_approvals": 2,
44 "max_risk_score": 0.7,
45 "require_signed_commits": True,
46 "require_no_breakage": True,
47 "require_test_coverage": True,
48 "require_domains_approved": ["code", "identity"],
49 "require_payment_settled": True,
50 },
51 },
52 {
53 "proposal_id": "sha256:d3642e390bfe558a23c04eda37a1d08964c63f49965e58b8b76e93c0301f23b2",
54 "number": 2,
55 "title": "Proposal type badges, 7-state lifecycle tabs, row redesign, and ghost object integrity fix",
56 "proposal_type": "state_merge",
57 "merge_strategy": "overlay",
58 "state": "in_review",
59 "from_branch": "task/proposal-models-v2",
60 "risk_score": 0.45,
61 "dimensional_risk": {"code": 0.45},
62 "breakage_count": 0,
63 "test_gap_count": 4,
64 # Exercises: require_approvals (pass — 1 of 1),
65 # max_risk_score (pass — 0.45 ≤ 0.5),
66 # require_test_coverage (fail — gap=4),
67 # require_dependency_merged (fail — blocked by #1),
68 # max_agent_commit_ratio (pass — 1 of 4 = 25% ≤ 50%)
69 "merge_conditions": {
70 "require_approvals": 1,
71 "max_risk_score": 0.5,
72 "require_test_coverage": True,
73 "require_dependency_merged": True,
74 "max_agent_commit_ratio": 0.5,
75 },
76 },
77 {
78 "proposal_id": "sha256:71957a1fa4631f58e7b9259272bc80da8ec26e3b364e372bd5f1b2f1dfe8fe26",
79 "number": 3,
80 "title": "Increase push stream timeout to prevent drops on large repos with deep commit history",
81 "proposal_type": "canonical_release",
82 "merge_strategy": "replay",
83 "state": "approved",
84 "from_branch": "fix/push-timeout",
85 "risk_score": 0.18,
86 "dimensional_risk": {"code": 0.18},
87 "breakage_count": 0,
88 "test_gap_count": 0,
89 # Exercises: require_approvals (pass — 2 of 2),
90 # max_risk_score (pass — 0.18 ≤ 0.3),
91 # require_no_breakage (pass),
92 # require_test_coverage (pass),
93 # require_dependency_merged (fail — still blocked by #2),
94 # max_agent_commit_ratio (pass — 3 of 5 = 60% ≤ 0.8)
95 "merge_conditions": {
96 "require_approvals": 2,
97 "max_risk_score": 0.3,
98 "require_no_breakage": True,
99 "require_test_coverage": True,
100 "require_dependency_merged": True,
101 "max_agent_commit_ratio": 0.8,
102 },
103 },
104 ]
105
106 # Dependency edges: #2 blocked by #1, #3 blocked by #2
107 DEPS = [
108 (PROPOSALS[1]["proposal_id"], PROPOSALS[0]["proposal_id"]),
109 (PROPOSALS[2]["proposal_id"], PROPOSALS[1]["proposal_id"]),
110 ]
111
112 # ── Commits to seed per proposal branch ──────────────────────────────────────
113 # Gives the gate real data for require_signed_commits and max_agent_commit_ratio.
114 def _commit_id(branch: str, n: int) -> str:
115 raw = f"{branch}:{n}".encode()
116 return "sha256:" + hashlib.sha256(raw).hexdigest()
117
118 def _snap_id(branch: str, n: int) -> str:
119 raw = f"snap:{branch}:{n}".encode()
120 return "sha256:" + hashlib.sha256(raw).hexdigest()
121
122 COMMITS: list[dict] = []
123 now = datetime.now(tz=timezone.utc)
124
125 # Proposal #1 — feat/identity-v2: 3 signed human + 1 unsigned human + 1 agent
126 for i, (signed, agent_id, model_id, msg) in enumerate([
127 (True, "", "", "feat: add Ed25519 keygen"),
128 (True, "", "", "feat: SLIP-0010 HD derivation"),
129 (False, "", "", "wip: key rotation draft"), # unsigned → gate fails
130 (True, "claude-code", "claude-sonnet-4-6", "feat: MSign auth v2"),
131 (True, "", "", "test: identity key round-trip"),
132 ]):
133 COMMITS.append({
134 "commit_id": _commit_id("feat/identity-v2", i),
135 "repo_id": REPO_ID,
136 "branch": "feat/identity-v2",
137 "message": msg,
138 "author": "gabriel",
139 "timestamp": now - timedelta(hours=10 - i),
140 "snapshot_id": _snap_id("feat/identity-v2", i),
141 "agent_id": agent_id,
142 "model_id": model_id,
143 "signature": "ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" if signed else "",
144 })
145
146 # Proposal #2 — task/proposal-models-v2: 3 human signed + 1 agent signed
147 for i, (agent_id, model_id, msg) in enumerate([
148 ("", "", "feat: proposal type badges"),
149 ("", "", "feat: 7-state lifecycle tabs"),
150 ("", "", "fix: ghost object integrity"),
151 ("claude-code", "claude-sonnet-4-6", "feat: row redesign"),
152 ]):
153 COMMITS.append({
154 "commit_id": _commit_id("task/proposal-models-v2", i),
155 "repo_id": REPO_ID,
156 "branch": "task/proposal-models-v2",
157 "message": msg,
158 "author": "gabriel" if not agent_id else "claude-code",
159 "timestamp": now - timedelta(hours=6 - i),
160 "snapshot_id": _snap_id("task/proposal-models-v2", i),
161 "agent_id": agent_id,
162 "model_id": model_id,
163 "signature": "ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
164 })
165
166 # Proposal #3 — fix/push-timeout: 2 human signed + 3 agent signed (60% agent ≤ 80%)
167 for i, (agent_id, model_id, msg) in enumerate([
168 ("", "", "fix: increase push stream timeout"),
169 ("", "", "test: large-repo push timeout regression"),
170 ("claude-code", "claude-sonnet-4-6", "feat: configurable timeout per remote"),
171 ("claude-code", "claude-sonnet-4-6", "refactor: extract timeout config"),
172 ("claude-code", "claude-sonnet-4-6", "docs: push timeout configuration"),
173 ]):
174 COMMITS.append({
175 "commit_id": _commit_id("fix/push-timeout", i),
176 "repo_id": REPO_ID,
177 "branch": "fix/push-timeout",
178 "message": msg,
179 "author": "gabriel" if not agent_id else "claude-code",
180 "timestamp": now - timedelta(hours=3 - i * 0.5),
181 "snapshot_id": _snap_id("fix/push-timeout", i),
182 "agent_id": agent_id,
183 "model_id": model_id,
184 "signature": "ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
185 })
186
187
188 def _dep_id(dependent: str, dependency: str) -> str:
189 raw = (dependent + dependency).encode()
190 return "sha256:" + hashlib.sha256(raw).hexdigest()
191
192
193 def main() -> None:
194 conn = psycopg2.connect(DSN)
195 conn.autocommit = False
196 cur = conn.cursor()
197
198 # ── Update proposals ─────────────────────────────────────────────────────
199 for p in PROPOSALS:
200 cur.execute(
201 """
202 UPDATE musehub_proposals
203 SET title = %s,
204 proposal_type = %s,
205 merge_strategy = %s,
206 state = %s,
207 from_branch = %s,
208 risk_score = %s,
209 dimensional_risk = %s,
210 breakage_count = %s,
211 test_gap_count = %s,
212 merge_conditions = %s,
213 updated_at = %s
214 WHERE proposal_id = %s
215 """,
216 (
217 p["title"],
218 p["proposal_type"],
219 p["merge_strategy"],
220 p["state"],
221 p["from_branch"],
222 p["risk_score"],
223 json.dumps(p["dimensional_risk"]),
224 p["breakage_count"],
225 p["test_gap_count"],
226 json.dumps(p["merge_conditions"]),
227 datetime.now(tz=timezone.utc),
228 p["proposal_id"],
229 ),
230 )
231 rows = cur.rowcount
232 conditions_list = ", ".join(p["merge_conditions"].keys())
233 print(f" proposal #{p['number']}: {rows} row(s) → {p['state']} / risk={p['risk_score']} / conditions=[{conditions_list}]")
234
235 # ── Upsert commits ───────────────────────────────────────────────────────
236 for c in COMMITS:
237 cur.execute(
238 """
239 INSERT INTO musehub_commits
240 (commit_id, repo_id, branch, parent_ids, message, author, timestamp,
241 snapshot_id, agent_id, model_id, signature, created_at)
242 VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
243 ON CONFLICT (commit_id) DO UPDATE
244 SET agent_id = EXCLUDED.agent_id,
245 model_id = EXCLUDED.model_id,
246 signature = EXCLUDED.signature,
247 message = EXCLUDED.message
248 """,
249 (
250 c["commit_id"], c["repo_id"], c["branch"], [],
251 c["message"], c["author"], c["timestamp"],
252 c["snapshot_id"], c["agent_id"], c["model_id"],
253 c["signature"], now,
254 ),
255 )
256 print(f" commits: {len(COMMITS)} upserted")
257
258 # ── Insert dependency edges ───────────────────────────────────────────────
259 seed_now = datetime.now(tz=timezone.utc)
260 for dependent_id, dependency_id in DEPS:
261 dep_id = _dep_id(dependent_id, dependency_id)
262 cur.execute(
263 """
264 INSERT INTO musehub_proposal_dependencies
265 (dep_id, dependent_proposal_id, dependency_proposal_id, created_at)
266 VALUES (%s, %s, %s, %s)
267 ON CONFLICT (dependent_proposal_id, dependency_proposal_id) DO NOTHING
268 """,
269 (dep_id, dependent_id, dependency_id, seed_now),
270 )
271 dep_num = next(p["number"] for p in PROPOSALS if p["proposal_id"] == dependent_id)
272 dep_on_num = next(p["number"] for p in PROPOSALS if p["proposal_id"] == dependency_id)
273 print(f" dependency: #{dep_num} blocked by #{dep_on_num}")
274
275 conn.commit()
276 cur.close()
277 conn.close()
278 print("\nDone. Refresh https://localhost:1337/gabriel/musehub/proposals")
279
280
281 if __name__ == "__main__":
282 main()
File History 3 commits
sha256:94ef169c149a452bff7c604ded8b280b19bd477c2dabcb56972780b0b784c7aa Merge 'fix/assignee-sigil-inline' into 'dev' — proposal: As… Human 1 day ago
sha256:6b1949fc2797ca4c1936a637a4cbfec828ef56cf52398a2e74ca3c4f494e728f fix: use wire_bytes not mpack_bytes_raw in compute_object_b… Sonnet 4.6 patch 10 days ago
sha256:4aed3d8601c8dd3ed37074de35f11f4a9699a0a4b99d43727048fd3f8e6fd13d chore: doc sweep, ignore wrangler build state, misc fixes Sonnet 4.6 minor 12 days ago