xtrm-tools 0.5.47 → 0.6.0

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.
Files changed (31) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/cli/dist/index.cjs +6218 -7897
  3. package/cli/dist/index.cjs.map +1 -1
  4. package/cli/package.json +6 -1
  5. package/config/pi/extensions/beads/index.ts +1 -1
  6. package/config/pi/extensions/beads/package.json +4 -1
  7. package/config/pi/extensions/core/package.json +18 -0
  8. package/config/pi/extensions/custom-footer/index.ts +138 -71
  9. package/config/pi/extensions/custom-footer/package.json +4 -1
  10. package/config/pi/extensions/quality-gates/index.ts +1 -1
  11. package/config/pi/extensions/quality-gates/package.json +4 -1
  12. package/config/pi/extensions/service-skills/index.ts +1 -1
  13. package/config/pi/extensions/service-skills/package.json +4 -1
  14. package/config/pi/extensions/session-flow/index.ts +1 -1
  15. package/config/pi/extensions/session-flow/package.json +4 -1
  16. package/config/pi/extensions/xtrm-loader/index.ts +1 -1
  17. package/config/pi/extensions/xtrm-loader/package.json +4 -1
  18. package/hooks/beads-compact-restore.mjs +11 -3
  19. package/hooks/beads-compact-save.mjs +13 -1
  20. package/hooks/tsconfig-cache.json +12 -2
  21. package/package.json +3 -2
  22. package/plugins/xtrm-tools/.claude-plugin/plugin.json +1 -1
  23. package/plugins/xtrm-tools/hooks/beads-compact-restore.mjs +11 -3
  24. package/plugins/xtrm-tools/hooks/beads-compact-save.mjs +13 -1
  25. package/plugins/xtrm-tools/hooks/tsconfig-cache.json +12 -2
  26. package/plugins/xtrm-tools/skills/test-planning/SKILL.md +257 -0
  27. package/plugins/xtrm-tools/skills/xt-end/SKILL.md +1 -0
  28. package/plugins/xtrm-tools/skills/xt-merge/SKILL.md +141 -18
  29. package/skills/test-planning/SKILL.md +257 -0
  30. package/skills/xt-end/SKILL.md +1 -0
  31. package/skills/xt-merge/SKILL.md +141 -18
@@ -206,3 +206,260 @@ Agent closes a feature issue that was done ad-hoc. No test issue found. Agent:
206
206
  2. Picks strategy
207
207
  3. Creates test issue as child of same parent
208
208
  4. Documents what to assert based on the actual code
