gabriel / musehub public
Open #217 Bug
filed by gabriel human · 29 days ago

muse push -u / --set-upstream writes to [remotes.<name>] but muse branch --json reads from [branch.<name>] — upstream is always null

0 Anchors
— Blast radius
— Churn 30d
0 Proposals
No description provided.
◎ Activity1
●
gabriel opened this issue 29 days ago
○
gabriel 29 days ago

Found while answering a question about Episode 01's muse branch --json output — "what should upstream show after a real push?" led to this. Confirmed with a live reproduction, not a code-read guess.

Repro:

muse push local main -u
# prints: "  Upstream set: main → local/main"
# prints: "✅ Pushed 1 commit(s), 1 object(s) to local/main (...)"

muse branch --json
# "upstream": null   <- always, even immediately after the above

Root cause — two disconnected schemas:

set_upstream() (muse/cli/config.py:1338) writes:

config["remotes"][remote_name]["branch"] = branch
# → TOML section [remotes."local"], key "branch"

_upstream_for() (muse/cli/commands/branch.py:196), which produces the upstream field in muse branch --json, reads:

config["branch"][branch]["remote"]   # → [branch."main"].remote
config["branch"][branch]["merge"]    # → [branch."main"].merge

These are two different top-level TOML sections ([remotes.<name>] vs [branch.<name>]) that never overlap. Grepped the entire codebase for any writer of [branch.<name>].remote / [branch.<name>].merge — found none. The only code that touches those two keys is _load_config's parser (muse/cli/config.py:281-286), which faithfully reads them if present, but nothing ever writes them there. The BranchMeta TypedDict docstring even documents "Fields written by muse push upstream tracking (preserved on read/write)" for remote/ merge under [branch.<name>] — describing a write path that doesn't exist.

Net effect: muse branch --json's upstream field can never be populated through any real, currently-existing code path. -u/ --set-upstream genuinely runs and reports success, but writes to a location nothing ever reads back for this purpose.

Suggested fix: pick one schema and make both sides agree — either make set_upstream() write remote/merge under [branch.<name>] (matching what _upstream_for() already expects and what the BranchMeta docstring already claims), or make _upstream_for() read from [remotes.<name>].branch (matching what set_upstream() actually writes). The former seems more consistent with how intent/resumable already work (per-branch metadata under [branch.<name>]), and with the existing docstring's stated intent.

Deferred per usual until after the season wraps.