wicked-crew 0.7.10 → 0.7.12

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 (61) hide show
  1. package/dist/api/delivery-index.d.ts +21 -0
  2. package/dist/api/delivery-index.d.ts.map +1 -1
  3. package/dist/api/delivery-index.js +35 -0
  4. package/dist/api/delivery-index.js.map +1 -1
  5. package/dist/api/group-index.d.ts +59 -0
  6. package/dist/api/group-index.d.ts.map +1 -0
  7. package/dist/api/group-index.js +100 -0
  8. package/dist/api/group-index.js.map +1 -0
  9. package/dist/api/post-hoc-deliver.d.ts +27 -0
  10. package/dist/api/post-hoc-deliver.d.ts.map +1 -1
  11. package/dist/api/post-hoc-deliver.js +54 -0
  12. package/dist/api/post-hoc-deliver.js.map +1 -1
  13. package/dist/api/retry-index.d.ts +6 -0
  14. package/dist/api/retry-index.d.ts.map +1 -1
  15. package/dist/api/retry-index.js +13 -6
  16. package/dist/api/retry-index.js.map +1 -1
  17. package/dist/api/routes.d.ts +55 -2
  18. package/dist/api/routes.d.ts.map +1 -1
  19. package/dist/api/routes.js +188 -17
  20. package/dist/api/routes.js.map +1 -1
  21. package/dist/api/seat-health.d.ts +11 -0
  22. package/dist/api/seat-health.d.ts.map +1 -1
  23. package/dist/api/seat-health.js +23 -0
  24. package/dist/api/seat-health.js.map +1 -1
  25. package/dist/api/server.d.ts.map +1 -1
  26. package/dist/api/server.js +19 -5
  27. package/dist/api/server.js.map +1 -1
  28. package/dist/api/testing.d.ts.map +1 -1
  29. package/dist/api/testing.js +11 -27
  30. package/dist/api/testing.js.map +1 -1
  31. package/dist/campaigns/rollup.d.ts +39 -0
  32. package/dist/campaigns/rollup.d.ts.map +1 -0
  33. package/dist/campaigns/rollup.js +84 -0
  34. package/dist/campaigns/rollup.js.map +1 -0
  35. package/dist/campaigns/routes.d.ts +10 -3
  36. package/dist/campaigns/routes.d.ts.map +1 -1
  37. package/dist/campaigns/routes.js +27 -5
  38. package/dist/campaigns/routes.js.map +1 -1
  39. package/dist/cli/index.js +13 -2
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/core/adapter.d.ts +0 -12
  42. package/dist/core/adapter.d.ts.map +1 -1
  43. package/dist/core/adapter.js +0 -14
  44. package/dist/core/adapter.js.map +1 -1
  45. package/dist/core/daemon-signal-log.d.ts +44 -0
  46. package/dist/core/daemon-signal-log.d.ts.map +1 -0
  47. package/dist/core/daemon-signal-log.js +57 -0
  48. package/dist/core/daemon-signal-log.js.map +1 -0
  49. package/dist/core/deliver.d.ts +20 -3
  50. package/dist/core/deliver.d.ts.map +1 -1
  51. package/dist/core/deliver.js +82 -9
  52. package/dist/core/deliver.js.map +1 -1
  53. package/dist/studio/assets/{index-gtE6scX0.js → index-DuU8RZg9.js} +95 -95
  54. package/dist/studio/index.html +1 -1
  55. package/dist/studio/testid-inventory.json +47 -17
  56. package/endpoint-manifest.json +4 -3
  57. package/package.json +3 -3
  58. package/dist/campaigns/worktrees.d.ts +0 -39
  59. package/dist/campaigns/worktrees.d.ts.map +0 -1
  60. package/dist/campaigns/worktrees.js +0 -95
  61. package/dist/campaigns/worktrees.js.map +0 -1