209
+
210
+ ## Anti-Pattern Checklist
211
+
212
+ Run this checklist at both trigger points (planning and closure review). Flag any anti-patterns in the test issue description before closing.
213
+
214
+ ### 1. Assertion-free tests
215
+ **Detect**: Test body calls functions/methods but has no `assert`, `expect`, or equivalent statement.
216
+ **Fix**: Add at least one meaningful assertion. If the goal is "doesn't throw", assert that explicitly — `with pytest.raises(...)` or `expect(() => fn()).not.toThrow()`.
217
+
218
+ ### 2. Tautological assertions
219
+ **Detect**: The assertion can only fail if the test framework itself is broken. E.g. `assert result == result`, `expect(true).toBe(true)`, asserting a value against the same expression used to produce it.
220
+ **Fix**: Assert against a concrete expected value derived independently from the production code. If you can't state what the expected value is without running the code, the test has no falsifiable claim.
221
+
222
+ ### 3. Context leakage / shared mutable state
223
+ **Detect**: Tests share module-level variables, database rows, file state, or global config without reset between runs. Symptoms: tests pass individually but fail in suite order.
224
+ **Fix**: Use fixtures with setup/teardown (`beforeEach`/`afterEach`, pytest fixtures with function scope). Every test starts from a clean slate.
225
+
226
+ ### 4. Over-mocking internal collaborators
227
+ **Detect**: Mocks are patching classes or functions that live in the same module under test — not external services. The test validates that internal wiring was called, not that the observable outcome is correct.
228
+ **Fix**: Only mock at system boundaries (HTTP clients, file I/O, external services). Test internal collaborators by letting them run. If they're hard to instantiate, extract the pure logic and test that directly.
229
+
230
+ ### 5. Tests that cannot fail under realistic regressions
231
+ **Detect**: Remove the core logic being tested and re-read the test — would it still pass? If yes, the test provides no protection. Common form: only testing the happy path of a function whose bug would only appear in error paths.
232
+ **Fix**: Add at least one negative-path or edge-case assertion that would catch the most likely regression. Consult the implementation for obvious failure modes.
233
+
234
+ ## Priority Heuristics
235
+
236
+ Test issues inherit priority from their implementation issues with bounded adjustment. The table below gives the deterministic mapping.
237
+
238
+ | Implementation risk | Test issue priority | Examples |
239
+ |---|---|---|
240
+ | Security / auth / protocol compat | P0 (equal) | Auth token validation, schema migration safety, API contract |
241
+ | Regression-critical boundary path | P0–P1 (equal) | Client URL routing, CLI exit codes used by external tooling |
242
+ | High-business-impact core logic | P1 (equal or +0) | Pricing computations, session state transitions |
243
+ | Standard domain logic | P2 (+0 or +1) | Config merge, output formatters, parsers |
244
+ | Low-risk internals / non-critical adapters | P3 (+1) | Helper utilities, optional UI formatting |
245
+ | Polish / test debt cleanup | P4 | Improving existing test coverage, test naming |
246
+
247
+ **Inheritance rule**: start from the implementation issue's priority. Apply +1 if the test is covering a well-understood path with low regression risk. Never go lower than P2 for boundary or shell layer tests — integration tests are load-bearing.
248
+
249
+ **Equal priority examples**:
250
+ - Impl is P1 (auth endpoint) → test issue is P1 (auth contract test must ship with the feature)
251
+ - Impl is P0 (critical fix) → test issue is P0 (regression test must land in same PR)
252
+
253
+ **+1 priority examples**:
254
+ - Impl is P2 (output formatter) → test issue is P3 (unit tests are useful but not blocking)
255
+ - Impl is P3 (optional config key) → test issue is P4 (test debt, tackle in cleanup)
256
+
257
+ ## Definition of Done Templates
258
+
259
+ Use these templates verbatim in test issue descriptions. Replace `<...>` placeholders.
260
+
261
+ ### Core layer DoD
262
+
263
+ ```
264
+ Layer: core
265
+ Strategy: <unit | property-based | example-based>
266
+ Covers: <impl issue IDs>
267
+
268
+ Assertions required:
269
+ - [ ] Positive path: <expected output for valid input>
270
+ - [ ] Negative path: <expected error/output for invalid input>
271
+ - [ ] Edge cases explicitly enumerated: <list: empty input, zero, max boundary, ...>
272
+ - [ ] Invariants/properties included: <e.g. "result is always sorted", "output length == input length">
273
+
274
+ Fixture policy:
275
+ - [ ] No shared mutable state between tests
276
+ - [ ] Deterministic fixtures (no random, no time.now() without injection)
277
+ - [ ] Each test constructs its own input independently
278
+
279
+ Done when: all assertions above are implemented and passing in CI.
280
+ ```
281
+
282
+ ### Boundary layer DoD
283
+
284
+ ```
285
+ Layer: boundary
286
+ Strategy: <live-contract | recorded-fixture | mock (last resort)>
287
+ Covers: <impl issue IDs>
288
+
289
+ Assertions required:
290
+ - [ ] Schema/contract assertions: <field presence, types, required vs optional>
291
+ - [ ] Error codes and retry/fallback: <e.g. 404→empty list, 500→raises ServiceError>
292
+ - [ ] Drift-safe: assertions check field presence and types, not brittle internal structure
293
+ - [ ] Live-first policy documented: <live | recorded-fixture | mock — reason for choice>
294
+
295
+ Done when: contract assertions pass against live service (or recorded fixture if live unavailable).
296
+ Fallback documented in issue if live is not accessible.
297
+ ```
298
+
299
+ ### Shell layer DoD
300
+
301
+ ```
302
+ Layer: shell
303
+ Strategy: integration (subprocess or function-level wiring test)
304
+ Covers: <impl issue IDs>
305
+
306
+ Assertions required:
307
+ - [ ] End-to-end observable outcomes: <what the user sees — output format, exit code>
308
+ - [ ] Failure-mode UX: <error messages, non-zero exit codes, stderr vs stdout>
309
+ - [ ] Cross-component wiring: <core + boundary are called and integrated correctly>
310
+ - [ ] At least one real-data scenario (not mocked) if service is accessible
311
+
312
+ Done when: integration tests run against real components (not mocked internals) and cover
313
+ both success and at least one failure path.
314
+ ```
315
+
316
+ ## Critical-Path Coverage
317
+
318
+ Do not frame test issues around coverage percentages. Frame them around critical paths and risk rationale.
319
+
320
+ Every test issue description must include a **critical path map**:
321
+
322
+ ```
323
+ Critical paths covered:
324
+ - <path 1 and risk rationale>
325
+ - <path 2 and risk rationale>
326
+
327
+ Known deferred paths (with follow-up refs):
328
+ - <path not covered yet> → follow-up: <bd issue ID or "to be created">
329
+ ```
330
+
331
+ **Why**: a 90% line-coverage number says nothing about whether the one path that processes payments is tested. A critical path map forces explicit reasoning about what matters and what was skipped.
332
+
333
+ **What counts as a critical path**:
334
+ - Any path that involves auth, money, data loss, or external contract compliance
335
+ - Any path exercised by the user-facing CLI commands described in the issue
336
+ - Any path explicitly mentioned in the implementation issue's acceptance criteria
337
+
338
+ **What to do with deferred paths**:
339
+ - Document them — don't silently skip
340
+ - Create a follow-up test issue if the deferred path is P2 or higher risk
341
+ - Reference the follow-up issue ID in the current test issue's description
342
+
343
+ ## Advisory vs Enforcement Boundary
344
+
345
+ This skill is advisory. It recommends test strategy, creates test issues, and flags anti-patterns. It does not block code execution or enforce pass/fail decisions — that is the job of hooks and quality gates.
346
+
347
+ | Concern | Who owns it | How enforced |
348
+ |---|---|---|
349
+ | Test strategy selection (TDD vs contract vs unit) | This skill | Recommendation only |
350
+ | Anti-pattern detection in test issues | This skill | Checklist in issue description |
351
+ | Priority assignment | This skill | Heuristics table above |
352
+ | DoD template in issue description | This skill | Template pasted into bd issue |
353
+ | CI test pass/fail | quality-gates hook | PostToolUse hook blocks on test failures |
354
+ | Test file lint/type correctness | quality-gates hook | ESLint + mypy on every edit |
355
+ | Branch not mergeable without tests | Not enforced | Human review — no automated gate today |
356
+ | Claiming work without test issue existing | Not enforced | Human judgment — skill creates test issue at closure if missing |
357
+
358
+ **Example — advisory boundary in practice**:
359
+
360
+ You are planning tests for `.14` (async HTTP client). This skill:
361
+ - Classifies as boundary layer ✓
362
+ - Recommends live-contract tests ✓
363
+ - Creates a test issue with DoD template ✓
364
+ - Flags if you try to describe tests that only mock the HTTP layer ✓ (anti-pattern 4)
365
+
366
+ It does NOT:
367
+ - Block `.14` from closing if the test issue isn't done
368
+ - Fail the build if the test issue is open
369
+ - Require approval before the implementation is merged
370
+
371
+ The test issue is a tracked commitment, not a gate. Gating is opt-in via `bd dep` dependencies you set up during planning.
372
+
373
+ ## v1.1 Format Examples
374
+
375
+ ### Example A — Planning phase, boundary + shell epic
376
+
377
+ Epic: "Implement gitnexus MCP sync in xtrm install"
378
+
379
+ Children: `.1` (MCP config writer), `.2` (sync-on-install integration), `.3` (CLI `xtrm mcp` command)
380
+
381
+ Classification:
382
+ - `.1` → boundary (writes to `.mcp.json`, file I/O)
383
+ - `.2` → shell (orchestrates install flow)
384
+ - `.3` → shell (CLI command)
385
+
386
+ Test issues created:
387
+
388
+ ```
389
+ bd create "Test: MCP config writer — contract tests for .mcp.json output" \
390
+ -t task -p 2 --parent <epic> \
391
+ -d "Layer: boundary
392
+ Strategy: example-based (file I/O, no external service)
393
+ Covers: .1
394
+
395
+ Assertions required:
396
+ - [ ] Positive path: valid servers config produces correct .mcp.json structure
397
+ - [ ] Negative path: invalid server entry raises validation error
398
+ - [ ] Edge cases: empty servers list, duplicate server names, existing .mcp.json is merged not overwritten
399
+ - [ ] Drift-safe: assert on field presence (name, command, args), not internal object identity
400
+
401
+ Critical paths covered:
402
+ - gitnexus server entry written with correct stdio transport — risk: wrong transport breaks MCP
403
+ - existing user entries preserved during merge — risk: data loss
404
+
405
+ Known deferred paths:
406
+ - test with malformed existing .mcp.json → follow-up: to be created (P3)
407
+
408
+ Done when: all assertions pass, no shared state between tests."
409
+ ```
410
+
411
+ ```
412
+ bd create "Test: xtrm install MCP sync + xtrm mcp CLI — integration tests" \
413
+ -t task -p 2 --parent <epic> \
414
+ -d "Layer: shell
415
+ Strategy: integration (subprocess)
416
+ Covers: .2, .3
417
+
418
+ Assertions required:
419
+ - [ ] End-to-end: xtrm install writes correct .mcp.json in temp project dir
420
+ - [ ] CLI: xtrm mcp list outputs expected server names
421
+ - [ ] Failure-mode: xtrm mcp add with duplicate name exits non-zero with clear error
422
+ - [ ] Cross-component: install flow calls MCP writer with correct config
423
+
424
+ Critical paths covered:
425
+ - full install → .mcp.json present and readable by Claude Code — risk: MCP servers not available
426
+ - CLI add + list roundtrip — risk: user cannot inspect installed servers
427
+
428
+ Known deferred paths:
429
+ - test with no write permission on project dir → follow-up: to be created (P4)
430
+
431
+ Done when: integration tests run against real file system in temp dir, no mocked internals."
432
+ ```
433
+
434
+ ---
435
+
436
+ ### Example B — Closure gate, core layer, implementation diverged
437
+
438
+ Closing `.22` (config merge logic). Existing test issue `.31` was written before implementation.
439
+
440
+ What `.22` actually built:
441
+ - Added precedence chain: env > file > defaults (original plan had only file > defaults)
442
+ - Added type coercion for boolean env vars ("true"/"false" → bool)
443
+ - Removed support for `.xtrm.yaml` (only `.xtrm/config.json` now)
444
+
445
+ Updated test issue `.31`:
446
+
447
+ ```
448
+ bd update xtrm-31 --notes "Scope updated after .22 completed:
449
+ + Add test: env var takes precedence over file config (new precedence chain)
450
+ + Add test: 'true'/'false' env vars coerced to bool correctly
451
+ + Add test: 'TRUE', '1', '0' edge cases for bool coercion
452
+ + Remove test: .xtrm.yaml loading (format removed in .22)
453
+
454
+ Anti-pattern check:
455
+ - [ ] tautological: none detected
456
+ - [ ] over-mocking: env injection via monkeypatch only, no internal mocking
457
+ - [ ] shared state: each test resets env via fixture
458
+
459
+ Critical paths covered:
460
+ - env > file > defaults chain — risk: wrong precedence silently overrides user config
461
+ - bool coercion — risk: 'false' string treated as truthy in Python
462
+
463
+ Known deferred paths:
464
+ - test with missing HOME dir (pathlib resolution edge case) → follow-up: xtrm-4x (P4)"
465
+ ```
@@ -19,6 +19,7 @@ Default to **autonomous execution**:
19
19
  - do not ask the user routine clarification questions
