Skip to content

feat(cos): migrate branch reconciler to a per-app CoS scheduled task#2240

Merged
atomantic merged 3 commits into
mainfrom
feat/branch-reconcile-cos-task
Jul 6, 2026
Merged

feat(cos): migrate branch reconciler to a per-app CoS scheduled task#2240
atomantic merged 3 commits into
mainfrom
feat/branch-reconcile-cos-task

Conversation

@atomantic

Copy link
Copy Markdown
Owner

What

Migrates the PortOS-only Branch & PR Reconciler into a first-class per-app Chief-of-Staff scheduled task (branch-reconcile), so it can finish unfinished local git work on any managed app — not just PortOS.

The reconciler still does the same job: delete fully-merged orphaned local branches (+ their worktrees), open PRs for pushed-but-unopened work, resolve merge conflicts, drive the review loop, and auto-merge when a PR is green. It's just no longer a bespoke subsystem.

How

  • New branch-reconcile task type (taskSchedule.js): perpetual (drain-until-done), disabled by default, daily recheck, per-app action toggles (cleanupMerged/openPr/resolveConflicts/autoMerge), useWorktree/openPR locked off via MANAGED_AGENT_OPTIONS (the coordinator runs in the app's live checkout to see sibling worktrees).
  • Deterministic per-app pre-step (cosTaskGenerator.js): runs the peer-safe reconcile() on the app's repo (mirroring how pr-watcher/reference-watch do programmatic work in the generator), injects {inFlightBranches} into a coordinator prompt, and dispatches only while actionable in-flight branches remain.
  • Convergence guard: a progress signature (branch:state:pr#:mergeable) suppresses the immediate back-to-back refill when a coordinator run makes no progress (a NEEDS_PR branch judged "not ready", an IN_REVIEW PR blocked on human review / red CI), but never suppresses the daily recheck — so a PR that becomes green overnight still gets driven to merge.
  • Pure core reused: branchReconcile.js (the Tier-1 classifier + cleanup) is unchanged and already repoPath-parameterized; the coordinator prompt helpers moved here from the deleted scheduler, now unit-tested.
  • Removed the bespoke scheduler, /api/branch-reconcile route, settings.branchReconcile key + Zod schema, the Settings tab, navManifest entry, and boot cron.
  • Migration 170 carries a previously-enabled branchReconcile opt-in into the new task (scoped to the PortOS app), so upgrading installs keep reconciling instead of silently stopping.

Peer-safety (unchanged)

Only local refs/heads/ are ever touched. A branch created on a federated sync peer exists here only as a remote-tracking ref and is structurally invisible.

Testing

  • Full server suite green (13925 passed, DB/platform tests skipped).
  • New coverage: actionableSignature, moved prompt helpers, perpetual signature persistence, and migration 170.
  • Local review by claude + codex (per --review-with default); their findings (convergence loop, IN_REVIEW stall, missing migration) fixed in this branch.

https://claude.ai/code/session_01WQ6wsghzJAWWV778jAER9o

atomantic added 3 commits July 6, 2026 10:21
The branch & PR reconciler (delete merged orphaned branches, open PRs for
pushed-but-unopened work, resolve conflicts, drive review, auto-merge when
green) was a PortOS-only feature with its own daily cron, /api/branch-reconcile
route, settings.branchReconcile toggle, and a dedicated Settings tab, hardwired
to the PortOS checkout.

It is now a first-class per-app CoS task type, `branch-reconcile`, configured
like every other improvement task (per-app enable, provider/model, cadence,
cooldown, budget). It runs `perpetual` (drain-until-done): each dispatch runs
the deterministic, peer-safe cleanup + classification against the app's repo,
dispatches the coordinator agent only while actionable in-flight branches
remain, then parks on a daily recheck — so no agent is burned on a no-op run.

- taskSchedule.js: new `branch-reconcile` type (perpetual, disabled default,
  daily recheck, per-app action toggles), MANAGED_AGENT_OPTIONS lock, description
- cosTaskGenerator.js: deterministic per-app pre-step (mirrors pr-watcher/
  reference-watch), injects {inFlightBranches}, parks/dispatches
- branchReconcile.js: coordinator prompt helpers moved here from the deleted
  scheduler (filterActionable/desiredEndState/formatInFlightForPrompt), tested
- taskPromptDefaults: coordinator prompt + version + integrity snapshot
- cosValidation.js: cleanupMerged/openPr/resolveConflicts/autoMerge are now
  sanitized per-app task-metadata booleans

Removes the bespoke scheduler, route, settings key, Settings tab, API helpers,
navManifest entry, and boot cron. Peer-safety (local refs only) is unchanged.

Claude-Session: https://claude.ai/code/session_01WQ6wsghzJAWWV778jAER9o
…a progress signature

The claude review flagged that making branch-reconcile `perpetual` with
metadata.perpetual=true removed the old daily-cron throttle, so a branch the
coordinator can't finish (a NEEDS_PR branch judged "not ready", or an IN_REVIEW
PR blocked on human approval / red CI) stayed actionable every tick and
re-dispatched an identical coordinator back-to-back forever.

Add a progress-signature convergence guard: dispatch only when the actionable
set changed since the last dispatch. A productive run advances branches through
states (changing the signature) so the drain keeps making progress; a run that
leaves the same branches in the same states parks on the recheck cadence instead
of looping.

- branchReconcile.js: actionableSignature() (order-independent {branch:state:pr})
- taskSchedule.js: getPerpetualSignature/setPerpetualSignature + parkPerpetual
  stores the signature on a no-progress park
- cosTaskGenerator.js: signature compare → park on no-progress, dispatch + record
  on change; document the transient-vs-persistent reconcile-failure trade-off

Claude-Session: https://claude.ai/code/session_01WQ6wsghzJAWWV778jAER9o
…hReconcile opt-ins

Codex review found two issues in the migration:

[P1] Signature stall for slow PRs. The progress signature (branch:state:pr#) did
not change when a stuck IN_REVIEW PR later became mergeable (CI turned green,
review cleared), so after the first no-progress park every daily recheck saw the
same signature and re-parked — auto-merge never fired. Fix: the signature only
suppresses the immediate back-to-back refill, never a recheck. On a no-progress
park the stored signature is now CLEARED, so the next daily recheck re-dispatches
unconditionally (re-driving the PR). Also add PR `mergeable` to the signature so
a UNKNOWN→MERGEABLE transition within the same state counts as progress.

[P2] No migration for existing opt-ins. Removing the old scheduler + defaulting
the new task to disabled would silently stop the reconciler for anyone who had
settings.branchReconcile.enabled: true. Add migration 170: carry the enabled
reconciler into the new branch-reconcile task (cron→recheckCron, actions→
taskMetadata), scoped to the PortOS app only (disable on other managed apps to
preserve the old PortOS-only scope), and drop the dead settings key.

- branchReconcile.js: actionableSignature includes mergeable
- cosTaskGenerator.js: clear signature on no-progress park (daily re-drive)
- scripts/migrations/170-branch-reconcile-to-cos-task.js (+ test)

Claude-Session: https://claude.ai/code/session_01WQ6wsghzJAWWV778jAER9o
@atomantic atomantic merged commit f4f5230 into main Jul 6, 2026
2 checks passed
@atomantic atomantic deleted the feat/branch-reconcile-cos-task branch July 6, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant