@planu/cli 5.3.6 → 5.3.8

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/config/runtime-policy.json +1 -0
  3. package/dist/engine/convention-scanner/codebase-scanner.js +1 -1
  4. package/dist/engine/implementation-contract/renderer.js +6 -6
  5. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
  6. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +4 -4
  7. package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
  8. package/dist/engine/planu-core.darwin-x64.node.sbom.json +4 -4
  9. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
  10. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +4 -4
  11. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
  12. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +4 -4
  13. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
  14. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +4 -4
  15. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
  16. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +4 -4
  17. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
  18. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +4 -4
  19. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
  20. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +4 -4
  21. package/dist/engine/runtime-policy.js +7 -0
  22. package/dist/engine/scope-boundaries/contradiction-checker.js +11 -10
  23. package/dist/tools/validate-assurance.js +1 -1
  24. package/dist/tools/validate-lint.js +1 -1
  25. package/dist/tools/validate.d.ts +1 -0
  26. package/dist/tools/validate.js +21 -2
  27. package/dist/types/runtime-policy.d.ts +1 -0
  28. package/package.json +9 -9
  29. package/planu-native.json +1 -1
  30. package/planu-plugin.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ ## [5.3.8] - 2026-08-09
2
+
3
+ ### Bug Fixes
4
+ - fix(spec-1460): exempt freshly published @planu/cli from the pnpm minimum-release-age gate
5
+ - fix(spec-1458): render index-only verification rows so the gate accepts its own output
6
+
7
+ ### Chores
8
+ - chore(planu): record SPEC-1460 done transition
9
+ - chore(planu): record SPEC-1458 done transition
10
+ - chore(planu): session checkpoint after v5.3.7 release
11
+
12
+
13
+ ## [5.3.7] - 2026-08-08
14
+
15
+ ### Bug Fixes
16
+ - fix(scope-boundaries): split sentences regardless of next-sentence casing
17
+ - fix(validate): fail lint on timeout and bound unbounded validate gates
18
+ - fix(spec-1450): anchor scope-contradiction substring checks to word boundaries
19
+
20
+ ### Chores
21
+ - chore(spec-1453): redact local home path from reproducer
22
+ - chore(planu): record SPEC-1449 and SPEC-1450 done transitions
23
+ - chore(spec-1450): integrate scope-contradiction matching after arbitrated review
24
+ - chore(spec-1449): integrate validate gate timeouts after dual-provider approve
25
+ - chore(specs): cut speculative scope from the skills backlog
26
+ - chore(spec-1449): approve after 6-round independent review + Discovery evidence
27
+ - chore(deps): override nanoid to >=3.3.17 for GHSA-2v37-7h3g-55p8
28
+ - chore(planu): sync session state after SPEC-1449/1450 review cycle
29
+
30
+
1
31
  ## [5.3.6] - 2026-08-07
2
32
 
3
33
  ### Bug Fixes
