@wildorder/nightshift 0.16.0 → 0.18.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 +289 -1
- package/dist/agent-runner.d.ts +95 -5
- package/dist/agent-runner.d.ts.map +1 -1
- package/dist/agent-runner.js +442 -32
- package/dist/agent-runner.js.map +1 -1
- package/dist/agent-session.d.ts +123 -0
- package/dist/agent-session.d.ts.map +1 -0
- package/dist/agent-session.js +114 -0
- package/dist/agent-session.js.map +1 -0
- package/dist/atomic-write.d.ts +18 -0
- package/dist/atomic-write.d.ts.map +1 -0
- package/dist/atomic-write.js +74 -0
- package/dist/atomic-write.js.map +1 -0
- package/dist/author.d.ts +118 -4
- package/dist/author.d.ts.map +1 -1
- package/dist/author.js +650 -163
- package/dist/author.js.map +1 -1
- package/dist/causal-analysis.d.ts +216 -0
- package/dist/causal-analysis.d.ts.map +1 -0
- package/dist/causal-analysis.js +786 -0
- package/dist/causal-analysis.js.map +1 -0
- package/dist/cli.js +22 -2
- package/dist/cli.js.map +1 -1
- package/dist/concurrency.d.ts +24 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +28 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/decider-review.d.ts +13 -1
- package/dist/decider-review.d.ts.map +1 -1
- package/dist/decider-review.js +21 -7
- package/dist/decider-review.js.map +1 -1
- package/dist/decision-ledger.d.ts +7 -0
- package/dist/decision-ledger.d.ts.map +1 -1
- package/dist/decision-ledger.js +25 -10
- package/dist/decision-ledger.js.map +1 -1
- package/dist/detect-package-manager.d.ts +10 -0
- package/dist/detect-package-manager.d.ts.map +1 -1
- package/dist/detect-package-manager.js +12 -0
- package/dist/detect-package-manager.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +24 -5
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +254 -108
- package/dist/manifest.js.map +1 -1
- package/dist/permits.d.ts +18 -0
- package/dist/permits.d.ts.map +1 -1
- package/dist/permits.js +155 -0
- package/dist/permits.js.map +1 -1
- package/dist/preflight.d.ts +3 -0
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +73 -57
- package/dist/preflight.js.map +1 -1
- package/dist/prompt-telemetry.d.ts +64 -0
- package/dist/prompt-telemetry.d.ts.map +1 -0
- package/dist/prompt-telemetry.js +112 -0
- package/dist/prompt-telemetry.js.map +1 -0
- package/dist/provider-telemetry.d.ts +106 -0
- package/dist/provider-telemetry.d.ts.map +1 -0
- package/dist/provider-telemetry.js +423 -0
- package/dist/provider-telemetry.js.map +1 -0
- package/dist/review-pass.d.ts +52 -10
- package/dist/review-pass.d.ts.map +1 -1
- package/dist/review-pass.js +114 -30
- package/dist/review-pass.js.map +1 -1
- package/dist/run-analytics-report.d.ts +286 -0
- package/dist/run-analytics-report.d.ts.map +1 -0
- package/dist/run-analytics-report.js +1242 -0
- package/dist/run-analytics-report.js.map +1 -0
- package/dist/run-analytics.d.ts +844 -0
- package/dist/run-analytics.d.ts.map +1 -0
- package/dist/run-analytics.js +777 -0
- package/dist/run-analytics.js.map +1 -0
- package/dist/run-log.d.ts +16 -0
- package/dist/run-log.d.ts.map +1 -0
- package/dist/run-log.js +32 -0
- package/dist/run-log.js.map +1 -0
- package/dist/run-program.d.ts +268 -4
- package/dist/run-program.d.ts.map +1 -1
- package/dist/run-program.js +2508 -762
- package/dist/run-program.js.map +1 -1
- package/dist/scheduler.d.ts +75 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +126 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/verify-cache.d.ts +64 -0
- package/dist/verify-cache.d.ts.map +1 -0
- package/dist/verify-cache.js +56 -0
- package/dist/verify-cache.js.map +1 -0
- package/dist/whole-program-review.d.ts +3 -0
- package/dist/whole-program-review.d.ts.map +1 -1
- package/dist/whole-program-review.js +8 -1
- package/dist/whole-program-review.js.map +1 -1
- package/dist/worktree.d.ts +124 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +397 -0
- package/dist/worktree.js.map +1 -0
- package/dist/write-queue.d.ts +25 -0
- package/dist/write-queue.d.ts.map +1 -0
- package/dist/write-queue.js +52 -0
- package/dist/write-queue.js.map +1 -0
- package/package.json +2 -2
- package/skills/plan-program/SKILL.md +37 -5
- package/dist/worktree-guard.d.ts +0 -40
- package/dist/worktree-guard.d.ts.map +0 -1
- package/dist/worktree-guard.js +0 -91
- package/dist/worktree-guard.js.map +0 -1
package/dist/worktree.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { mkdir, realpath } from "node:fs/promises";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { lockfileManager } from "./detect-package-manager.js";
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
/** The fixed prefix for every runner-created worktree branch. Not a config key. */
|
|
10
|
+
export const WORKTREE_BRANCH_PREFIX = "nightshift/";
|
|
11
|
+
/** `nightshift/<programId>/<workstreamId>` — a branch under refs/heads/. Ids are lowercased so a case-insensitive filesystem can never make two refs collide. */
|
|
12
|
+
export function worktreeBranchName(programId, workstreamId) {
|
|
13
|
+
return `${WORKTREE_BRANCH_PREFIX}${programId.toLowerCase()}/${workstreamId.toLowerCase()}`;
|
|
14
|
+
}
|
|
15
|
+
/** Every branch this program's worktrees may use, for prefix-matched reaping. */
|
|
16
|
+
export function worktreeBranchPrefix(programId) {
|
|
17
|
+
return `${WORKTREE_BRANCH_PREFIX}${programId.toLowerCase()}/`;
|
|
18
|
+
}
|
|
19
|
+
/** `refs/nightshift/<programId>/<workstreamId>` — where a verified-but-unintegrated commit is retained. */
|
|
20
|
+
export function retainedRefName(programId, workstreamId) {
|
|
21
|
+
return `refs/nightshift/${programId.toLowerCase()}/${workstreamId.toLowerCase()}`;
|
|
22
|
+
}
|
|
23
|
+
function hash8(value) {
|
|
24
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 8);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The realpath of `repoRoot`, falling back to the resolved path when
|
|
28
|
+
* `realpath` cannot be taken (a repository whose parent no longer exists —
|
|
29
|
+
* an edge case, but one this function must not throw on).
|
|
30
|
+
*/
|
|
31
|
+
async function repoRealpath(repoRoot) {
|
|
32
|
+
try {
|
|
33
|
+
return await realpath(repoRoot);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return resolve(repoRoot);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Where a workstream's worktree lives: `<base>/<programId>-<hash8>/<workstreamId>`,
|
|
41
|
+
* where `hash8` is derived from the repository's own realpath — see
|
|
42
|
+
* `resolveWorktreeBase`'s doc comment for why this segment is load-bearing.
|
|
43
|
+
*/
|
|
44
|
+
export function worktreePathFor(base, programId, workstreamId, repoHash) {
|
|
45
|
+
return join(base, `${programId}-${repoHash}`, workstreamId);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolves the base directory under which this run's worktrees are created,
|
|
49
|
+
* once per run: `<dirname(realpath(repoRoot))>/.nightshift-worktrees`, or —
|
|
50
|
+
* when that parent is not writable (a read-only mount, a container) —
|
|
51
|
+
* `<os tmpdir>/nightshift-worktrees`. Eagerly `mkdir -p`s the chosen base so
|
|
52
|
+
* the fallback decision is made once, up front, rather than discovered mid-run.
|
|
53
|
+
*
|
|
54
|
+
* The `hash8(realpath(repoRoot))` segment (folded into every returned path via
|
|
55
|
+
* `repoHash`) is load-bearing, not decoration: without it, two checkouts of
|
|
56
|
+
* the same repository — or two test fixtures sharing an OS temp directory and
|
|
57
|
+
* the same programId — would resolve to the same worktree path and the
|
|
58
|
+
* second `git worktree add` would fail with `already exists`.
|
|
59
|
+
*/
|
|
60
|
+
export async function resolveWorktreeBase(repoRoot, override) {
|
|
61
|
+
const realRepoRoot = await repoRealpath(repoRoot);
|
|
62
|
+
const repoHash = hash8(realRepoRoot);
|
|
63
|
+
if (override !== undefined) {
|
|
64
|
+
const base = resolve(override);
|
|
65
|
+
await mkdir(base, { recursive: true });
|
|
66
|
+
return { base, repoHash, usedFallback: false };
|
|
67
|
+
}
|
|
68
|
+
const siblingBase = join(dirname(realRepoRoot), ".nightshift-worktrees");
|
|
69
|
+
if (await insideGitWorkTree(dirname(realRepoRoot))) {
|
|
70
|
+
// The repository itself sits inside another checkout: a sibling
|
|
71
|
+
// directory would land in *that* repository's tracked tree.
|
|
72
|
+
const fallbackBase = join(tmpdir(), "nightshift-worktrees");
|
|
73
|
+
await mkdir(fallbackBase, { recursive: true });
|
|
74
|
+
return { base: fallbackBase, repoHash, usedFallback: true };
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
await mkdir(siblingBase, { recursive: true });
|
|
78
|
+
return { base: siblingBase, repoHash, usedFallback: false };
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
const fallbackBase = join(tmpdir(), "nightshift-worktrees");
|
|
82
|
+
await mkdir(fallbackBase, { recursive: true });
|
|
83
|
+
return { base: fallbackBase, repoHash, usedFallback: true };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** The install command for a worktree, or undefined when there is no lockfile. */
|
|
87
|
+
export function worktreeInstallCommand(worktreeRoot) {
|
|
88
|
+
const manager = lockfileManager(worktreeRoot);
|
|
89
|
+
if (manager === undefined)
|
|
90
|
+
return undefined;
|
|
91
|
+
return installCommandFor(manager);
|
|
92
|
+
}
|
|
93
|
+
function installCommandFor(manager) {
|
|
94
|
+
switch (manager) {
|
|
95
|
+
case "npm":
|
|
96
|
+
return "npm ci";
|
|
97
|
+
case "pnpm":
|
|
98
|
+
return "pnpm install --frozen-lockfile";
|
|
99
|
+
case "yarn":
|
|
100
|
+
return "yarn install";
|
|
101
|
+
case "bun":
|
|
102
|
+
return "bun install --frozen-lockfile";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* True for a path the runner owns in the tree being inspected. `git status
|
|
107
|
+
* --porcelain` paths are relative to that tree's root — the root checkout
|
|
108
|
+
* during the report commit, the worktree during a build. In a worktree the
|
|
109
|
+
* runner writes nothing here, so this only ever fires when an agent itself
|
|
110
|
+
* edited planning artifacts, which is equally not the work it was asked to
|
|
111
|
+
* do.
|
|
112
|
+
*/
|
|
113
|
+
export function isRunnerOwnedArtifactPath(path) {
|
|
114
|
+
return path.replaceAll("\\", "/").startsWith("docs/programs/");
|
|
115
|
+
}
|
|
116
|
+
async function run(cwd, args) {
|
|
117
|
+
return execFileAsync("git", args, { cwd, maxBuffer: 32 * 1024 * 1024 });
|
|
118
|
+
}
|
|
119
|
+
function outputOf(error) {
|
|
120
|
+
const err = error;
|
|
121
|
+
const streams = `${err.stdout ?? ""}${err.stderr ?? ""}`;
|
|
122
|
+
// A spawn-level failure (git never ran: ENAMETOOLONG on an oversized argv,
|
|
123
|
+
// ENOENT, EPERM) leaves both streams empty. The error's own message is
|
|
124
|
+
// then the only diagnosis there is, and an empty reason is the one thing
|
|
125
|
+
// the caller's report must never render.
|
|
126
|
+
if (streams.trim() !== "")
|
|
127
|
+
return streams;
|
|
128
|
+
return err.message ?? String(error);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* `git` with `input` written to stdin — for `--pathspec-from-file=-`, so a
|
|
132
|
+
* pathspec of any length never rides on argv (Windows caps a command line at
|
|
133
|
+
* 32K characters; a workstream touching a few hundred files exceeds it).
|
|
134
|
+
*/
|
|
135
|
+
async function runWithStdin(cwd, args, input) {
|
|
136
|
+
return new Promise((resolvePromise, reject) => {
|
|
137
|
+
const child = execFile("git", args, { cwd, maxBuffer: 32 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
138
|
+
if (error) {
|
|
139
|
+
reject(Object.assign(error, { stdout, stderr }));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
resolvePromise({ stdout, stderr });
|
|
143
|
+
});
|
|
144
|
+
child.stdin?.on("error", () => undefined);
|
|
145
|
+
child.stdin?.end(input);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
async function insideGitWorkTree(dir) {
|
|
149
|
+
try {
|
|
150
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dir });
|
|
151
|
+
return stdout.trim() === "true";
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function parseWorktreeList(raw) {
|
|
158
|
+
const entries = [];
|
|
159
|
+
let current = {};
|
|
160
|
+
const flush = () => {
|
|
161
|
+
if (current.path !== undefined && current.head !== undefined) {
|
|
162
|
+
entries.push({
|
|
163
|
+
path: current.path,
|
|
164
|
+
head: current.head,
|
|
165
|
+
...(current.branch === undefined ? {} : { branch: current.branch }),
|
|
166
|
+
...(current.prunable === true ? { prunable: true } : {}),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
current = {};
|
|
170
|
+
};
|
|
171
|
+
for (const line of raw.split("\n")) {
|
|
172
|
+
if (line === "") {
|
|
173
|
+
flush();
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (line.startsWith("worktree ")) {
|
|
177
|
+
current.path = line.slice("worktree ".length);
|
|
178
|
+
}
|
|
179
|
+
else if (line.startsWith("HEAD ")) {
|
|
180
|
+
current.head = line.slice("HEAD ".length);
|
|
181
|
+
}
|
|
182
|
+
else if (line.startsWith("branch ")) {
|
|
183
|
+
current.branch = line.slice("branch ".length);
|
|
184
|
+
}
|
|
185
|
+
else if (line.startsWith("prunable")) {
|
|
186
|
+
current.prunable = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
flush();
|
|
190
|
+
return entries;
|
|
191
|
+
}
|
|
192
|
+
export const defaultWorktreeOps = {
|
|
193
|
+
async add(root, path, branch, commitish) {
|
|
194
|
+
await mkdir(dirname(path), { recursive: true });
|
|
195
|
+
await run(root, ["worktree", "add", "-b", branch, path, commitish]);
|
|
196
|
+
},
|
|
197
|
+
async list(root) {
|
|
198
|
+
const { stdout } = await run(root, ["worktree", "list", "--porcelain"]);
|
|
199
|
+
return parseWorktreeList(stdout);
|
|
200
|
+
},
|
|
201
|
+
async remove(root, path) {
|
|
202
|
+
try {
|
|
203
|
+
await run(root, ["worktree", "remove", "--force", path]);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Already missing, or git's administrative entry disagrees with the
|
|
207
|
+
// filesystem — either way, there is nothing more to remove.
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
async prune(root) {
|
|
211
|
+
try {
|
|
212
|
+
await run(root, ["worktree", "prune"]);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Best-effort housekeeping; never fatal.
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
async branchesUnder(root, prefix) {
|
|
219
|
+
try {
|
|
220
|
+
const { stdout } = await run(root, [
|
|
221
|
+
"for-each-ref",
|
|
222
|
+
"--format=%(refname:short)",
|
|
223
|
+
`refs/heads/${prefix}`,
|
|
224
|
+
]);
|
|
225
|
+
return stdout
|
|
226
|
+
.split("\n")
|
|
227
|
+
.map((line) => line.trim())
|
|
228
|
+
.filter((line) => line !== "");
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
async deleteBranch(root, branch) {
|
|
235
|
+
try {
|
|
236
|
+
await run(root, ["branch", "-D", branch]);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// Absent branch — nothing to delete.
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
async mergeInto(worktree, commitish, message) {
|
|
243
|
+
try {
|
|
244
|
+
const { stdout, stderr } = await run(worktree, ["merge", "--no-edit", "-m", message, commitish]);
|
|
245
|
+
return { ok: true, conflicts: [], output: `${stdout}${stderr}` };
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
let conflicts = [];
|
|
249
|
+
try {
|
|
250
|
+
const { stdout } = await run(worktree, ["diff", "--name-only", "--diff-filter=U"]);
|
|
251
|
+
conflicts = stdout.split("\n").map((line) => line.trim()).filter((line) => line !== "");
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Nothing more we can learn.
|
|
255
|
+
}
|
|
256
|
+
return { ok: false, conflicts, output: outputOf(error) };
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
async abortMerge(worktree) {
|
|
260
|
+
try {
|
|
261
|
+
await run(worktree, ["merge", "--abort"]);
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// Nothing to abort, or the abort itself failed — the caller's own
|
|
265
|
+
// recovery path (reset --merge, worktree retention) covers this.
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
async squashIntegrate(cwd, commitish, message, paths) {
|
|
269
|
+
try {
|
|
270
|
+
await run(cwd, ["merge", "--squash", commitish]);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
try {
|
|
274
|
+
await run(cwd, ["reset", "--merge"]);
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// Best-effort recovery; the caller reports the original failure.
|
|
278
|
+
}
|
|
279
|
+
return { ok: false, output: outputOf(error) };
|
|
280
|
+
}
|
|
281
|
+
if (paths.length === 0) {
|
|
282
|
+
// Nothing to stage or commit — an identical branch. Treat as a no-op
|
|
283
|
+
// integration, not a failure: there is no state to recover.
|
|
284
|
+
return { ok: true, commit: undefined };
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
await runWithStdin(cwd, ["commit", "-m", message, "--pathspec-from-file=-", "--pathspec-file-nul"], paths.join(""));
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
const output = outputOf(error);
|
|
291
|
+
// "nothing to commit" for the given pathspec is not a failure — the
|
|
292
|
+
// squash staged nothing under these paths (can legitimately happen
|
|
293
|
+
// when every incoming path was itself an exclusion).
|
|
294
|
+
if (/nothing to commit|nothing added to commit|no changes added to commit/u.test(output)) {
|
|
295
|
+
return { ok: true, commit: undefined };
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
await run(cwd, ["reset", "--merge"]);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// Best-effort recovery; the caller reports the original failure.
|
|
302
|
+
}
|
|
303
|
+
return { ok: false, output };
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
const { stdout } = await run(cwd, ["rev-parse", "HEAD"]);
|
|
307
|
+
return { ok: true, commit: stdout.trim() };
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
return { ok: true, commit: undefined };
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
async commitEverything(worktree, message) {
|
|
314
|
+
try {
|
|
315
|
+
await run(worktree, ["add", "-A"]);
|
|
316
|
+
try {
|
|
317
|
+
await run(worktree, ["commit", "-m", message]);
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
if (!/nothing to commit|nothing added to commit|no changes added to commit/u.test(outputOf(error))) {
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const { stdout } = await run(worktree, ["rev-parse", "HEAD"]);
|
|
325
|
+
return stdout.trim();
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
async updateRef(root, ref, commit) {
|
|
332
|
+
await run(root, ["update-ref", ref, commit]);
|
|
333
|
+
},
|
|
334
|
+
async isAncestor(root, ancestor, descendant) {
|
|
335
|
+
try {
|
|
336
|
+
await run(root, ["merge-base", "--is-ancestor", ancestor, descendant]);
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
async changedPaths(cwd, a, b) {
|
|
344
|
+
try {
|
|
345
|
+
const { stdout } = await run(cwd, ["diff", "--name-only", "-z", "--no-renames", a, b]);
|
|
346
|
+
return stdout.split("\0").filter((path) => path !== "");
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
async resolveCommit(cwd, rev) {
|
|
353
|
+
try {
|
|
354
|
+
const { stdout } = await run(cwd, ["rev-parse", "--verify", `${rev}^{commit}`]);
|
|
355
|
+
return stdout.trim();
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
async discardRunnerArtifacts(worktree) {
|
|
362
|
+
let discovered = [];
|
|
363
|
+
try {
|
|
364
|
+
const { stdout } = await run(worktree, [
|
|
365
|
+
"status",
|
|
366
|
+
"--porcelain",
|
|
367
|
+
"-z",
|
|
368
|
+
"--",
|
|
369
|
+
":(top)docs/programs",
|
|
370
|
+
]);
|
|
371
|
+
discovered = stdout
|
|
372
|
+
.split("\0")
|
|
373
|
+
.filter((token) => token !== "")
|
|
374
|
+
.map((token) => token.slice(3));
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
// No tracked or untracked docs/programs paths — nothing to discard.
|
|
378
|
+
}
|
|
379
|
+
if (discovered.length === 0)
|
|
380
|
+
return [];
|
|
381
|
+
try {
|
|
382
|
+
await run(worktree, ["checkout", "--", ":(top)docs/programs"]);
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
// "did not match any file(s) known to git" is an ordinary case — no
|
|
386
|
+
// tracked file under docs/programs in this worktree.
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
await run(worktree, ["clean", "-fd", "--", ":(top)docs/programs"]);
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
// Nothing untracked to remove.
|
|
393
|
+
}
|
|
394
|
+
return discovered;
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A per-program serialized write queue: the single door every access to a
|
|
3
|
+
* program's manifest and decision ledger goes through. Manifest saves and
|
|
4
|
+
* ledger appends share one chain keyed by program (not per file) — that
|
|
5
|
+
* costs a few milliseconds of false serialization and buys a total order
|
|
6
|
+
* over a program's runner-owned state, which is what makes "the ledger
|
|
7
|
+
* never describes a manifest state that never existed" true by
|
|
8
|
+
* construction.
|
|
9
|
+
*
|
|
10
|
+
* A queued task may only do filesystem work. It must never spawn an agent,
|
|
11
|
+
* run a verify command, or call another queued API — this queue is not
|
|
12
|
+
* re-entrant, and a re-entrant call (a task that itself calls
|
|
13
|
+
* `enqueueProgramWrite` for the same key and awaits it) self-deadlocks.
|
|
14
|
+
*/
|
|
15
|
+
/** Stable key for one program's runner-owned files. */
|
|
16
|
+
export declare function programWriteKey(root: string, programId: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Runs `task` after everything already queued for `key` has settled, and
|
|
19
|
+
* returns its result. FIFO by call order. A rejected task rejects only its
|
|
20
|
+
* own caller — the chain is never poisoned for the next task.
|
|
21
|
+
*/
|
|
22
|
+
export declare function enqueueProgramWrite<T>(key: string, task: () => Promise<T>): Promise<T>;
|
|
23
|
+
/** Resolves when everything currently queued for `key` has settled. */
|
|
24
|
+
export declare function drainProgramWrites(key: string): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=write-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-queue.d.ts","sourceRoot":"","sources":["../src/write-queue.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvE;AAOD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACrB,OAAO,CAAC,CAAC,CAAC,CAmBZ;AAED,uEAAuE;AACvE,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAInE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
/**
|
|
3
|
+
* A per-program serialized write queue: the single door every access to a
|
|
4
|
+
* program's manifest and decision ledger goes through. Manifest saves and
|
|
5
|
+
* ledger appends share one chain keyed by program (not per file) — that
|
|
6
|
+
* costs a few milliseconds of false serialization and buys a total order
|
|
7
|
+
* over a program's runner-owned state, which is what makes "the ledger
|
|
8
|
+
* never describes a manifest state that never existed" true by
|
|
9
|
+
* construction.
|
|
10
|
+
*
|
|
11
|
+
* A queued task may only do filesystem work. It must never spawn an agent,
|
|
12
|
+
* run a verify command, or call another queued API — this queue is not
|
|
13
|
+
* re-entrant, and a re-entrant call (a task that itself calls
|
|
14
|
+
* `enqueueProgramWrite` for the same key and awaits it) self-deadlocks.
|
|
15
|
+
*/
|
|
16
|
+
/** Stable key for one program's runner-owned files. */
|
|
17
|
+
export function programWriteKey(root, programId) {
|
|
18
|
+
return `${resolve(root)} ${programId}`;
|
|
19
|
+
}
|
|
20
|
+
// The tail of each key's chain — always a promise that resolves (never
|
|
21
|
+
// rejects), so the next task's `priorTail` is never poisoned by a prior
|
|
22
|
+
// task's failure.
|
|
23
|
+
const tails = new Map();
|
|
24
|
+
/**
|
|
25
|
+
* Runs `task` after everything already queued for `key` has settled, and
|
|
26
|
+
* returns its result. FIFO by call order. A rejected task rejects only its
|
|
27
|
+
* own caller — the chain is never poisoned for the next task.
|
|
28
|
+
*/
|
|
29
|
+
export function enqueueProgramWrite(key, task) {
|
|
30
|
+
const priorTail = tails.get(key) ?? Promise.resolve();
|
|
31
|
+
const run = () => task();
|
|
32
|
+
const started = priorTail.then(run, run);
|
|
33
|
+
const settledTail = started.then(() => undefined, () => undefined);
|
|
34
|
+
tails.set(key, settledTail);
|
|
35
|
+
void settledTail.then(() => {
|
|
36
|
+
// Delete the entry only when this task's settlement is still the tail
|
|
37
|
+
// — a long-lived process must not accumulate one entry per program
|
|
38
|
+
// forever, but a newer task queued behind this one must not have its
|
|
39
|
+
// tail yanked out from under it.
|
|
40
|
+
if (tails.get(key) === settledTail)
|
|
41
|
+
tails.delete(key);
|
|
42
|
+
});
|
|
43
|
+
return started;
|
|
44
|
+
}
|
|
45
|
+
/** Resolves when everything currently queued for `key` has settled. */
|
|
46
|
+
export async function drainProgramWrites(key) {
|
|
47
|
+
const tail = tails.get(key);
|
|
48
|
+
if (tail === undefined)
|
|
49
|
+
return;
|
|
50
|
+
await tail;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=write-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-queue.js","sourceRoot":"","sources":["../src/write-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,SAAiB;IAC7D,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;AACzC,CAAC;AAED,uEAAuE;AACvE,wEAAwE;AACxE,kBAAkB;AAClB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;AAE/C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAW,EACX,IAAsB;IAEtB,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,MAAM,GAAG,GAAG,GAAe,EAAE,CAAC,IAAI,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAC9B,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;IAEF,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC5B,KAAK,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE;QACzB,sEAAsE;QACtE,mEAAmE;QACnE,qEAAqE;QACrE,iCAAiC;QACjC,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,WAAW;YAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAW;IAClD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,MAAM,IAAI,CAAC;AACb,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wildorder/nightshift",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "The crew that builds while you're gone: walk-away, CI-dispatched engineering programs with a decision ledger instead of gates",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@eslint/js": "^10.0.1",
|
|
60
60
|
"@types/cross-spawn": "^6.0.6",
|
|
61
61
|
"@types/node": "^26.1.2",
|
|
62
|
-
"@wildorder/nightshift": "^0.
|
|
62
|
+
"@wildorder/nightshift": "^0.17.0",
|
|
63
63
|
"eslint": "^10.8.0",
|
|
64
64
|
"typescript": "^6.0.3",
|
|
65
65
|
"typescript-eslint": "^8.66.0",
|
|
@@ -135,12 +135,28 @@ orchestration is necessary.
|
|
|
135
135
|
shared-contract migration requires expand → migrate consumers →
|
|
136
136
|
contract/delete ordering.
|
|
137
137
|
|
|
138
|
+
The runner builds independent workstreams concurrently, bounded by
|
|
139
|
+
`--max-parallel`, and starts a workstream's build as soon as its own spec is
|
|
140
|
+
finished and its dependencies have landed. Material parallelism is therefore
|
|
141
|
+
evidence you can bank, not a hope. **Pipelining pays on chains, too** — a
|
|
142
|
+
shape planners have been told not to expect a win from: a chain of *n*
|
|
143
|
+
workstreams overlaps each spec's authoring with the previous workstream's
|
|
144
|
+
build, so the run's length is roughly `author(1) + Σ build` rather than
|
|
145
|
+
`Σ author + Σ build`. A chain is no longer a reason to prefer `atomic`.
|
|
146
|
+
|
|
138
147
|
Do not choose orchestrated merely because the feature is important, spans
|
|
139
|
-
many files, or has a high estimated token count.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
many files, or has a high estimated token count. And it is still not a
|
|
149
|
+
licence to over-decompose: every workstream carries a spec, a critique
|
|
150
|
+
cycle, a verify pass, and an integration, and two workstreams that edit the
|
|
151
|
+
same file now collide at integration where serially they would have queued.
|
|
152
|
+
`atomic` remains the default when one agent can hold the change coherently.
|
|
153
|
+
State the concrete evidence in `program.executionModeReason`. In atomic mode
|
|
154
|
+
create exactly one workstream covering the entire program, with no
|
|
155
|
+
dependencies and task file `tasks/{program-id}/implementation.md`. In
|
|
156
|
+
orchestrated mode use the decomposition and checkpoint rules below, and name
|
|
157
|
+
the expected **width** (the widest dependency level, by workstream id) and
|
|
158
|
+
the expected **critical path** (the chain of workstreams expected to
|
|
159
|
+
determine the run's length) — one sentence each, in the program document.
|
|
144
160
|
|
|
145
161
|
## 2.5. Create the program's branch
|
|
146
162
|
|
|
@@ -253,6 +269,9 @@ when one exists. Otherwise use:
|
|
|
253
269
|
## Execution Mode
|
|
254
270
|
**Mode:** atomic | orchestrated
|
|
255
271
|
**Reason:** [Concrete causal evidence for the choice.]
|
|
272
|
+
**Shape:** [Orchestrated only: the widest dependency level (which workstream
|
|
273
|
+
ids run together) and the chain expected to determine the run's length. Omit
|
|
274
|
+
for atomic.]
|
|
256
275
|
|
|
257
276
|
## Strategic Goals
|
|
258
277
|
[Three to five outcome-focused bullets.]
|
|
@@ -403,6 +422,14 @@ workstream:
|
|
|
403
422
|
neighbors might reasonably assume it handles something, say that it does
|
|
404
423
|
not.
|
|
405
424
|
|
|
425
|
+
Under concurrent builds, `excludes` carries one more job: two workstreams
|
|
426
|
+
that both edit a shared file — a barrel like `src/index.ts`, the README, a
|
|
427
|
+
shared test file, a config schema module — no longer queue behind each
|
|
428
|
+
other; they build against the same base and one of them hits a conflict at
|
|
429
|
+
integration. So **name an owner for each shared file one workstream will
|
|
430
|
+
touch and others might**: the owner's `includes` says it owns the file,
|
|
431
|
+
every other workstream's `excludes` says it does not.
|
|
432
|
+
|
|
406
433
|
### Every orchestrated workstream is an independently green checkpoint
|
|
407
434
|
|
|
408
435
|
Design the roster so that, starting from a green repository containing only
|
|
@@ -423,6 +450,11 @@ For a shared contract migration, prefer an explicit sequence:
|
|
|
423
450
|
The destructive cleanup depends on every migration workstream. Do not place
|
|
424
451
|
foundational deletion first merely because it is conceptually central.
|
|
425
452
|
|
|
453
|
+
Green now means green against the program-branch tip **including the
|
|
454
|
+
siblings that landed while this workstream was building**, because the
|
|
455
|
+
runner re-verifies after integration — a checkpoint that is green only
|
|
456
|
+
against the tip it started from is a checkpoint that fails integration.
|
|
457
|
+
|
|
426
458
|
## 4.5. Absorb backlog items
|
|
427
459
|
|
|
428
460
|
When any part of this program's scope came from an item captured under
|
package/dist/worktree-guard.d.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A pre-attempt snapshot of the git worktree. Rejected replanner attempts
|
|
3
|
-
* were "rolled back" by restoring exactly two files, but the replanner is a
|
|
4
|
-
* full agentic CLI that can touch anything — out-of-scope edits and stray
|
|
5
|
-
* temp scripts survived the rollback while the progress line claimed a clean
|
|
6
|
-
* transaction. This guard makes the claim true: capture the whole tracked
|
|
7
|
-
* tree via `git stash create` (which snapshots without touching the
|
|
8
|
-
* worktree) and record which paths were untracked, so a restore can revert
|
|
9
|
-
* every tracked change and delete files the agent created.
|
|
10
|
-
*
|
|
11
|
-
* Limits, stated rather than hidden: modifications to files that were
|
|
12
|
-
* already untracked at capture time cannot be restored (git has no snapshot
|
|
13
|
-
* of them), and gitignored files are invisible to the status listing. In a
|
|
14
|
-
* non-git directory `available` is false and callers fall back to the
|
|
15
|
-
* legacy two-file restore with a warning.
|
|
16
|
-
*/
|
|
17
|
-
export interface WorktreeCapture {
|
|
18
|
-
available: boolean;
|
|
19
|
-
/** Git repository toplevel; all recorded paths are relative to it. */
|
|
20
|
-
toplevel?: string;
|
|
21
|
-
/** Commit whose tree is the captured tracked state. */
|
|
22
|
-
source?: string;
|
|
23
|
-
/** Paths untracked at capture; never deleted by a restore. */
|
|
24
|
-
untracked?: Set<string>;
|
|
25
|
-
}
|
|
26
|
-
export declare function captureWorktree(root: string): Promise<WorktreeCapture>;
|
|
27
|
-
export interface WorktreeRestoreResult {
|
|
28
|
-
restored: boolean;
|
|
29
|
-
/** Tracked paths whose state drifted from the capture (before restoring). */
|
|
30
|
-
drifted: string[];
|
|
31
|
-
/** Newly created untracked files that were deleted. */
|
|
32
|
-
removed: string[];
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Revert the worktree to `capture`, except `preservePaths` (absolute or
|
|
36
|
-
* root-relative), which keep their current content. Newly created untracked
|
|
37
|
-
* files outside the preserve list are deleted.
|
|
38
|
-
*/
|
|
39
|
-
export declare function restoreWorktree(root: string, capture: WorktreeCapture, preservePaths?: string[]): Promise<WorktreeRestoreResult>;
|
|
40
|
-
//# sourceMappingURL=worktree-guard.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worktree-guard.d.ts","sourceRoot":"","sources":["../src/worktree-guard.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AAyBD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAc5E;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAID;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,eAAe,EACxB,aAAa,GAAE,MAAM,EAAO,GAC3B,OAAO,CAAC,qBAAqB,CAAC,CAwChC"}
|