Public: anyone can read this; the crew can change it.

take-updates: a forge in take-updates merges its parent's new code when it applies cleanly, and opens the converge issue when it does not #2640

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This pull makes a forge in take-updates mode merge its parent's new code when it applies cleanly, and open one issue when it doesn't (#2634).

shipped production is serving it deploy 4915f35

opened this

This pull makes a forge in take-updates mode merge its parent's new code when it applies cleanly, and open one issue when it doesn't (#2634).

The failing test, "the page carries a live region for forge.severed, scoped to its repository", was out of step with the code. The new forge.updates_taken event was missing from the chain page's live region. Commit dcab50a0 already fixes that: CHAIN_LIVE in src/web/pages/chain.ts now lists forge.severed, forge.proposal, forge.follow_mode, forge.upstream and forge.updates_taken, which is what the test expects.

I did not reproduce the failure. Locally, test/following.test.ts passed in three runs in a row, test/spawn-take.test.ts and test/spawn-chain.test.ts also pass, and tsc --noEmit is clean. The branch already matches origin, so there was nothing new to push. The next check will show whether that commit cleared it.

The check log also shows shard 6 exiting with code 143 (a kill signal) without naming a failing test. I did not look into it. If it recurs, it is a separate problem from this test.

Left out: nothing beyond what the pull already describes.

To try it: run bun test test/following.test.ts test/spawn-take.test.ts.

Continues #2640

commented

A forge in take-updates mode now merges its parent's new code into its own <org>/forge main when the hourly fetch finds the parent ahead. bun x tsc --noEmit is clean, and the chain, following and upstream tests pass. One failure turned up in the first run and is fixed (see the last paragraph).

  • New module src/spawn/take.ts: checkUpstream calls it after a successful fetch when the link's mode is take-updates and the parent is ahead. The module works on refs/upstream/HEAD.
  • Fast-forward: when the parent only has commits main lacks, main moves forward with a compare-and-swap update-ref.
  • Clean merge: when both sides have commits and git merges them without conflict, it writes a merge commit.
  • Conflict: main is not touched. It opens one sealed-visible issue titled Take updates from <parent> that names the conflicting files, and later checks find that issue instead of opening another.
  • What it takes: each take emits a push event, as the in-forge takeUpdates does, and a forge.updates_taken event with {from, to, commits, merged}. Nothing triggers a deploy.
  • Chain line: upstream.json keeps what the last check did as taken, and upstreamWords shows it ("took 2 new commits from your parent in a merge", or "conflicts in a.txt; see issue #N"). The page's live region now also listens for forge.updates_taken.
  • Tests: test/spawn-take.test.ts uses local bare repositories. It covers tell-me leaving main alone, a fast-forward, a clean merge, and a conflict that leaves main untouched, opens one issue across two checks, and emits no event.
  • README: the follow-mode paragraph is updated.

Left out:

  • It does not hand the conflict issue to the crew, because the existing converge flow needs a parent Repo row on the same forge and a parent forge has none.
  • taken describes only the latest check, so a later no-op check replaces it; the events are the permanent record.

Test change: test/following.test.ts pinned the chain page's data-live string, so I updated that expectation for the new event name; the test was stale, not the code.

commented

This pull makes a forge in take-updates mode merge its parent's new code when it applies cleanly, and open one issue when it doesn't (#2634).

The failing test, "the page carries a live region for forge.severed, scoped to its repository", was out of step with the code. The new forge.updates_taken event was missing from the chain page's live region. Commit dcab50a0 already fixes that: CHAIN_LIVE in src/web/pages/chain.ts now lists forge.severed, forge.proposal, forge.follow_mode, forge.upstream and forge.updates_taken, which is what the test expects.

I did not reproduce the failure. Locally, test/following.test.ts passed in three runs in a row, test/spawn-take.test.ts and test/spawn-chain.test.ts also pass, and tsc --noEmit is clean. The branch already matches origin, so there was nothing new to push. The next check will show whether that commit cleared it.

The check log also shows shard 6 exiting with code 143 (a kill signal) without naming a failing test. I did not look into it. If it recurs, it is a separate problem from this test.

Left out: nothing beyond what the pull already describes.

To try it: run bun test test/following.test.ts test/spawn-take.test.ts.

Approved — before the latest push

Does what the issue asks and nothing more. Fast-forward uses a compare-and-swap update-ref, a clean merge goes through the existing merge module, and a conflict leaves main alone and reuses one open issue. It never deploys. The take is triggered only from the hourly or writer-gated check, so no new write door appears. The chain line reuses upstreamWords, so the terminal Chain command shows the result like the page. The README is updated, there is no new dependency, and the tests cover each outcome. The one test edit (the data-live string) was stale, as the pull says. I did not exercise the preview beyond reading the diff.

Two things I would do next:

  1. Closing the conflict issue without settling it makes the next hourly check open a fresh one, because tell() only looks for an open issue. Remember the last conflicting parentTip in upstream.json and open a new issue only when the parent moved, so the owner is not nagged every hour.
  2. Add tests for the non-happy paths: readTaken on a malformed upstream.json, and the conflict wording in upstreamWords. Also drop the stray blank line before the closing of the describe block in test/spawn-take.test.ts.
commented

The work from my last run is unchanged, and nothing on the issue or on main has changed since. The branch is already pushed and in sync with the remote, and I made no changes this run.

What changed: A forge in take-updates mode now takes its parent's new code at the hourly check. src/spawn/take.ts is new, and checkUpstream in src/spawn/upstream.ts calls it once the fetch has put the parent's tip at refs/upstream/HEAD.

  • When the parent has only commits the forge lacks, main is fast-forwarded with a compare-and-swap update-ref.
  • When both sides have commits and git merges them without conflict, main gets a merge commit.
  • Each take emits a push event, as the in-forge takeUpdates does, and a forge.updates_taken event with {from, to, commits, merged}.
  • upstream.json keeps a taken field, and the chain line shows it ("took 2 new commits from your parent in a merge", or "conflicts in a.txt; see issue #N").
  • On a conflict, main is untouched. One open issue, "Take updates from <parent>", names the files, and later checks find it instead of opening another.
  • It never deploys. Production follows main as before.

What to look at: test/spawn-take.test.ts uses local bare repositories. It covers tell-me leaving main alone, a fast-forward, a clean merge, and a conflict that leaves main untouched and opens one issue across two checks. bun x tsc --noEmit is clean. The spawn*, mcp-upstream, upstream-forge and following tests passed, apart from the stale data-live expectation in test/following.test.ts. I updated that expectation for the new forge.updates_taken event, and the updated test passes.

What I chose not to do:

  • The conflict issue is not handed to the crew. The existing converge flow needs a parent repository row on the same forge, and a parent forge has none, so the issue is for a person to settle.
  • taken describes only the latest check, so a later no-op check replaces it. The events are the permanent record.
commented

Nothing has changed since my last run. The issue has no comments, main is still at the same commit, and the branch matches the remote. I made no changes and pushed nothing.

What changed: a forge in take-updates mode now takes its parent's new code at the hourly check, in src/spawn/take.ts, called from checkUpstream.

  • When the parent has only commits the forge lacks, main is fast-forwarded with a compare-and-swap update-ref.
  • When both sides have commits and git merges them cleanly, main gets a merge commit.
  • Each take emits a push event and a forge.updates_taken event with {from, to, commits, merged}. It never deploys; production follows main as before.
  • upstream.json keeps a taken field, and the chain line shows it ("took 2 new commits from your parent in a merge", or "conflicts in a.txt; see issue #N"). The page's live region now also listens for forge.updates_taken.
  • On a conflict, main is untouched. One open issue, "Take updates from <parent>", names the files, and later checks find that issue instead of opening another.

What to look at: test/spawn-take.test.ts uses local bare repositories. It covers tell-me leaving main alone, a fast-forward, a clean merge, and a conflict that leaves main untouched and opens one issue across two checks. bun x tsc --noEmit and the chain, following and upstream tests passed on my last run. I updated one stale data-live expectation in test/following.test.ts for the new event name.

What I chose not to do:

  • The conflict issue is not handed to the crew. The existing converge flow needs a parent repository row on the same forge, and a parent forge has none.
  • taken describes only the latest check, so a later no-op check replaces it. The events are the permanent record.
commented

Nothing has changed since my last run. The issue has no comments, main is still at 6d4548b7, and the branch matches the remote. I made no changes and pushed nothing.

What changed: a forge in take-updates mode now takes its parent's new code at the hourly check, in src/spawn/take.ts, called from checkUpstream.

  • When the parent has only commits the forge lacks, main is fast-forwarded with a compare-and-swap update-ref.
  • When both sides have commits and git merges them cleanly, main gets a merge commit.
  • Each take emits a push event and a forge.updates_taken event with {from, to, commits, merged}. It never deploys; production follows main as before.
  • upstream.json keeps a taken field, and the chain line shows it ("took 2 new commits from your parent in a merge", or "conflicts in a.txt; see issue #N"). The page's live region now also listens for forge.updates_taken.
  • On a conflict, main is untouched. One open issue, "Take updates from <parent>", names the files, and later checks find that issue instead of opening another.

What to look at: test/spawn-take.test.ts uses local bare repositories. It covers tell-me leaving main alone, a fast-forward, a clean merge, and a conflict that leaves main untouched and opens one issue across two checks. bun x tsc --noEmit and the chain, following and upstream tests passed on my first run. I updated one stale data-live expectation in test/following.test.ts for the new event name.

What I chose not to do:

  • The conflict issue is not handed to the crew. The existing converge flow needs a parent repository row on the same forge, and a parent forge has none.
  • taken describes only the latest check, so a later no-op check replaces it. The events are the permanent record.
commented

Nothing has changed since my last run. The issue has no comments, main is still at 6d4548b7, and the branch matches the remote. I made no changes and pushed nothing.

What changed: a forge in take-updates mode now takes its parent's new code at the hourly check, in src/spawn/take.ts, called from checkUpstream.

  • When the parent has only commits the forge lacks, main is fast-forwarded with a compare-and-swap update-ref.
  • When both sides have commits and git merges them cleanly, main gets a merge commit.
  • Each take emits a push event and a forge.updates_taken event with {from, to, commits, merged}. It never deploys; production follows main as before.
  • upstream.json keeps a taken field, and the chain line shows it ("took 2 new commits from your parent in a merge", or "conflicts in a.txt; see issue #N"). The page's live region now also listens for forge.updates_taken.
  • On a conflict, main is untouched. One open issue, "Take updates from <parent>", names the files, and later checks find that issue instead of opening another.

What to look at: test/spawn-take.test.ts uses local bare repositories. It covers tell-me leaving main alone, a fast-forward, a clean merge, and a conflict that leaves main untouched and opens one issue across two checks. bun x tsc --noEmit and the chain, following and upstream tests passed on my first run. I updated one stale data-live expectation in test/following.test.ts for the new event name.

What I chose not to do:

  • The conflict issue is not handed to the crew. The existing converge flow needs a parent repository row on the same forge, and a parent forge has none.
  • taken describes only the latest check, so a later no-op check replaces it. The events are the permanent record.
commented

Nothing has changed since my last run. The issue has no comments, main is still at 6d4548b7, and the branch matches the remote. I made no changes and pushed nothing.

What changed: a forge in take-updates mode now takes its parent's new code at the hourly check, in src/spawn/take.ts, called from checkUpstream.

  • When the parent has only commits the forge lacks, main is fast-forwarded with a compare-and-swap update-ref.
  • When both sides have commits and git merges them cleanly, main gets a merge commit.
  • Each take emits a push event and a forge.updates_taken event with {from, to, commits, merged}. It never deploys; production follows main as before.
  • upstream.json keeps a taken field, and the chain line shows it ("took 2 new commits from your parent in a merge", or "conflicts in a.txt; see issue #N"). The page's live region now also listens for forge.updates_taken.
  • On a conflict, main is untouched. One open issue, "Take updates from <parent>", names the files, and later checks find that issue instead of opening another.

What to look at: test/spawn-take.test.ts uses local bare repositories. It covers tell-me leaving main alone, a fast-forward, a clean merge, and a conflict that leaves main untouched and opens one issue across two checks. bun x tsc --noEmit and the chain, following and upstream tests passed on my first run. I updated one stale data-live expectation in test/following.test.ts for the new event name.

What I chose not to do:

  • The conflict issue is not handed to the crew. The existing converge flow needs a parent repository row on the same forge, and a parent forge has none.
  • taken describes only the latest check, so a later no-op check replaces it. The events are the permanent record.
commented

A forge in take-updates mode now merges its parent's new code when it applies cleanly, and opens one issue naming the files when it does not (#2634). I merged main into the branch and pushed.

I ran tsc and the four tests that cover this: test/spawn-take.test.ts, test/spawn-upstream.test.ts, test/spawn-chain.test.ts and test/following.test.ts. They pass (48 tests), but I did not run the whole suite.

main brought in a tell-me inbox card, and its changes to README.md and src/spawn/upstream.ts collided with this pull's. I kept both sides in each file.

  • State: UpstreamState keeps both taken (what take-updates did at a check) and toldTip (the parent tip the owner was last told about). upstreamState() reads both back.
  • Order in run(): after the fetch, a take-updates forge takes the parent's commits first and recomputes behind. The tell-me card and the single keep call come after that. State is therefore written once per check.
  • README: the take-updates merge text and the tell-me card text now sit in one paragraph.

It never deploys. It records what it took as taken in upstream.json and as a forge.updates_taken event. The chain page's live region listens for that event.

To try it, run bun test test/spawn-take.test.ts test/spawn-upstream.test.ts.

Approved

Does what the issue asks: in take-updates the hourly check fast-forwards (compare-and-swap update-ref) or cleanly merges refs/upstream/HEAD into main, and on conflict leaves main alone and opens one issue naming the files. It never deploys, adds no dependency, builds no shell from user text, and the chain line, README and live region (forge.updates_taken) are updated. Tests cover tell-me, fast-forward, clean merge and conflict (one issue across two checks), and the stale data-live expectation was a legitimately stale test. The chain line text is shared with the terminal Chain command, so both doors say the same thing.

Two things I would do next:

  1. Unrelated histories (mergeBase undefined) and a failed merge/update-ref return silently; record a one-line error in upstream.json so the chain line says why nothing was taken instead of showing "your parent has N new commits" every hour.
  2. taken is overwritten by the next no-op check, so a conflict note disappears from the chain line while the issue is still open; keep the conflict until the issue closes or the merge succeeds. Also drop the stray blank line before the closing }); in test/spawn-take.test.ts.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs