@xn-intenton-z2a/agentic-lib 7.4.27 → 7.4.28

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.
@@ -87,7 +87,7 @@ jobs:
87
87
  steps:
88
88
  - uses: actions/checkout@v6
89
89
  with:
90
- ref: ${{ inputs.ref || github.sha }}
90
+ ref: ${{ inputs.ref || github.ref }}
91
91
  sparse-checkout: ${{ env.configPath }}
92
92
  sparse-checkout-cone-mode: false
93
93
  - name: Normalise params
@@ -116,7 +116,7 @@ jobs:
116
116
  - uses: actions/checkout@v6
117
117
  with:
118
118
  fetch-depth: 0
119
- ref: ${{ inputs.ref || github.sha }}
119
+ ref: ${{ inputs.ref || github.ref }}
120
120
 
121
121
  - uses: actions/setup-node@v6
122
122
  with:
@@ -96,6 +96,8 @@ jobs:
96
96
  update:
97
97
  if: inputs.mode != 'skip'
98
98
  uses: ./.github/workflows/agentic-lib-update.yml
99
+ with:
100
+ skip-tests: 'true'
99
101
  secrets: inherit
100
102
 
101
103
  # ── Phase 0b: Init (purge/reseed/update) ───────────────────────────
@@ -693,7 +695,42 @@ jobs:
693
695
  git add BENCHMARK_REPORT_FLOW_*.md
694
696
  git diff --staged --quiet && echo "No report to commit" && exit 0
695
697
  git commit -m "flow: benchmark report for ${{ inputs.mission-seed }} (${{ inputs.workflow-runs }} runs) [skip ci]"
696
- git push origin main || echo "Push failed — report saved as artifact"
698
+ MAX_RETRIES=3
699
+ PUSH_SUCCESS=""
700
+ for attempt in $(seq 1 $MAX_RETRIES); do
701
+ case $attempt in
702
+ 1)
703
+ echo "=== Attempt 1: rebase (cleanest) ==="
704
+ git pull --rebase origin main || {
705
+ git rebase --abort 2>/dev/null || true
706
+ sleep $((10 + RANDOM % 10))
707
+ continue
708
+ }
709
+ ;;
710
+ 2)
711
+ echo "=== Attempt 2: merge (handles non-overlapping changes) ==="
712
+ git pull --no-rebase origin main || {
713
+ git merge --abort 2>/dev/null || true
714
+ sleep $((20 + RANDOM % 10))
715
+ continue
716
+ }
717
+ ;;
718
+ 3)
719
+ echo "=== Attempt 3: merge -X theirs (accept remote on conflict) ==="
720
+ git pull -X theirs --no-rebase origin main || {
721
+ git merge --abort 2>/dev/null || true
722
+ sleep $((30 + RANDOM % 10))
723
+ continue
724
+ }
725
+ ;;
726
+ esac
727
+ git push origin main && { PUSH_SUCCESS="true"; break; }
728
+ echo "Push failed on attempt $attempt"
729
+ sleep $((attempt * 10 + RANDOM % 10))
730
+ done
731
+ if [ "$PUSH_SUCCESS" != "true" ]; then
732
+ echo "::warning::Failed to push report after $MAX_RETRIES attempts — saved as artifact"
733
+ fi
697
734
 
698
735
  - name: Upload report artifact
699
736
  uses: actions/upload-artifact@v4
@@ -350,20 +350,47 @@ jobs:
350
350
  else
351
351
  git commit -m "init ${INIT_MODE} (agentic-lib@${VERSION}) [skip ci]"
352
352
  fi