20
20
  - prefer deterministic fallbacks over conversational review
21
21
  - only stop when a real blocker prevents safe progress
22
+ - **always invoke `xt end --yes`** — never call `xt end` without this flag; the bare command prompts interactively for worktree removal which blocks autonomous execution
22
23
 
23
24
  ## Success States
24
25
 
@@ -40,6 +40,46 @@ you're rebasing more than necessary and risk conflicts that wouldn't have existe
40
40
 
41
41
  ---
42
42
 
43
+ ## Stage 0 — Pre-flight checks
44
+
45
+ Run these before touching any branch.
46
+
47
+ **1. Verify you are in a git repository:**
48
+ ```bash
49
+ git rev-parse --git-dir
50
+ ```
51
+ Stop immediately if this fails.
52
+
53
+ **2. Verify gh auth:**
54
+ ```bash
55
+ gh auth status
56
+ ```
57
+ If this fails, stop immediately. Without auth, `gh pr merge` will silently fail or
58
+ produce confusing errors mid-run.
59
+
60
+ **3. Fetch all remotes:**
61
+ ```bash
62
+ git fetch --all --prune
63
+ ```
64
+ This ensures local remote-tracking refs reflect current upstream state before any
65
+ rebase or CI check. Without this, CI status checks and rebase targets may be stale.
66
+
67
+ **4. Check for uncommitted local changes:**
68
+ ```bash
69
+ git status --porcelain
70
+ ```
71
+ If the output is non-empty, **warn the user and stop**. The rebase cascade will
72
+ check out other branches (`git checkout xt/<branch>`), which will either fail or
73
+ silently carry dirty changes into the wrong branch. Resolve before continuing:
74
+ - `git stash push -m "xt-merge cascade stash"` — stash and pop after cascade finishes
75
+ - Or commit the work first
76
+ - Or abort if the changes belong to a live worktree session
77
+
78
+ If the user stashes, record the stash ref (`git stash list | head -1`) so you can
79
+ pop it when done in Stage 6.
80
+
81
+ ---
82
+
43
83
  ## Stage 1 — Build the queue
