next-issue 1.0.4 → 1.0.6

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 CHANGED
@@ -233,7 +233,8 @@ adds `.next-issue/` to `.gitignore`. It stops when the config file is there
233
233
  already; `--force` replaces it. The file holds no `setupCommand`, because the
234
234
  harness deduces one from the lock file of the project.
235
235
 
236
- A field that is not in the file keeps its default. A file that is not valid
236
+ With no file, every field keeps its default. A field that is not in the file
237
+ keeps its default. A file that cannot be read, a file that is not valid
237
238
  JSON, a field with a value of the wrong type and a field name that the harness
238
239
  does not know all stop the run, so a typo cannot pass without a word.
239
240
 
@@ -325,8 +326,9 @@ A `setupCommand` that fails, or that does not finish in `setupTimeoutMinutes`,
325
326
  hands the issue to a person. The wait for the checks has its own limit, so
326
327
  `commandTimeoutMinutes` holds for every other `git` and `gh` command. State per
327
328
  issue goes to `.next-issue/<issue>.json`, so a new run continues where the last
328
- run stopped. A state file that is not valid JSON gives the issue an `error`
329
- outcome that names the file, rather than a silent skip.
329
+ run stopped. A state file that is not valid JSON, or that does not hold a valid
330
+ state, gives the issue an `error` outcome that names the file, rather than a
331
+ silent skip.
330
332
 
331
333
  ## Retry an issue that waits for a person
332
334
 
package/dist/config.mjs CHANGED
@@ -73,8 +73,11 @@ export async function loadConfig(root) {
73
73
  try {
74
74
  raw = await readFile(join(root, CONFIG_FILE), "utf8");
75
75
  }
76
- catch {
77
- return defaults();
76
+ catch (error) {
77
+ if (error.code === "ENOENT") {
78
+ return defaults();
79
+ }
80
+ throw new Error(`${CONFIG_FILE} cannot be read: ${message(error)}`);
78
81
  }
79
82
  let parsed;
80
83
  try {
package/dist/state.mjs CHANGED
@@ -19,12 +19,48 @@ export async function readState(repo, issue) {
19
19
  }
20
20
  throw error;
21
21
  }
22
+ let parsed;
22
23
  try {
23
- return JSON.parse(text);
24
+ parsed = JSON.parse(text);
24
25
  }
25
26
  catch {
26
27
  throw new Error(`the state file ${path} is not valid JSON`);
27
28
  }
29
+ if (!isState(parsed)) {
30
+ throw new Error(`the state file ${path} does not hold a valid state`);
31
+ }
32
+ return parsed;
33
+ }
34
+ const PHASES = ["claimed", "implemented", "review"];
35
+ function isRecord(value) {
36
+ return typeof value === "object" && value !== null;
37
+ }
38
+ function isOptional(value, type) {
39
+ return value === undefined || typeof value === type;
40
+ }
41
+ function isBaseFix(value) {
42
+ return isRecord(value) && typeof value.sha === "string" && typeof value.head === "string";
43
+ }
44
+ function isReviewRound(value) {
45
+ return (isRecord(value) &&
46
+ typeof value.round === "number" &&
47
+ typeof value.fingerprint === "string" &&
48
+ typeof value.findings === "string" &&
49
+ isOptional(value.dispute, "string"));
50
+ }
51
+ function isState(value) {
52
+ return (isRecord(value) &&
53
+ typeof value.issue === "number" &&
54
+ PHASES.includes(value.phase) &&
55
+ typeof value.branch === "string" &&
56
+ isOptional(value.pr, "number") &&
57
+ isOptional(value.title, "string") &&
58
+ typeof value.ciFixes === "number" &&
59
+ isOptional(value.commitFixes, "number") &&
60
+ (value.baseFix === undefined || isBaseFix(value.baseFix)) &&
61
+ typeof value.reviewRounds === "number" &&
62
+ Array.isArray(value.reviewLog) &&
63
+ value.reviewLog.every(isReviewRound));
28
64
  }
29
65
  export async function savedIssues(repo) {
30
66
  let names;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-issue",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Autonomous issue harness driven by the Claude Agent SDK",
5
5
  "keywords": [
6
6
  "github",