353
- for attempt in 1 2 3; do
354
- # Use HEAD:refs/heads/main to handle detached HEAD state
355
- git push origin HEAD:refs/heads/main && break
356
- echo "Push failed (attempt $attempt) — pulling and retrying"
357
- git pull --rebase origin main || {
358
- echo "Rebase conflict — aborting rebase and retrying"
359
- git rebase --abort 2>/dev/null || true
360
- }
361
- sleep $((attempt * 2))
362
- if [ "$attempt" -eq 3 ]; then
363
- echo "::error::Failed to push after 3 attempts"
364
- exit 1
365
- fi
353
+ MAX_RETRIES=3
354
+ PUSH_SUCCESS=""
355
+ for attempt in $(seq 1 $MAX_RETRIES); do
356
+ # Tiered conflict resolution (mirrors fix-stuck strategy)
357
+ case $attempt in
358
+ 1)
359
+ echo "=== Attempt 1: rebase (cleanest) ==="
360
+ git pull --rebase origin main || {
361
+ echo "Rebase conflict on attempt 1 — aborting rebase"
362
+ git rebase --abort 2>/dev/null || true
363
+ sleep $((10 + RANDOM % 10))
364
+ continue
365
+ }
366
+ ;;
367
+ 2)
368
+ echo "=== Attempt 2: merge (handles non-overlapping changes) ==="
369
+ git pull --no-rebase origin main || {
370
+ echo "Merge conflict on attempt 2 — aborting merge"
371
+ git merge --abort 2>/dev/null || true
372
+ sleep $((20 + RANDOM % 10))
373
+ continue
374
+ }
375
+ ;;
376
+ 3)
377
+ echo "=== Attempt 3: merge -X theirs (accept remote on conflict) ==="
378
+ git pull -X theirs --no-rebase origin main || {
379
+ echo "Merge -X theirs failed on attempt 3"
380
+ git merge --abort 2>/dev/null || true
381
+ sleep $((30 + RANDOM % 10))
382
+ continue
383
+ }
384
+ ;;
385
+ esac
386
+ git push origin HEAD:refs/heads/main && { PUSH_SUCCESS="true"; break; }
387
+ echo "Push failed on attempt $attempt"
388
+ sleep $((attempt * 10 + RANDOM % 10))
366
389
  done
390
+ if [ "$PUSH_SUCCESS" != "true" ]; then
391
+ echo "::error::Failed to push after $MAX_RETRIES attempts"
392
+ exit 1
393
+ fi
367
394
 
368
395
  - name: "Verify schedule registered with GitHub"
369
396
  if: github.repository != 'xn-intenton-z2a/agentic-lib' && inputs.schedule != '' && inputs.dry-run != 'true' && inputs.dry-run != true
@@ -269,20 +269,47 @@ jobs:
269
269
  else
270
270
  git commit -m "schedule: set to ${FREQUENCY}, model ${MODEL:-gpt-5-mini}"
271
271
  fi
272
- for attempt in 1 2 3; do
273
- # Use HEAD:refs/heads/main to handle detached HEAD state
274
- git push origin HEAD:refs/heads/main && break
275
- echo "Push failed (attempt $attempt) — pulling and retrying"
276
- git pull --rebase origin main || {
277
- echo "Rebase conflict — aborting rebase and retrying"
278
- git rebase --abort 2>/dev/null || true
279
- }
280
- sleep $((attempt * 2))
281
- if [ "$attempt" -eq 3 ]; then
282
- echo "::error::Failed to push after 3 attempts"
283
- exit 1
284
- fi
272
+ MAX_RETRIES=3
273
+ PUSH_SUCCESS=""
274
+ for attempt in $(seq 1 $MAX_RETRIES); do
275
+ # Tiered conflict resolution (mirrors fix-stuck strategy)
276
+ case $attempt in
277
+ 1)
278
+ echo "=== Attempt 1: rebase (cleanest) ==="
279
+ git pull --rebase origin main || {
280
+ echo "Rebase conflict on attempt 1 — aborting rebase"
281
+ git rebase --abort 2>/dev/null || true
282
+ sleep $((10 + RANDOM % 10))
283
+ continue
284
+ }
285
+ ;;
286
+ 2)
287
+ echo "=== Attempt 2: merge (handles non-overlapping changes) ==="
288
+ git pull --no-rebase origin main || {
289
+ echo "Merge conflict on attempt 2 — aborting merge"
290
+ git merge --abort 2>/dev/null || true
291
+ sleep $((20 + RANDOM % 10))
292
+ continue
293
+ }
294
+ ;;
295
+ 3)
296
+ echo "=== Attempt 3: merge -X theirs (accept remote on conflict) ==="
297
+ git pull -X theirs --no-rebase origin main || {
298
+ echo "Merge -X theirs failed on attempt 3"
299
+ git merge --abort 2>/dev/null || true
300
+ sleep $((30 + RANDOM % 10))
301
+ continue
302
+ }
303
+ ;;
304
+ esac
305
+ git push origin HEAD:refs/heads/main && { PUSH_SUCCESS="true"; break; }
306
+ echo "Push failed on attempt $attempt"
307
+ sleep $((attempt * 10 + RANDOM % 10))
285
308
  done
