toga-ai 1.0.272 → 1.0.273

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
5
  | [Worker (worker2) Architecture](architecture.md) | Worker (repo `worker2`) is an AWS Elastic Beanstalk **Worker Tier** application that processes background jobs. | worker2/Controller/Index.php, worker2/Worker/, worker2/LambdaFunctions/, _underscore/Worker.php |
6
+ | [Automated PR Merger — Concurrent Force-Push Clobber Race](features/automated-pr-merger-force-push-race.md) | The automated PR merger `_Worker_Team_GitHub::Merge` (`worker2` `Worker/Team/Github.php`) merges approved PRs to `_production` by **force-pushing from a clone t | Worker/Team/Github.php |
6
7
  | [ClickUp Connectivity Watchdog](features/clickup-connectivity-watchdog.md) | A cron watchdog that emails when the ClickUp integration looks disconnected during business hours. | worker2/Worker/Clickup/Health.php, worker2/Database/ClickupHealthWatchdog.sql |
7
8
  | [ClickUp Design Sprint Automation (Final Design Outcome)](features/clickup-design-sprint-automation.md) | `_Worker_Clickup_Design` is meant to drive the design-sprint workflow in ClickUp via the API, replacing a set of native ClickUp automations. | worker2/Worker/Clickup/Design.php, worker2/Worker/Clickup.php |
8
9
  | [ClickUp GitHub-tab Auto-linking & Ticket-id Branch Naming](features/clickup-github-autolink.md) | How ClickUp surfaces branches/PRs/commits in a ticket's **GitHub tab**, and the branch / PR-title naming convention that triggers it. | |
@@ -0,0 +1,71 @@
1
+ ---
2
+ title: Automated PR Merger — Concurrent Force-Push Clobber Race
3
+ framework: "2.0"
4
+ repo: worker2
5
+ project: Worker
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-07-06
10
+ owners: [dfranks]
11
+ files:
12
+ - Worker/Team/Github.php
13
+ related:
14
+ - ./clickup-github-autolink.md
15
+ ---
16
+
17
+ ## Summary
18
+
19
+ The automated PR merger `_Worker_Team_GitHub::Merge` (`worker2` `Worker/Team/Github.php`)
20
+ merges approved PRs to `_production` by **force-pushing from a clone taken before the read,
21
+ without rebasing**. When two merges run concurrently, the second force-push silently
22
+ overwrites the first merge's commit, orphaning it. This is a silent data-loss failure mode
23
+ in shared deploy tooling — it affects **every repo** merged through the automated merger,
24
+ not just one file.
25
+
26
+ ## How it works (the failure mode)
27
+
28
+ - The merger clones/reads `_production`, applies the merge, then `git push --force`.
29
+ - Because the push is a `--force` from a stale clone with no rebase, a merge that landed
30
+ in the window between the read and the push is **overwritten** — its commit is no longer
31
+ an ancestor of `_production`.
32
+ - GitHub still reports the clobbered PR as **MERGED** (the merge happened; it was just
33
+ force-pushed away afterward), so **no error surfaces**. The changed files never reach the
34
+ deployed bundle.
35
+ - Real occurrence: a merged `worker2` webhook handler file was orphaned ~40s later by a
36
+ concurrent automated merge — MERGED on GitHub, absent from `_production`.
37
+
38
+ ## Diagnostic — auditing for clobbered PRs
39
+
40
+ For every PR GitHub reports as MERGED into `_production`, test whether its merge commit is
41
+ still an ancestor of the branch:
42
+
43
+ ```
44
+ gh pr list --repo <repo> --base _production --state merged --json number,mergeCommit
45
+ git merge-base --is-ancestor <mergeCommitSHA> origin/_production # exit 0 = ancestor (fine)
46
+ ```
47
+
48
+ If `--is-ancestor` is **false**, the PR was clobbered (orphaned). Then **confirm real loss**:
49
+ check whether the PR's changed files actually exist on `_production` today. A stale
50
+ force-push artifact can be non-ancestor yet have had its content re-applied by a later
51
+ merge — that case is benign.
52
+
53
+ ## Fix direction (do NOT just add a rebase)
54
+
55
+ Rebasing alone is **insufficient**: it only narrows the check-then-act window. A `--force`
56
+ push after a rebase can still clobber a merge that lands between the rebase and the push.
57
+
58
+ Durable fix:
59
+ - **Drop `--force`** — use a plain `push` (or `--force-with-lease`) so a stale push is
60
+ **rejected as non-fast-forward** instead of silently overwriting.
61
+ - Add a **retry loop** (re-fetch → re-rebase → retry) on rejection, **or serialize merges
62
+ behind a lock**.
63
+ - Rebase makes the non-force push succeed on the happy path; **dropping `--force` is what
64
+ makes it safe.**
65
+
66
+ ## Change history
67
+
68
+ - 2026-07-06 — Initial doc: documented the `_Worker_Team_GitHub::Merge` concurrent
69
+ force-push clobber race (MERGED-on-GitHub-but-orphaned, silent), the `git merge-base
70
+ --is-ancestor` audit procedure with the benign-artifact caveat, and the fix direction
71
+ (drop `--force`/use `--force-with-lease` + retry/lock, not rebase alone) (dfranks)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.272",
3
+ "version": "1.0.273",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",