44
84
 
45
85
  List all open PRs from xt worktree branches:
@@ -54,6 +94,9 @@ This sorts by creation time. The top row is the **head of the queue** — merge
54
94
 
55
95
  If there are draft PRs in the list, skip them. Drafts are not ready to merge.
56
96
 
97
+ If `gh pr list` returns an error (network, auth, wrong repo), stop and report the
98
+ error. Do not continue with stale or incomplete data.
99
+
57
100
  Present the sorted queue to the user before proceeding:
58
101
  ```
59
102
  Queue (oldest → newest):
@@ -70,6 +113,14 @@ Queue (oldest → newest):
70
113
  gh pr checks <number>
71
114
  ```
72
115
 
116
+ **Stale CI warning:** After a rebase cascade the PR's HEAD SHA changes. Always verify
117
+ the SHA that CI ran against matches the current branch tip before trusting a green result:
118
+ ```bash
119
+ gh pr view <number> --json headRefOid --jq '.headRefOid'
120
+ # compare against the commit SHA shown in gh pr checks output
121
+ ```
122
+ If they differ, the green result is from before the rebase — wait for the new run.
123
+
73
124
  Wait for all checks to pass. If CI is still running, tell the user and pause — don't
74
125
  merge a PR with pending or failing checks.
75
126
 