309
+ if [ "$PUSH_SUCCESS" != "true" ]; then
310
+ echo "::error::Failed to push after $MAX_RETRIES attempts"
311
+ exit 1
312
+ fi
286
313
 
287
314
  - name: "Problem 2 fix: Verify schedule registered with GitHub"
288
315
  if: github.repository != 'xn-intenton-z2a/agentic-lib' && inputs.dry-run != 'true' && inputs.dry-run != true
@@ -107,20 +107,47 @@ jobs:
107
107
  git diff --cached --quiet && echo "No changes" && exit 0
108
108
  VERSION=$(npx @xn-intenton-z2a/agentic-lib version 2>/dev/null || echo "latest")
109
109
  git commit -m "update agentic-lib@${VERSION} [skip ci]"
110
- for attempt in 1 2 3; do
111
- # Use HEAD:refs/heads/main to handle detached HEAD state
112
- git push origin HEAD:refs/heads/main && break
113
- echo "Push failed (attempt $attempt) — pulling and retrying"
114
- git pull --rebase origin main || {
115
- echo "Rebase conflict — aborting rebase and retrying"
116
- git rebase --abort 2>/dev/null || true
117
- }
118
- sleep $((attempt * 2))
119
- if [ "$attempt" -eq 3 ]; then
120
- echo "::error::Failed to push after 3 attempts"
121
- exit 1
122
- fi
110
+ MAX_RETRIES=3
111
+ PUSH_SUCCESS=""
112
+ for attempt in $(seq 1 $MAX_RETRIES); do
113
+ # Tiered conflict resolution (mirrors fix-stuck strategy)
114
+ case $attempt in
115
+ 1)
116
+ echo "=== Attempt 1: rebase (cleanest) ==="
117
+ git pull --rebase origin main || {
118
+ echo "Rebase conflict on attempt 1 — aborting rebase"
119
+ git rebase --abort 2>/dev/null || true
120
+ sleep $((10 + RANDOM % 10))
121
+ continue
122
+ }
123
+ ;;
124
+ 2)
125
+ echo "=== Attempt 2: merge (handles non-overlapping changes) ==="
126
+ git pull --no-rebase origin main || {
127
+ echo "Merge conflict on attempt 2 — aborting merge"
128
+ git merge --abort 2>/dev/null || true
129
+ sleep $((20 + RANDOM % 10))
130
+ continue
131
+ }
132
+ ;;
133
+ 3)
134
+ echo "=== Attempt 3: merge -X theirs (accept remote on conflict) ==="
135
+ git pull -X theirs --no-rebase origin main || {
136
+ echo "Merge -X theirs failed on attempt 3"
137
+ git merge --abort 2>/dev/null || true
138
+ sleep $((30 + RANDOM % 10))
139
+ continue
140
+ }
141
+ ;;
142
+ esac
143
+ git push origin HEAD:refs/heads/main && { PUSH_SUCCESS="true"; break; }
144
+ echo "Push failed on attempt $attempt"
145
+ sleep $((attempt * 10 + RANDOM % 10))
123
146
  done
147
+ if [ "$PUSH_SUCCESS" != "true" ]; then
148
+ echo "::error::Failed to push after $MAX_RETRIES attempts"
149
+ exit 1
150
+ fi
124
151
 
125
152
  - name: Job Summary
126
153
  if: always()
@@ -139,7 +139,7 @@ jobs:
139
139
  steps:
140
140
  - uses: actions/checkout@v6
141
141
  with:
142
- ref: ${{ inputs.ref || github.sha }}
142
+ ref: ${{ inputs.ref || github.ref }}
143
143
  sparse-checkout: |
144
144
  ${{ env.configPath }}
145
145
  MISSION_COMPLETE.md
@@ -308,7 +308,7 @@ jobs:
308
308
  steps:
309
309
  - uses: actions/checkout@v6
310
310
  with:
311
- ref: ${{ inputs.ref || github.sha }}
311
+ ref: ${{ inputs.ref || github.ref }}
312
312
 
313
313
  - uses: actions/setup-node@v6
314
314
  with:
@@ -351,7 +351,7 @@ jobs:
351
351
  steps:
352
352
  - uses: actions/checkout@v6
353
353
  with:
354
- ref: ${{ inputs.ref || github.sha }}
354
+ ref: ${{ inputs.ref || github.ref }}
355
355
 
356
356
  - uses: actions/setup-node@v6
357
357
  with:
@@ -623,7 +623,7 @@ jobs:
623
623
  - uses: actions/checkout@v6
624
624
  with:
625
625
  fetch-depth: 0
626
- ref: ${{ inputs.ref || github.sha }}
626
+ ref: ${{ inputs.ref || github.ref }}
627
627
 
628
628
  - name: Fetch log and screenshot from log branch
629
629
  env:
@@ -735,6 +735,7 @@ jobs:
735
735
  with:
736
736
  commit-message: "agentic-step: maintain features and library"
737
737
  push-ref: ${{ github.ref_name }}
738
+ fail-on-conflict: "false"
738
739
 
739
740
  - name: Capture commit SHA
740
741
  id: get-sha
@@ -762,7 +763,7 @@ jobs:
762
763
  - uses: actions/checkout@v6
763
764
  with:
764
765
  fetch-depth: 0
765
- ref: ${{ inputs.ref || github.sha }}
766
+ ref: ${{ inputs.ref || github.ref }}
766
767
 
767
768
  - name: Fetch log and agent logs from log branch
768
769
  env:
@@ -830,7 +831,7 @@ jobs:
830
831
  - uses: actions/checkout@v6
831
832
  with:
832
833
  fetch-depth: 0
833
- ref: ${{ needs.maintain.outputs.commit-sha || github.sha }}
834
+ ref: ${{ needs.maintain.outputs.commit-sha || github.ref }}
834
835
 
835
836
  - name: Fetch log and screenshot from log branch
836
837
  env:
@@ -895,7 +896,7 @@ jobs:
895
896
  - uses: actions/checkout@v6
896
897
  with:
897
898
  fetch-depth: 0
898
- ref: ${{ needs.maintain.outputs.commit-sha || github.sha }}
899
+ ref: ${{ needs.maintain.outputs.commit-sha || github.ref }}
899
900
 
900
901
  - name: Fetch log, screenshot and state from log branch
901
902
  env:
@@ -980,7 +981,7 @@ jobs:
980
981
  - uses: actions/checkout@v6
981
982
  with:
982
983
  fetch-depth: 0
983
- ref: ${{ needs.maintain.outputs.commit-sha || github.sha }}
984
+ ref: ${{ needs.maintain.outputs.commit-sha || github.ref }}
984
985
 
985
986
  - uses: actions/setup-node@v6
986
987
  with:
@@ -1366,7 +1367,7 @@ jobs:
1366
1367
  steps:
1367
1368
  - uses: actions/checkout@v6
1368
1369
  with:
1369
- ref: ${{ needs.maintain.outputs.commit-sha || github.sha }}
1370
+ ref: ${{ needs.maintain.outputs.commit-sha || github.ref }}
1370
1371
 
1371
1372
  - uses: actions/setup-node@v6
1372
1373
  with:
@@ -1421,7 +1422,7 @@ jobs:
1421
1422
  with:
1422
1423
  fetch-depth: 0
1423
1424
  token: ${{ secrets.GITHUB_TOKEN }}
1424
- ref: ${{ needs.maintain.outputs.commit-sha || github.sha }}
1425
+ ref: ${{ needs.maintain.outputs.commit-sha || github.ref }}
1425
1426
 
