Proposal Reimagination: State Transition Manifests for a Multidimensional Universe
Proposal Reimagination: State Transition Manifests for a Multidimensional Universe
The Paradigm Break
A pull request asks someone to pull a change to a text file. That framing belongs to the era when version control was a word processor with history.
A Muse proposal is something categorically different: it is a cryptographically-attested declaration that the world should transition from one multidimensional state to another.
The current implementation is a PR clone: from_branch + to_branch, three states, four review verdicts. That's Git vocabulary bolted onto a domain-agnostic state machine. This ticket is the complete reimagination.
What Muse Actually Tracks
Muse commits are snapshots of a state vector — not a file tree. Each commit captures the full multidimensional state across all active domains simultaneously:
┌─────────────────────────────────────────────────────────────────────────────┐
│ MUSE COMMIT = STATE VECTOR │
├─────────────┬──────────────┬─────────────┬──────────────┬───────────────────┤
│ CODE │ MIDI │ STEMS │ PROSE │ PAYMENTS │
│ ───────── │ ───────── │ ───────── │ ───────── │ ───────────── │
│ AST diffs │ Note events │ Waveform │ Semantic │ Ledger deltas │
│ Symbol │ Tracks │ Fingerprint │ Readability │ Claim chains │
│ blast │ Tempo map │ Frequency │ Structure │ Settlement state │
│ Breakage │ Harmony │ Amplitude │ Citations │ AVAX settlement │
│ Test gaps │ Rhythm │ Stems tree │ Sections │ nonce linkage │
└─────────────┴──────────────┴─────────────┴──────────────┴───────────────────┘
A proposal declares: "apply this state transition to the shared timeline". The reviewers are not just reading diffs — they are deciding whether the proposed future state is valid, safe, and desirable across every dimension the repo tracks.
What a Proposal IS (Redesigned)
╔══════════════════════════════════════════════════════════════════════════════╗
║ PROPOSAL — A SIGNED STATE TRANSITION DECLARATION ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ ║
║ PROPOSER: @gabriel (Ed25519 sig over canonical PROPOSE message) ║
║ TYPE: state_merge | stem_integration | midi_evolution | agent_delegation ║
║ payment_settlement | identity_transition | canonical_release ║
║ ║
║ FROM STATE: sha256(from_branch HEAD snapshot) ← cryptographic anchor ║
║ TO STATE: sha256(to_branch HEAD snapshot) ← cryptographic anchor ║
║ ║
║ DIMENSIONAL MANIFEST DELTA: ║
║ ┌──────────────────────────────────────────────────────────────────────┐ ║
║ │ CODE ▓▓▓▓▓▓▓░░░░ risk: HIGH breakage: 2 test_gap: 3 │ ║
║ │ MIDI ▓▓▓▓░░░░░░░ risk: MED tracks: +1 harmony_Δ: 0.23 │ ║
║ │ STEMS ▓░░░░░░░░░░ risk: LOW added: 2 waveform_Δ: 0.05 │ ║
║ │ PROSE ░░░░░░░░░░░ risk: NONE no changes │ ║
║ │ PAYMENTS▓▓░░░░░░░░░ risk: LOW Δ: +150K nanoMUSE │ ║
║ └──────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ OVERALL RISK VECTOR: [0.7, 0.4, 0.1, 0.0, 0.2] ║
║ AGGREGATE RISK SCORE: 0.48 (MEDIUM) ║
║ ║
║ MERGE CONDITIONS: ║
║ [✓] 2 approvals required [✓] code domain reviewed ║
║ [✗] midi domain not yet reviewed ║
║ [✓] all commits signed [✓] risk_score ≤ 60 ║
║ ║
║ DEPENDENCIES: none | BLOCKS: proposal #7 ║
╚══════════════════════════════════════════════════════════════════════════════╝
The Seven Proposal Types
Not all state transitions are the same. Type determines which domains are relevant, which reviewers are appropriate, and which merge strategies are valid.
╔══════════════════════════════════════════════════════════════════════════════╗
║ PROPOSAL TYPES ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ ║
║ 1. STATE_MERGE — general cross-domain branch integration ║
║ Domains: any | Merge: overlay | weave | rebase | domain_selective ║
║ ║
║ 2. STEM_INTEGRATION — integrate new/updated audio stems ║
║ Domains: stems (primary), payments (optional: licensing fees) ║
║ Reviewer type: human musician or certified audio_review agent ║
║ ║
║ 3. MIDI_EVOLUTION — propose a musical arrangement change ║
║ Domains: midi (primary), stems (secondary if bounce attached) ║
║ Diff shows: per-track note delta, harmonic tension, complexity ║
║ ║
║ 4. PAYMENT_SETTLEMENT — settle a chain of MPay claims to AVAX L1 ║
║ Domains: payments (primary) ║
║ Auto-merge when: all claims valid + on-chain confirmation received ║
║ ║
║ 5. AGENT_DELEGATION — propose spawning/updating an agent identity ║
║ Domains: identity | Requires: human + quorum if org ║
║ Scope changes are diff-visible: before/after capability sets ║
║ ║
║ 6. IDENTITY_TRANSITION — key rotation / HD migration / org restructure ║
║ Domains: identity | Highest security review requirement ║
║ ║
║ 7. CANONICAL_RELEASE — tag a state as a versioned, immutable release ║
║ Domains: all | Locks all dimensions at the tagged commit ║
║ On merge: creates signed release manifest + fires release webhooks ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════╝
The New State Machine
Current: open → merged | closed (three states, flat)
Redesigned: a typed, guarded lifecycle where each transition has preconditions.
┌─────────────────────────────────────────────┐
│ PROPOSAL LIFECYCLE │
└─────────────────────────────────────────────┘
create(draft=True)
│
▼
┌─────────┐ publish() ┌──────┐
│ DRAFTING │───────────▶│ OPEN │
└─────────┘ └──────┘
│ ╔════════════════════════════════════╗
│ ║ Open → In-Review ║
│ ║ Trigger: first reviewer assigned ║
│ ╚════════════════════════════════════╝
│
▼
┌───────────┐
│ IN_REVIEW │◀────────────────────┐
└───────────┘ │
│ │ request_changes()
│ approve() [N approvals] │
▼ │
┌──────────┐ needs_changes() │
│ APPROVED │──────────────────────┘
└──────────┘
│
│ merge_conditions_met()
▼
┌──────────┐ on-chain confirm ┌─────────┐
│ SETTLING │────────────────────▶│ MERGED │
└──────────┘ └─────────┘
│
┌────┴────┐ reject() ┌──────────┐
│ │────────────▶│ REJECTED │
│ (any) │ └──────────┘
│ │ abandon() ┌───────────┐
└─────────┘────────────▶│ ABANDONED │
└───────────┘
State meanings:
- DRAFTING: author still composing; not visible to others except invited reviewers
- OPEN: published; accepting reviews
- IN_REVIEW: at least one reviewer assigned; under active evaluation
- APPROVED: all merge_conditions met; eligible to merge
- SETTLING: merge initiated; for payment_settlement type, awaiting on-chain confirmation
- MERGED: state transition applied; from_branch retired
- REJECTED: explicitly closed by reviewer quorum or author after changes_requested
- ABANDONED: closed by author; no activity; archivable
Dimensional Diff as the Core UX
The diff is not a unified text patch. It is a per-domain divergence manifest:
Code Domain
┌─────────────────────────────────────────────────────────────────────────────┐
│ CODE DOMAIN DIFF │
├─────────────────────────────────────────────────────────────────────────────┤
│ SYMBOLS CHANGED (7) BLAST RADIUS │
│ ┌─────────────────────────────────────┐ ┌───────────────────────────┐ │
│ │ + auth.py::AuthService.login │ │ auth.py::AuthService (★) │ │
│ │ ~ auth.py::AuthService.verify │ │ → api/routes/auth.py (2) │ │
│ │ - auth.py::LegacyAuth │ │ → services/user.py (1) │ │
│ │ + core/msign.py::verify_msign_hdr │ │ → tests/test_auth.py (4) │ │
│ │ ~ core/msign.py::canonical_msg │ └───────────────────────────┘ │
│ │ + tests/test_auth.py::TestMSign │ │
│ │ ~ pyproject.toml │ BREAKAGE COUNT: 2 │
│ └─────────────────────────────────────┘ TEST GAP COUNT: 1 │
│ STRUCTURAL RISK: HIGH │
└─────────────────────────────────────────────────────────────────────────────┘
MIDI Domain
┌─────────────────────────────────────────────────────────────────────────────┐
│ MIDI DOMAIN DIFF │
├─────────────────────────────────────────────────────────────────────────────┤
│ TRACKS CHANGED (2 of 8) HARMONIC ANALYSIS │
│ ┌─────────────────────────────────────┐ ┌───────────────────────────┐ │
│ │ ~ Piano [bars 1–32] │ │ Key: C minor → C major │ │
│ │ notes: 128→156 (+28) │ │ Tension delta: +0.23 │ │
│ │ velocity avg: 82→91 (+9) │ │ Consonance: ↑ (brighter) │ │
│ │ density: 3.2→3.9 notes/beat │ └───────────────────────────┘ │
│ │ │ │
│ │ + Strings [bars 17–32] (new track) │ RHYTHMIC ANALYSIS │
│ │ notes: 64 │ ┌───────────────────────────┐ │
│ │ velocity avg: 68 │ │ Tempo: 120 BPM (no Δ) │ │
│ │ range: C3–G5 │ │ Groove delta: +0.08 │ │
│ └─────────────────────────────────────┘ │ Complexity: ↑ LOW │ │
│ └───────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Payments Domain
┌─────────────────────────────────────────────────────────────────────────────┐
│ PAYMENTS DOMAIN DIFF │
├─────────────────────────────────────────────────────────────────────────────┤
│ LEDGER DELTA: +150,000 nanoMUSE │
│ CLAIMS INTRODUCED: 3 │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ claim[0] nonce: 0xabc… from: gabriel to: aaronrene 50,000 nanoMUSE │ │
│ │ claim[1] nonce: sha256(claim[0].sig) from: gabriel to: studio9 … │ │
│ │ claim[2] nonce: sha256(claim[1].sig) from: gabriel to: mixeng7 … │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ AVAX SETTLEMENT: pending | payer_avax: 0x1a2b… recipient: 0xcd3e… │
│ CHAIN VERIFIED: all nonce links valid | Ed25519 sigs: ✓ all pass │
└─────────────────────────────────────────────────────────────────────────────┘
Dimensional Comments
Comments are not line numbers. They are coordinates in state space:
class ProposalCommentTarget(TypedDict, total=False):
"""Domain-agnostic comment coordinate system.
Exactly one of the domain-specific groups should be populated.
'general' targets the proposal as a whole (no domain coordinate).
"""
# Universal
target_type: Literal["general", "code", "midi", "stem", "payment", "identity"]
# Code domain
symbol_address: str # "auth.py::AuthService.login"
line_start: int # within the symbol's source range
line_end: int
# MIDI domain
track_name: str # "Piano", "Strings"
beat_start: float # 0.0-based within the track
beat_end: float
note_pitch: int # 0–127 MIDI pitch (None = whole region)
# Stem domain
stem_id: str # stem object_id
timestamp_start: float # seconds from stem start
timestamp_end: float
# Payment domain
nonce_hex: str # specific claim in the payment chain
# Identity domain
identity_handle: str # which identity this comment targets
Proposal Chaining (Dependency DAG)
┌─────────────────────────────────────────────┐
│ PROPOSAL DAG │
└─────────────────────────────────────────────┘
#1 [MERGED] #2 [MERGED]
auth refactor midi key change
│ │
└──────────┬───────────────┘
│ depends_on: [#1, #2]
▼
#3 [OPEN] #5 [OPEN]
release v2.0 agent scope expansion
│ │
│ blocks: [#4] │ depends_on: [#3]
▼ │
#4 [DRAFTING] ◀──────────────┘
payment settlement for v2.0
class ProposalDependency(TypedDict):
"""Proposal dependency edge in the state transition DAG."""
proposal_id: str # this proposal
depends_on: list[str] # cannot merge until all of these are merged
blocks: list[str] # merging this proposal unblocks these
soft_depends_on: list[str] # informational only; does not gate merge
Merge Strategies (Expanded)
┌──────────────────────────────────────────────────────────────────────────────┐
│ MERGE STRATEGIES │
├─────────────────────┬────────────────────────────────────────────────────────┤
│ state_overlay │ Current: from_branch wins all conflicts (theirs) │
│ (current default) │ │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ state_weave │ Per-domain strategy selection: │
│ │ code: overlay (from_branch wins) │
│ │ midi: interleave (merge tracks additively) │
│ │ payments: append (both chains preserved) │
│ │ stems: union (all stems kept) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ state_rebase │ Replay from_branch commits on top of to_branch HEAD │
│ │ (one synthetic commit per from_branch commit) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ domain_selective │ Only merge specified domains: │
│ │ --domains code,midi (leave stems/payments alone) │
│ │ Enables: "take the MIDI changes, not the code" │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ phased │ Merge domains in sequence with approval gates: │
│ │ phase 1: code (requires code reviewer approval) │
│ │ phase 2: midi (requires midi reviewer approval) │
│ │ phase 3: payments (requires on-chain confirm) │
└─────────────────────┴────────────────────────────────────────────────────────┘
Merge Conditions — Declarative Gating
class MergeConditions(TypedDict, total=False):
"""Declarative preconditions that must all be satisfied before merge."""
require_approvals: int # minimum distinct approvals
require_domains_approved: list[str] # each domain must have ≥1 approval
max_risk_score: float # 0.0–1.0; proposal blocked if exceeded
require_signed_commits: bool # all commits in from_branch must be MSign-signed
require_no_breakage: bool # breakage_count must be 0
require_test_coverage: bool # no test_gap_count > 0
require_payment_settled: bool # for payment_settlement type: on-chain confirmation
require_dependency_merged: bool # all depends_on proposals must be merged first
max_agent_commit_ratio: float # 0.0–1.0; max fraction of commits from agents
These can be set in .muse/proposal_defaults.toml at the repo level, overridden per proposal.
Proposal as a Cryptographic Claim
A proposal is not just a database row. It is a signed state transition declaration:
PROPOSE\n{proposer_handle}\n{from_state_hash}\n{to_state_hash}\n{proposal_type}\n{ts}
from_state_hash= SHA-256 of the from_branch HEAD snapshot manifestto_state_hash= SHA-256 of the to_branch HEAD snapshot manifestproposal_type= one of the seven types above
This canonical message is signed with the proposer's Ed25519 key and stored as proposer_sig_b64. It means:
- The proposal is tamper-evident — any change to the from/to state hashes invalidates the signature
- The proposal is attribution-certain — we know exactly who declared this state transition, with cryptographic proof
- Review approvals are also signed:
APPROVE_PROPOSAL\n{reviewer}\n{proposal_id}\n{ts}— reviewers attest to the proposed state
Reviewer Archetypes
Not all reviewers are equal. Reviewers have domain expertise:
┌──────────────────────────────────────────────────────────────────────────────┐
│ REVIEWER ARCHETYPES │
├───────────────────────┬──────────────────────────────────────────────────────┤
│ code_reviewer │ human or agent; reviews code domain diff │
│ midi_reviewer │ human musician or certified MIDI analysis agent │
│ audio_reviewer │ human producer or certified stem analysis agent │
│ payment_reviewer │ human + AVAX wallet holder; approves settlements │
│ security_reviewer │ human; required for identity_transition proposals │
│ release_manager │ can approve canonical_release proposals │
└───────────────────────┴──────────────────────────────────────────────────────┘
A proposal's merge_conditions.require_domains_approved gates specifically on domain-appropriate reviewers. A MIDI_EVOLUTION proposal doesn't need a code reviewer to approve.
Agent reviewers sign their reviews just like humans — ATTEST\n{reviewer}\n{proposal_id}\n{verdict}\n{ts} — making every approval cryptographically verifiable.
New Data Model
musehub_proposals — extended columns
-- New columns on top of existing schema:
ALTER TABLE musehub_proposals ADD COLUMN
proposal_type TEXT NOT NULL DEFAULT 'state_merge',
state TEXT NOT NULL DEFAULT 'open', -- extended state machine
proposer_sig_b64 TEXT, -- Ed25519 sig over PROPOSE canonical message
from_state_hash TEXT, -- SHA-256 of from_branch HEAD snapshot
to_state_hash TEXT, -- SHA-256 of to_branch HEAD snapshot
dimensional_risk JSON, -- per-domain risk vector: {"code": 0.7, ...}
depends_on JSON, -- list[proposal_id]
blocks JSON, -- list[proposal_id]
soft_depends_on JSON, -- list[proposal_id]
merge_conditions JSON, -- MergeConditions dict
merge_strategy TEXT NOT NULL DEFAULT 'state_overlay',
selective_domains JSON, -- for domain_selective strategy
is_draft BOOLEAN NOT NULL DEFAULT FALSE,
published_at TIMESTAMPTZ,
settling_at TIMESTAMPTZ,
abandoned_at TIMESTAMPTZ,
rejection_reason TEXT;
musehub_proposal_dependencies — DAG edges
CREATE TABLE musehub_proposal_dependencies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
proposal_id TEXT NOT NULL REFERENCES musehub_proposals(proposal_id) ON DELETE CASCADE,
depends_on_id TEXT NOT NULL REFERENCES musehub_proposals(proposal_id),
dependency_type TEXT NOT NULL DEFAULT 'hard', -- 'hard' | 'soft'
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE (proposal_id, depends_on_id)
);
CREATE INDEX ON musehub_proposal_dependencies (depends_on_id);
musehub_proposal_reviews — extended
-- New columns:
ALTER TABLE musehub_proposal_reviews ADD COLUMN
review_domain TEXT, -- 'code' | 'midi' | 'stems' | etc.
reviewer_sig_b64 TEXT, -- Ed25519 sig over APPROVE_PROPOSAL canonical
reviewer_archetype TEXT; -- 'code_reviewer' | 'midi_reviewer' | etc.
musehub_proposal_simulations — dry-merge results
CREATE TABLE musehub_proposal_simulations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
proposal_id TEXT NOT NULL REFERENCES musehub_proposals(proposal_id) ON DELETE CASCADE,
run_at TIMESTAMPTZ DEFAULT now(),
strategy TEXT NOT NULL,
result_state TEXT NOT NULL, -- 'success' | 'conflict' | 'error'
conflicts JSON, -- per-domain conflict list
merged_state_hash TEXT, -- SHA-256 of what merged state WOULD be
simulation_log TEXT,
agent_id TEXT -- which agent ran the simulation, if any
);
API Extensions
New/extended endpoints
POST /api/repos/{repo_id}/proposals ← draft support, type field
PATCH /api/repos/{repo_id}/proposals/{id} ← update draft, merge_conditions
POST /api/repos/{repo_id}/proposals/{id}/publish ← draft → open
POST /api/repos/{repo_id}/proposals/{id}/simulate ← dry-merge + conflict detection
GET /api/repos/{repo_id}/proposals/{id}/simulate ← fetch latest simulation result
GET /api/repos/{repo_id}/proposals/{id}/diff ← extended: all domain diffs
POST /api/repos/{repo_id}/proposals/{id}/abandon ← open/approved → abandoned
GET /api/repos/{repo_id}/proposals?depends_on=X ← filter by DAG dependency
GET /api/repos/{repo_id}/proposals?type=midi_evolution ← filter by proposal type
Extended ProposalCreate
class ProposalCreate(BaseModel):
title: str = Field(min_length=1, max_length=500)
from_branch: str = Field(min_length=1, max_length=255)
to_branch: str = Field(min_length=1, max_length=255)
body: str = Field(max_length=50_000, default="")
proposal_type: ProposalType = ProposalType.STATE_MERGE
is_draft: bool = False
merge_conditions: MergeConditions | None = None
merge_strategy: MergeStrategy = MergeStrategy.STATE_OVERLAY
selective_domains: list[str] | None = None
depends_on: list[str] = Field(default_factory=list) # proposal IDs
CLI Extensions
# Draft workflow
muse hub proposal create --title "feat: auth v2" --draft
muse hub proposal publish <id>
# Type-aware creation
muse hub proposal create --title "Piano arrangement v3" --type midi_evolution
muse hub proposal create --title "Settle Q1 payments" --type payment_settlement
# Merge conditions
muse hub proposal create --title "Release v2.0" \
--type canonical_release \
--require-approvals 3 \
--require-domains code,midi \
--require-signed-commits \
--require-no-breakage
# Domain-selective merge
muse hub proposal merge <id> --strategy domain_selective --domains code
# Dry-merge simulation (shows conflicts without applying)
muse hub proposal simulate <id>
muse hub proposal simulate <id> --strategy state_weave --json
# Dependency management
muse hub proposal depends-on <id> --add <other-id>
muse hub proposal depends-on <id> --remove <other-id>
muse hub proposal dag <id> # show full dependency graph
# Enhanced diff
muse hub proposal diff <id> # all domains
muse hub proposal diff <id> --domain midi # MIDI domain only
muse hub proposal diff <id> --domain code # code domain blast radius
# Abandon
muse hub proposal abandon <id> [--reason "..."]
# Domain-aware review
muse hub proposal review <id> --verdict approve --domain code
muse hub proposal review <id> --verdict approve --domain midi
Symbol Anchors & Blast Radius
New symbols to create
| Symbol | File | Purpose |
|---|---|---|
ProposalType |
musehub/models/musehub.py |
Enum: 7 proposal types |
ProposalState |
musehub/models/musehub.py |
Extended state machine (7 states) |
MergeStrategy |
musehub/models/musehub.py |
Enum: 5 strategies |
MergeConditions |
musehub/models/musehub.py |
Typed merge preconditions |
ProposalCommentTarget |
musehub/models/musehub.py |
Domain-agnostic coordinate |
DimensionalRiskVector |
musehub/models/musehub.py |
Per-domain risk dict |
simulate_merge |
musehub/services/musehub_proposals.py |
Dry-merge without committing |
check_merge_conditions |
musehub/services/musehub_proposals.py |
Evaluate all conditions |
compute_dimensional_risk |
musehub/services/musehub_proposal_risk.py |
Per-domain risk vector |
verify_proposal_sig |
musehub/services/musehub_proposals.py |
Ed25519 sig over PROPOSE msg |
build_proposal_claim |
muse/core/msign.py |
Sign PROPOSE canonical message |
ProposalDependencyGraph |
musehub/services/proposal_dag.py |
DAG cycle detection + ordering |
Blast radius of changes
musehub/models/musehub.py ← ProposalType, MergeStrategy, MergeConditions
↓ imported by
musehub/api/routes/musehub/proposals.py ← all 10+ endpoints
musehub/services/musehub_proposals.py ← service layer
musehub/services/musehub_proposal_risk.py ← risk engine
musehub/mcp/write_tools/proposals.py ← MCP tools
↓
musehub/db/musehub_models.py ← ORM + migrations
musehub/db/migrations/ ← 3 new migrations
↓
muse/cli/commands/hub.py ← CLI: proposal create/merge/simulate/dag
muse/core/msign.py ← build_proposal_claim (new primitive)
↓
tests/test_proposals_*.py ← all 8 tiers per phase
8-Tier Test Plan
Tier 1 — Shape / Schema
ProposalTypeenum has exactly 7 valuesProposalStateenum has exactly 7 statesMergeStrategyenum has exactly 5 valuesMergeConditionsTypedDict has all expected keysProposalCreate.proposal_typedefaults toSTATE_MERGEProposalCreate.is_draftdefaults toFalseProposalCreate.merge_strategydefaults toSTATE_OVERLAYProposalCommentTargetallows all 6 target_type values- DB model has
proposal_type,is_draft,dimensional_risk,depends_on,merge_conditionscolumns musehub_proposal_dependenciestable exists with correct FK constraints
Tier 2 — Round-Trip / Integration
- Create draft proposal → state is
drafting, invisible to other users - Publish draft → state becomes
open - Assign reviewers → state becomes
in_review - Submit N approvals → when conditions met, state becomes
approved - Merge approved proposal → state becomes
merged, from_branch deleted - Abandon open proposal → state becomes
abandoned - Create with
depends_on→ merge blocked until dependency merged simulate_merge→ returns conflicts without advancing state- Domain-selective merge (code only) → MIDI/stems state unchanged
- Payment_settlement proposal →
settlingstate while awaiting on-chain confirm
Tier 3 — Edge Cases
- Merge a proposal that depends on an already-merged proposal (should succeed)
- Merge a proposal that depends on an open proposal (should be blocked)
- Circular dependency detection: proposal A depends on B depends on A → 400
domain_selectivemerge with no valid domains → 400- Simulate merge on already-merged proposal → 409
- Abandon a merged proposal → 409 (cannot abandon merged)
- Submit review for a domain not in the proposal's diff → warning but allowed
merge_conditions.require_approvals: 0→ merge immediately on publish- Draft proposal → merge attempt → 400 (cannot merge a draft)
require_no_breakage: truebut breakage_count=1 → merge blocked
Tier 4 — Stress
- Create 1,000 proposals on one repo → list query under 200ms (keyset pagination)
- Proposal with 50 depends_on edges → dependency resolution under 50ms
compute_dimensional_riskon a 500-symbol proposal under 100mssimulate_mergeon a repo with 10,000 tracked files under 2 seconds- 100 concurrent simulate_merge requests → no deadlock, all complete
list_proposal_commentswith 5,000 threaded comments → under 500ms
Tier 5 — Data Integrity
from_state_hashmatches SHA-256 of from_branch HEAD snapshot at creation timeproposer_sig_b64verifies against proposer's registered public key- Review
reviewer_sig_b64verifies against reviewer's public key depends_onJSON array entries are valid proposal UUIDs in the same repodimensional_riskvalues are all in [0.0, 1.0]- Merging proposal X automatically unblocks proposals that list X in depends_on
merged_atis set iffstate = merged; null otherwisesettling_atis set iffstate = settling; null otherwise
Tier 6 — Performance / Benchmarks
compute_dimensional_risk(code domain) under 50ms for 100-symbol proposalsimulate_merge(state_overlay strategy) under 500ms for 1,000-file repo- DAG cycle detection (Kahn's algorithm) under 10ms for 100-node graph
check_merge_conditionsunder 5ms (pure in-memory check after data loaded)list_proposalswith cursor pagination under 10ms per pageget_proposal_diff(all domains) under 300ms
Tier 7 — Security
- Non-owner cannot merge a proposal (must be repo owner)
- Forged
proposer_sig_b64(wrong key) → proposal creation rejected - Forged
reviewer_sig_b64(reviewer signs with wrong key) → review rejected - Proposal cannot self-reference in
depends_on→ 400 selective_domainslist with injection payload (e.g.,"../../etc") → 400- Agent with scope
proposal:readcannot create (needsproposal:write) - Agent with
proposal:writecannot merge (merge requires repo owner) merge_conditions.max_agent_commit_ratio = 0.0→ proposal from agent-only branch blocked- Creating proposal with
from_branch = to_branch→ 400
Tier 8 — Docstrings / API Contract
simulate_mergehas full docstring with Args, Returns, Raisescheck_merge_conditionsdocuments every condition fieldcompute_dimensional_riskdocuments the per-domain formulaProposalTypeenum has per-member docstringsMergeStrategyenum has per-member docstrings explaining domain behaviourverify_proposal_sigdocuments the canonical message formatbuild_proposal_claiminmuse.core.msigndocuments domain separatorPROPOSEProposalDependencyGraphdocuments cycle detection algorithm
7-Phase Implementation Plan
Phase 1 — Extended Data Models & Migrations
Branch: task/proposal-models-v2
- Add
ProposalType,ProposalState(extended),MergeStrategyenums tomusehub/models/musehub.py - Add
MergeConditions,ProposalCommentTarget,DimensionalRiskVectorTypedDicts - Extend
MusehubProposalORM:proposal_type,is_draft,proposer_sig_b64,from_state_hash,to_state_hash,dimensional_risk,depends_on,blocks,merge_conditions,merge_strategy,selective_domains,published_at,settling_at,abandoned_at,rejection_reason - Extend
MusehubProposalReviewORM:review_domain,reviewer_sig_b64,reviewer_archetype - Create
musehub_proposal_dependenciestable + ORM - Create
musehub_proposal_simulationstable + ORM - Three Alembic migrations (proposals extension, dependencies table, simulations table)
- Tier 1 + 5 tests
Symbols: ProposalType, MergeStrategy, MergeConditions, ProposalCommentTarget
Phase 2 — Cryptographic Primitives
Branch: task/proposal-crypto
- Add
build_proposal_claim(signing, from_state_hash, to_state_hash, proposal_type, ts) -> ProposalClaimtomuse/core/msign.py- Canonical:
PROPOSE\n{proposer}\n{from_state_hash}\n{to_state_hash}\n{proposal_type}\n{ts}
- Canonical:
- Add
verify_proposal_sig(sig_b64, proposer_handle, from_state_hash, to_state_hash, proposal_type, ts, public_key_b64) -> tuple[bool, str] - Add
build_review_attestation(signing, proposal_id, verdict, domain, ts) -> ReviewAttestation- Canonical:
APPROVE_PROPOSAL\n{reviewer}\n{proposal_id}\n{verdict}\n{domain}\n{ts}
- Canonical:
- Tier 3 + 7 (crypto + security) tests
Phase 3 — DAG & Merge Condition Service
Branch: task/proposal-dag
ProposalDependencyGraphinmusehub/services/proposal_dag.pyadd_dependency(proposal_id, depends_on_id)— Kahn's cycle detectiontopological_order(root_id) -> list[str]is_merge_unblocked(proposal_id, db) -> tuple[bool, list[str]]— checks all hard deps are merged
check_merge_conditions(proposal, db) -> tuple[bool, list[str]]— evaluates every field ofMergeConditions- Tier 2 + 6 (round-trip + performance) tests
Phase 4 — Simulation Engine
Branch: task/proposal-simulate
simulate_merge(proposal_id, strategy, db) -> SimulationResult— dry-merge returning conflicts without committingPOST /api/repos/{repo_id}/proposals/{id}/simulate— runs simulation, stores inmusehub_proposal_simulationsGET /api/repos/{repo_id}/proposals/{id}/simulate— fetch latest simulation result- Tier 4 (stress) tests — 100 concurrent simulations, 10K-file repos
Phase 5 — Extended State Machine & API Routes
Branch: task/proposal-state-machine
- Implement full 7-state lifecycle in service layer
PATCH /api/repos/{repo_id}/proposals/{id}— update draftPOST /api/repos/{repo_id}/proposals/{id}/publishPOST /api/repos/{repo_id}/proposals/{id}/abandonPOST /api/repos/{repo_id}/proposals—proposal_type,is_draft,merge_conditionsinProposalCreateGET /proposals?type=midi_evolution— type filteringGET /proposals?depends_on=X— DAG filtering- Tier 2 + 7 (round-trip + security) tests
Phase 6 — CLI Extensions
Branch: task/proposal-cli-v2
muse hub proposal create --draft
muse hub proposal publish <id>
muse hub proposal simulate <id>
muse hub proposal dag <id>
muse hub proposal diff <id> --domain midi
muse hub proposal abandon <id>
muse hub proposal depends-on <id> --add <other-id>
muse hub proposal merge <id> --strategy domain_selective --domains code
All new flags carry --body-file support (already merged). --assignee added to proposal create for reviewer designation at creation time.
Phase 7 — MCP Tool Exposure
Branch: task/proposal-mcp-v2
New MCP tools:
muse_proposal_simulate(proposal_id, strategy)— agents can dry-run mergesmuse_proposal_dag(proposal_id)— agents can visualize dependency graphmuse_proposal_diff(proposal_id, domain)— agents can read domain-specific diffsmuse_proposal_abandon(proposal_id, reason)— agents can clean up stale proposalsmuse_proposal_publish(proposal_id)— agents can promote drafts to openmuse_check_merge_conditions(proposal_id)— agents can poll readiness
Why This Matters
GitHub pull requests evolved from email patches. They carry thirty years of assumptions about version control being about text files and line numbers.
Muse proposals are about state. When a musician changes a chord progression, there is no "line number" for the harmonic tension. When an agent proposes a payment settlement, there is no "diff hunk" for the ledger delta. When an org votes to rotate their signing key, the "change" is a cryptographic event, not a file.
The proposal system should be the most expressive part of MuseHub — the place where humans and agents negotiate, review, and collectively decide what the shared state of the world should be next. That deserves a model built from the ground up for this problem.
Assignee
@aaronrene — this is the biggest design surface on the platform. Coordinate with @gabriel on muse.core.msign primitives for build_proposal_claim and the review attestation format.
Priority
Critical. Every other feature converges here: stems integration, MIDI review, payment settlement, agent delegation. The proposal system is the governance layer for the entire multidimensional state machine.
✅ Phase 1 Complete — Proposal Type System, Merge Strategy & Dimensional Models
Branch: task/proposal-models-v2
Commits: sha256:d70f04ec (models + ORM) · sha256:a6942164 (tests)
Tests: 43 passed, 0 failed
What landed
musehub/models/musehub.py
ProposalTypeenum — 7 values:state_merge,stem_integration,midi_evolution,payment_settlement,agent_delegation,identity_transition,canonical_releaseProposalStateenum — 7 states:drafting → open → in_review → approved → settling → merged | abandonedMergeStrategyenum — 5 strategies:state_overlay,state_weave,state_rebase,domain_selective,phasedMergeConditionsPydantic model — 9 merge gate fields with validated defaultsProposalCommentTarget— domain-agnostic coordinate system for code/midi/stem/payment/identity inline commentsDimensionalRiskVectortype alias (dict[str, float])ProposalCreateextended:proposal_type,is_draft,merge_conditions,merge_strategy,selective_domains,depends_onProposalResponseextended: same new fields +risk_score,dimensional_risk
musehub/db/musehub_models.py
MusehubProposal+15 columns: type/strategy/conditions, MIDI summary, payment summary, agent provenanceMusehubProposalReview+3 columns:reviewed_domains,domain_risk_acknowledged,suggested_merge_strategyMusehubProposalDependencynew table — DAG edges with unique constraint + cascade deleteMusehubProposalSimulationnew table — phased-merge simulation cache with (proposal, type) unique constraint
Migrations (applied to local Docker Postgres ✅)
0045— proposal_type, is_draft, merge_conditions, merge_strategy, selective_domains, dimensional_risk0046— MIDI/payment domain summaries, agent_model/agent_spawned_by, review dimensional fields0047— musehub_proposal_dependencies + musehub_proposal_simulations tables
Test coverage (43 tests in tests/test_proposal_reimagination_phase1.py)
| Tier | Tests | What |
|---|---|---|
| Unit | 28 | Enum values, MergeConditions bounds, ProposalCommentTarget domains, DimensionalRiskVector, ProposalCreate/Response defaults + camelCase JSON |
| Integration | 15 | ORM round-trips for all new columns, unique constraint fires, cascade delete verified |
Up next — Phase 2: Dependency DAG Engine
Kahn's algorithm, cycle detection, blocked_by/blocks resolution, merge ordering — wired into the proposal service layer.
Phase 1 Complete — Proposal Type System, Merge Strategy and Dimensional Models
Branch: task/proposal-models-v2 Commits: sha256:d70f04ec (models + ORM) and sha256:a6942164 (tests) Tests: 43 passed, 0 failed
What landed
musehub/models/musehub.py
- ProposalType enum — 7 values: state_merge, stem_integration, midi_evolution, payment_settlement, agent_delegation, identity_transition, canonical_release
- ProposalState enum — 7 states: drafting > open > in_review > approved > settling > merged | abandoned
- MergeStrategy enum — 5 strategies: state_overlay, state_weave, state_rebase, domain_selective, phased
- MergeConditions Pydantic model — 9 merge gate fields with validated defaults
- ProposalCommentTarget — domain-agnostic coordinate system for code/midi/stem/payment/identity inline comments
- DimensionalRiskVector type alias (dict[str, float])
- ProposalCreate extended: proposal_type, is_draft, merge_conditions, merge_strategy, selective_domains, depends_on
- ProposalResponse extended: same new fields + risk_score, dimensional_risk
musehub/db/musehub_models.py
- MusehubProposal +15 columns: type/strategy/conditions, MIDI summary, payment summary, agent provenance
- MusehubProposalReview +3 columns: reviewed_domains, domain_risk_acknowledged, suggested_merge_strategy
- MusehubProposalDependency new table — DAG edges with unique constraint + cascade delete
- MusehubProposalSimulation new table — phased-merge simulation cache with (proposal, type) unique constraint
Migrations applied to local Docker Postgres:
- 0045: proposal_type, is_draft, merge_conditions, merge_strategy, selective_domains, dimensional_risk
- 0046: MIDI/payment domain summaries, agent_model/agent_spawned_by, review dimensional fields
- 0047: musehub_proposal_dependencies + musehub_proposal_simulations tables
Test coverage (43 tests)
- Unit (28): Enum values, MergeConditions bounds, ProposalCommentTarget domains, DimensionalRiskVector, ProposalCreate/Response defaults + camelCase JSON round-trip
- Integration (15): ORM round-trips for all new columns, unique constraint fires, cascade delete verified
Up next — Phase 2: Dependency DAG Engine
Kahn's algorithm, cycle detection, blocked_by/blocks resolution, merge ordering — wired into the proposal service layer.
Phase 2 Complete — Dependency DAG Engine
Branch: task/proposal-models-v2 Commit: sha256:fca4d08c Tests: 29 passed (72 total across Phases 1+2)
What landed
musehub/services/proposal_dag.py — pure DAG engine (no DB, independently testable):
- ProposalDag dataclass: depends_on/required_by adjacency + merged_ids + number_by_id
- build_dag: construct from raw (dependent_id, dependency_id) edge pairs
- topological_sort: Kahn's algorithm; raises CycleError(cycle_ids) on any cycle
- detect_cycle: bool test without mutating the DAG
- blocked_by_numbers: live deps only (merged proposals excluded)
- blocks_numbers: reverse edge — who is waiting on this proposal
- is_blocked: True iff any unmerged dependency exists
- load_dag_for_proposals: partial DAG from DB, scoped to current page + transitive neighbours (2 queries)
- create_dependency_edges: validates existence, detects cycles pre-write, persists MusehubProposalDependency rows
Service integration (musehub_proposals.py):
- create_proposal: accepts proposal_type, is_draft, merge_strategy, merge_conditions, selective_domains, depends_on
- _prefetch_for_batch: adds Query 3 — DAG load for the page
- _enrich_one: populates blocked_by/blocks/is_blocked; all_merge_conditions_met respects require_dependency_merged; reads MIDI/payment/agent fields from ORM
- merge_proposal: gates on unmerged hard dependencies; raises RuntimeError listing blocked proposal numbers
API route: forwards all ProposalCreate fields; CycleError surfaced as HTTP 422
Test coverage (29 tests)
- Unit pure DAG (24): build_dag, linear/diamond/multi-root topo sort, two/three-node cycles, CycleError message, merged-breaks-cycle, blocked_by/blocks sorted + merged-excluded
- Integration DB (5): dependency edges persisted, unknown dep rejected, cycle rejected via create_dependency_edges, enrich_batch populates blocked_by/blocks/is_blocked, merge gated by unmerged dep
Up next — Phase 3: Merge Strategy Engine
STATE_OVERLAY, STATE_WEAVE, STATE_REBASE, DOMAIN_SELECTIVE, PHASED — each strategy gets its own execution path in merge_proposal.
Phase 3 complete — Merge Strategy Engine ✅
Commit: sha256:a6ab45613263 on task/proposal-models-v2
What landed
musehub/services/proposal_merge_strategies.py (new — pure, no DB)
classify_domain(path)— single authority for domain routing; path-prefix rules beat extension rules_manifest_delta(base, head)— (added, modified, removed) path sets_apply_delta(target, delta_head, delta_base)— applies a branch's delta onto a target manifestMergeResult/ConflictEntrydataclasses — structured merge output with conflict audit trail- Five strategy functions:
merge_state_overlay— from_branch wins all files; conflicts populated for audit when ancestor providedmerge_state_weave— true three-way file-level merge; clean changes applied conflict-free; both-sides changes surfacedmerge_state_rebase— applies only from_branch delta (vs ancestor) onto to_branch; to-only changes preservedmerge_domain_selective— applies only from_branch files whose domain is inselective_domains;files_skippedcountedmerge_phased— applies dependency deltas in Kahn topological order then self; falls back to overlay when no phase_manifests
execute_merge_strategy(strategy, ...))— router for all 5; state_weave/rebase fall back to overlay with warning when no ancestor
musehub/services/musehub_proposals.py (updated)
_resolve_ancestor_manifest(session, repo_id, from_branch, to_branch)— bounded walk (depth 200) to find merge-base commitmerge_proposal— dependency gate → load ancestor manifest →execute_merge_strategy→ applymerge_result.manifest
tests/test_proposal_reimagination_phase3.py (new — 56 tests, all passing)
- Uses
muse.core.types.fake_idandblob_id(os.urandom())throughout — no UUIDs - Unit coverage: domain classifier, all 5 strategy functions, router fallbacks
- Integration coverage: default overlay merges correctly, domain_selective only applies selected domain
Status
- Phase 1 (type system + ORM): ✅ committed
- Phase 2 (DAG engine): ✅ committed
- Phase 3 (merge strategy engine): ✅ committed
- Phase 4 (simulation engine): 🔜
- Phase 5 (API surface): 🔜
- Phase 6 (MCP tools): 🔜
Phase 4 complete — Simulation Engine ✅
Commit: sha256:2c9d8d66ec34 on task/proposal-models-v2
What landed
musehub/core/genesis.py
compute_simulation_id(proposal_id, simulation_type, from_branch_commit_id)— content-addressed ID; different commit tip → different ID (re-runs are distinguishable)
musehub/models/musehub.py
SimulationTypeenum:conflict_scan|risk_projection|dependency_orderSimulationResponse— simulation_id, proposal_id, type, result (JSONB payload), is_stale, from_branch_commit_id, duration_ms, created_at, expires_atSimulationListResponse
musehub/services/proposal_simulation.py (new — pure, no DB)
simulate_conflict_scan— dry-runs the merge strategy; returns conflict_count, conflicting_files, conflicts_by_domain, files_added/modified/removed, domains_affected, strategy_usedsimulate_risk_projection— weighted blend: 40% change_ratio + 40% conflict_ratio + 20% existing dimensional_risk; returns projected_domain_risk, overall_projected_risk, risk_band (low/medium/high), risk_deltasimulate_dependency_order— topological sort of live DAG; groups into parallel phases; cycle → cycle_detected=True + cycle_ids
musehub/services/musehub_proposals.py (updated)
run_simulation(session, repo_id, proposal_id, simulation_type)— always recomputes; upserts into musehub_proposal_simulations (one row per proposal×type)get_simulation(session, repo_id, proposal_id, simulation_type)— reads cache; sets is_stale=True when from_branch has advancedlist_simulations(session, repo_id, proposal_id)— all cached simulations for a proposal
musehub/api/routes/musehub/proposals.py (updated)
POST /repos/{repo_id}/proposals/{proposal_id}/simulations/{simulation_type}— run/refreshGET /repos/{repo_id}/proposals/{proposal_id}/simulations/{simulation_type}— read cacheGET /repos/{repo_id}/proposals/{proposal_id}/simulations— list all
tests/test_proposal_reimagination_phase4.py (new — 38 tests, all passing)
Status
- Phase 1 (type system + ORM): ✅
- Phase 2 (DAG engine): ✅
- Phase 3 (merge strategy engine): ✅
- Phase 4 (simulation engine): ✅
- Phase 5 (API surface / enrichment): 🔜
- Phase 6 (MCP tools): 🔜
Phase 5 complete — API Surface Enrichment ✅
Commit: sha256:dd31f05becd9 on task/proposal-models-v2
What landed
ProposalResponse (single-proposal read) — 3 new fields:
blocked_by: list[int]— proposal_numbers of unmerged dependencies blocking this oneblocks: list[int]— proposal_numbers that depend on this proposalis_blocked: bool— convenience flaglatest_simulations: dict[str, dict]— inline summary of all cached simulation results (keyed by type)
get_proposal — now runs two extra queries (one DAG load, one simulation load) and feeds both into _to_proposal_response so every single-proposal read is fully enriched.
list_proposals — three previously-silently-ignored filters now wired:
proposal_typelist filter → DB predicate onmusehub_proposals.proposal_typeis_draftbool filter → DB predicate onmusehub_proposals.is_draftmerge_strategylist filter → DB predicate onmusehub_proposals.merge_strategy
ProposalListFilters — two new fields: is_draft: bool | None, merge_strategy: list[str] | None
ProposalListEntry — two new fields:
merge_strategy: str— strategy value per rowsimulation_conflict_count: int | None— conflict count from prefetched conflict_scan; None if never run
_enrich_one — uses dimensional_risk dict (Phase 1 columns) for multi-domain risk breakdown instead of the single scalar risk_score → code-domain path; falls back to scalar for backwards compatibility.
_prefetch_for_batch — Query 4 loads the latest conflict_scan result for every proposal in the current page; zero extra I/O per row in _enrich_one.
tests/test_proposal_reimagination_phase5.py — 21 tests, all passing. 187 total across all 5 phases.
Status
- Phase 1 (type system + ORM): ✅
- Phase 2 (DAG engine): ✅
- Phase 3 (merge strategy engine): ✅
- Phase 4 (simulation engine): ✅
- Phase 5 (API surface enrichment): ✅
- Phase 6 (MCP tools): 🔜
Phase 6 complete — MCP tools ✅
Commit: sha256:f0572b29 on task/proposal-models-v2
Changes
musehub/mcp/write_tools/proposals.py
_simulation_data()— new serialiser forSimulationResponse_proposal_data()— now includes all Phase 1-5 fields:proposal_type,is_draft,merge_strategy,merge_conditions,selective_domains,blocked_by,blocks,is_blocked,latest_simulationsexecute_create_proposal— accepts and forwardsproposal_type,is_draft,merge_strategy,merge_conditions,selective_domains,depends_onexecute_get_proposal— new; returns enriched proposal with DAG + simulation dataexecute_run_simulation— new; runs and persists conflict_scan / risk_projection / dependency_orderexecute_get_simulation— new; reads cached result, returnsnot_foundwith hint when absentexecute_list_simulations— new; lists all cached simulations (up to 3) for a proposal
musehub/mcp/tools/musehub.py
musehub_create_proposalschema updated: addsproposal_type,is_draft,merge_strategy,merge_conditions,selective_domains,depends_onmusehub_get_proposal— new toolmusehub_run_proposal_simulation— new toolmusehub_get_proposal_simulation— new toolmusehub_list_proposal_simulations— new tool
musehub/mcp/dispatcher.py
- Routes all 5 new/updated tool calls to executors with correct arg forwarding
tests/test_proposal_reimagination_phase6.py — 38 tests, all passing
_proposal_dataserialiser covers all new fields_simulation_datashapeexecute_create_proposalkwargs forwardingexecute_get_proposalenrichment + not_foundexecute_run_simulation+ validationexecute_get_simulation+ not_found hintexecute_list_simulations+ empty list- Tool schema presence + enum values
- Dispatcher routing for all new tools
Tally
| Phase | Area | Status |
|---|---|---|
| Phase 1 | Models (ProposalType, MergeStrategy, SimulationType) | ✅ |
| Phase 2 | DAG engine (dependency ordering, cycle detection) | ✅ |
| Phase 3 | Merge strategy engine | ✅ |
| Phase 4 | Simulation engine (pure + persistence) | ✅ |
| Phase 5 | API surface enrichment (blocked_by, latest_simulations, filters) | ✅ |
| Phase 6 | MCP tools | ✅ |
Next: Phase 7 — UI/template updates, then open the merge proposal.
Phase 7 complete — UI/templates ✅
Commit: sha256:8f6f64b7 on task/proposal-models-v2
Changes
proposal_rows.html (list view rows)
strategy_label+proposal_type_labelmacros — both skip defaults (state_overlay / state_merge) to avoid badge noisemerge_strategybadge in title row: shows weave / rebase / selective / phasedproposal_typebadge in title row: shows stem / midi / payment / agent / identity / releasesimulation_conflict_countpill in signals row: red conflict count with icon, or clean green check when 0
proposal_row_detail.html (expand panel)
- Merge strategy + proposal type section (hidden when both are defaults)
- Simulation results: ready/blocked badge from conflict count
proposal_detail.html (full page)
- Hero eyebrow: draft, merge_strategy, proposal_type, and 🔒 Blocked badges
- New Simulations section: 3-card grid — conflict_scan (count), risk_projection (band + % + Δ), dependency_order (phase count or cycle warning)
- New sidebar Merge Approach scard: strategy, type, blocked_by links, blocks links (only renders when non-default or DAG dependencies exist)
Opening merge proposal now.
All 7 phases complete. Merge proposal opened: sha256:89cde827 (task/proposal-models-v2 → dev). Closing.
Implementation Plan — Load-Bearing Execution Map
Phase 1 — Extended Data Models & Migrations
Branch:
task/proposal-models-v2Touches:musehub/models/musehub.py,musehub/db/musehub_models.py,alembic/versions/1a — New Pydantic Enums & TypedDicts (
musehub/models/musehub.py)Add these before the existing
ProposalCreateclass:Extend
ProposalCreateto accept new fields:1b — ORM Extension (
musehub/db/musehub_models.py)MusehubProposal— new columns to add:MusehubProposalReview— new columns:Two new ORM classes:
1c — Three Alembic Migrations
Note: migrations 0045 uses
op.add_column(transactional, no CONCURRENTLY needed). Migrations 0046 and 0047 useCREATE TABLE(transactional). No autocommit block required.1d — Phase 1 Tests
File:
tests/test_proposal_reimagination_phase1.pyPhase 2 — Cryptographic Primitives
Branch:
task/proposal-cryptoTouches:muse/core/msign.py,musehub/services/musehub_proposals.py2a — Canonical Message Formats
2b — New symbols in
muse/core/msign.py2c — Integration in proposal creation
In
musehub/services/musehub_proposals.py,create_proposal():2d — Phase 2 Tests
Phase 3 — DAG & Merge Condition Service
Branch:
task/proposal-dagNew file:musehub/services/proposal_dag.pyTouches:musehub/services/musehub_proposals.py3a — DAG Engine (
musehub/services/proposal_dag.py)Symbols to create:
3b — Phase 3 Tests
Phase 4 — Simulation Engine
Branch:
task/proposal-simulateNew symbol:simulate_mergeinmusehub/services/musehub_proposals.pyNew routes:POST+GET/api/repos/{id}/proposals/{id}/simulate4a — SimulationResult model
4b — simulate_merge implementation shape
4c — API routes
4d — Phase 4 Tests
Phase 5 — Extended State Machine & API Routes
Branch:
task/proposal-state-machineTouches:musehub/services/musehub_proposals.py,musehub/api/routes/musehub/proposals.py5a — Transition table (guards are checked in service layer, not route layer)
5b — New/extended service functions
5c — New API routes
Extend
GET /api/repos/{repo_id}/proposals:POST /api/repos/{repo_id}/proposals— extendProposalCreatehandler:5d — Phase 5 Tests
Phase 6 — CLI Extensions
Branch:
task/proposal-cli-v2Touches:muse/cli/commands/hub.py(or wherevermuse hub proposalsubcommands live)6a — Extended
muse hub proposal create6b — New subcommands
6c — Phase 6 Tests
Phase 7 — MCP Tool Exposure
Branch:
task/proposal-mcp-v2Touches:musehub/mcp/tools/musehub.py,musehub/mcp/dispatcher.py,musehub/services/musehub_mcp_executor.py7a — New MCP tools (6 tools → tool count 120 → 126)
7b — Tool description philosophy
Each tool description must answer:
Example for
musehub_simulate_proposal:7c — Phase 7 Tests
Cross-Phase Dependency Map
File Touch Map
Spectral Risk Visualization — Dimensional Heat at Merge Time
When this is fully implemented, the proposal detail page will render the dimensional heat spectrum — the full risk vector across all active domains:
This visualization is the end state of all seven phases working together. The risk vector is computed in Phase 1, the DAG state in Phase 3, the conditions evaluation in Phase 3, and the full UI render is downstream of this ticket in the proposal detail redesign.
Notes for Executor
devindependently — no phase waits for a later phase's PRtests/test_proposal_reimagination_phase{N}.pyMusehubProposalqueries — all new columns have server-side defaultsstatecolumn already exists; Phase 1 extends its value set but the column does not change type (TEXT, max 20 chars → extend to 32 to accommodatepayment_settlement)muserepo (muse/core/msign.py), notmusehub— coordinate commits across both reposmuserepo — same cross-repo coordination requiredmerge_conditions