@@ -90,12 +141,18 @@ gh pr merge <number> --rebase --delete-branch
90
141
  Use `--rebase` (not `--squash` or `--merge`) to keep linear history and preserve
91
142
  individual commits from the session. Use `--delete-branch` to clean up the remote branch.
92
143
 
93
- After merge, confirm main advanced:
144
+ If `gh pr merge` fails with "No commits between main and xt/<branch>", the branch's
145
+ commits were already absorbed into main (e.g. from a previous push). Close the PR
146
+ and continue to the next.
147
+
148
+ After merge, fetch and confirm main advanced:
94
149
  ```bash
95
150
  git fetch origin
96
151
  git log origin/main --oneline -3
97
152
  ```
98
153
 
154
+ Record the new HEAD SHA of main — you will verify the cascade rebases onto it.
155
+
99
156
  ---
100
157
 
101
158
  ## Stage 4 — Rebase cascade (all remaining PRs)
@@ -106,10 +163,24 @@ For every remaining PR in the queue, rebase its branch onto the new main:
106
163
  git fetch origin main
107
164
  git checkout xt/<branch>
108
165
  git rebase origin/main
109
- git push origin xt/<branch> --force-with-lease
166
+ git push origin xt/<branch> --force-with-lease --force-if-includes
167
+ ```
168
+
169
+ `--force-with-lease` rejects the push if the remote has commits your local ref
170
+ doesn't know about. `--force-if-includes` additionally verifies that whatever
171
+ you're overwriting was reachable from your local history — together they prevent
172
+ accidentally overwriting a collaborator's push that arrived after your last fetch.
173
+ (Requires Git 2.30+. If not available, `--force-with-lease` alone is acceptable.)
174
+
175
+ **After each push, verify it landed:**
176
+ ```bash
177
+ git rev-parse HEAD
178
+ git rev-parse origin/xt/<branch>
110
179
  ```
180
+ Both SHAs must match. If the push was rejected (lease violation or other error),
181
+ stop and report — do not silently continue to the next branch.
111
182
 
112
- Repeat for each remaining branch. Do them in queue order (oldest next).
183
+ Repeat for each remaining branch in queue order (oldest next).
113
184
 
114
185
  After pushing, GitHub will re-trigger CI on each rebased PR. You don't need to wait
115
186
  for CI here — the rebase just gets the branches current. CI will run in parallel.
@@ -123,27 +194,43 @@ git add <resolved-files>
123
194
  git rebase --continue
124
195
  ```
