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.
- package/.claude-plugin/plugin.json +1 -1
- package/cli/dist/index.cjs +6218 -7897
- package/cli/dist/index.cjs.map +1 -1
- package/cli/package.json +6 -1
- package/config/pi/extensions/beads/index.ts +1 -1
- package/config/pi/extensions/beads/package.json +4 -1
- package/config/pi/extensions/core/package.json +18 -0
- package/config/pi/extensions/custom-footer/index.ts +138 -71
- package/config/pi/extensions/custom-footer/package.json +4 -1
- package/config/pi/extensions/quality-gates/index.ts +1 -1
- package/config/pi/extensions/quality-gates/package.json +4 -1
- package/config/pi/extensions/service-skills/index.ts +1 -1
- package/config/pi/extensions/service-skills/package.json +4 -1
- package/config/pi/extensions/session-flow/index.ts +1 -1
- package/config/pi/extensions/session-flow/package.json +4 -1
- package/config/pi/extensions/xtrm-loader/index.ts +1 -1
- package/config/pi/extensions/xtrm-loader/package.json +4 -1
- package/hooks/beads-compact-restore.mjs +11 -3
- package/hooks/beads-compact-save.mjs +13 -1
- package/hooks/tsconfig-cache.json +12 -2
- package/package.json +3 -2
- package/plugins/xtrm-tools/.claude-plugin/plugin.json +1 -1
- package/plugins/xtrm-tools/hooks/beads-compact-restore.mjs +11 -3
- package/plugins/xtrm-tools/hooks/beads-compact-save.mjs +13 -1
- package/plugins/xtrm-tools/hooks/tsconfig-cache.json +12 -2
- package/plugins/xtrm-tools/skills/test-planning/SKILL.md +257 -0
- package/plugins/xtrm-tools/skills/xt-end/SKILL.md +1 -0
- package/plugins/xtrm-tools/skills/xt-merge/SKILL.md +141 -18
- package/skills/test-planning/SKILL.md +257 -0
- package/skills/xt-end/SKILL.md +1 -0
- 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
|
-
|
|
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
|
|
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,
|
|
208
|
+
- When unsure, abort and escalate to the user
|
|
130
209
|
|
|
131
|
-
After resolving, push with `--force-with-lease` and
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
146
|
-
|
|
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
|
-
**
|
|
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.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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.
|