mandrel 1.90.0 → 1.92.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/.agents/README.md +5 -5
- package/.agents/docs/SDLC.md +75 -107
- package/.agents/docs/configuration.md +1 -1
- package/.agents/docs/quality-gates.md +8 -7
- package/.agents/docs/workflows.md +4 -10
- package/.agents/instructions.md +2 -2
- package/.agents/schemas/agentrc.schema.json +1 -1
- package/.agents/schemas/lifecycle/README.md +2 -1
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/risk-verdict.schema.json +14 -1
- package/.agents/scripts/README.md +0 -46
- package/.agents/scripts/analyze-execution.js +76 -1
- package/.agents/scripts/check-doc-links.js +3 -0
- package/.agents/scripts/check-workflow-cli-lint.js +208 -0
- package/.agents/scripts/epic-audit-recheck.js +1 -1
- package/.agents/scripts/epic-deliver-preflight.js +37 -1
- package/.agents/scripts/epic-plan-clarity.js +16 -6
- package/.agents/scripts/epic-plan-decompose.js +37 -59
- package/.agents/scripts/epic-plan-healthcheck.js +39 -6
- package/.agents/scripts/epic-plan-spec.js +41 -168
- package/.agents/scripts/epic-reconcile.js +7 -1
- package/.agents/scripts/lib/Logger.js +4 -4
- package/.agents/scripts/lib/audit-suite/index.js +4 -5
- package/.agents/scripts/lib/audit-suite/runner.js +6 -4
- package/.agents/scripts/lib/audit-suite/selector.js +2 -3
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/command-header.js +20 -0
- package/.agents/scripts/lib/config/github.js +1 -3
- package/.agents/scripts/lib/config-settings-schema.js +4 -3
- package/.agents/scripts/lib/degraded-mode.js +1 -1
- package/.agents/scripts/lib/label-constants.js +18 -3
- package/.agents/scripts/lib/label-taxonomy.js +14 -3
- package/.agents/scripts/lib/mutation/baseline-snapshot.js +7 -6
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +18 -8
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -4
- package/.agents/scripts/lib/orchestration/docs-digest.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +4 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +22 -12
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +6 -1
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +18 -2
- package/.agents/scripts/{lifecycle-diff.js → lib/orchestration/lifecycle/ledger-diff.js} +10 -76
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +32 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +510 -0
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +177 -0
- package/.agents/scripts/lib/orchestration/plan-metrics.js +445 -0
- package/.agents/scripts/lib/orchestration/plan-persist/amend.js +359 -0
- package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +127 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +978 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +191 -0
- package/.agents/scripts/lib/orchestration/plan-reachability.js +160 -0
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +3 -3
- package/.agents/scripts/lib/orchestration/planning-risk.js +1 -1
- package/.agents/scripts/lib/orchestration/remote-verifier.js +165 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +10 -0
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +1 -1
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +11 -2
- package/.agents/scripts/lib/plan-phase-cleanup.js +16 -0
- package/.agents/scripts/lib/presentation/manifest-persistence.js +2 -3
- package/.agents/scripts/lib/templates/spec-author-prompts.js +74 -0
- package/.agents/scripts/plan-context.js +186 -0
- package/.agents/scripts/plan-critics.js +227 -0
- package/.agents/scripts/plan-persist.js +383 -0
- package/.agents/scripts/pr-watch-with-update.js +8 -7
- package/.agents/scripts/run-lint.js +10 -11
- package/.agents/scripts/single-story-init.js +22 -0
- package/.agents/scripts/story-plan.js +19 -7
- package/.agents/scripts/sync-claude-commands.js +10 -5
- package/.agents/skills/core/knowledge-transfer/SKILL.md +11 -7
- package/.agents/workflows/audit-documentation.md +5 -7
- package/.agents/workflows/audit-lighthouse.md +1 -0
- package/.agents/workflows/audit-security.md +1 -0
- package/.agents/workflows/audit-to-stories.md +16 -5
- package/.agents/workflows/deliver.md +8 -0
- package/.agents/workflows/git-deliver.md +76 -243
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/code-review.md +7 -6
- package/.agents/workflows/helpers/deliver-epic.md +16 -4
- package/.agents/workflows/helpers/plan-epic-reference.md +109 -96
- package/.agents/workflows/helpers/plan-epic.md +246 -999
- package/.agents/workflows/helpers/plan-story.md +14 -14
- package/.agents/workflows/helpers/scope-triage-gate.md +6 -4
- package/.agents/workflows/helpers/single-story-deliver.md +13 -4
- package/.agents/workflows/helpers/worktree-lifecycle.md +9 -7
- package/.agents/workflows/mandrel-update.md +144 -466
- package/.agents/workflows/plan.md +73 -143
- package/docs/CHANGELOG.md +20 -0
- package/lib/cli/registry.js +21 -3
- package/package.json +1 -1
- package/.agents/schemas/loop-unit.schema.json +0 -70
- package/.agents/scripts/assert-branch.js +0 -81
- package/.agents/scripts/check-loop-units.js +0 -204
- package/.agents/scripts/detect-merges.js +0 -111
- package/.agents/scripts/git-pr-quality-gate.js +0 -205
- package/.agents/scripts/git-rebase-and-resolve.js +0 -234
- package/.agents/scripts/hierarchy-gate.js +0 -192
- package/.agents/scripts/hydrate-context.js +0 -179
- package/.agents/scripts/lib/loop-units/validate-loop-unit.js +0 -197
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +0 -167
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +0 -69
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +0 -86
- package/.agents/scripts/loc-delta.js +0 -205
- package/.agents/scripts/render-manifest.js +0 -143
- package/.agents/scripts/run-audit-suite.js +0 -97
- package/.agents/scripts/select-audits.js +0 -155
- package/.agents/scripts/update-mutation-baseline.js +0 -189
- package/.agents/workflows/explain.md +0 -118
- package/.agents/workflows/git-merge-pr.md +0 -377
- package/.agents/workflows/helpers/epic-plan-decompose.md +0 -22
- package/.agents/workflows/helpers/epic-plan-spec.md +0 -22
- package/.agents/workflows/loops/README.md +0 -65
- package/.agents/workflows/loops/fix-failing-tests.md +0 -74
- package/.agents/workflows/loops/nightly-audit.md +0 -81
- package/.agents/workflows/loops/watch-ci.md +0 -68
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* update-mutation-baseline.js — Refresh `baselines/mutation.json` from a
|
|
4
|
-
* fresh Stryker run (Story #1736, Task #1752 + #1753).
|
|
5
|
-
*
|
|
6
|
-
* Reads the configured mutation gate (`delivery.quality.gates.mutation`),
|
|
7
|
-
* invokes the Stryker runner from `lib/mutation/stryker-runner.js`, and
|
|
8
|
-
* atomically rewrites the baseline file with the new per-workspace
|
|
9
|
-
* mutation scores. Preserves the configured `tolerancePct` (from the
|
|
10
|
-
* gate's `tolerance.value`) so the file is self-contained — consumers
|
|
11
|
-
* don't need to read `.agentrc.json` to interpret the baseline.
|
|
12
|
-
*
|
|
13
|
-
* Exit codes:
|
|
14
|
-
* 0 — baseline refreshed (didChange true) or no change (didChange false)
|
|
15
|
-
* 0 — Stryker skipped (no config) with an explanatory line on stderr
|
|
16
|
-
* 1 — Stryker invocation failed
|
|
17
|
-
*
|
|
18
|
-
* The refresh is intentionally non-fatal when Stryker is not configured:
|
|
19
|
-
* the operator can run `npx stryker init` and re-invoke this script.
|
|
20
|
-
*
|
|
21
|
-
* DORMANT BY DESIGN (Story #3665). This script and the rest of the
|
|
22
|
-
* mutation-testing surface ship built-but-unwired: Mandrel declares no
|
|
23
|
-
* `delivery.quality.gates.mutation` block and no `stryker.conf.*`, so the
|
|
24
|
-
* gate never enters the `check-baselines` pipeline and this refresh
|
|
25
|
-
* self-skips (no Stryker config detected). The dormancy is an evaluated,
|
|
26
|
-
* intentional opt-in — not an oversight. A spike (Story #3665) concluded
|
|
27
|
-
* DEFER on activation. The essential rationale, preserved here so it does
|
|
28
|
-
* not depend on a point-in-time writeup:
|
|
29
|
-
*
|
|
30
|
-
* - Cost model: a full `.agents/scripts` Stryker baseline is a
|
|
31
|
-
* tens-of-minutes-to-multi-hour job even under `coverageAnalysis:
|
|
32
|
-
* 'perTest'` at `concurrency: 8`. Stryker reruns the affected test set
|
|
33
|
-
* once per mutant (~5,000–6,500 mutants over ~19k effective LOC), and
|
|
34
|
-
* Mandrel's tests live in a sibling `tests/` tree (not colocated with
|
|
35
|
-
* source), so there is no cheap per-source-file partition for `perTest`
|
|
36
|
-
* to exploit. Activation therefore belongs on a nightly `schedule`
|
|
37
|
-
* (never per-PR), with PR-time `check-baselines` consuming the
|
|
38
|
-
* nightly-produced baseline as a read-only ratchet.
|
|
39
|
-
* - Baseline-shape fit: the kernel/check side keys mutation rows by
|
|
40
|
-
* `path` (`lib/baselines/kinds/mutation.js` — per-file rows + per-
|
|
41
|
-
* component rollup, the right fit for this single-package repo), but
|
|
42
|
-
* this refresh writer (`lib/mutation/baseline-snapshot.js`) emits a
|
|
43
|
-
* workspace-keyed `{ workspaces: { '*': score } }` snapshot that
|
|
44
|
-
* collapses to one repo-wide number with zero per-file resolution.
|
|
45
|
-
* The two shapes do not agree; activation must first reconcile this
|
|
46
|
-
* writer to emit the kernel's path-keyed `rows[]`/`rollup` envelope.
|
|
47
|
-
* - Prerequisites: a `node --test` Stryker runner integration (no
|
|
48
|
-
* first-party `@stryker-mutator` plugin exists for the Node built-in
|
|
49
|
-
* runner), the shape reconciliation above, and nightly CI plumbing —
|
|
50
|
-
* together an Epic-sized effort, not a config flip.
|
|
51
|
-
*
|
|
52
|
-
* Re-evaluate (promote to an activation Epic) when a coverage-gamed
|
|
53
|
-
* regression ships, the project migrates to a test runner with first-party
|
|
54
|
-
* Stryker support, or an operator opts in for a specific high-risk subtree.
|
|
55
|
-
*/
|
|
56
|
-
|
|
57
|
-
import path from 'node:path';
|
|
58
|
-
|
|
59
|
-
import { resolveDiffScope } from './lib/baselines/diff-scope-cli.js';
|
|
60
|
-
import { runAsCli } from './lib/cli-utils.js';
|
|
61
|
-
import {
|
|
62
|
-
getQuality,
|
|
63
|
-
PROJECT_ROOT,
|
|
64
|
-
resolveConfig,
|
|
65
|
-
} from './lib/config-resolver.js';
|
|
66
|
-
import { Logger } from './lib/Logger.js';
|
|
67
|
-
import {
|
|
68
|
-
DEFAULT_BASELINE_PATH,
|
|
69
|
-
DEFAULT_TOLERANCE_PCT,
|
|
70
|
-
writeBaseline,
|
|
71
|
-
} from './lib/mutation/baseline-snapshot.js';
|
|
72
|
-
import { runStryker } from './lib/mutation/stryker-runner.js';
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Resolve the mutation gate config relevant to baseline refresh.
|
|
76
|
-
*
|
|
77
|
-
* @param {object} config Canonical resolved config (`resolveConfig()` output).
|
|
78
|
-
* @returns {{ baselinePath: string, tolerancePct: number, strykerConfigPath: string | null }}
|
|
79
|
-
*/
|
|
80
|
-
export function resolveMutationGate(config) {
|
|
81
|
-
const quality = getQuality(config);
|
|
82
|
-
const gate = quality.gates?.mutation ?? {};
|
|
83
|
-
const baselinePath =
|
|
84
|
-
typeof gate.baselinePath === 'string' && gate.baselinePath.length > 0
|
|
85
|
-
? gate.baselinePath
|
|
86
|
-
: DEFAULT_BASELINE_PATH;
|
|
87
|
-
const tol = gate.tolerance;
|
|
88
|
-
const tolerancePct =
|
|
89
|
-
tol &&
|
|
90
|
-
typeof tol === 'object' &&
|
|
91
|
-
Number.isFinite(tol.value) &&
|
|
92
|
-
tol.value >= 0
|
|
93
|
-
? tol.value
|
|
94
|
-
: DEFAULT_TOLERANCE_PCT;
|
|
95
|
-
const strykerConfigPath =
|
|
96
|
-
typeof gate.strykerConfigPath === 'string' &&
|
|
97
|
-
gate.strykerConfigPath.length > 0
|
|
98
|
-
? gate.strykerConfigPath
|
|
99
|
-
: null;
|
|
100
|
-
return { baselinePath, tolerancePct, strykerConfigPath };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* @param {{
|
|
105
|
-
* cwd?: string,
|
|
106
|
-
* runStrykerFn?: typeof runStryker,
|
|
107
|
-
* writeBaselineFn?: typeof writeBaseline,
|
|
108
|
-
* resolveConfigFn?: typeof resolveConfig,
|
|
109
|
-
* logger?: { info?: (m: string) => void, warn?: (m: string) => void, error?: (m: string) => void },
|
|
110
|
-
* }} [opts]
|
|
111
|
-
* @returns {Promise<{ status: 0 | 1, didChange: boolean, skipped: boolean, reason?: string, baselinePath: string }>}
|
|
112
|
-
*/
|
|
113
|
-
export async function refreshMutationBaseline({
|
|
114
|
-
cwd = PROJECT_ROOT,
|
|
115
|
-
runStrykerFn = runStryker,
|
|
116
|
-
writeBaselineFn = writeBaseline,
|
|
117
|
-
resolveConfigFn = resolveConfig,
|
|
118
|
-
logger = Logger,
|
|
119
|
-
} = {}) {
|
|
120
|
-
const config = resolveConfigFn({ cwd });
|
|
121
|
-
const gate = resolveMutationGate(config);
|
|
122
|
-
const absBaseline = path.isAbsolute(gate.baselinePath)
|
|
123
|
-
? gate.baselinePath
|
|
124
|
-
: path.resolve(cwd, gate.baselinePath);
|
|
125
|
-
|
|
126
|
-
logger.info?.(`[mutation] refreshing baseline → ${gate.baselinePath}`);
|
|
127
|
-
const runResult = await runStrykerFn({
|
|
128
|
-
cwd,
|
|
129
|
-
configPath: gate.strykerConfigPath,
|
|
130
|
-
});
|
|
131
|
-
if (runResult.skipped) {
|
|
132
|
-
logger.info?.(
|
|
133
|
-
`[mutation] skipped — ${runResult.reason ?? 'runner reported skip'}`,
|
|
134
|
-
);
|
|
135
|
-
return {
|
|
136
|
-
status: 0,
|
|
137
|
-
didChange: false,
|
|
138
|
-
skipped: true,
|
|
139
|
-
reason: runResult.reason ?? 'runner-skip',
|
|
140
|
-
baselinePath: absBaseline,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
if (!runResult.ok) {
|
|
144
|
-
logger.error?.(
|
|
145
|
-
`[mutation] Stryker invocation failed: ${runResult.error ?? 'unknown error'}`,
|
|
146
|
-
);
|
|
147
|
-
return {
|
|
148
|
-
status: 1,
|
|
149
|
-
didChange: false,
|
|
150
|
-
skipped: false,
|
|
151
|
-
reason: runResult.error ?? 'stryker-failed',
|
|
152
|
-
baselinePath: absBaseline,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const writeResult = writeBaselineFn(absBaseline, {
|
|
157
|
-
tolerancePct: gate.tolerancePct,
|
|
158
|
-
workspaces: runResult.byWorkspace,
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
if (writeResult.didChange) {
|
|
162
|
-
logger.info?.(`[mutation] baseline updated at ${gate.baselinePath}`);
|
|
163
|
-
} else {
|
|
164
|
-
logger.info?.(`[mutation] baseline unchanged at ${gate.baselinePath}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
status: 0,
|
|
169
|
-
didChange: writeResult.didChange,
|
|
170
|
-
skipped: false,
|
|
171
|
-
baselinePath: absBaseline,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async function main() {
|
|
176
|
-
// Story #1974 — `--diff-scope` accepted for CLI parity with other
|
|
177
|
-
// update-*-baseline scripts; no row-narrowing effect because mutation
|
|
178
|
-
// baseline is workspace-keyed (not file-keyed).
|
|
179
|
-
const diffScope = resolveDiffScope({ argv: process.argv.slice(2) });
|
|
180
|
-
if (diffScope) {
|
|
181
|
-
Logger.info(
|
|
182
|
-
`[mutation] --diff-scope ${diffScope.ref}: noted (informational only).`,
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
const result = await refreshMutationBaseline();
|
|
186
|
-
if (result.status !== 0) process.exit(result.status);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
runAsCli(import.meta.url, main, { source: 'update-mutation-baseline' });
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description:
|
|
3
|
-
Walk the operator through a code change until they genuinely understand it.
|
|
4
|
-
Targets a PR, a branch, or the working-tree diff, then drives the
|
|
5
|
-
`core/knowledge-transfer` skill (restate-first, why-ladder, mastery gates,
|
|
6
|
-
persistent checklist) with an operator-controlled stop at every checkpoint.
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# /explain [PR# | branch | --staged | --diff <ref>]
|
|
10
|
-
|
|
11
|
-
## Overview
|
|
12
|
-
|
|
13
|
-
`/explain` is the **operator-facing comprehension command**. Use it when code
|
|
14
|
-
landed (or is about to) and you want to be sure you actually understand it —
|
|
15
|
-
the problem it solves, why it was solved this way, the design decisions, the
|
|
16
|
-
edge cases, and the blast radius. It is the after-the-fact counterpart to
|
|
17
|
-
[`/plan`](helpers/plan-epic.md) Phase 11 (which walks the operator through a
|
|
18
|
-
*plan* before delivery); both drive the same engine.
|
|
19
|
-
|
|
20
|
-
```text
|
|
21
|
-
/explain 1234 → walk through merged/open PR #1234
|
|
22
|
-
/explain story-104 → walk through the diff of branch story-104 vs main
|
|
23
|
-
/explain --staged → walk through the currently staged changes
|
|
24
|
-
/explain --diff HEAD~3 → walk through the diff from HEAD~3 to working tree
|
|
25
|
-
/explain → no subject given: ask what to explain, then proceed
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
This command **delegates the method** to the
|
|
29
|
-
[`core/knowledge-transfer`](../skills/core/knowledge-transfer/SKILL.md) skill.
|
|
30
|
-
This file owns only **subject resolution** (which diff) and the handoff into
|
|
31
|
-
that skill. Read the skill before running — it defines the loop, the
|
|
32
|
-
why-ladder, the depth levels, the quizzing rules, and the operator-stop
|
|
33
|
-
contract.
|
|
34
|
-
|
|
35
|
-
## When to use `/explain`
|
|
36
|
-
|
|
37
|
-
| Scenario | Command |
|
|
38
|
-
| --- | --- |
|
|
39
|
-
| Understand a change that already merged | `/explain <PR#>` |
|
|
40
|
-
| Understand a branch before merging it | `/explain <branch>` |
|
|
41
|
-
| Understand what you are about to commit | `/explain --staged` |
|
|
42
|
-
| Understand a freshly planned Epic backlog | `/plan` Phase 11 (automatic) |
|
|
43
|
-
|
|
44
|
-
## Step 1 — Resolve the subject
|
|
45
|
-
|
|
46
|
-
Determine the change to explain from the argument:
|
|
47
|
-
|
|
48
|
-
- **PR number** (`/explain 1234`) — read the PR metadata and diff:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
gh pr view 1234 --json title,body,state,headRefName,baseRefName
|
|
52
|
-
gh pr diff 1234
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
- **Branch** (`/explain story-104`) — diff the branch against the base
|
|
56
|
-
branch (`project.baseBranch`, default `main`):
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
git diff main...story-104
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
- **`--staged`** — the staged working-tree changes:
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
git diff --staged
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
- **`--diff <ref>`** — an explicit diff range:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
git diff <ref>
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
- **No argument** — ask the operator which PR, branch, or range they want
|
|
75
|
-
walked through, then resolve as above. Do not guess.
|
|
76
|
-
|
|
77
|
-
Read the resolved diff **and** the surrounding code it touches before
|
|
78
|
-
explaining anything — ground every explanation in the real artifact, never
|
|
79
|
-
the title alone.
|
|
80
|
-
|
|
81
|
-
## Step 2 — Drive the comprehension loop
|
|
82
|
-
|
|
83
|
-
Activate [`core/knowledge-transfer`](../skills/core/knowledge-transfer/SKILL.md)
|
|
84
|
-
with the resolved change as the subject. Follow the skill exactly:
|
|
85
|
-
|
|
86
|
-
1. **Frame & gather** — state the change in one sentence; write the checklist
|
|
87
|
-
to `temp/comprehension-<subject>.md`.
|
|
88
|
-
2. **Restate-first** — ask the operator what they already understand before
|
|
89
|
-
explaining.
|
|
90
|
-
3. **Fill gaps one layer at a time** along the why-ladder
|
|
91
|
-
(problem → why → branches → solution → why this solution → design
|
|
92
|
-
decisions → edge cases → broader impact), confirming mastery at each layer
|
|
93
|
-
and offering the stop exit before advancing.
|
|
94
|
-
4. **Close** — summarize coverage, note any unchecked items, leave the
|
|
95
|
-
checklist artifact in place.
|
|
96
|
-
|
|
97
|
-
Honor depth requests (ELI5 / intern / peer) and quiz via the host's
|
|
98
|
-
structured-question mechanism when it sharpens understanding.
|
|
99
|
-
|
|
100
|
-
## Constraints
|
|
101
|
-
|
|
102
|
-
- **Operator-invoked only.** Never auto-fire `/explain`, and never run it
|
|
103
|
-
inside a non-interactive delivery sub-agent — there is no operator to
|
|
104
|
-
teach.
|
|
105
|
-
- **Operator controls the exit.** Stop the moment the operator says they are
|
|
106
|
-
satisfied; this command never blocks or gates anything.
|
|
107
|
-
- **Read-only.** `/explain` teaches; it does not modify code, tickets, or
|
|
108
|
-
branches. The only file it writes is the `temp/` checklist artifact.
|
|
109
|
-
|
|
110
|
-
## See also
|
|
111
|
-
|
|
112
|
-
- [`core/knowledge-transfer`](../skills/core/knowledge-transfer/SKILL.md) —
|
|
113
|
-
the comprehension engine this command drives.
|
|
114
|
-
- [`/plan`](helpers/plan-epic.md) — Phase 11 runs the same engine over a plan
|
|
115
|
-
before delivery.
|
|
116
|
-
- `/code-review` (Claude Code built-in) — correctness review of a diff. A
|
|
117
|
-
different concern: `/explain` builds *operator* understanding, not a defect
|
|
118
|
-
list.
|
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: >-
|
|
3
|
-
Analyze, validate, resolve conflicts, and merge a given pull request by
|
|
4
|
-
number.
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# /git-merge-pr [#PR_LIST]
|
|
8
|
-
|
|
9
|
-
This workflow performs a full end-to-end merge of one or more pull requests: it
|
|
10
|
-
analyzes each PR diff, validates linting and tests, resolves any merge
|
|
11
|
-
conflicts, and completes the merge into the target base branch.
|
|
12
|
-
|
|
13
|
-
> **When to run**: Any time one or more PRs are ready for merge review and you
|
|
14
|
-
> want an automated merge with conflict resolution and quality gates enforced.
|
|
15
|
-
>
|
|
16
|
-
> **Persona**: `devops-engineer` · **Skills**:
|
|
17
|
-
> `core/git-workflow-and-versioning`
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Step 0 — Resolve Context
|
|
22
|
-
|
|
23
|
-
1. Resolve one or more `[PR_NUMBER]` values from the slash-command argument
|
|
24
|
-
(e.g. `/git-merge-pr 42 43 45` → `PR_LIST=[42, 43, 45]`).
|
|
25
|
-
2. **Sequential Loop**: Steps 1 through 7 must be performed **sequentially** for
|
|
26
|
-
each PR in the `PR_LIST`. Complete the full merge and cleanup for one PR
|
|
27
|
-
before starting the next.
|
|
28
|
-
3. For the current `[PR_NUMBER]`, fetch metadata from GitHub by calling
|
|
29
|
-
`mcp__github__pull_request_read` (method `get`) with the repo's
|
|
30
|
-
`owner` / `repo` and `pullNumber: [PR_NUMBER]`. From the response, read
|
|
31
|
-
`number`, `title`, `headRefName`, `baseRefName`, `state`, `mergeable`,
|
|
32
|
-
and `mergeStateStatus`.
|
|
33
|
-
|
|
34
|
-
4. From the output, resolve:
|
|
35
|
-
- `[PR_TITLE]` — the PR title.
|
|
36
|
-
- `[HEAD_BRANCH]` — the source branch (`headRefName`).
|
|
37
|
-
- `[BASE_BRANCH]` — the merge target (`baseRefName`).
|
|
38
|
-
- `[PR_STATE]` — must be `OPEN`. If `CLOSED` or `MERGED`, **SKIP** this PR
|
|
39
|
-
and proceed to the next one in the list.
|
|
40
|
-
- `[MERGEABLE]` — initial GitHub mergeability signal (`MERGEABLE`,
|
|
41
|
-
`CONFLICTING`, or `UNKNOWN`).
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## Step 1 — PR Analysis
|
|
46
|
-
|
|
47
|
-
Fetch the full diff and review the scope of changes:
|
|
48
|
-
|
|
49
|
-
```powershell
|
|
50
|
-
gh pr diff [PR_NUMBER]
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
> **Why `gh pr diff` and not MCP?** `mcp__github__pull_request_read`'s
|
|
54
|
-
> `get_diff` method returns a structured response rather than the raw
|
|
55
|
-
> unified diff this step renders to the operator. `gh pr diff` stays
|
|
56
|
-
> until MCP exposes a raw-diff equivalent.
|
|
57
|
-
|
|
58
|
-
Summarize the following to the operator before proceeding:
|
|
59
|
-
|
|
60
|
-
- **Files changed** (count and list).
|
|
61
|
-
- **Lines added / removed**.
|
|
62
|
-
- **Areas of concern** — any files that touch shared utilities, schemas,
|
|
63
|
-
migrations, or critical infrastructure.
|
|
64
|
-
- **Initial mergeability status** from Step 0.
|
|
65
|
-
|
|
66
|
-
> This is a read-only analysis step. No files are modified yet.
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Step 2 — Checkout & Sync
|
|
71
|
-
|
|
72
|
-
Delegate the rebase orchestration to `git-rebase-and-resolve.js`. It fetches
|
|
73
|
-
`origin`, checks out the head branch, and rebases it onto the base — then
|
|
74
|
-
reports the outcome in structured form so this skill routes on outcome
|
|
75
|
-
instead of re-implementing the retry loop.
|
|
76
|
-
|
|
77
|
-
```powershell
|
|
78
|
-
node .agents/scripts/git-rebase-and-resolve.js --onto origin/[BASE_BRANCH] --head [HEAD_BRANCH] --json
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Parse the JSON result. Route on `outcome`:
|
|
82
|
-
|
|
83
|
-
- `clean` → rebase landed with no conflicts. Proceed to Step 3.
|
|
84
|
-
- `conflict` → `conflictedFiles[]` lists the unmerged paths. Proceed to
|
|
85
|
-
Step 2.5.
|
|
86
|
-
- `error` → git returned an error unrelated to conflicts. **STOP** and
|
|
87
|
-
surface `stderr` to the operator.
|
|
88
|
-
|
|
89
|
-
### Step 2.5 — Conflict Resolution
|
|
90
|
-
|
|
91
|
-
Follow the shared conflict-resolution procedure in
|
|
92
|
-
[`helpers/_merge-conflict-template.md`](helpers/_merge-conflict-template.md):
|
|
93
|
-
read both sides
|
|
94
|
-
of each file in `conflictedFiles[]`, apply both when compatible (or choose a
|
|
95
|
-
side with an explicit rationale), never silently drop code, then stage the
|
|
96
|
-
resolutions.
|
|
97
|
-
|
|
98
|
-
Continue the rebase via the script — it calls `git rebase --continue` and
|
|
99
|
-
reports whether the rebase is now clean, still has conflicts (cascading
|
|
100
|
-
conflict), or hit a different error:
|
|
101
|
-
|
|
102
|
-
```powershell
|
|
103
|
-
node .agents/scripts/git-rebase-and-resolve.js --continue --json
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Loop until `outcome` is `continued`. If you need to bail out entirely:
|
|
107
|
-
|
|
108
|
-
```powershell
|
|
109
|
-
node .agents/scripts/git-rebase-and-resolve.js --abort --json
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Once the rebase completes cleanly, force-push the rebased branch:
|
|
113
|
-
|
|
114
|
-
```powershell
|
|
115
|
-
git push --force-with-lease origin [HEAD_BRANCH]
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## Step 3 — Quality Gate (Lint + Format + Test)
|
|
121
|
-
|
|
122
|
-
Run the full lint / format / test suite via the gate wrapper. The wrapper
|
|
123
|
-
owns the command list so this skill doesn't rot when a project renames
|
|
124
|
-
`lint` → `lint:ci` or swaps Biome for ESLint. The default check set is
|
|
125
|
-
`lint`, `format:check`, `test`; override via `.agentrc.json → github.branchProtection`.
|
|
126
|
-
|
|
127
|
-
```powershell
|
|
128
|
-
node .agents/scripts/git-pr-quality-gate.js --json
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
The script emits a JSON result `{ ok, checks: [...], failed: [...] }`.
|
|
132
|
-
Exit code 0 means every check passed. On failure:
|
|
133
|
-
|
|
134
|
-
1. Read the `failed[]` entries (each has `name` and `reason`).
|
|
135
|
-
2. For **format** failures, run `npx biome format --write .` to auto-fix,
|
|
136
|
-
then re-run the gate.
|
|
137
|
-
3. For **lint** failures, apply the minimal manual fix.
|
|
138
|
-
4. For **test** failures, classify before fixing:
|
|
139
|
-
- **Pre-existing failures** (unrelated to this PR's diff): alert the
|
|
140
|
-
operator and ask whether to proceed or block.
|
|
141
|
-
- **Regression introduced by this PR**: apply the fix.
|
|
142
|
-
5. Commit the fixes and re-push:
|
|
143
|
-
|
|
144
|
-
```powershell
|
|
145
|
-
git add .
|
|
146
|
-
# justification: post-CI remediation; CI lint+test gate ran upstream and produced the failure being fixed here. Local hook would re-run the same gate.
|
|
147
|
-
git commit --no-verify -m "fix(ci): resolve quality-gate failures on [HEAD_BRANCH] for PR #[PR_NUMBER]"
|
|
148
|
-
git push origin [HEAD_BRANCH]
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
6. Re-run the gate until it exits 0 before continuing to Step 3.5.
|
|
152
|
-
|
|
153
|
-
> If a failure cannot be resolved after exhausting reasonable remediation
|
|
154
|
-
> attempts, **STOP** and escalate to the operator with a detailed summary.
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Step 3.5 — Unified Baselines Gate (`check-baselines`)
|
|
159
|
-
|
|
160
|
-
`check-baselines.js` is the single canonical floor + tolerance + schema
|
|
161
|
-
gate for every baseline kind (coverage, crap, maintainability, mutation).
|
|
162
|
-
It owns absolute-floor enforcement, schema validation, and kernel-mismatch
|
|
163
|
-
surfacing. The per-kind regression CLIs that previously layered on top of
|
|
164
|
-
this gate were removed in Epic #1943 — `check-baselines.js` is now the
|
|
165
|
-
sole source of truth for baseline regressions at merge time.
|
|
166
|
-
|
|
167
|
-
```powershell
|
|
168
|
-
node .agents/scripts/check-baselines.js --format text
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Exit codes:
|
|
172
|
-
|
|
173
|
-
- `0` — every enabled gate's floors are met and no schema errors.
|
|
174
|
-
- `1` — any floor breach. Inspect the JSON output
|
|
175
|
-
(`--format json`, the default) to see which kind / component / axis
|
|
176
|
-
fell below floor.
|
|
177
|
-
- `2` — any baseline failed schema validation. Regenerate the offending
|
|
178
|
-
baseline through its per-kind update script.
|
|
179
|
-
- `3` — config resolution error (typically a malformed `.agentrc.json`).
|
|
180
|
-
|
|
181
|
-
Treat any non-zero exit as a hard merge block before proceeding to Step 4.
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
|
-
## Step 4 — Final Mergeability Check
|
|
186
|
-
|
|
187
|
-
Re-query GitHub to confirm the PR is now clean and ready to merge by
|
|
188
|
-
calling `mcp__github__pull_request_read` (method `get`) with the repo's
|
|
189
|
-
`owner` / `repo` and `pullNumber: [PR_NUMBER]`. Read `mergeable`,
|
|
190
|
-
`mergeStateStatus`, `reviewDecision`, and `statusCheckRollup` from the
|
|
191
|
-
response (the same fields the prior `gh pr view --json` shape exposed).
|
|
192
|
-
|
|
193
|
-
Verify:
|
|
194
|
-
|
|
195
|
-
- `mergeable` is `MERGEABLE`.
|
|
196
|
-
- `mergeStateStatus` is `CLEAN` or `HAS_HOOKS`.
|
|
197
|
-
- Required CI checks (if any) are passing (`statusCheckRollup` → all `SUCCESS`
|
|
198
|
-
or `NEUTRAL`).
|
|
199
|
-
|
|
200
|
-
If any blocking condition remains, resolve it before proceeding to the merge
|
|
201
|
-
step.
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
## Step 5 — Merge
|
|
206
|
-
|
|
207
|
-
Merge the PR as a squash commit and delete the head branch. Call
|
|
208
|
-
`mcp__github__merge_pull_request` with the repo's `owner` / `repo`,
|
|
209
|
-
`pullNumber: [PR_NUMBER]`, `merge_method: "squash"`, and
|
|
210
|
-
`delete_branch: true`.
|
|
211
|
-
|
|
212
|
-
> **MCP coverage gap — auto-merge queueing.** `mcp__github__merge_pull_request`
|
|
213
|
-
> performs an **immediate** merge; it does not enable GitHub's native
|
|
214
|
-
> auto-merge queue. When the workflow needs to queue the merge behind
|
|
215
|
-
> required-check completion (the default `--auto` posture), fall back to
|
|
216
|
-
> the shell form `gh pr merge [PR_NUMBER] --auto --squash --delete-branch`.
|
|
217
|
-
> Use the MCP call when CI is already green and the merge can fire
|
|
218
|
-
> synchronously (the common case after a clean Step 4 verdict).
|
|
219
|
-
>
|
|
220
|
-
> **Merge strategy guidance** (override with operator instruction):
|
|
221
|
-
>
|
|
222
|
-
> - `mcp__github__merge_pull_request` with `merge_method: "squash"` — the
|
|
223
|
-
> default for an already-green PR; clean history, single squash commit.
|
|
224
|
-
> - `gh pr merge [PR_NUMBER] --auto --squash --delete-branch` — auto-merge
|
|
225
|
-
> queueing fallback when required checks are still pending. Requires
|
|
226
|
-
> `allow_auto_merge=true` on the repo (Story #1239 turns this on).
|
|
227
|
-
> - `gh pr merge [PR_NUMBER] --squash` (no `--auto`) — synchronous merge;
|
|
228
|
-
> use only when bypassing auto-merge is intentional (e.g. CI is broken
|
|
229
|
-
> and a hotfix is going in under admin override).
|
|
230
|
-
> - `--merge` — preserves the full commit history from `[HEAD_BRANCH]` (use for
|
|
231
|
-
> Epic branches with meaningful commit granularity); pass `merge_method:
|
|
232
|
-
> "merge"` to the MCP call or `--merge` to `gh pr merge`.
|
|
233
|
-
> - `--rebase` — linear history; ideal for small, atomic PRs; pass
|
|
234
|
-
> `merge_method: "rebase"` to the MCP call or `--rebase` to `gh pr merge`.
|
|
235
|
-
|
|
236
|
-
After the merge command returns, perform a conflict marker scan to confirm no
|
|
237
|
-
stray markers entered the base branch. Delegate to `detect-merges.js` — it
|
|
238
|
-
owns the scan logic and is the same script used by `/deliver` Phase 5.3.
|
|
239
|
-
|
|
240
|
-
```powershell
|
|
241
|
-
git checkout [BASE_BRANCH]
|
|
242
|
-
git pull origin [BASE_BRANCH]
|
|
243
|
-
node .agents/scripts/detect-merges.js
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
If the script exits non-zero: **STOP**, alert the operator immediately, and
|
|
247
|
-
do not proceed until the conflict markers are resolved.
|
|
248
|
-
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
## Step 6 — Post-Merge Verification & Cleanup
|
|
252
|
-
|
|
253
|
-
Confirm the merge landed correctly on the base branch:
|
|
254
|
-
|
|
255
|
-
```powershell
|
|
256
|
-
git log origin/[BASE_BRANCH] -5 --oneline
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
Verify that the top commit corresponds to the merged PR.
|
|
260
|
-
|
|
261
|
-
Explicitly delete the remote head branch. This is **mandatory** and must always
|
|
262
|
-
succeed — even if the Husky pre-push hook blocks `git push origin --delete`. Use
|
|
263
|
-
the **two-stage** approach below:
|
|
264
|
-
|
|
265
|
-
**Stage 1 — git push (fast path):**
|
|
266
|
-
|
|
267
|
-
```powershell
|
|
268
|
-
# Attempt standard deletion first (fast, uses existing auth)
|
|
269
|
-
git push origin --delete [HEAD_BRANCH] 2>$null
|
|
270
|
-
$gitDeleteOk = $LASTEXITCODE -eq 0
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
**Stage 2 — REST API fallback (always run if Stage 1 failed):**
|
|
274
|
-
|
|
275
|
-
If Stage 1 fails (exit code ≠ 0, e.g., due to Husky hook blocking the push),
|
|
276
|
-
fall back to the GitHub REST API using the token from the git credential store:
|
|
277
|
-
|
|
278
|
-
```powershell
|
|
279
|
-
if (-not $gitDeleteOk) {
|
|
280
|
-
# Retrieve token from git's native credential manager
|
|
281
|
-
$creds = "protocol=https`nhost=github.com`n" | git credential fill 2>$null
|
|
282
|
-
$token = ($creds | Select-String 'password=(.+)').Matches[0].Groups[1].Value
|
|
283
|
-
|
|
284
|
-
if ($token) {
|
|
285
|
-
$url = "https://api.github.com/repos/[OWNER]/[REPO]/git/refs/heads/[HEAD_BRANCH]"
|
|
286
|
-
$headers = @{ Authorization = "token $token"; Accept = "application/vnd.github.v3+json" }
|
|
287
|
-
try {
|
|
288
|
-
Invoke-RestMethod -Method DELETE -Uri $url -Headers $headers -ErrorAction Stop
|
|
289
|
-
Write-Host "Remote branch deleted via REST API: [HEAD_BRANCH]"
|
|
290
|
-
} catch {
|
|
291
|
-
$status = $_.Exception.Response.StatusCode.value__
|
|
292
|
-
if ($status -eq 422 -or $status -eq 404) {
|
|
293
|
-
Write-Host "Branch already gone (HTTP $status) — skipping."
|
|
294
|
-
} else {
|
|
295
|
-
Write-Warning "Failed to delete remote branch via API (HTTP $status): [HEAD_BRANCH]"
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
} else {
|
|
299
|
-
Write-Warning "No GitHub token found in credential store — remote branch may not be deleted."
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
Prune stale remote-tracking refs and delete the local branch:
|
|
305
|
-
|
|
306
|
-
```powershell
|
|
307
|
-
git fetch --prune
|
|
308
|
-
git branch -D [HEAD_BRANCH] 2>$null
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
> **Note:** `git branch -D` is safe to ignore if the local branch does not
|
|
312
|
-
> exist. `git fetch --prune` must always run to keep the local ref list clean.
|
|
313
|
-
|
|
314
|
-
Explicitly close the GitHub PR object. Because this workflow squash-merges
|
|
315
|
-
directly into the base branch (bypassing GitHub's native merge flow), GitHub
|
|
316
|
-
**will not** auto-close the PR — it must be closed explicitly.
|
|
317
|
-
|
|
318
|
-
Call `mcp__github__update_pull_request` with the repo's `owner` / `repo`,
|
|
319
|
-
`pullNumber: [PR_NUMBER]`, and `state: "closed"`.
|
|
320
|
-
|
|
321
|
-
> **Note:** This is a hard requirement — leaving the PR open after merging
|
|
322
|
-
> pollutes the repository's open PR list and causes confusion for reviewers.
|
|
323
|
-
|
|
324
|
-
Optionally, run the test suite one final time on the base branch to confirm no
|
|
325
|
-
regressions were introduced by the merge:
|
|
326
|
-
|
|
327
|
-
```powershell
|
|
328
|
-
npm test
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
---
|
|
332
|
-
|
|
333
|
-
## Step 7 — Summary Report
|
|
334
|
-
|
|
335
|
-
Post a structured summary comment to the PR (now closed) for traceability.
|
|
336
|
-
Call `mcp__github__add_issue_comment` with the repo's `owner` / `repo`,
|
|
337
|
-
`issue_number: [PR_NUMBER]` (PR comments use the issues comments endpoint),
|
|
338
|
-
and `body` set to:
|
|
339
|
-
|
|
340
|
-
```markdown
|
|
341
|
-
✅ **Merged by agent** via `/git-merge-pr`
|
|
342
|
-
|
|
343
|
-
- **Branch**: `[HEAD_BRANCH]` → `[BASE_BRANCH]`
|
|
344
|
-
- **Conflicts resolved**: [YES/NO — list files if YES]
|
|
345
|
-
- **Lint fixes applied**: [YES/NO]
|
|
346
|
-
- **Test fixes applied**: [YES/NO]
|
|
347
|
-
- **Merge strategy**: squash
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
## Constraint
|
|
353
|
-
|
|
354
|
-
- **Never** merge a PR that has unresolved lint errors or failing tests. Running
|
|
355
|
-
a passing quality gate is mandatory before the merge commit.
|
|
356
|
-
- **Never** silently drop code when resolving merge conflicts. When in doubt,
|
|
357
|
-
ask the operator.
|
|
358
|
-
- **Never** bypass required GitHub branch protection checks (required reviewers,
|
|
359
|
-
required status checks). If these are blocking, surface them to the operator
|
|
360
|
-
rather than attempting to force-merge.
|
|
361
|
-
- **Always** explicitly delete the remote head branch in Step 6 with
|
|
362
|
-
`git push origin --delete [HEAD_BRANCH]`. Do **not** rely solely on the
|
|
363
|
-
Step 5 merge call's `delete_branch` flag (whether passed via
|
|
364
|
-
`mcp__github__merge_pull_request` or `gh pr merge --delete-branch`) — that
|
|
365
|
-
flag is silently skipped when a PR auto-closes without a normal merge
|
|
366
|
-
commit (e.g., duplicate rebase scenarios).
|
|
367
|
-
- **Always** treat a "remote ref not found" error from the delete command as a
|
|
368
|
-
non-fatal, idempotent success — the branch is already gone.
|
|
369
|
-
- **Always** use `--force-with-lease` (never bare `--force`) when pushing
|
|
370
|
-
rebased branches to avoid overwriting concurrent pushes.
|
|
371
|
-
- **Always** explicitly close the GitHub PR via
|
|
372
|
-
`mcp__github__update_pull_request` with `state: "closed"` in Step 6 after
|
|
373
|
-
branch cleanup. Because this workflow pushes directly to the base branch,
|
|
374
|
-
GitHub will **never** auto-close the PR — it must be closed manually
|
|
375
|
-
every time.
|
|
376
|
-
- **Always** post a Step 7 summary comment for auditability, even if no fixes
|
|
377
|
-
were required.
|