125
196
 
197
+ If you cannot safely resolve a conflict, **abort the rebase immediately**:
198
+ ```bash
199
+ git rebase --abort
200
+ ```
201
+ The branch is left unchanged. Report the branch name and conflicted files to the
202
+ user. Continue the cascade for remaining branches; the user can resolve and push
203
+ this one manually before you loop back to merge it.
204
+
126
205
  Conflicts mean two sessions touched the same file. Resolve carefully:
127
206
  - Keep both changes if they're in different parts of the file
128
207
  - If they overlap, understand what each session was doing and merge the intent
129
- - When unsure, call the user in to review before continuing
208
+ - When unsure, abort and escalate to the user
130
209
 
131
- After resolving, push with `--force-with-lease` and move to the next branch.
210
+ After resolving, push with `--force-with-lease --force-if-includes` and verify the
211
+ push landed (SHA check above) before moving to the next branch.
132
212
 
133
213
  ---
134
214
 
135
215
  ## Stage 5 — Repeat
136
216
 
137
- Go back to Stage 2 with the new head of the queue. Check CI, merge, rebase cascade,
217
+ Go back to Stage 2 with the new head of the queue. Check CI on the **new SHA**
218
+ produced by the rebase cascade push — not a pre-rebase result. Merge, cascade,
138
219
  repeat until the queue is empty.
139
220
 
140
221
  The full loop:
141
222
  ```
142
223
  while queue not empty:
143
- wait for CI green on head PR
224
+ check CI on head PR
225
+ → verify: gh pr view <n> headRefOid == SHA in gh pr checks output
226
+ → wait for green; stop if failing
144
227
  merge head PR (--rebase --delete-branch)
145
- rebase all remaining PRs onto new main
146
- push each (--force-with-lease)
228
+ git fetch origin confirm main advanced
229
+ for each remaining branch in queue order:
230
+ git checkout xt/<branch>
231
+ git rebase origin/main
232
+ git push --force-with-lease --force-if-includes
233
+ verify: git rev-parse HEAD == git rev-parse origin/xt/<branch>
147
234
  ```