1426
1427
  - name: Fetch log and screenshot from log branch
1427
1428
  env:
@@ -1722,7 +1723,7 @@ jobs:
1722
1723
  - uses: actions/checkout@v6
1723
1724
  with:
1724
1725
  fetch-depth: 0
1725
- ref: ${{ needs.maintain.outputs.commit-sha || github.sha }}
1726
+ ref: ${{ needs.maintain.outputs.commit-sha || github.ref }}
1726
1727
 
1727
1728
  - name: Summary
1728
1729
  run: |
@@ -1815,7 +1816,7 @@ jobs:
1815
1816
  && needs.params.result == 'success'
1816
1817
  uses: ./.github/workflows/agentic-lib-test.yml
1817
1818
  with:
1818
- ref: ${{ needs.maintain.outputs.commit-sha || github.sha }}
1819
+ ref: ${{ needs.maintain.outputs.commit-sha || github.ref }}
1819
1820
  secrets: inherit
1820
1821
 
1821
1822
  # ─── Schedule change (if requested) ────────────────────────────────
@@ -1824,7 +1825,7 @@ jobs:
1824
1825
  if: ${{ !cancelled() && needs.params.outputs.dry-run != 'true' && needs.params.outputs.schedule != '' && needs.params.result == 'success' }}
1825
1826
  uses: ./.github/workflows/agentic-lib-schedule.yml
1826
1827
  with:
1827
- ref: ${{ needs.maintain.outputs.commit-sha || github.sha }}
1828
+ ref: ${{ needs.maintain.outputs.commit-sha || github.ref }}
1828
1829
  frequency: ${{ needs.params.outputs.schedule }}
1829
1830
  model: ${{ needs.params.outputs.model }}
1830
1831
  profile: ${{ needs.params.outputs.profile }}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xn-intenton-z2a/agentic-lib",
3
- "version": "7.4.27",
3
+ "version": "7.4.28",
4
4
  "description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -228,14 +228,26 @@ async function executeMissionComplete(octokit, repo, reason) {
228
228
  }
229
229
 
230
230
  // W3: Disable schedule on mission-complete (Benchmark 011 FINDING-4)
231
+ // W6: Skip dispatch if schedule is already at target frequency
231
232
  try {
232
- await octokit.rest.actions.createWorkflowDispatch({
233
- ...repo,
234
- workflow_id: "agentic-lib-schedule.yml",
235
- ref: "main",
236
- inputs: { frequency: "off" },
237
- });
238
- core.info("Dispatched schedule change to off after mission-complete");
233
+ let skipDispatch = false;
234
+ try {
235
+ const tomlContent = readFileSync("agentic-lib.toml", "utf8");
236
+ const supervisorMatch = tomlContent.match(/^\s*supervisor\s*=\s*"([^"]*)"/m);
237
+ if (supervisorMatch && supervisorMatch[1] === "off") {
238
+ core.info("Schedule already off — skipping dispatch");
239
+ skipDispatch = true;
240
+ }
241
+ } catch { /* toml read failed — dispatch anyway */ }
242
+ if (!skipDispatch) {
243
+ await octokit.rest.actions.createWorkflowDispatch({
244
+ ...repo,
245
+ workflow_id: "agentic-lib-schedule.yml",
246
+ ref: "main",
247
+ inputs: { frequency: "off" },
248
+ });
249
+ core.info("Dispatched schedule change to off after mission-complete");
250
+ }
239
251
  } catch (err) {
240
252
  core.warning(`Could not dispatch schedule change: ${err.message}`);
241
253
  }
@@ -315,14 +327,26 @@ async function executeMissionFailed(octokit, repo, reason, metricAssessment) {
315
327
  }
316
328
 
317
329
  // C3: Dispatch schedule change to weekly
