docs(spec): per-list base branch and fetch before unit branch
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Unit-Branch + Draft-PR — Design
|
# Unit-Branch + Draft-PR — Design
|
||||||
|
|
||||||
**Datum:** 2026-10-01 · **Status:** Entwurf, Review durch Mika ausstehend
|
**Datum:** 2026-10-01 · **Status:** abgenommen (mit Basis-Branch pro Liste + Fetch)
|
||||||
**Diagramme:** Obsidian `07 - Plattform & Technik/ClaudeDo/ClaudeDo - Branch und PR Ablauf.md`
|
**Diagramme:** Obsidian `07 - Plattform & Technik/ClaudeDo/ClaudeDo - Branch und PR Ablauf.md`
|
||||||
**Ersetzt:** die offene Entscheidung aus `2026-08-06-planning-chain-fork-base-design.md`
|
**Ersetzt:** die offene Entscheidung aus `2026-08-06-planning-chain-fork-base-design.md`
|
||||||
|
|
||||||
@@ -33,6 +33,8 @@ Vorgängers.
|
|||||||
| PR-Automatik | Kein Auto-PR bei einem Statuswechsel. Automatisiert wird über den Merge-Helper, dessen `approve` dann den PR erzeugt. |
|
| PR-Automatik | Kein Auto-PR bei einem Statuswechsel. Automatisiert wird über den Merge-Helper, dessen `approve` dann den PR erzeugt. |
|
||||||
| ADO-Zugang | Eigener PAT (Scope Code Read & Write). Er wird per `DpapiTokenStore` in `~/.claudeDo/azdo.pat` gespeichert, ohne Abhängigkeit auf das work-conventions-Plugin. |
|
| ADO-Zugang | Eigener PAT (Scope Code Read & Write). Er wird per `DpapiTokenStore` in `~/.claudeDo/azdo.pat` gespeichert, ohne Abhängigkeit auf das work-conventions-Plugin. |
|
||||||
| dependsOn über Units | Wartet bewusst auf den echten Merge. Abhängige Arbeit gehört in eine Unit mit einem PR. |
|
| dependsOn über Units | Wartet bewusst auf den echten Merge. Abhängige Arbeit gehört in eine Unit mit einem PR. |
|
||||||
|
| Basis-Branch | Pro Liste einstellbar (`list_config.base_branch`, z.B. `dev` für die LagerApp). Leer heißt: privat der gerade ausgecheckte Branch des Main-Checkouts (wie heute), Firma der Default-Branch des ADO-Repos. Gilt als Basis des Unit-Branches, als PR-Ziel und als Vorauswahl des privaten Merge-Ziels. |
|
||||||
|
| Basis aktualisieren | Firmen-Repo: `git fetch origin <basis>`, bevor der Unit-Branch angelegt wird. Der Unit-Branch startet von `origin/<basis>`, der lokale Basis-Branch wird best-effort per fast-forward nachgezogen. |
|
||||||
|
|
||||||
## 1. Branch-Naming (`WorktreeManager`)
|
## 1. Branch-Naming (`WorktreeManager`)
|
||||||
|
|
||||||
@@ -69,6 +71,37 @@ Vorgängers.
|
|||||||
**Basis eines Childs** (`ResolveBaseCommitAsync`): Das ist immer der aktuelle Head des
|
**Basis eines Childs** (`ResolveBaseCommitAsync`): Das ist immer der aktuelle Head des
|
||||||
Unit-Branches (`rev-parse <unitBranch>`). Der Sonderfall „nur bei `PlanningPhase.None`“ fällt weg.
|
Unit-Branches (`rev-parse <unitBranch>`). Der Sonderfall „nur bei `PlanningPhase.None`“ fällt weg.
|
||||||
|
|
||||||
|
**Basis eines Unit-Roots** (Einzeltask, Parent):
|
||||||
|
- **Privat:** `list_config.base_branch`, sonst der ausgecheckte Branch des Main-Checkouts. Heute
|
||||||
|
ist es `RevParseHeadAsync`. Ist `base_branch` gesetzt, wird es `rev-parse <base_branch>`, auch
|
||||||
|
wenn gerade ein anderer Branch ausgecheckt ist.
|
||||||
|
- **Firma:**
|
||||||
|
- Zuerst `git fetch origin <basis>`. `<basis>` ist `base_branch`, sonst der Default-Branch des
|
||||||
|
Repos per ADO-API, ersatzweise `git symbolic-ref refs/remotes/origin/HEAD`.
|
||||||
|
- Der Unit-Branch startet von `origin/<basis>`.
|
||||||
|
- Dann wird der lokale `<basis>` best-effort nachgezogen:
|
||||||
|
- nicht ausgecheckt: `git fetch origin <basis>:<basis>` (lehnt einen nicht-fast-forward
|
||||||
|
selbst ab)
|
||||||
|
- ausgecheckt und sauber: `git -C <workingDir> merge --ff-only origin/<basis>`
|
||||||
|
- schmutzig oder divergiert: Warnung ins Log, kein Eingriff in den Checkout
|
||||||
|
- Schlägt der Fetch fehl (offline, keine Credentials), wird gewarnt und es geht mit dem
|
||||||
|
lokalen `<basis>` weiter. Der Task scheitert daran nicht.
|
||||||
|
- Der Fetch nutzt dieselbe PAT-Übergabe wie der Push (Abschnitt 3).
|
||||||
|
- Der Fetch läuft einmal pro Unit-Root, nicht pro Child. Children starten vom Unit-Branch.
|
||||||
|
|
||||||
|
**Spalte `list_config.base_branch`** (nullable string, Migration):
|
||||||
|
- Sie wird im Listeneinstellungs-Modal gepflegt, als Freitext mit Vorschlägen aus
|
||||||
|
`ListLocalBranchesAsync`, und in `get_list_config` mitgeliefert.
|
||||||
|
- ⚠️ `ListRepository.SetConfigAsync` kopiert verbatim (siehe `TicketProjectId`). Jeder Schreiber
|
||||||
|
muss den Wert durchreichen, sonst setzt ihn z.B. `set_list_config` still zurück:
|
||||||
|
`WorkerHub.UpdateListConfig`, `ConfigMcpTools`, alle Stellen, die eine frische
|
||||||
|
`ListConfigEntity` bauen.
|
||||||
|
- `UpdateListConfigDto` braucht ihn tri-state wie `TicketProjectId`, damit ein Aufrufer, der das
|
||||||
|
Feld nicht kennt, es nicht löscht.
|
||||||
|
- **Merge-Ziel privat:** `TaskMergeService.GetTargetsAsync` setzt `DefaultBranch` auf
|
||||||
|
`base_branch`, falls gesetzt. Das leere Ziel (`ApproveAndMergeAsync:1077`, `PreviewAsync:910`)
|
||||||
|
löst ebenfalls auf `base_branch` auf, sonst wie heute.
|
||||||
|
|
||||||
**Absorbieren eines Childs:** Neu ist `UnitBranchService.AbsorbChildAsync(childId)`, das in
|
**Absorbieren eines Childs:** Neu ist `UnitBranchService.AbsorbChildAsync(childId)`, das in
|
||||||
`git -C <parentWorktree> merge --ff-only <childBranch>` mündet.
|
`git -C <parentWorktree> merge --ff-only <childBranch>` mündet.
|
||||||
- Die Kette ist seriell (`BlockedByTaskId`), und das Child startet vom Head des Unit-Branches.
|
- Die Kette ist seriell (`BlockedByTaskId`), und das Child startet vom Head des Unit-Branches.
|
||||||
@@ -119,8 +152,8 @@ einem Folge-Task, sobald keine solchen Units mehr offen sind.
|
|||||||
- `GET …/pullrequests?searchCriteria.sourceRefName=refs/heads/<branch>&searchCriteria.status=active`.
|
- `GET …/pullrequests?searchCriteria.sourceRefName=refs/heads/<branch>&searchCriteria.status=active`.
|
||||||
Gibt es schon einen PR, wird er wiederverwendet (idempotent, auch wenn der Skill in der
|
Gibt es schon einen PR, wird er wiederverwendet (idempotent, auch wenn der Skill in der
|
||||||
Session schon einen angelegt hat).
|
Session schon einen angelegt hat).
|
||||||
- Sonst `POST …/pullrequests` mit `isDraft: true` und
|
- Sonst `POST …/pullrequests` mit `isDraft: true` und `targetRefName = <basis>`. Das ist
|
||||||
`targetRefName = <defaultBranch des Repos>` (`GET …/repositories/<repo>`).
|
`list_config.base_branch`, sonst `defaultBranch` aus `GET …/repositories/<repo>`.
|
||||||
- Die API-Version fällt auf die zurück, die der Server in seiner Fehlermeldung nennt, wie im
|
- Die API-Version fällt auf die zurück, die der Server in seiner Fehlermeldung nennt, wie im
|
||||||
Skript.
|
Skript.
|
||||||
4. Die **PR-URL** wird gespeichert, in der neuen nullable Spalte `worktrees.pull_request_url`
|
4. Die **PR-URL** wird gespeichert, in der neuen nullable Spalte `worktrees.pull_request_url`
|
||||||
@@ -181,8 +214,7 @@ Die Prompts in `PromptFiles` (merge und merge_final) bekommen einen Firmen-Repo-
|
|||||||
PR-API ist ADO-spezifisch.
|
PR-API ist ADO-spezifisch.
|
||||||
- Aufräumen von Worktree und lokalem Branch, wenn ein Firmen-Task auf `Done` gesetzt wird. Das
|
- Aufräumen von Worktree und lokalem Branch, wenn ein Firmen-Task auf `Done` gesetzt wird. Das
|
||||||
geht über die bestehende Worktree-Übersicht.
|
geht über die bestehende Worktree-Übersicht.
|
||||||
- `git fetch` vor dem Anlegen des Unit-Branches. Die Basis ist der lokale `main`-HEAD wie heute,
|
- `git fetch` in privaten Repos. Die Basis bleibt dort lokal.
|
||||||
siehe offene Frage.
|
|
||||||
- Eine UI für `company_remote_pattern`.
|
- Eine UI für `company_remote_pattern`.
|
||||||
|
|
||||||
## 7. Tests
|
## 7. Tests
|
||||||
@@ -204,12 +236,14 @@ Keine Tests mit der echten Claude-CLI und keine gegen das echte ADO.
|
|||||||
- `AzureDevOpsClient` wird gefakt: PR anlegen, vorhandenen PR wiederverwenden, Verify rot
|
- `AzureDevOpsClient` wird gefakt: PR anlegen, vorhandenen PR wiederverwenden, Verify rot
|
||||||
heißt kein Push, PR-Fehler nach dem Push.
|
heißt kein Push, PR-Fehler nach dem Push.
|
||||||
- **Remote-Parsing:** die Fälle aus dem `-SelfTest` des Skripts.
|
- **Remote-Parsing:** die Fälle aus dem `-SelfTest` des Skripts.
|
||||||
|
- **Basis:**
|
||||||
|
- `base_branch` gesetzt: Der Unit-Root startet davon, auch wenn ein anderer Branch ausgecheckt
|
||||||
|
ist.
|
||||||
|
- Firma: Nach dem Fetch startet der Unit-Root von `origin/<basis>`. Das testet ein Bare-Repo,
|
||||||
|
das einen Commit voraus ist.
|
||||||
|
- Der lokale Basis-Branch wird nachgezogen, ein schmutziger Checkout bleibt unangetastet.
|
||||||
|
- Ein Fetch-Fehler bricht den Task nicht.
|
||||||
|
- `SetConfigAsync`, `set_list_config` und `UpdateListConfig` erhalten `base_branch`.
|
||||||
- **Manueller Smoke-Test (Mika):** PAT eintragen, Test-Button. Ein Ticket-Task in einem echten
|
- **Manueller Smoke-Test (Mika):** PAT eintragen, Test-Button. Ein Ticket-Task in einem echten
|
||||||
Firmen-Repo bis zum Draft-PR durchspielen, danach eine Planning-Unit mit 2 Children.
|
Firmen-Repo bis zum Draft-PR durchspielen, danach eine Planning-Unit mit 2 Children.
|
||||||
|
|
||||||
## Offene Frage
|
|
||||||
|
|
||||||
- **Basis im Firmen-Repo:** Der lokale `main` ist in Firmen-Repos oft veraltet. Vorschlag für
|
|
||||||
später: `git fetch origin <default>` und dann `origin/<default>` als Basis des Unit-Branches.
|
|
||||||
In v1 ist das draußen. Wenn es dich jetzt schon stört, ist es ein kleiner Zusatz in
|
|
||||||
`ResolveBaseCommitAsync`.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user