148
235
 
149
236
  ---
@@ -151,7 +238,11 @@ while queue not empty:
151
238
  ## Stage 6 — Done
152
239
 
153
240
  When the queue is empty:
241
+
154
242
  ```bash
243
+ # If you stashed changes in Stage 0, pop now:
244
+ git stash pop # report any conflicts — do not discard silently
245
+
155
246
  gh pr list --state open
156
247
  git log origin/main --oneline -5
157
248
  ```
@@ -164,7 +255,10 @@ Confirm no open xt/ PRs remain and show the user the final state of main.
164
255
 
165
256
  **PR was already merged**: `gh pr merge` will error. Skip it and continue.
166
257
 
167
- **Branch was deleted** (worktree cleaned up by `xt end --keep`): The remote branch
258
+ **No commits between main and xt/branch**: branch was already absorbed into main.
259
+ Close the PR and continue.
260
+
261
+ **Branch was deleted** (worktree cleaned up by `xt end`): The remote branch
168
262
  still exists (pushed by `xt end`). The local branch may not. Check out from remote:
169
263
  ```bash
170
264
  git fetch origin
@@ -178,13 +272,42 @@ git commit --allow-empty -m "trigger CI"
178
272
  git push origin xt/<branch>
179
273
  ```
180
274
 
275
+ **Stale CI result after rebase**: Always confirm the SHA in `gh pr checks` matches
276
+ `git rev-parse origin/xt/<branch>` before treating a green result as valid. If they
277
+ differ, wait for the new run.
278
+
279
+ **Push rejected (lease violation)**: Do not retry blindly. Fetch and inspect:
280
+ ```bash
281
+ git fetch origin xt/<branch>
282
+ git log origin/xt/<branch> --oneline -5
283
+ ```
284
+ Decide whether the remote commits should be incorporated or overwritten, then act
285
+ deliberately.
286
+
287
+ **`gh auth` expired mid-run**: Stop the cascade immediately. Report which branches
288
+ were successfully rebased/pushed and which were not, so the user can resume from the
289
+ right point after re-authenticating.
290
+
291
+ **Uncommitted changes on current branch**: Stash before the cascade, pop after:
292
+ ```bash
293
+ git stash push -m "xt-merge cascade stash"
294
+ # ... run cascade ...
295
+ git stash pop
296
+ ```
297
+ If `git stash pop` produces conflicts, report them — do not silently discard work.
298
+
181
299
  **Dependent sessions** (B was intentionally built on A's work): If session B was
182
300
  started from inside session A's worktree rather than from main, B's branch already
183
- contains A's commits. In this case B will rebase cleanly onto main after A merges —
184
- its commits are a superset. No special handling needed; the rebase just eliminates
185
- the duplicate commits.
186
-
187
- **Multiple conflicts across many PRs**: If the cascade produces conflicts in several
188
- branches, tackle them one at a time in queue order. Don't try to resolve all of them
189
- before pushing any — push each one as you resolve it so CI starts running in parallel
190
- while you work on the next.
301
+ contains A's commits. B will rebase cleanly onto main after A merges — the rebase
302
+ eliminates the duplicate commits. No special handling needed.
303
+
304
+ **Multiple conflicts across many PRs**: Abort each failing rebase (`git rebase --abort`)
305
+ and tackle them one at a time in queue order after the user resolves. Push each
306
+ resolved branch immediately so CI starts running in parallel.
307
+
308
+ **Rollback / abort mid-cascade**: If anything goes wrong and you need to stop cleanly:
309
+ 1. `git rebase --abort` if a rebase is in progress
310
+ 2. `git checkout <original-branch>` to return to where you started
311
+ 3. `git stash pop` if you stashed in Stage 0
312
+ 4. Report exactly which PRs were merged, which were rebased-and-pushed, and which
313
+ were untouched — so the user can resume or restart from the correct point.