330
+ // W6: Skip dispatch if schedule is already at target frequency
318
331
  try {
319
- await octokit.rest.actions.createWorkflowDispatch({
320
- ...repo,
321
- workflow_id: "agentic-lib-schedule.yml",
322
- ref: "main",
323
- inputs: { frequency: "weekly" },
324
- });
325
- core.info("Dispatched schedule change to weekly after mission-failed");
332
+ let skipDispatch = false;
333
+ try {
334
+ const tomlContent = readFileSync("agentic-lib.toml", "utf8");
335
+ const supervisorMatch = tomlContent.match(/^\s*supervisor\s*=\s*"([^"]*)"/m);
336
+ if (supervisorMatch && supervisorMatch[1] === "weekly") {
337
+ core.info("Schedule already weekly — skipping dispatch");
338
+ skipDispatch = true;
339
+ }
340
+ } catch { /* toml read failed — dispatch anyway */ }
341
+ if (!skipDispatch) {
342
+ await octokit.rest.actions.createWorkflowDispatch({
343
+ ...repo,
344
+ workflow_id: "agentic-lib-schedule.yml",
345
+ ref: "main",
346
+ inputs: { frequency: "weekly" },
347
+ });
348
+ core.info("Dispatched schedule change to weekly after mission-failed");
349
+ }
326
350
  } catch (err) {
327
351
  core.warning(`Could not dispatch schedule change: ${err.message}`);
328
352
  }
@@ -16,6 +16,10 @@ inputs:
16
16
  description: "Branch ref to push to (default: current ref_name)"
17
17
  required: false
18
18
  default: ""
19
+ fail-on-conflict:
20
+ description: "Fail the step if push conflicts cannot be resolved (default: true)"
21
+ required: false
22
+ default: "true"
19
23
 
20
24
  runs:
21
25
  using: "composite"
@@ -32,6 +36,13 @@ runs:
32
36
  git reset HEAD -- 'intentïon.md' 'SCREENSHOT_INDEX.png' 2>/dev/null || true
33
37
  git reset HEAD -- agent-log-*.md 2>/dev/null || true
34
38
  git reset HEAD -- agentic-lib-state.toml 2>/dev/null || true
39
+ # W1: Validate test files — reject empty test suites that break vitest
40
+ for f in $(git diff --cached --name-only --diff-filter=ACM -- '*.test.js' '*.test.ts' '*.test.mjs'); do
41
+ if [ -f "$f" ] && ! grep -qE '(describe|test|it)\s*\(' "$f"; then
42
+ echo "::warning::${f} has no test suite (no describe/test/it blocks) — removing from commit"
43
+ git reset HEAD -- "$f"
44
+ fi
45
+ done
35
46
  if git diff --cached --quiet; then
36
47
  echo "No changes to commit"
37
48
  fi
@@ -49,44 +60,75 @@ runs:
49
60
  else
50
61
  REMOTE_REF_EXISTS="true"
51
62
  fi
63
+ # Determine push command based on ref
64
+ if [ -n "$REF" ]; then
65
+ PUSH_CMD="git push origin HEAD:refs/heads/$REF"
66
+ else
67
+ PUSH_CMD="git push"
68
+ fi
69
+ PULL_TARGET="${REF:+origin $REF}"
52
70
  for attempt in $(seq 1 $MAX_RETRIES); do
53
- if [ -n "$REF" ]; then
54
- if [ "$REMOTE_REF_EXISTS" = "true" ]; then
55
- git pull --rebase origin "$REF" || {
56
- echo "Rebase conflict on attempt $attempt aborting rebase and retrying"
71
+ # New branch no pull needed, just push
72
+ if [ -n "$REF" ] && [ "$REMOTE_REF_EXISTS" != "true" ]; then
73
+ $PUSH_CMD && { REMOTE_REF_EXISTS="true"; PUSH_SUCCESS="true"; break; } || true
74
+ sleep $((attempt * 10 + RANDOM % 10))
75
+ continue
76
+ fi
77
+ # Tiered conflict resolution (mirrors fix-stuck strategy)
78
+ case $attempt in
79
+ 1)
80
+ echo "=== Attempt 1: rebase (cleanest) ==="
81
+ git pull --rebase $PULL_TARGET || {
82
+ echo "Rebase conflict on attempt 1 — aborting rebase"
57
83
  git rebase --abort 2>/dev/null || true
58
- sleep $((attempt * 2))
84
+ sleep $((10 + RANDOM % 10))
59
85
  continue
60
86
  }