@@ -29,6 +29,7 @@
29
29
  "validation": {
30
30
  "defaultDeadlineMs": 1800000,
31
31
  "durableLintTimeoutMs": 150000,
32
+ "perGateTimeoutMs": 180000,
32
33
  "complianceCommandTimeoutMs": 900000,
33
34
  "noProgressTimeoutMs": 960000,
34
35
  "submissionLockWaitMs": 180,
@@ -7,7 +7,7 @@ const MAX_VIOLATIONS_PER_CATEGORY = 50;
7
7
  // ── helpers ───────────────────────────────────────────────────────────────────
8
8
  function execSafe(cmd, cwd) {
9
9
  try {
10
- return execSync(cmd, { cwd, stdio: ['pipe', 'pipe', 'pipe'] }).toString();
10
+ return execSync(cmd, { cwd, timeout: 5_000, stdio: ['pipe', 'pipe', 'pipe'] }).toString();
11
11
  }
12
12
  catch {
13
13
  return '';
@@ -84,12 +84,12 @@ function renderVerificationRows(criteria, testFiles, commands) {
84
84
  }
85
85
  if (testFiles.length >= criteria.length) {
86
86
  const rows = [];
87
- criteria.forEach((criterion, index) => {
87
+ for (let index = 0; index < criteria.length; index += 1) {
88
88
  const test = testFiles[index];
89
89
  if (test !== undefined) {
90
- rows.push(renderPerCriterionRow(criterion.text, index + 1, test, commands));
90
+ rows.push(renderPerCriterionRow(index + 1, test, commands));
91
91
  }
92
- });
92
+ }
93
93
  return rows;
94
94
  }
95
95
  if (testFiles.length === 0) {
@@ -97,11 +97,11 @@ function renderVerificationRows(criteria, testFiles, commands) {
97
97
  }
98
98
  return renderSharedVerificationRow(testFiles, commands);
99
99
  }
100
- function renderPerCriterionRow(criterion, index, test, commands) {
100
+ function renderPerCriterionRow(index, test, commands) {
101
101
  const command = commands[index - 1] ?? commands[0];
102
102
  return command
103
- ? `- AC${String(index)}: ${criterion} -> unit test \`${test}\` plus \`${command}\`.`
104
- : `- AC${String(index)}: ${criterion} -> unit test \`${test}\`.`;
103
+ ? `- AC${String(index)}: verified by unit test \`${test}\` plus \`${command}\`.`
104
+ : `- AC${String(index)}: verified by unit test \`${test}\`.`;
105
105
  }
106
106
  /**
107
107
  * Emitted once when there are fewer declared test files than criteria — never repeat
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.6",
4
- "engineVersion": "5.3.6",
5
- "buildId": "1-5.3.6-aarch64-apple-darwin",
3
+ "cliVersion": "5.3.8",
4
+ "engineVersion": "5.3.8",
5
+ "buildId": "1-5.3.8-aarch64-apple-darwin",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -17,15 +17,15 @@
17
17
  "platform": "darwin",
18
18
  "arch": "arm64",
19
19
  "libc": null,
20
- "artifactSha256": "c319951a5aff69954e19a04ccc2165fbbe7e334ca1ec42defd9bc81543748bd7",
20
+ "artifactSha256": "8dcbd7a6a1225d68658cb50ce32b174e52e820a3ebdc3f125cdec568966a7759",
21
21
  "sbom": {
22
22
  "format": "CycloneDX-1.5",
23
23
  "path": "planu-core.darwin-arm64.node.sbom.json",
24
- "sha256": "5c09e19e2aed6fc51ffdbc569f291fbadf84d6fe76e9f889df7a0e8b2896b154"
24
+ "sha256": "34ae39426563dbd6b2d4f61119ac53e362ddb2fb78a27b074518b477cb808d37"
25
25
  },
26
26
  "provenance": {
27
27
  "builder": "scripts/build-rust-local.sh",
28
- "sourceCommit": "2dae5f5028632004802bad4c143eb86d8abba774",
28
+ "sourceCommit": "c0d406e9b49a5ed393b2dcade39eb951223974c6",
29
29
  "sourceTreeDirty": false,
30
30
  "sourceDateEpoch": 1,
31
31
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -34,6 +34,6 @@
34
34
  "signature": {
35
35
  "algorithm": "Ed25519",
36
36
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
37
- "value": "eyypjiaqLdyOHXK4eND7lnJCagGpBRp5htIhZvkik7iU/KYdTS0Vfl0Wsl4XKo/sfoiY2gHaqf624PZABAK0DQ=="
37
+ "value": "zulz2seIniwXGYJINphaouLeL8/CHmjKezWwOUf/8l1tBlpk8z/7jl/SL3SspVFih3PWPM9+0kJAo7aHixOUDA=="
38
38
  }
39
39
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.6",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.8",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.6",
10
+ "version": "5.3.8",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "c319951a5aff69954e19a04ccc2165fbbe7e334ca1ec42defd9bc81543748bd7"
14
+ "content": "8dcbd7a6a1225d68658cb50ce32b174e52e820a3ebdc3f125cdec568966a7759"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.6",
1109
+ "ref": "pkg:cargo/planu-core@5.3.8",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.6",
4
- "engineVersion": "5.3.6",
5
- "buildId": "1-5.3.6-x86_64-apple-darwin",
3
+ "cliVersion": "5.3.8",
4
+ "engineVersion": "5.3.8",
5
+ "buildId": "1-5.3.8-x86_64-apple-darwin",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -17,15 +17,15 @@
17
17
  "platform": "darwin",
18
18
  "arch": "x64",
19
19
  "libc": null,
20
- "artifactSha256": "3e9868d3264b3df31e81a6f16ed50b11e5438a7336b9f131b5d333d6e1704e97",
20
+ "artifactSha256": "f3affc187e9c20766667848f9742e59cbba41b4791238e5aeaffaeccaaaa5dc2",
21
21
  "sbom": {
22
22
  "format": "CycloneDX-1.5",
23
23
  "path": "planu-core.darwin-x64.node.sbom.json",
24
- "sha256": "e2508d46acae3efb316207d0c15be64fc875207d0fc780f2530cc72f7db2652c"
24
+ "sha256": "417f4b08fcf6c6668ad620c9019fad7bbe79a09f613c253ee59863d6f44b8039"
25
25
  },
26
26
  "provenance": {
27
27
  "builder": "scripts/build-rust-local.sh",
28
- "sourceCommit": "2dae5f5028632004802bad4c143eb86d8abba774",
28
+ "sourceCommit": "c0d406e9b49a5ed393b2dcade39eb951223974c6",
29
29
  "sourceTreeDirty": false,
30
30
  "sourceDateEpoch": 1,
31
31
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -34,6 +34,6 @@
34
34
  "signature": {
35
35
  "algorithm": "Ed25519",
36
36
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
37
- "value": "cGf49U9+LuqEiQc9Ro+8XJHMfOm9AE6/tftEmeMVZIPUohMd+MpprFCMdAZu41VMhiu/MZb91+5phEaBIbmlCw=="
37
+ "value": "AepYMvl7NAYszjYlB691/Cp+P7MEM3/9rd1CcbHbYaz8G+T0HTNQ8U80LddrdKnPAcje/nrwolUapbRScWs+Bg=="
38
38
  }
39
39
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.6",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.8",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.6",
10
+ "version": "5.3.8",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "3e9868d3264b3df31e81a6f16ed50b11e5438a7336b9f131b5d333d6e1704e97"
14
+ "content": "f3affc187e9c20766667848f9742e59cbba41b4791238e5aeaffaeccaaaa5dc2"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.6",
1109
+ "ref": "pkg:cargo/planu-core@5.3.8",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.6",
4
- "engineVersion": "5.3.6",
5
- "buildId": "1-5.3.6-aarch64-unknown-linux-gnu",
3
+ "cliVersion": "5.3.8",
4
+ "engineVersion": "5.3.8",
5
+ "buildId": "1-5.3.8-aarch64-unknown-linux-gnu",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "linux",
17
17
  "arch": "arm64",
18
18
  "libc": "glibc",
19
- "artifactSha256": "a057f9f806c472c60949ec962a7872ffed5d858e6e231d7864de285a2ae87ffe",
19
+ "artifactSha256": "c8f8315bb60f966e6250dffc7c094adcb1516499056b6c88a4d714e1b85ae52e",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.linux-arm64-gnu.node.sbom.json",
23
- "sha256": "8722f7dead83c0a64bbb596258edfa5e098f7289652a7317c4fc130a44b5debb"
23
+ "sha256": "14a46a7d84851076b7549b8ff8d412a31d845c7ff291c90fff08bfb39e4b2ea0"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "2dae5f5028632004802bad4c143eb86d8abba774",
27
+ "sourceCommit": "c0d406e9b49a5ed393b2dcade39eb951223974c6",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "rokl1fIAhfFkN+cVkmHqWJKL9V5rwP5iCJ2nFZ+n8+N3NwhIbxX6v/ZNjYXYH2sOwIx6aRFFd3EN1pBApwU/AQ=="
36
+ "value": "3002w64/CbUXeA8G0etnA8f8k6EnI8HpTXL0D97EghZLaU4lmqJ4KTgnjaVV7XtemRz4QkGXrlfQ8MW6nJTnAg=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.6",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.8",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.6",
10
+ "version": "5.3.8",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "a057f9f806c472c60949ec962a7872ffed5d858e6e231d7864de285a2ae87ffe"
14
+ "content": "c8f8315bb60f966e6250dffc7c094adcb1516499056b6c88a4d714e1b85ae52e"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.6",
1109
+ "ref": "pkg:cargo/planu-core@5.3.8",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.6",
4
- "engineVersion": "5.3.6",
5
- "buildId": "1-5.3.6-aarch64-unknown-linux-musl",
3
+ "cliVersion": "5.3.8",
4
+ "engineVersion": "5.3.8",
5
+ "buildId": "1-5.3.8-aarch64-unknown-linux-musl",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "linux",
17
17
  "arch": "arm64",
18
18
  "libc": "musl",
19
- "artifactSha256": "2ea8f19e0cc3365dd23cbe0e7fbf7767a5b5fa0f58e6a044d0f6fd0b39949d24",
19
+ "artifactSha256": "ffc21930a3ffa37a8ffb963d1c3bd88194261c5f47d6645df01024d9e2b60d5a",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.linux-arm64-musl.node.sbom.json",
23
- "sha256": "868d8d54cef201e1454ccf935e8fe7946e8543530803140d7b97c5886c4e4237"
23
+ "sha256": "421f01e0c237604db423ba74d1a1e91849d4bbf6223f2fa5d2c6c110888b08d1"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "2dae5f5028632004802bad4c143eb86d8abba774",
27
+ "sourceCommit": "c0d406e9b49a5ed393b2dcade39eb951223974c6",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "tSU/uWkXwhZZ+29DcUXHWI9KYfGspNLieVr4sZzD0ds0qwTeYDSxioNMNRdCeNaieCT1nKNNM1jf+ftF2wU4DQ=="
36
+ "value": "Euk8g0nxITjsBrUfmZltIfPHxy6x8s6iySzbxjxYSuuLlgHPYgCQwR8blrV+CNnzXbLhCf7bDseprKw9ZxhcDg=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.6",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.8",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.6",
10
+ "version": "5.3.8",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "2ea8f19e0cc3365dd23cbe0e7fbf7767a5b5fa0f58e6a044d0f6fd0b39949d24"
14
+ "content": "ffc21930a3ffa37a8ffb963d1c3bd88194261c5f47d6645df01024d9e2b60d5a"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.6",
1109
+ "ref": "pkg:cargo/planu-core@5.3.8",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.6",
4
- "engineVersion": "5.3.6",
5
- "buildId": "1-5.3.6-x86_64-unknown-linux-gnu",
3
+ "cliVersion": "5.3.8",
4
+ "engineVersion": "5.3.8",
5
+ "buildId": "1-5.3.8-x86_64-unknown-linux-gnu",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "linux",
17
17
  "arch": "x64",
18
18
  "libc": "glibc",
19
- "artifactSha256": "6353c0738f92143dbb24b5cc2312f76ad613c35a81c7f868f4a9bed4942cecd1",
19
+ "artifactSha256": "2fd2f2b88fba7345f0d0e19ea16b9600230ef6fae6b1f1a044ee02764214c5e3",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.linux-x64-gnu.node.sbom.json",
23
- "sha256": "608d97eda6a7982dde93f50d1d704d5753abb383835dc8795d7912d645158046"
23
+ "sha256": "51e56ab54ee969b571b7009169db4edbc533c5a92476395c20764cd2ab6654bf"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "2dae5f5028632004802bad4c143eb86d8abba774",
27
+ "sourceCommit": "c0d406e9b49a5ed393b2dcade39eb951223974c6",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "dRBPmA3Yh3EfsVYIBGbjaECPAL9z/HerkHRzn5f7p30F97UEF2shfvVofKV01SNYcGB/hqa54ofvKq6GbZYWBg=="
36
+ "value": "3dDrNnVtW/juPnD67kL7ZA3tvrDFvyfbIBX+eVVi26Ly9tCDUM9abpvX28TPazuGUJ+Paipj+W1/cOEyGsKhDA=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.6",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.8",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.6",
10
+ "version": "5.3.8",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "6353c0738f92143dbb24b5cc2312f76ad613c35a81c7f868f4a9bed4942cecd1"
14
+ "content": "2fd2f2b88fba7345f0d0e19ea16b9600230ef6fae6b1f1a044ee02764214c5e3"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.6",
1109
+ "ref": "pkg:cargo/planu-core@5.3.8",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.6",
4
- "engineVersion": "5.3.6",
5
- "buildId": "1-5.3.6-x86_64-unknown-linux-musl",
3
+ "cliVersion": "5.3.8",
4
+ "engineVersion": "5.3.8",
5
+ "buildId": "1-5.3.8-x86_64-unknown-linux-musl",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "linux",
17
17
  "arch": "x64",
18
18
  "libc": "musl",
19
- "artifactSha256": "fc1ffffc7aa8178117d75efb21a21cbf47789de550aaa09010c6acbd22a3fa3f",
19
+ "artifactSha256": "d93c3691b0f835b4056bf2d8b17d0448566616222d5221fc292fc87019120318",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.linux-x64-musl.node.sbom.json",
23
- "sha256": "016bcd4176cb0c11f7fa2890b13bd882afcaea12cb92a3316586f421a379ab9b"
23
+ "sha256": "02609054c32df79175cfa245c503e1df1ca4b32b15ad34ec0eda5b34a28fda27"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "2dae5f5028632004802bad4c143eb86d8abba774",
27
+ "sourceCommit": "c0d406e9b49a5ed393b2dcade39eb951223974c6",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "evwVuvJPRtdHXxc0wulqvQSSTm+IoeWgcuZuZjXnI2OZXQ+M9P4dAS/7ChFxoIul52xBCEB1MIZObdMynhWvCA=="
36
+ "value": "/echWzVigxiZs4/TF6wUHv1MEHBi7ulWLA9+JpDnd+otBDX8HIM3O6oOhDc4Lu1wmfQJMPyPCvutWkZxcXj6Dg=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.6",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.8",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.6",
10
+ "version": "5.3.8",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "fc1ffffc7aa8178117d75efb21a21cbf47789de550aaa09010c6acbd22a3fa3f"
14
+ "content": "d93c3691b0f835b4056bf2d8b17d0448566616222d5221fc292fc87019120318"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.6",
1109
+ "ref": "pkg:cargo/planu-core@5.3.8",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.6",
4
- "engineVersion": "5.3.6",
5
- "buildId": "1-5.3.6-aarch64-pc-windows-msvc",
3
+ "cliVersion": "5.3.8",
4
+ "engineVersion": "5.3.8",
5
+ "buildId": "1-5.3.8-aarch64-pc-windows-msvc",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "win32",
17
17
  "arch": "arm64",
18
18
  "libc": null,
19
- "artifactSha256": "16659d578e84674eeab01f7f53943e46ae8b2c1836fef583eb126a4eee15c2ff",
19
+ "artifactSha256": "1e7250f120e58d9ebbf26b22ae9b0fd3d963ad80479b61e3f4be59feb0bcc97e",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.win32-arm64-msvc.node.sbom.json",
23
- "sha256": "411f35f08ebc1f8dc65a665bc355e2b10dc0b8db2bb279e33bb1a4d49f1bad34"
23
+ "sha256": "9a10362845e48f91b5604552de6509fcf1125c0161b43f2b6b0b3abb0fbc4295"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "2dae5f5028632004802bad4c143eb86d8abba774",
27
+ "sourceCommit": "c0d406e9b49a5ed393b2dcade39eb951223974c6",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "om/6aNSNCXZ8rbF+dw+8mWFhr1Zj0+z06pFccowCC3dcXtzDkLwDuquUlcHq8OSmFJNyjXFJ5i+OopjRPXKqCg=="
36
+ "value": "xvRJaOZmJ82Ru9UzRQENpyiU64mTOOcdVfh8gdS6jK0T5QUTJ9wUtz28xhnh24uyZLjyO3niKQky3Sq2GpEhCw=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.6",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.8",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.6",
10
+ "version": "5.3.8",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "16659d578e84674eeab01f7f53943e46ae8b2c1836fef583eb126a4eee15c2ff"
14
+ "content": "1e7250f120e58d9ebbf26b22ae9b0fd3d963ad80479b61e3f4be59feb0bcc97e"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.6",
1109
+ "ref": "pkg:cargo/planu-core@5.3.8",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.6",
4
- "engineVersion": "5.3.6",
5
- "buildId": "1-5.3.6-x86_64-pc-windows-msvc",
3
+ "cliVersion": "5.3.8",
4
+ "engineVersion": "5.3.8",
5
+ "buildId": "1-5.3.8-x86_64-pc-windows-msvc",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "win32",
17
17
  "arch": "x64",
18
18
  "libc": null,
19
- "artifactSha256": "e6750baae63b969de18daae8150796a785638847bb7b3645b214b252f0bf32d3",
19
+ "artifactSha256": "00609e4d5c5cb1f41a9b3824bbe4775bdab365a7bc95f9936a947b5ce2103e69",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.win32-x64-msvc.node.sbom.json",
23
- "sha256": "323e01b4f75869ec688a26524c5e4aa756a6ad5c0e7fd968a9090ad6e58a75bf"
23
+ "sha256": "805d4becff3818b502a44b83b3152b74d2064a74b0808f5e0587e6d9b8eec274"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "2dae5f5028632004802bad4c143eb86d8abba774",
27
+ "sourceCommit": "c0d406e9b49a5ed393b2dcade39eb951223974c6",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "YmufISTxo4Tx7ceRSzMxv6fXdoi19oHTokfKkevZzBkaCi3msjDRWJqerPQjjDr5Vkip8LB+2oQUS2DC3Ea+AQ=="
36
+ "value": "fZQLZB7+7E55Gy/9luaF7isRmDht9nrNnvy8a922yCp4AnYXqRfhztkVPQPkMgoN5+wv793XOfY/6iS4rvmhBw=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.6",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.8",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.6",
10
+ "version": "5.3.8",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "e6750baae63b969de18daae8150796a785638847bb7b3645b214b252f0bf32d3"
14
+ "content": "00609e4d5c5cb1f41a9b3824bbe4775bdab365a7bc95f9936a947b5ce2103e69"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.6",
1109
+ "ref": "pkg:cargo/planu-core@5.3.8",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -47,6 +47,7 @@ const runtimePolicySchema = z
47
47
  .object({
48
48
  defaultDeadlineMs: z.number().int().positive(),
49
49
  durableLintTimeoutMs: z.number().int().positive(),
50
+ perGateTimeoutMs: z.number().int().positive(),
50
51
  complianceCommandTimeoutMs: z.number().int().positive(),
51
52
  noProgressTimeoutMs: z.number().int().positive(),
52
53
  submissionLockWaitMs: z.number().int().positive(),
@@ -72,6 +73,12 @@ const runtimePolicySchema = z
72
73
  })
73
74
  .refine((value) => value.durableLintTimeoutMs < value.complianceCommandTimeoutMs, {
74
75
  message: 'durableLintTimeoutMs must be below complianceCommandTimeoutMs',
76
+ })
77
+ .refine((value) => value.durableLintTimeoutMs < value.perGateTimeoutMs, {
78
+ message: 'durableLintTimeoutMs must be below perGateTimeoutMs',
79
+ })
80
+ .refine((value) => value.perGateTimeoutMs < value.noProgressTimeoutMs, {
81
+ message: 'perGateTimeoutMs must be below noProgressTimeoutMs',
75
82
  })
76
83
  .refine((value) => value.complianceCommandTimeoutMs + PROCESS_TERMINATION_GRACE_MS <
77
84
  value.noProgressTimeoutMs, {
@@ -57,9 +57,12 @@ function preservesBoundary(text) {
57
57
  /\bno changes? to\b/,
58
58
  ].some((pattern) => pattern.test(normalized));
59
59
  }
60
+ const CLAUSE_KEYWORD_OR_PUNCTUATION = /\b(?:but|while|although|however)\b|[,;]/i;
61
+ const SENTENCE_ENDING_PERIOD = /\.(?=\s|$)/;
60
62
  function splitClauses(text) {
61
63
  return text
62
- .split(/\b(?:but|while|although|however)\b|[,;.]/i)
64
+ .split(CLAUSE_KEYWORD_OR_PUNCTUATION)
65
+ .flatMap((segment) => segment.split(SENTENCE_ENDING_PERIOD))
63
66
  .map((clause) => clause.trim())
64
67
  .filter(Boolean);
65
68
  }
@@ -89,6 +92,9 @@ function hasAffirmativeDrift(text, outOfScopeItem) {
89
92
  function containsKeyword(normalizedHaystack, keyword) {
90
93
  return new RegExp(`\\b${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(normalizedHaystack);
91
94
  }
95
+ function isDirectPhraseMatch(normCriterion, normScope) {
96
+ return containsKeyword(normCriterion, normScope) || containsKeyword(normScope, normCriterion);
97
+ }
92
98
  function clauseMatchesScope(clause, outOfScopeItem) {
93
99
  const normalizedClause = normalize(clause);
94
100
  const normalizedScope = normalize(outOfScopeItem);
@@ -102,7 +108,9 @@ function clauseMatchesScope(clause, outOfScopeItem) {
102
108
  }
103
109
  function clauseMentionsScope(clause, outOfScopeItem) {
104
110
  const normalizedClause = normalize(clause);
105
- return keywords(outOfScopeItem).some((keyword) => containsKeyword(normalizedClause, keyword));
111
+ const scopeKeywords = keywords(outOfScopeItem);
112
+ const mentionCandidates = scopeKeywords.length > 0 ? scopeKeywords : [normalize(outOfScopeItem)];
113
+ return mentionCandidates.some((keyword) => containsKeyword(normalizedClause, keyword));
106
114
  }
107
115
  /**
108
116
  * Determine whether a criterion text contradicts an out-of-scope item.
@@ -119,16 +127,9 @@ function contradicts(criterionText, outOfScopeItem) {
119
127
  !relevantClauses.some((clause) => hasAffirmativeDrift(clause, outOfScopeItem))) {
120
128
  return false;
121
129
  }
122
- // 1. Direct substring match
123
- if (normCriterion.includes(normScope)) {
124
- return true;
125
- }
126
- // 2. Reverse — the out-of-scope item contains the whole criterion (unlikely but safe)
127
- if (normScope.includes(normCriterion)) {
130
+ if (isDirectPhraseMatch(normCriterion, normScope)) {
128
131
  return true;
129
132
  }
130
- // 3. Keyword overlap, scoped to a single clause: if 2+ significant keywords from
131
- // outOfScope appear within the same clause of the criterion
132
133
  if (keywords(outOfScopeItem).length === 0) {
133
134
  return false;
134
135
  }
@@ -20,7 +20,7 @@ function readChangedSpecLogicFiles(projectPath) {
20
20
  'src/tools/create-spec',
21
21
  'src/tools/validate.ts',
22
22
  'src/engine',
23
- ], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
23
+ ], { encoding: 'utf-8', timeout: 5_000, stdio: ['ignore', 'pipe', 'ignore'] });
24
24
  changedFiles = output
25
25
  .split('\n')
26
26
  .map((line) => line.trim())
@@ -128,7 +128,7 @@ export async function runLintCheck(projectPath, lintCommand, context = {}) {
128
128
  source: 'local:planu-validate',
129
129
  });
130
130
  }
131
- return { passed: true, command, issueCount: 0, output };
131
+ return { passed: false, command, issueCount: 1, output };
132
132
  }
133
133
  const raw = commandOutput(err);
134
134
  const output = formatLintFailureDiagnostics(plan, raw);
@@ -4,6 +4,7 @@ import type { ValidateInput, ToolResult, ContractSpec, ContractValidation } from
4
4
  import type { ValidateWorkerProgress } from '../types/validate-worker.js';
5
5
  export { validateContractCompliance };
6
6
  export type { ContractSpec, ContractValidation };
7
+ export declare function createValidationStageRunner(onProgress?: (progress: ValidateWorkerProgress) => void): <T>(phase: string, command: string, work: () => T | Promise<T>) => Promise<T>;
7
8
  /** Public MCP boundary: persist and acknowledge validation work without running it inline. */
8
9
  export declare function handleValidate(args: ValidateInput): Promise<ToolResult>;
9
10
  export declare function executeValidate(args: ValidateInput, server?: McpServer, onProgress?: (progress: ValidateWorkerProgress) => void): Promise<ToolResult>;
@@ -85,7 +85,26 @@ import { resolveValidationWorktree, ValidationWorktreeError, } from '../engine/v
85
85
  // Re-export for external use (SPEC-018)
86
86
  export { validateContractCompliance };
87
87
  const VALIDATION_GATE_TOTAL = 8;
88
- function createValidationStageRunner(onProgress) {
88
+ const GATE_DEADLINE_EXEMPT_PHASES = new Set(['lint', 'compliance']);
89
+ async function runWithGateDeadline(phase, command, work) {
90
+ if (GATE_DEADLINE_EXEMPT_PHASES.has(phase)) {
91
+ return work();
92
+ }
93
+ const deadlineMs = getRuntimePolicy().validation.perGateTimeoutMs;
94
+ let timer;
95
+ const deadline = new Promise((_resolve, reject) => {
96
+ timer = setTimeout(() => {
97
+ reject(new Error(`[Planu] validate gate "${phase}" (${command}) exceeded its ${String(deadlineMs)}ms deadline`));
98
+ }, deadlineMs);
99
+ });
100
+ try {
101
+ return await Promise.race([Promise.resolve(work()), deadline]);
102
+ }
103
+ finally {
104
+ clearTimeout(timer);
105
+ }
106
+ }
107
+ export function createValidationStageRunner(onProgress) {
89
108
  const startedAt = Date.now();
90
109
  let completedGates = 0;
91
110
  return async (phase, command, work) => {
@@ -102,7 +121,7 @@ function createValidationStageRunner(onProgress) {
102
121
  };
103
122
  emit('running');
104
123
  try {
105
- const result = await work();
124
+ const result = await runWithGateDeadline(phase, command, work);
106
125
  completedGates += 1;
107
126
  emit('passed');
108
127
  return result;
@@ -24,6 +24,7 @@ export interface RuntimePolicy {
24
24
  validation: {
25
25
  defaultDeadlineMs: number;
26
26
  durableLintTimeoutMs: number;
27
+ perGateTimeoutMs: number;
27
28
  complianceCommandTimeoutMs: number;
28
29
  noProgressTimeoutMs: number;
29
30
  submissionLockWaitMs: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planu/cli",
3
- "version": "5.3.6",
3
+ "version": "5.3.8",
4
4
  "description": "Planu — MCP Server for Spec Driven Development with native Rust acceleration for hot paths. Cross-platform (Linux/macOS/Windows, x64/arm64, glibc/musl).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -35,14 +35,14 @@
35
35
  "packageName": "@planu/core"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@planu/core-darwin-arm64": "5.3.6",
39
- "@planu/core-darwin-x64": "5.3.6",
40
- "@planu/core-linux-arm64-gnu": "5.3.6",
41
- "@planu/core-linux-arm64-musl": "5.3.6",
42
- "@planu/core-linux-x64-gnu": "5.3.6",
43
- "@planu/core-linux-x64-musl": "5.3.6",
44
- "@planu/core-win32-arm64-msvc": "5.3.6",
45
- "@planu/core-win32-x64-msvc": "5.3.6"
38
+ "@planu/core-darwin-arm64": "5.3.8",
39
+ "@planu/core-darwin-x64": "5.3.8",
40
+ "@planu/core-linux-arm64-gnu": "5.3.8",
41
+ "@planu/core-linux-arm64-musl": "5.3.8",
42
+ "@planu/core-linux-x64-gnu": "5.3.8",
43
+ "@planu/core-linux-x64-musl": "5.3.8",
44
+ "@planu/core-win32-arm64-msvc": "5.3.8",
45
+ "@planu/core-win32-x64-msvc": "5.3.8"
46
46
  },
47
47
  "engines": {
48
48
  "node": ">=24.0.0"
package/planu-native.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dev.planu.native",
3
3
  "displayName": "Planu Native Lightweight Surface",
4
- "version": "5.3.6",
4
+ "version": "5.3.8",
5
5
  "packageName": "@planu/cli",
6
6
  "modes": {
7
7
  "lightweight": {
package/planu-plugin.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dev.planu.cli",
3
3
  "displayName": "Planu — Spec Driven Development",
4
4
  "description": "Manage software specs, estimations, and autonomous SDD workflows. Language-agnostic MCP server for Claude Code.",
5
- "version": "5.3.6",
5
+ "version": "5.3.8",
6
6
  "icon": "assets/plugin/icon.svg",
7
7
  "command": ["npx", "@planu/cli@latest"],
8
8
  "packageName": "@planu/cli",