@christang/keel 5.1.2 → 5.2.1

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.
@@ -5,7 +5,12 @@
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
7
  const { spawnSync } = require("child_process");
8
- const { compileTaskContract, field, parseTasks } = require("./task-contract");
8
+ const {
9
+ ACCEPTED_REVIEW_STATUSES,
10
+ compileTaskContract,
11
+ field,
12
+ parseTasks,
13
+ } = require("./task-contract");
9
14
 
10
15
  const NEXT_ACTIONS = new Set([
11
16
  "discuss",
@@ -68,8 +73,10 @@ function taskHasCompletionEvidence(record, contract) {
68
73
  (match) => match[1]
69
74
  )
70
75
  );
71
- const reviewPassed =
72
- /^\s+- Status:\s*(?:pass|passed|complete|completed|ok)\s*$/im.test(evidence);
76
+ const reviewPassed = new RegExp(
77
+ `^\\s*-\\s*Status:\\s*(?:${ACCEPTED_REVIEW_STATUSES.join("|")})\\s*$`,
78
+ "im"
79
+ ).test(evidence);
73
80
  return (
74
81
  commandIds.length > 0
75
82
  && commandIds.every((commandId) => evidenceIds.has(commandId))
package/src/core/gates.js CHANGED
@@ -6,10 +6,12 @@ const fs = require("fs");
6
6
  const path = require("path");
7
7
  const { spawnSync } = require("child_process");
8
8
  const {
9
+ ACCEPTED_REVIEW_STATUSES,
9
10
  RED_GREEN_VERIFICATION_STRATEGIES,
10
11
  compileTaskContract,
11
12
  field,
12
13
  isConcrete,
14
+ isPassingReviewStatus,
13
15
  parseTasks,
14
16
  } = require("./task-contract");
15
17
  const { startGuard } = require("./guard");
@@ -235,7 +237,16 @@ function gitPaths(repo) {
235
237
  return status.stdout
236
238
  .split(/\r?\n/)
237
239
  .filter(Boolean)
238
- .map((line) => line.slice(3).trim().replace(/\\/g, "/"));
240
+ .flatMap((line) => {
241
+ // A staged rename/copy is one porcelain line, `R old -> new`; attribute
242
+ // both endpoints so a rename whose old and new paths are in Touch is not
243
+ // a false outside-Touch failure. Every other line carries one path.
244
+ const entry = line.slice(3).trim().replace(/\\/g, "/");
245
+ const arrow = entry.indexOf(" -> ");
246
+ return arrow === -1
247
+ ? [entry]
248
+ : [entry.slice(0, arrow), entry.slice(arrow + 4)];
249
+ });
239
250
  }
240
251
 
241
252
  function touchEntries(task, contract = null) {
@@ -376,16 +387,29 @@ function completionChecks(repo, task, contract = null) {
376
387
  : [["Findings", reviewFields.Findings]]
377
388
  ),
378
389
  ];
379
- const reviewPassed = /^(?:pass|passed|complete|completed|ok)$/i.test(
380
- reviewFields.Status
381
- );
390
+ const reviewPassed = isPassingReviewStatus(reviewFields.Status);
382
391
  const reviewProblems = [];
383
392
  if (reviewMissing.length > 0 || !reviewPassed) {
393
+ const details = [];
394
+ if (!reviewPassed) {
395
+ const got = isConcrete(reviewFields.Status)
396
+ ? ` (got "${String(reviewFields.Status).trim()}")`
397
+ : "";
398
+ details.push(
399
+ `Status must be one of ${ACCEPTED_REVIEW_STATUSES.join(", ")}${got}`
400
+ );
401
+ }
402
+ const otherMissing = reviewMissing.filter(([name]) => name !== "Status");
403
+ if (otherMissing.length > 0) {
404
+ details.push(
405
+ "these Review fields need concrete Evidence: "
406
+ + otherMissing.map(([name]) => name).join(", ")
407
+ );
408
+ }
384
409
  reviewProblems.push(
385
410
  problem(
386
411
  "semantic-review",
387
- "Current-agent Review requires passing Status, Acceptance check, "
388
- + "Scope check, and Findings."
412
+ `Current-agent Review is incomplete ${details.join("; ")}.`
389
413
  )
390
414
  );
391
415
  } else if (
@@ -395,8 +419,10 @@ function completionChecks(repo, task, contract = null) {
395
419
  problems.push(
396
420
  problem(
397
421
  "finding-owner",
398
- "Review findings require an OpenSpec, archive-evidence, or explicit "
399
- + "discard owner; HANDOFF is not an owner."
422
+ "Review Findings must be `none` or carry a durable owner — a "
423
+ + "`Discard reason:`/`Discard rationale:` prefix, a `keel/archive/…` "
424
+ + "path, or an existing `openspec/changes/…` artifact; "
425
+ + "`keel/HANDOFF.md` is not an owner."
400
426
  )
401
427
  );
402
428
  }
@@ -441,7 +467,9 @@ function expectationProblems(content, tasks) {
441
467
  return [
442
468
  problem(
443
469
  "expectation-coverage",
444
- "tasks.md requires an Expectation Coverage section."
470
+ "tasks.md requires a `## Expectation Coverage` section: one "
471
+ + "`- E<n>: <expectation> Covered by: <task ids>` line per "
472
+ + "expectation, or `- None.`."
445
473
  ),
446
474
  ];
447
475
  }
@@ -459,7 +487,9 @@ function expectationProblems(content, tasks) {
459
487
  return [
460
488
  problem(
461
489
  "expectation-coverage",
462
- "Expectation Coverage must declare each E<n> closure or `None`."
490
+ "Expectation Coverage must declare each `E<n>` closure "
491
+ + "`- E<n>: <expectation> Covered by: <task ids>`, a `Durable owner:` "
492
+ + "path, or a `Discard reason:` — or `- None.`."
463
493
  ),
464
494
  ];
465
495
  }
@@ -97,6 +97,25 @@ const RED_GREEN_VERIFICATION_STRATEGIES = new Set([
97
97
  "regression-first",
98
98
  ]);
99
99
 
100
+ // Single source of truth for the accepted completion Review `Status`
101
+ // vocabulary. Consumed by both the completion gate (src/core/gates.js) and the
102
+ // context "already reviewed" probe (src/core/context.js) so the two never
103
+ // diverge.
104
+ const ACCEPTED_REVIEW_STATUSES = [
105
+ "pass",
106
+ "passed",
107
+ "complete",
108
+ "completed",
109
+ "ok",
110
+ "done",
111
+ ];
112
+
113
+ function isPassingReviewStatus(value) {
114
+ return ACCEPTED_REVIEW_STATUSES.includes(
115
+ String(value == null ? "" : value).trim().toLowerCase()
116
+ );
117
+ }
118
+
100
119
  function verification(task) {
101
120
  const compact = fieldValues(task, "Verify");
102
121
  const strategyEntry = compact.find((entry) => /^Strategy:\s*/i.test(entry));
@@ -725,11 +744,13 @@ function loadTaskContract(repo, change, taskId) {
725
744
  }
726
745
 
727
746
  module.exports = {
747
+ ACCEPTED_REVIEW_STATUSES,
728
748
  RED_GREEN_VERIFICATION_STRATEGIES,
729
749
  SUPPORTED_VERIFICATION_STRATEGIES,
730
750
  compileTaskContract,
731
751
  field,
732
752
  isConcrete,
753
+ isPassingReviewStatus,
733
754
  loadTaskContract,
734
755
  parseTasks,
735
756
  taskStartContractProblems,