@@ -0,0 +1,44 @@
1
+ /**
2
+ * In-process signal receipt log (crew#411).
3
+ *
4
+ * Node's `process.on('SIGTERM'/'SIGINT')` callbacks receive no sender pid —
5
+ * there is no SA_SIGINFO equivalent in the JS runtime. The best-effort record
6
+ * is therefore: signal name + epoch-ms timestamp.
7
+ *
8
+ * The log is read by `SeatHealthTracker` when an ACP bridge dies silently
9
+ * (`acpFallback` with `fallbackKind: session_died`) to distinguish:
10
+ *
11
+ * - "daemon also received SIGTERM at T" → likely a group/terminal signal that
12
+ * killed both the bridge and the daemon together
13
+ * - "no daemon signal within ±5s" → a pid-targeted external signal sent
14
+ * directly to the bridge, or a transport close (neither woke the daemon)
15
+ *
16
+ * The ring is bounded (64 entries) because signals are rare and the entries are
17
+ * tiny; the oldest entry is dropped once the cap is exceeded.
18
+ */
19
+ export interface SignalEntry {
20
+ signal: 'SIGTERM' | 'SIGINT';
21
+ at: number;
22
+ }
23
+ /** Half-width of the default correlation window used by `findInWindow`. */
24
+ export declare const SIGNAL_CORRELATION_WINDOW_MS = 5000;
25
+ export declare class DaemonSignalLog {
26
+ private readonly _entries;
27
+ private static readonly MAX;
28
+ /**
29
+ * Append a signal receipt. `at` defaults to `Date.now()`; injectable so tests
30
+ * can set exact timestamps without mocking the clock process-wide.
31
+ */
32
+ record(signal: 'SIGTERM' | 'SIGINT', at?: number): void;
33
+ /**
34
+ * Return the closest entry within [`aroundMs` ± `halfWindowMs`], or
35
+ * `undefined` when no entry falls in that interval.
36
+ * `halfWindowMs` defaults to `SIGNAL_CORRELATION_WINDOW_MS` (5 s).
37
+ */
38
+ findInWindow(aroundMs: number, halfWindowMs?: number): SignalEntry | undefined;
39
+ /** All recorded entries, oldest first — a defensive copy, so a caller cannot mutate the ring. */
40
+ entries(): readonly SignalEntry[];
41
+ }
42
+ /** The daemon-wide singleton. The CLI records into it; the seat-health tracker reads from it. */
43
+ export declare const daemonSignalLog: DaemonSignalLog;
44
+ //# sourceMappingURL=daemon-signal-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-signal-log.d.ts","sourceRoot":"","sources":["../../src/core/daemon-signal-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,2EAA2E;AAC3E,eAAO,MAAM,4BAA4B,OAAQ,CAAC;AAElD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAM;IAEjC;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,EAAE,EAAE,SAAa,GAAG,IAAI;IAM3D;;;;OAIG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,SAA+B,GAAG,WAAW,GAAG,SAAS;IAapG,iGAAiG;IACjG,OAAO,IAAI,SAAS,WAAW,EAAE;CAGlC;AAED,iGAAiG;AACjG,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * In-process signal receipt log (crew#411).
3
+ *
4
+ * Node's `process.on('SIGTERM'/'SIGINT')` callbacks receive no sender pid —
5
+ * there is no SA_SIGINFO equivalent in the JS runtime. The best-effort record
6
+ * is therefore: signal name + epoch-ms timestamp.
7
+ *
8
+ * The log is read by `SeatHealthTracker` when an ACP bridge dies silently
9
+ * (`acpFallback` with `fallbackKind: session_died`) to distinguish:
10
+ *
11
+ * - "daemon also received SIGTERM at T" → likely a group/terminal signal that
12
+ * killed both the bridge and the daemon together
13
+ * - "no daemon signal within ±5s" → a pid-targeted external signal sent
14
+ * directly to the bridge, or a transport close (neither woke the daemon)
15
+ *
16
+ * The ring is bounded (64 entries) because signals are rare and the entries are
17
+ * tiny; the oldest entry is dropped once the cap is exceeded.
18
+ */
19
+ /** Half-width of the default correlation window used by `findInWindow`. */
20
+ export const SIGNAL_CORRELATION_WINDOW_MS = 5_000;
21
+ export class DaemonSignalLog {
22
+ _entries = [];
23
+ static MAX = 64;
24
+ /**
25
+ * Append a signal receipt. `at` defaults to `Date.now()`; injectable so tests
26
+ * can set exact timestamps without mocking the clock process-wide.
27
+ */
28
+ record(signal, at = Date.now()) {
29
+ this._entries.push({ signal, at });
30
+ if (this._entries.length > DaemonSignalLog.MAX)
31
+ this._entries.splice(0, this._entries.length - DaemonSignalLog.MAX);
32
+ }
33
+ /**
34
+ * Return the closest entry within [`aroundMs` ± `halfWindowMs`], or
35
+ * `undefined` when no entry falls in that interval.
36
+ * `halfWindowMs` defaults to `SIGNAL_CORRELATION_WINDOW_MS` (5 s).
37
+ */
38
+ findInWindow(aroundMs, halfWindowMs = SIGNAL_CORRELATION_WINDOW_MS) {
39
+ let best;
40
+ let bestDelta = Infinity;
41
+ for (const e of this._entries) {
42
+ const delta = Math.abs(e.at - aroundMs);
43
+ if (delta <= halfWindowMs && delta < bestDelta) {
44
+ best = e;
45
+ bestDelta = delta;
46
+ }
47
+ }
48
+ return best;
49
+ }
50
+ /** All recorded entries, oldest first — a defensive copy, so a caller cannot mutate the ring. */
51
+ entries() {
52
+ return [...this._entries];
53
+ }
54
+ }
55
+ /** The daemon-wide singleton. The CLI records into it; the seat-health tracker reads from it. */
56
+ export const daemonSignalLog = new DaemonSignalLog();
57
+ //# sourceMappingURL=daemon-signal-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-signal-log.js","sourceRoot":"","sources":["../../src/core/daemon-signal-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,MAAM,OAAO,eAAe;IACT,QAAQ,GAAkB,EAAE,CAAC;IACtC,MAAM,CAAU,GAAG,GAAG,EAAE,CAAC;IAEjC;;;OAGG;IACH,MAAM,CAAC,MAA4B,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;QAClD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,eAAe,CAAC,GAAG;YAC5C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACxE,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,QAAgB,EAAE,YAAY,GAAG,4BAA4B;QACxE,IAAI,IAA6B,CAAC;QAClC,IAAI,SAAS,GAAG,QAAQ,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC;YACxC,IAAI,KAAK,IAAI,YAAY,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;gBAC/C,IAAI,GAAG,CAAC,CAAC;gBACT,SAAS,GAAG,KAAK,CAAC;YACpB,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iGAAiG;IACjG,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;;AAGH,iGAAiG;AACjG,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC"}
@@ -12,9 +12,13 @@
12
12
  * (b) the script REFUSES to push `main`/`master` (or an empty/detached branch name) — the
