alignfirst 0.4.0 → 0.6.0-preview.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/README.md +54 -2
- package/dist/cli.js +2 -1
- package/dist/commands/config.js +15 -4
- package/dist/commands/context.js +23 -3
- package/dist/commands/docmap.js +4 -1
- package/dist/commands/doctor.js +27 -4
- package/dist/commands/guide.js +6 -6
- package/dist/commands/plans.js +18 -15
- package/dist/commands/sync.js +5 -5
- package/dist/commands/ticket.js +22 -19
- package/dist/context.d.ts +2 -0
- package/dist/conventions.js +11 -9
- package/dist/format.d.ts +2 -0
- package/dist/format.js +8 -0
- package/dist/plans/archive.d.ts +8 -2
- package/dist/plans/archive.js +41 -17
- package/dist/plans/catchup.js +12 -10
- package/dist/plans/layout.d.ts +4 -7
- package/dist/plans/layout.js +10 -14
- package/dist/plans/link.d.ts +1 -1
- package/dist/plans/link.js +8 -6
- package/dist/plans/mode.d.ts +3 -1
- package/dist/plans/mode.js +11 -7
- package/dist/plans/ticket.d.ts +7 -4
- package/dist/plans/ticket.js +30 -20
- package/dist/project-config.d.ts +4 -3
- package/dist/project-config.js +7 -9
- package/dist/project-layout.d.ts +30 -0
- package/dist/project-layout.js +174 -0
- package/package.json +3 -3
- package/templates/guide/code-review/correctness-reviewer.md +1 -0
- package/templates/guide/code-review/quality-reviewer.md +1 -0
- package/templates/guide/code-review/reviewer-common.md +2 -0
- package/templates/guide/core.md +1 -1
- package/templates/guide/protocols/aad.md +24 -17
- package/templates/guide/protocols/description.md +8 -8
- package/templates/guide/protocols/merge.md +5 -5
- package/templates/guide/protocols/plan.md +59 -70
- package/templates/guide/protocols/spec.md +44 -37
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { existsSync, lstatSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { type } from "arktype";
|
|
4
|
+
import { CliError } from "./cli-error.js";
|
|
5
|
+
import { errorMessage } from "./errors.js";
|
|
6
|
+
import { gitOutputOrUndefined } from "./git.js";
|
|
7
|
+
export const ITEM_NAMES = [
|
|
8
|
+
".alignfirst.json",
|
|
9
|
+
".alignfirst.md",
|
|
10
|
+
"DEVELOPERS.md",
|
|
11
|
+
"docs",
|
|
12
|
+
".plans",
|
|
13
|
+
"_aligndev",
|
|
14
|
+
];
|
|
15
|
+
const FLAG = "boolean | 'auto'";
|
|
16
|
+
const flagsSchema = type({
|
|
17
|
+
"+": "reject",
|
|
18
|
+
".alignfirst.json?": FLAG,
|
|
19
|
+
".alignfirst.md?": FLAG,
|
|
20
|
+
"DEVELOPERS.md?": FLAG,
|
|
21
|
+
"docs?": FLAG,
|
|
22
|
+
".plans?": FLAG,
|
|
23
|
+
"_aligndev?": FLAG,
|
|
24
|
+
});
|
|
25
|
+
const companionsSchema = type({
|
|
26
|
+
"+": "reject",
|
|
27
|
+
root: "string > 0",
|
|
28
|
+
paths: type.Record("string", flagsSchema),
|
|
29
|
+
});
|
|
30
|
+
export function layoutOf(ctx) {
|
|
31
|
+
ctx.layout ??= resolveProjectLayout(ctx.cwd, ctx.home);
|
|
32
|
+
return ctx.layout;
|
|
33
|
+
}
|
|
34
|
+
export function resolveProjectLayout(cwd, home) {
|
|
35
|
+
const companion = resolveCompanion(cwd, home);
|
|
36
|
+
return { companion, locations: resolveLocations(cwd, companion) };
|
|
37
|
+
}
|
|
38
|
+
function resolveCompanion(cwd, home) {
|
|
39
|
+
const file = readCompanionsFile(home);
|
|
40
|
+
if (file === undefined)
|
|
41
|
+
return null;
|
|
42
|
+
const mainWorktree = findMainWorktree(cwd);
|
|
43
|
+
if (mainWorktree === undefined)
|
|
44
|
+
return null;
|
|
45
|
+
const realHome = realOrResolved(home);
|
|
46
|
+
const matches = matchingEntries(file, mainWorktree, realHome);
|
|
47
|
+
if (matches.length === 0)
|
|
48
|
+
return null;
|
|
49
|
+
const flags = mergeFlags(matches);
|
|
50
|
+
assertValidFlags(file, flags, matches);
|
|
51
|
+
const dir = join(normalizePath(file.root, realHome), companionName(mainWorktree, realHome));
|
|
52
|
+
return { dir, exists: pathExists(dir), entries: matches.map((match) => match.key), flags };
|
|
53
|
+
}
|
|
54
|
+
function readCompanionsFile(home) {
|
|
55
|
+
const path = companionsPath(home);
|
|
56
|
+
if (!pathExists(path))
|
|
57
|
+
return;
|
|
58
|
+
let value;
|
|
59
|
+
try {
|
|
60
|
+
value = JSON.parse(readFileSync(path, "utf-8"));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
throw invalidCompanions(path, errorMessage(error));
|
|
64
|
+
}
|
|
65
|
+
const file = companionsSchema(value);
|
|
66
|
+
if (file instanceof type.errors)
|
|
67
|
+
throw invalidCompanions(path, file.summary.split("\n", 1)[0]);
|
|
68
|
+
if (!isUserPath(file.root))
|
|
69
|
+
throw invalidCompanions(path, `root must be an absolute path or start with ~/: ${file.root}`);
|
|
70
|
+
const badKey = Object.keys(file.paths).find((key) => !isUserPath(key));
|
|
71
|
+
if (badKey !== undefined)
|
|
72
|
+
throw invalidCompanions(path, `paths key must be an absolute path or start with ~/: ${badKey}`);
|
|
73
|
+
return { path, root: file.root, paths: file.paths };
|
|
74
|
+
}
|
|
75
|
+
export function companionsPath(home) {
|
|
76
|
+
return join(home, ".config", "alignfirst", "companions.json");
|
|
77
|
+
}
|
|
78
|
+
function invalidCompanions(path, detail) {
|
|
79
|
+
return new CliError(`Invalid ${path}: ${detail}`);
|
|
80
|
+
}
|
|
81
|
+
function isUserPath(value) {
|
|
82
|
+
return value === "~" || value.startsWith("~/") || isAbsolute(value);
|
|
83
|
+
}
|
|
84
|
+
/** The main worktree is the parent of the common `.git` directory; a bare repository has none. */
|
|
85
|
+
function findMainWorktree(cwd) {
|
|
86
|
+
const commonDir = gitOutputOrUndefined(cwd, "rev-parse", "--path-format=absolute", "--git-common-dir");
|
|
87
|
+
if (commonDir === undefined || basename(commonDir) !== ".git")
|
|
88
|
+
return;
|
|
89
|
+
return realpathSync(dirname(commonDir));
|
|
90
|
+
}
|
|
91
|
+
function normalizePath(value, realHome) {
|
|
92
|
+
if (value === "~")
|
|
93
|
+
return realHome;
|
|
94
|
+
return realOrResolved(value.startsWith("~/") ? join(realHome, value.slice(2)) : value);
|
|
95
|
+
}
|
|
96
|
+
function realOrResolved(path) {
|
|
97
|
+
return existsSync(path) ? realpathSync(path) : resolve(path);
|
|
98
|
+
}
|
|
99
|
+
function matchingEntries(file, mainWorktree, realHome) {
|
|
100
|
+
return Object.entries(file.paths)
|
|
101
|
+
.map(([key, flags]) => ({ key, path: normalizePath(key, realHome), flags }))
|
|
102
|
+
.filter((entry) => isSameOrInside(mainWorktree, entry.path))
|
|
103
|
+
.toSorted((left, right) => right.path.length - left.path.length);
|
|
104
|
+
}
|
|
105
|
+
function isSameOrInside(path, ancestor) {
|
|
106
|
+
return path === ancestor || path.startsWith(ancestor.endsWith(sep) ? ancestor : ancestor + sep);
|
|
107
|
+
}
|
|
108
|
+
function mergeFlags(matches) {
|
|
109
|
+
const flagOf = (item) => matches.find((match) => match.flags[item] !== undefined)?.flags[item] ?? "auto";
|
|
110
|
+
return {
|
|
111
|
+
".alignfirst.json": flagOf(".alignfirst.json"),
|
|
112
|
+
".alignfirst.md": flagOf(".alignfirst.md"),
|
|
113
|
+
"DEVELOPERS.md": flagOf("DEVELOPERS.md"),
|
|
114
|
+
docs: flagOf("docs"),
|
|
115
|
+
".plans": flagOf(".plans"),
|
|
116
|
+
_aligndev: flagOf("_aligndev"),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function assertValidFlags(file, flags, matches) {
|
|
120
|
+
if (flags._aligndev !== true || flags[".plans"] !== "auto")
|
|
121
|
+
return;
|
|
122
|
+
const keys = matches.map((match) => match.key).join(", ");
|
|
123
|
+
throw invalidCompanions(file.path, `"_aligndev": true requires ".plans" set to true or false (matching keys: ${keys})`);
|
|
124
|
+
}
|
|
125
|
+
function companionName(mainWorktree, realHome) {
|
|
126
|
+
const name = mainWorktree !== realHome && isSameOrInside(mainWorktree, realHome)
|
|
127
|
+
? relative(realHome, mainWorktree)
|
|
128
|
+
: mainWorktree.slice(1);
|
|
129
|
+
return name.replaceAll("/", "_");
|
|
130
|
+
}
|
|
131
|
+
function resolveLocations(cwd, companion) {
|
|
132
|
+
const locate = (name) => locateItem(cwd, companion, name);
|
|
133
|
+
const plans = locate(".plans");
|
|
134
|
+
return {
|
|
135
|
+
".alignfirst.json": locate(".alignfirst.json"),
|
|
136
|
+
".alignfirst.md": locate(".alignfirst.md"),
|
|
137
|
+
"DEVELOPERS.md": locate("DEVELOPERS.md"),
|
|
138
|
+
docs: locate("docs"),
|
|
139
|
+
".plans": plans,
|
|
140
|
+
_aligndev: companion?.flags._aligndev === true ? companionCopy(companion, ".plans") : plans,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function locateItem(cwd, companion, name) {
|
|
144
|
+
const project = projectCopy(cwd, name);
|
|
145
|
+
if (companion === null || companion.flags[name] === false)
|
|
146
|
+
return project;
|
|
147
|
+
const copy = companionCopy(companion, name);
|
|
148
|
+
if (companion.flags[name] === true || copy.exists || !project.exists)
|
|
149
|
+
return copy;
|
|
150
|
+
return project;
|
|
151
|
+
}
|
|
152
|
+
function projectCopy(cwd, name) {
|
|
153
|
+
const path = join(cwd, name);
|
|
154
|
+
return { path, in: "project", exists: pathExists(path) };
|
|
155
|
+
}
|
|
156
|
+
function companionCopy(companion, name) {
|
|
157
|
+
const path = join(companion.dir, name);
|
|
158
|
+
return { path, in: "companion", exists: pathExists(path) };
|
|
159
|
+
}
|
|
160
|
+
/** An lstat check, so a broken `.plans` symlink still resolves in place. */
|
|
161
|
+
function pathExists(path) {
|
|
162
|
+
return lstatSync(path, { throwIfNoEntry: false }) !== undefined;
|
|
163
|
+
}
|
|
164
|
+
/** One line: `<name>: <path> (<in>)`, with `, missing` when absent. */
|
|
165
|
+
export function renderItemLocation(name, location) {
|
|
166
|
+
return `${name}: ${location.path} (${location.in}${location.exists ? "" : ", missing"})`;
|
|
167
|
+
}
|
|
168
|
+
/** The `_aligndev` tree when it is not the resolved `.plans` and exists. */
|
|
169
|
+
export function separateSessionTree(layout) {
|
|
170
|
+
const sessions = layout.locations._aligndev;
|
|
171
|
+
if (!sessions.exists || sessions.path === layout.locations[".plans"].path)
|
|
172
|
+
return;
|
|
173
|
+
return sessions.path;
|
|
174
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alignfirst",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0-preview.0",
|
|
4
4
|
"license": "CC0-1.0",
|
|
5
5
|
"author": "Thomas MUR",
|
|
6
6
|
"description": "The AlignFirst CLI: protocols, work files and docs in one command.",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@alignfirst/docmap": "~0.11.
|
|
38
|
+
"@alignfirst/docmap": "~0.11.1",
|
|
39
39
|
"arktype": "^2.2.3",
|
|
40
40
|
"semver": "^7.8.5"
|
|
41
41
|
},
|
|
@@ -44,6 +44,6 @@
|
|
|
44
44
|
"@types/semver": "~7.8.0",
|
|
45
45
|
"rimraf": "~6.1.3",
|
|
46
46
|
"typescript": "~7.0.2",
|
|
47
|
-
"vitest": "~
|
|
47
|
+
"vitest": "~5.0.1"
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -38,6 +38,7 @@ The most useful findings come from here, because nobody looks for them.
|
|
|
38
38
|
| Signal | Question | Severity |
|
|
39
39
|
| --- | --- | --- |
|
|
40
40
|
| Function modified | Who calls it? Do callers outside the diff assume the old behavior? | 🔴 |
|
|
41
|
+
| Code, branch or condition removed | What did it handle? Is that case now impossible, handled elsewhere, or silently dropped? | 🔴 |
|
|
41
42
|
| One occurrence of a pattern fixed | Does the same pattern exist elsewhere, unfixed? Report once, with the count. | 🟡 |
|
|
42
43
|
| Code the diff touches contains a bug unrelated to the diff | Report it as 🟣, without requiring a fix in this PR. | 🟣 |
|
|
43
44
|
| Constant, enumeration, or type union extended | Was every place that exhausts it updated? | 🔴 |
|
|
@@ -52,3 +52,4 @@ Also check consistency by example: does the new code match its neighbors in stru
|
|
|
52
52
|
| Mock added | Does it reproduce the real contract of the dependency, or an idealized version that can never fail? | 🟡 |
|
|
53
53
|
| Test depending on the clock, network, execution order, or shared state | Source of flakiness. | 🟡 |
|
|
54
54
|
| Assertion modified to make a test pass | Was the test fixed, or aligned with a bug? Strong signal: find out why it failed. | 🔴 |
|
|
55
|
+
| Test deleted or skipped | Which behavior stops being verified? Was it fixed, or made to stop failing? | 🔴 |
|
|
@@ -10,6 +10,8 @@ You are one of several reviewers examining the same branch, each from a differen
|
|
|
10
10
|
|
|
11
11
|
## Method
|
|
12
12
|
|
|
13
|
+
Assume nothing works until the code shows it does: your job is to find how this change goes wrong. The bar below decides what you report, not what you look for.
|
|
14
|
+
|
|
13
15
|
Work signal by signal: each checklist item is a signal/question pair, and applies only when its signal is visible in the diff. This keeps the review on the change, away from a general audit of the repository. A defect in the changed code is a finding even without a matching checklist item.
|
|
14
16
|
|
|
15
17
|
To answer a checklist question, read the code — including files outside the diff (callers, configuration, the installed version of a dependency). Never guess.
|
package/templates/guide/core.md
CHANGED
|
@@ -16,7 +16,7 @@ When the user says there is no ticket or asks for a side ticket, run `{{CMD}} ti
|
|
|
16
16
|
|
|
17
17
|
Files use `{CYCLE_LETTER}{FILE_NUMBER}-{FILE_TYPE}.md`. FILE_PREFIX combines the cycle letter and the file number within that cycle. FILE_NAME includes the prefix and extension.
|
|
18
18
|
|
|
19
|
-
Immediately before creating each file, run `{{TICKET_CMD}} --next <filename>` with the extension included. It returns TICKET_DIR, CYCLE_LETTER, FILE_NUMBER, and FILE_NAME. Append FILE_NAME to TICKET_DIR to get the file path
|
|
19
|
+
Immediately before creating each file, run `{{TICKET_CMD}} --next <filename>` with the extension included. It returns TICKET_DIR, CYCLE_LETTER, FILE_NUMBER, and FILE_NAME. Append FILE_NAME to TICKET_DIR exactly as printed to get the file path.
|
|
20
20
|
|
|
21
21
|
With no filename, `{{TICKET_CMD}} --next` returns FILE_PREFIX instead of FILE_NAME. To name several files at once, repeat `--next <filename>` once per file: the command returns FILE_NAMES, numbered in that order. Add `--new-cycle` to any form when the protocol or user calls for a new cycle.
|
|
22
22
|
|
|
@@ -10,30 +10,37 @@ This is a 4-step protocol. Follow each step in order.
|
|
|
10
10
|
|
|
11
11
|
## 1. Investigate
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Your context lists the available **documentation** and **skills**. Read every document and skill that applies to any aspect of the task, and the relevant references of each skill. A familiar-looking task tempts you to skip this reading; the project conventions live in these files.
|
|
14
14
|
|
|
15
|
-
Explore the codebase. Take the time to understand how it
|
|
15
|
+
Explore the codebase. Take the time to understand how it works today and what needs to change.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Seek a clean break solution by default. Consider backward compatibility only when the user asks for it.
|
|
18
18
|
|
|
19
19
|
## 2. Discuss
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Nothing is implemented, and nothing is written in TICKET_DIR, before the user agrees. This step is where a small task reveals itself as a large one. It is never skipped.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
The user is a developer who carries the global vision of the project and decides the choices that matter. You carry the details of the code you just read. The discussion keeps the user aware of what you found and hands them every decision worth taking.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Manage the reader's attention. Open with the task as you understand it and the approach you propose, in two or three sentences. Then write one block per point that deserves a decision. Write for a reader who has not opened the code today: a function, module or mechanism gets a few words of definition the first time you name it.
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
- **Current implementation analysis**: Share what you discovered and ask for confirmation or corrections
|
|
29
|
-
- **Approach evaluation**: Discuss potential solutions and their trade-offs
|
|
30
|
-
- **Edge cases and implications**: Explore potential issues and broader system impacts
|
|
27
|
+
A block is a question and a recommendation:
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
```
|
|
30
|
+
❓ **Q1 - <title>**: <the problem in plain words, what the code does today in the words needed to decide, the options with their consequence>
|
|
31
|
+
|
|
32
|
+
➡️ <your recommendation>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Look for edge cases and impacts on the rest of the system; each one that needs a decision gets its block.
|
|
36
|
+
|
|
37
|
+
A ❓ is open: the user's answer shapes what you build. When the only answers are go or veto, the block shrinks to a single ➡️ line stating your choice. The more obvious the choice, the shorter the line. Leave out the investigation narrative and the list of files you read.
|
|
38
|
+
|
|
39
|
+
Settle on your own what the code can answer. Ask the user what needs their judgement: product behavior, scope, priorities, constraints the code does not show. Every ❓ carries a ➡️, so that "fine with all recommendations" is a valid answer. Ask in rounds: a question whose answer depends on another question still open waits for the next round.
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
When there is nothing to decide, say so in a few lines and ask for an explicit go.
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
Do not use your question tool. Ask in plain text: your questions open a real discussion, a multiple-choice widget closes it.
|
|
37
44
|
|
|
38
45
|
## 3. Act
|
|
39
46
|
|
|
@@ -47,7 +54,7 @@ Use subagents (your subagent tool) for distinct, isolated units of work when ben
|
|
|
47
54
|
|
|
48
55
|
Finalize the summary file: replace the working notes with the final content described below.
|
|
49
56
|
|
|
50
|
-
Start the summary with a header, then a suggested commit message {{COMMIT_RULE}}. The shorter the better. Omit any field with nothing to list.
|
|
57
|
+
Start the summary with a header, then a suggested commit message {{COMMIT_RULE}}. The shorter the better. Omit any field with nothing to list. Exclude `alignfirst` from the skills.
|
|
51
58
|
|
|
52
59
|
Example:
|
|
53
60
|
|
|
@@ -64,15 +71,15 @@ Used documentation:
|
|
|
64
71
|
Used skills: `skill-a`, `skill-b`
|
|
65
72
|
```
|
|
66
73
|
|
|
67
|
-
The finalized summary is a **very concise handover document
|
|
74
|
+
The finalized summary is a **very concise handover document**. It captures:
|
|
68
75
|
|
|
69
76
|
- What was the topic or problem
|
|
70
77
|
- What was decided or discovered
|
|
71
|
-
- What action was taken
|
|
78
|
+
- What action was taken, if any
|
|
72
79
|
- Key outcomes or next steps
|
|
73
80
|
|
|
74
81
|
The shorter the better.
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
Ignore Markdown lint errors in the summary file.
|
|
77
84
|
|
|
78
85
|
At the end, give the path of the summary file to the user.
|
|
@@ -24,23 +24,23 @@ Run `{{TICKET_CMD}}` once to identify TICKET_DIR and load the ticket directory c
|
|
|
24
24
|
[description body]
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Start with a suggested commit message {{COMMIT_RULE}}. Refine it from the suggested commit messages found in the specs and summaries you read. Keep it brief
|
|
27
|
+
Start with a suggested commit message {{COMMIT_RULE}}. Refine it from the suggested commit messages found in the specs and summaries you read. Keep it brief, usually 3 to 5 words for the description part. Shorter is better when it stays clear.
|
|
28
28
|
|
|
29
29
|
## Guidelines for the Description Body
|
|
30
30
|
|
|
31
31
|
- Write in markdown:
|
|
32
32
|
- If there is one subject, write a single paragraph.
|
|
33
33
|
- Otherwise, write a bulleted list with one subject per item.
|
|
34
|
-
- **Describe
|
|
35
|
-
- **Keep it minimal and functional.** Mention each subject very concisely
|
|
36
|
-
- **
|
|
37
|
-
- **
|
|
34
|
+
- **Describe what was done, never why.** Explanations, justifications and reasoning stay out; the reader gets the result.
|
|
35
|
+
- **Keep it minimal and functional.** Mention each subject very concisely, the essentials only. Most subjects fit in one sentence of about 5 to 15 words.
|
|
36
|
+
- **Prefer functional descriptions.** Technical implementation details appear only when the reader needs them.
|
|
37
|
+
- **Merge related subjects.** A long list of small items is the usual failure of a description; combine similar changes into one cohesive subject.
|
|
38
38
|
- Include technical details only for major structural changes (e.g., renaming a database table, significant linter config changes, major codebase refactors).
|
|
39
|
-
-
|
|
40
|
-
- **Absorb fix-only summaries
|
|
39
|
+
- Leave out specs that were not implemented. In doubt, explore the codebase to confirm what was done.
|
|
40
|
+
- **Absorb fix-only summaries.** A summary that fixes issues introduced by earlier work in the same ticket is not a subject of its own. An external reader only cares about the end state.
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Ignore Markdown lint errors in the description file.
|
|
45
45
|
|
|
46
46
|
At the end, give the path of the description file to the user.
|
|
@@ -12,7 +12,7 @@ This protocol applies when a merge or rebase has produced conflicts, or when the
|
|
|
12
12
|
|
|
13
13
|
Run `git status` to check for conflicts.
|
|
14
14
|
|
|
15
|
-
**If there are no conflicts:** start the merge — use the incoming branch if the user provided one, {{BASE_BRANCH_RULE}}
|
|
15
|
+
**If there are no conflicts:** start the merge — use the incoming branch if the user provided one, {{BASE_BRANCH_RULE}} A merge that completes cleanly ends the protocol, with no summary file. Otherwise, continue with the steps below.
|
|
16
16
|
|
|
17
17
|
## 2. Investigate
|
|
18
18
|
|
|
@@ -22,9 +22,9 @@ Take the time to understand how things work in the incoming branch and in the cu
|
|
|
22
22
|
|
|
23
23
|
Run `{{TICKET_CMD}} --next merge.summary.md` to continue the current cycle. Append FILE_NAME to TICKET_DIR, then immediately create the summary at that path. Log each notable resolution in it as you resolve (see step 5 for the expected content).
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Preserve both intents whenever possible. Accepting one side wholesale is the usual failure of a merge; it silently drops the other branch's work.
|
|
26
26
|
|
|
27
|
-
Resolve conflicts one at a time.
|
|
27
|
+
Resolve conflicts one at a time. Batch processing, broad search-and-replace and other brute-force edits are out.
|
|
28
28
|
|
|
29
29
|
**Special case for lock files:** If a lock file has conflicts:
|
|
30
30
|
|
|
@@ -41,7 +41,7 @@ If you need to execute the project, whether through E2E tests or manual checks,
|
|
|
41
41
|
|
|
42
42
|
Finalize the summary file.
|
|
43
43
|
|
|
44
|
-
**Keep it lean.**
|
|
44
|
+
**Keep it lean.** Document only the challenging conflicts and the choices made to resolve them. Straightforward resolutions stay out: when everything was trivial, the summary is a header and a one-line note that nothing was tricky. No commit message either; Git provides one for merges.
|
|
45
45
|
|
|
46
46
|
Example:
|
|
47
47
|
|
|
@@ -59,6 +59,6 @@ Example:
|
|
|
59
59
|
|
|
60
60
|
Omit any section with nothing to report.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Ignore Markdown lint errors in the summary file.
|
|
63
63
|
|
|
64
64
|
At the end, give the path of the summary file to the user.
|