61
- # After rebase, check if our changes survived (rebase may drop empty commits)
62
- LOCAL_SHA=$(git rev-parse HEAD)
63
- REMOTE_SHA=$(git rev-parse "origin/$REF" 2>/dev/null || echo "")
64
- if [ "$LOCAL_SHA" = "$REMOTE_SHA" ]; then
65
- echo "Rebase dropped local commit (already on remote) nothing to push"
66
- PUSH_SUCCESS="true"
67
- break
68
- fi
87
+ ;;
88
+ 2)
89
+ echo "=== Attempt 2: merge (handles non-overlapping changes) ==="
90
+ git pull --no-rebase $PULL_TARGET || {
91
+ echo "Merge conflict on attempt 2aborting merge"
92
+ git merge --abort 2>/dev/null || true
93
+ sleep $((20 + RANDOM % 10))
94
+ continue
95
+ }
96
+ ;;
97
+ 3)
98
+ echo "=== Attempt 3: merge -X theirs (accept remote on conflict) ==="
99
+ git pull -X theirs --no-rebase $PULL_TARGET || {
100
+ echo "Merge -X theirs failed on attempt 3"
101
+ git merge --abort 2>/dev/null || true
102
+ sleep $((30 + RANDOM % 10))
103
+ continue
104
+ }
105
+ ;;
106
+ esac
107
+ # Check if rebase/merge dropped our commit (already on remote)
108
+ if [ -n "$REF" ]; then
109
+ LOCAL_SHA=$(git rev-parse HEAD)
110
+ REMOTE_SHA=$(git rev-parse "origin/$REF" 2>/dev/null || echo "")
111
+ if [ "$LOCAL_SHA" = "$REMOTE_SHA" ]; then
112
+ echo "Rebase/merge dropped local commit (already on remote) — nothing to push"
113
+ PUSH_SUCCESS="true"
114
+ break
69
115
  fi
70
- # Use HEAD:refs/heads/$REF to handle detached HEAD state
71
- # (actions/checkout with a SHA puts us in detached HEAD, so
72
- # "git push origin main" fails with "src refspec main does not match any")
73
- git push origin "HEAD:refs/heads/$REF" && { REMOTE_REF_EXISTS="true"; PUSH_SUCCESS="true"; break; }
74
- else
75
- git pull --rebase || {
76
- echo "Rebase conflict on attempt $attempt — aborting rebase and retrying"
77
- git rebase --abort 2>/dev/null || true
78
- sleep $((attempt * 2))
79
- continue
80
- }
81
- git push && { PUSH_SUCCESS="true"; break; }
82
116
  fi
83
- echo "Push failed on attempt $attempt, retrying..."
84
- sleep $((attempt * 2))
117
+ $PUSH_CMD && { REMOTE_REF_EXISTS="true"; PUSH_SUCCESS="true"; break; }
118
+ echo "Push failed on attempt $attempt"
119
+ sleep $((attempt * 10 + RANDOM % 10))
85
120
  done
86
121
  # Restore stashed unstaged changes
87
122
  git stash pop 2>/dev/null || true
88
123
  if [ "$PUSH_SUCCESS" != "true" ]; then
89
- echo "::error::Failed to push after $MAX_RETRIES attempts"
90
- exit 1
124
+ FAIL_ON_CONFLICT="${{ inputs.fail-on-conflict }}"
125
+ if [ "$FAIL_ON_CONFLICT" = "false" ]; then
126
+ echo "::warning::Failed to push after $MAX_RETRIES attempts — skipping (fail-on-conflict=false)"
127
+ git rebase --abort 2>/dev/null || true
128
+ git merge --abort 2>/dev/null || true
129
+ else
130
+ echo "::error::Failed to push after $MAX_RETRIES attempts"
131
+ exit 1
132
+ fi
91
133
  fi
92
134
  fi
@@ -17,7 +17,7 @@
17
17
  "author": "",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "@xn-intenton-z2a/agentic-lib": "^7.4.27"
20
+ "@xn-intenton-z2a/agentic-lib": "^7.4.28"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@playwright/test": "^1.58.0",