diff --git a/tasks/in-progress/37-sync-resolves-its-remote-and-says-when-it-cannot.md b/tasks/in-progress/37-sync-resolves-its-remote-and-says-when-it-cannot.md index 9fa70f5..e16fcf5 100644 --- a/tasks/in-progress/37-sync-resolves-its-remote-and-says-when-it-cannot.md +++ b/tasks/in-progress/37-sync-resolves-its-remote-and-says-when-it-cannot.md @@ -125,3 +125,30 @@ branch point with a hardcoded `origin` too, and its silent fallback ("branched from local HEAD") is the same failure wearing a friendlier face — on a repo whose remote is named otherwise, every agent branches from a stale local `main` and the ticker sounds fine about it. + + +--- + +## Work report — 2026-08-01 07:35 (Wren) + +Done and committed on `task/37-sync-resolves-its-remote-and-says-when-it-cannot`. + +## Report + +The work is complete and committed in two commits; the full suite (`python3 -m unittest discover -s tests`, 641 tests) passes on a clean tree. Nothing is blocked and nothing in scope was left out. + +**What changed** + +- `manager/core/config.py` — new `git_remotes()` and `git_remote()`. `git_remote()` returns `BOARD_GIT_REMOTE` when set (as named, never swapped for another), else the checkout's first remote, else `None`. Both shell out on demand, never at import, so `config` still costs nothing to load. +- `manager/core/github.py` — `remote()` is now a one-line call to `config.git_remote()`, so PR opening and sync can never name different remotes. +- `manager/core/sync.py` — the `REMOTE`/`UPSTREAM` module constants are gone. `_origin_present()` became `_remote()`, which resolves through `config`, verifies the name against the checkout's actual remotes, and returns the name or `None`. The remote is threaded as a parameter through `_fetch`, `_ahead`, `_publish`, `_replay`, `_lost`, `_integrate` and `_record_arrivals`, with `_upstream(remote)` replacing the constant in every message. +- Where the code returned a bare `"no-origin"` that both callers discarded, `_remote()` now calls `_note()` at `stalled` level first (the return value is `"no-remote"`): no remote at all names both fixes (`git remote add`, or `BOARD_GIT_REMOTE`); a `BOARD_GIT_REMOTE` naming a remote the checkout does not have stalls naming it and listing what the checkout does have. It clears with a "sync is converging again" line, and `install()` resolves once at startup so the chip is on the header from first paint rather than the first beat. The existing `_note()` dedupe keeps it to one line per condition, and the whole path is behind `config.SYNC`. +- Docs: the `BOARD_GIT_REMOTE` and `BOARD_SYNC` blocks in `manager/core/.env.example`, and a new bullet in the "Syncing boards" section of `AGENTS.md` (which is also the source of the site's Team mode page). + +**How it was verified** — `tests/test_boards_sync.py` gained eleven tests, all against real clones of a real bare upstream as the rest of that module does: a checkout whose only remote is `upstream` pushes and pulls normally; `BOARD_GIT_REMOTE=fork` wins over an alphabetically-first decoy remote and agrees with `github.remote()`; no remote stalls with one deduped ticker line naming both fixes and a non-`ok` `status()`; adding a remote clears it; `BOARD_GIT_REMOTE=typo` in a checkout that has `origin` stalls naming `typo` without touching `origin`; the condition is on the header after `install()` alone; and with `BOARD_SYNC` off nothing runs or renders. A `TheRemoteResolver` class covers `config.git_remote()`/`git_remotes()` directly, including a path that is not a git checkout. + +**Worth a reviewer's first look** + +- `manager/core/sync.py:107` — `_remote()`, where the resolution, the verification and the three narrations live; it is the whole behaviour change. +- The one test whose assertion may read oddly: `test_the_stall_clears_when_a_remote_appears` expects `push_now()` to return `"up-to-date"`, not `"ok"`. That is pre-existing `_converge()` behaviour (it returns the integrate outcome, not the publish one) surfacing on the first converge after a remote appears; the test also asserts origin actually received the commit. +- Deliberately untouched, as the task scoped it: `manager/core/agents.py:176-194` still hardcodes `origin` when choosing a work agent's branch point and falls back silently to local HEAD. Same bug class, and the task's own Notes ask for a separate card now that the resolver exists.