13
13
  * deliver phase only ever pushes run branches;
14
14
  * (c) it STAGES AND COMMITS the run's work, then rebases onto origin's default branch before
15
- * pushing, and a conflicting rebase FAILS the phase visibly (aborting the rebase, pushing
16
- * nothing) rather than pushing a conflicted tree;
17
- * (d) `git push -u origin <branch>`;
15
+ * pushing. A conflict whose conflicted paths are ALL `CHANGELOG.md` is union-merged (both
16
+ * sides' additive lines kept) and the rebase continues — the crew#418 collision magnet, made
17
+ * to just deliver; ANY other conflict aborts the rebase (pushing nothing) and exits carrying
18
+ * {@link DELIVER_LIFT_CONFLICT_MARKER}, which crew reads as a recoverable STRAND, never a
19
+ * pushed conflicted tree;
20
+ * (d) `git push -u origin <branch>` — a non-fast-forward rejection carries the same lift-conflict
21
+ * marker (the remote branch moved), every other push failure stays a loud plain failure;
18
22
  * (e) `gh pr create --head <branch> --fill`, with gh's output and exit status captured
19
23
  * SEPARATELY so a gh failure fails the phase carrying gh's own message;
20
24
  * (f) the PR URL is the last line of the phase output.
@@ -57,6 +61,19 @@
57
61
  import type { PhaseDef, WorkflowDef } from './types.js';
58
62
  /** The id of the appended phase — also the collision probe when a def already delivers. */
59
63
  export declare const DELIVER_PHASE_ID = "deliver";
64
+ /**
65
+ * The sentinel the deliver script prints when it refuses on a LIFT COLLISION (crew#418) — a
66
+ * rebase conflict it could not union-resolve, or a non-fast-forward push. The run's WORK is
67
+ * complete and committed on its `wicked/<id>` branch; only the lift into origin collided.
68
+ *
69
+ * crew keys the "stranded, recoverable" reinterpretation on this EXACT substring appearing in
70
+ * the deliver unit's `denial_reason` (which carries the head+TAIL excerpt of the script's
71
+ * output, and the marker is always the script's last line — see {@link isDeliverConflictStranded}
72
+ * in `api/delivery-index.ts`). The script's OTHER loud refusals — wrong worktree branch, nothing
73
+ * to deliver, `gh` failure — and a spawn/infra failure deliberately OMIT the marker, so they stay
74
+ * terminal run failures exactly as before (the crew#400 refusal-vs-infra posture).
75
+ */
76
+ export declare const DELIVER_LIFT_CONFLICT_MARKER = "deliver: LIFT-CONFLICT";
60
77
  /**
61
78
  * The hardened deliver script, run as `bash -lc <script>` (login shell so the operator's PATH —
62
79
  * where `gh` lives — is loaded, same as the field overlay).
@@ -1 +1 @@
1
- {"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/core/deliver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExD,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,YAAY,CAAC;AA0B1C;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAqEvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,SAAS,GAAE,MAAM,EAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAgBlF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,WAAW,CAmBb"}
1
+ {"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/core/deliver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExD,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B,2BAA2B,CAAC;AA0BrE;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CA6HvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,SAAS,GAAE,MAAM,EAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAgBlF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,WAAW,CAmBb"}
@@ -12,9 +12,13 @@
12
12
  * (b) the script REFUSES to push `main`/`master` (or an empty/detached branch name) — the
13
13
  * deliver phase only ever pushes run branches;
14
14
  * (c) it STAGES AND COMMITS the run's work, then rebases onto origin's default branch before
15
- * pushing, and a conflicting rebase FAILS the phase visibly (aborting the rebase, pushing
16
- * nothing) rather than pushing a conflicted tree;
17
- * (d) `git push -u origin <branch>`;
15
+ * pushing. A conflict whose conflicted paths are ALL `CHANGELOG.md` is union-merged (both
16
+ * sides' additive lines kept) and the rebase continues — the crew#418 collision magnet, made
17
+ * to just deliver; ANY other conflict aborts the rebase (pushing nothing) and exits carrying
18
+ * {@link DELIVER_LIFT_CONFLICT_MARKER}, which crew reads as a recoverable STRAND, never a
19
+ * pushed conflicted tree;
20
+ * (d) `git push -u origin <branch>` — a non-fast-forward rejection carries the same lift-conflict
21
+ * marker (the remote branch moved), every other push failure stays a loud plain failure;
18
22
  * (e) `gh pr create --head <branch> --fill`, with gh's output and exit status captured
19
23
  * SEPARATELY so a gh failure fails the phase carrying gh's own message;
20
24
  * (f) the PR URL is the last line of the phase output.
@@ -56,6 +60,19 @@
56
60
  */
57
61
  /** The id of the appended phase — also the collision probe when a def already delivers. */
58
62
  export const DELIVER_PHASE_ID = 'deliver';
63
+ /**
64
+ * The sentinel the deliver script prints when it refuses on a LIFT COLLISION (crew#418) — a
65
+ * rebase conflict it could not union-resolve, or a non-fast-forward push. The run's WORK is
66
+ * complete and committed on its `wicked/<id>` branch; only the lift into origin collided.
67
+ *
68
+ * crew keys the "stranded, recoverable" reinterpretation on this EXACT substring appearing in
69
+ * the deliver unit's `denial_reason` (which carries the head+TAIL excerpt of the script's
70
+ * output, and the marker is always the script's last line — see {@link isDeliverConflictStranded}
71
+ * in `api/delivery-index.ts`). The script's OTHER loud refusals — wrong worktree branch, nothing
72
+ * to deliver, `gh` failure — and a spawn/infra failure deliberately OMIT the marker, so they stay
73
+ * terminal run failures exactly as before (the crew#400 refusal-vs-infra posture).
74
+ */
75
+ export const DELIVER_LIFT_CONFLICT_MARKER = 'deliver: LIFT-CONFLICT';
59
76
  /** How much of the run's intent rides in the commit subject before it is truncated. */
60
77
  const INTENT_SUBJECT_CAP = 72;
61
78
  /**
@@ -134,16 +151,72 @@ export function deliverPrScript(intent) {
134
151
  // remote is touched: an empty ref pushed under a run id is worse than a failed phase.
135
152
  'A=$(git rev-list --count "$D..$B")',
136
153
  '[ "$A" -ge 1 ] || { echo "deliver: nothing to deliver — the run produced no committed change ($B is not ahead of $D); nothing was pushed"; exit 1; }',
137
- // (c3) Rebase onto origin's default branch so the PR opens mergeable. A conflict must fail
138
- // the phase VISIBLY with nothing pushed — the abort leaves the worktree on the pre-rebase
139
- // branch tip instead of mid-rebase.
140
- 'git rebase "$D" "$B" || { git rebase --abort >/dev/null 2>&1 || true; echo "deliver: rebase of $B onto $D failed (conflicts) — resolve on the branch and re-run; nothing was pushed"; exit 1; }',
154
+ // (c3) Rebase onto origin's default branch so the PR opens mergeable.
155
+ //
156
+ // crew#418 B — the CHANGELOG collision magnet: two runs that both append to CHANGELOG's
157
+ // `[Unreleased]` section conflict on the rebase BY CONSTRUCTION, though their added bullet
158
+ // lines never truly disagree. A conflict whose conflicted paths are ALL `CHANGELOG.md`
159
+ // (matched by basename) is resolved automatically with a UNION merge — `git merge-file
160
+ // --union` keeps BOTH sides' lines, no markers — and the rebase continues. This is scoped to
161
+ // the changelog and touches NOTHING else: a conflict in any other file is left exactly as
162
+ // loud as before (the "never weaken rebase loudness for non-changelog files" rule).
163
+ //
164
+ // crew#418 A — a conflict that is NOT changelog-only (or a changelog union that fails) is a
165
+ // real LIFT collision: abort the rebase (nothing pushed; the abort leaves the worktree on the
166
+ // pre-rebase branch tip, not mid-rebase) and exit carrying DELIVER_LIFT_CONFLICT_MARKER. The
167
+ // run's committed work is safe on its branch, so crew reinterprets THIS refusal as `completed`
168
+ // + `delivery: 'stranded'` (recoverable via POST /runs/:id/deliver) rather than a run failure.
169
+ '_rebasing() { [ -d "$(git rev-parse --git-path rebase-merge 2>/dev/null)" ] || [ -d "$(git rev-parse --git-path rebase-apply 2>/dev/null)" ]; }',
170
+ 'if ! git rebase "$D" "$B"; then',
171
+ // A rebase that failed WITHOUT leaving in-progress state never started — a preflight error
172
+ // (bad ref, unexpected worktree state), not a conflict. Fail LOUD rather than fall through to
173
+ // the push as if the rebase had succeeded; nothing was pushed.
174
+ ' if ! _rebasing; then echo "deliver: git rebase of $B onto $D failed before it started (preflight error); nothing was pushed"; exit 1; fi',
175
+ ' while _rebasing; do',
176
+ ' CF=$(git diff --name-only --diff-filter=U || true)',
177
+ ' [ -n "$CF" ] || break',
178
+ // Any conflicted path that is not a CHANGELOG.md → a real collision; stop resolving and strand.
179
+ ' if printf "%s\\n" "$CF" | grep -qvE "(^|/)CHANGELOG\\.md$"; then break; fi',
180
+ // Union-merge every conflicted changelog — but ONLY when the two sides differ SOLELY within
181
+ // the `## [Unreleased]` section. A union keeps both sides of every conflict hunk, so a
182
+ // whole-file union of two edits to the SAME released-version line would silently combine
183
+ // them; we refuse that. Guard: strip the [Unreleased] block (from `## [Unreleased]` up to the
184
+ // next `## [` heading) from both stage-2 (ours) and stage-3 (theirs); if the remainder is not
185
+ // byte-identical, the divergence is outside [Unreleased] → a real conflict → break (strand).
186
+ // When they ARE identical outside it, the only conflicting hunks are within [Unreleased], so
187
+ // the whole-file --union affects nothing else. A missing base stage (add/add) unions against
188
+ // an empty base; any git failure breaks out to the loud abort below.
189
+ ' if ! printf "%s\\n" "$CF" | while IFS= read -r F; do',
190
+ ' [ -n "$F" ] || continue;',
191
+ ' TB=$(mktemp); TO=$(mktemp); TT=$(mktemp);',
192
+ ' git show ":1:$F" >"$TB" 2>/dev/null || : >"$TB";',
193
+ ' git show ":2:$F" >"$TO" 2>/dev/null || { rm -f "$TB" "$TO" "$TT"; exit 1; };',
194
+ ' git show ":3:$F" >"$TT" 2>/dev/null || { rm -f "$TB" "$TO" "$TT"; exit 1; };',
195
+ " _strip='/^## \\[Unreleased\\]/{s=1;next} s&&/^## \\[/{s=0} !s{print}';",
196
+ ' SO=$(awk "$_strip" "$TO"); ST=$(awk "$_strip" "$TT");',
197
+ ' if [ "$SO" != "$ST" ]; then rm -f "$TB" "$TO" "$TT"; exit 1; fi;',
198
+ ' git merge-file -q --union "$TO" "$TB" "$TT" || { rm -f "$TB" "$TO" "$TT"; exit 1; };',
199
+ ' cat "$TO" >"$F"; git add -- "$F"; rm -f "$TB" "$TO" "$TT";',
200
+ ' done; then break; fi',
201
+ ' GIT_EDITOR=true git -c core.editor=true rebase --continue >/dev/null 2>&1 || break;',
202
+ ' done',
203
+ ` if _rebasing; then git rebase --abort >/dev/null 2>&1 || true; echo "${DELIVER_LIFT_CONFLICT_MARKER} — rebase of $B onto $D hit conflicts outside the changelog; resolve on the branch and re-run; nothing was pushed"; exit 1; fi`,
204
+ 'fi',
141
205
  // Re-derive after the rebase: it drops commits already upstream (patch-id equal), so a branch
142
206
  // that WAS ahead can come out of a rebase carrying nothing of its own.
143
207
  'A=$(git rev-list --count "$D..$B")',
144
208
  '[ "$A" -ge 1 ] || { echo "deliver: nothing to deliver — the run produced no committed change (after rebasing onto $D, $B carries no commit of its own); nothing was pushed"; exit 1; }',
145
- // (d) Push.
146
- 'git push -u origin "$B"',
209
+ // (d) Push. A non-fast-forward rejection (the remote run branch moved under us) is the
210
+ // push-side twin of the rebase collision — mark it LIFT-CONFLICT so crew strands the run
211
+ // recoverably; ANY other push failure (auth, network, a rejecting remote hook) stays a loud
212
+ // terminal failure with git's own words.
213
+ 'if PUSHOUT=$(git push -u origin "$B" 2>&1); then echo "$PUSHOUT"; else',
214
+ ' echo "$PUSHOUT"',
215
+ ' case "$PUSHOUT" in',
216
+ ` *non-fast-forward*|*"fetch first"*|*"[rejected]"*|*"Updates were rejected"*) echo "${DELIVER_LIFT_CONFLICT_MARKER} — push of $B was rejected because the remote branch moved (non-fast-forward); rebase and re-run; nothing was pushed"; exit 1;;`,
217
+ ' *) echo "deliver: git push of $B failed — nothing was pushed"; exit 1;;',
218
+ ' esac',
219
+ 'fi',
147
220
  // (e) Open the PR with gh's OUTPUT and EXIT STATUS captured separately (crew#317). The old
148
221
  // `| tail -1` threw away everything gh said but one line and made the phase's verdict a
149
222
  // property of a shell option; a gh failure now fails the phase carrying gh's own message.
@@ -1 +1 @@
1
- {"version":3,"file":"deliver.js","sourceRoot":"","sources":["../../src/core/deliver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deliver.js","sourceRoot":"","sources":["../../src/core/deliver.ts"],"names":[],"mappings":"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"}