@planu/cli 4.3.2 → 4.3.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [4.3.3] - 2026-05-22
2
+
3
+ **Tarball SHA-256:** `d681d7d176a263f3b059c4ed5fbc7647a17758dc3c56cc79fd47658bab082fe6`
4
+
5
+ ### Bug Fixes
6
+ - fix(planu): expose update_status routing evidence
7
+
8
+
1
9
  ## [4.3.2] - 2026-05-22
2
10
 
3
11
  **Tarball SHA-256:** `7732de964d5e10d24bdab5ea79baee3d281654cf5caff1f713671502c8e09ee3`
@@ -286,6 +286,49 @@ export function registerCoreSpecTools(server) {
286
286
  .optional()
287
287
  .describe('Absolute path to the project root. Used to derive projectId automatically.'),
288
288
  status: SpecStatusEnum.describe('New status'),
289
+ sessionId: z
290
+ .string()
291
+ .max(500)
292
+ .optional()
293
+ .describe('Agent/session ID performing this lifecycle transition.'),
294
+ modelId: z
295
+ .string()
296
+ .max(500)
297
+ .optional()
298
+ .describe('Concrete model ID used for this transition. Can satisfy SDD model-routing evidence when it maps to the required tier.'),
299
+ modelTierUsed: z
300
+ .enum(['max', 'implementation', 'review'])
301
+ .optional()
302
+ .describe('SDD model tier evidence for this phase: max for approval/arbitration, implementation for coding, review for reviewer work.'),
303
+ contextHash: z
304
+ .string()
305
+ .max(500)
306
+ .optional()
307
+ .describe('SHA-256 hash of the persisted context package used to prove context continuity across agents.'),
308
+ handoffPath: z
309
+ .string()
310
+ .max(4096)
311
+ .optional()
312
+ .describe('Path to the persisted handoff package generated by package_handoff. Required for implementing/done unless handoffArtifactId is provided.'),
313
+ handoffArtifactId: z
314
+ .string()
315
+ .max(500)
316
+ .optional()
317
+ .describe('External handoff artifact ID when the host stores handoff evidence outside the filesystem.'),
318
+ reviewedBy: z
319
+ .string()
320
+ .max(500)
321
+ .optional()
322
+ .describe('Reviewer identity/evidence required before status=done.'),
323
+ arbitratedBy: z
324
+ .string()
325
+ .max(500)
326
+ .optional()
327
+ .describe('Arbiter identity/evidence required before status=done.'),
328
+ reconcileRequired: z
329
+ .boolean()
330
+ .optional()
331
+ .describe('Set true when implementation drift requires reconcile_spec before status=done; true blocks done.'),
289
332
  actuals: z
290
333
  .object({
291
334
  devHours: z.number().min(0),
@@ -67,6 +67,29 @@ async function autoCaptureLessonEstimation(spec, actuals, accuracy, projectId, p
67
67
  /* best-effort — never throw */
68
68
  }
69
69
  }
70
+ const SPEC_BODY_SHRINK_ABSOLUTE_THRESHOLD = 200;
71
+ const SPEC_BODY_SHRINK_RATIO_THRESHOLD = 0.05;
72
+ function bodyWithoutFrontmatter(content) {
73
+ return parseFrontmatter(content).body;
74
+ }
75
+ function hasExplicitTruncationMarker(content) {
76
+ return /\btruncated\b|SPEC_INTEGRITY_ALLOWED_REDUCTION/i.test(content);
77
+ }
78
+ function assertSpecBodyIntegrity(args) {
79
+ if (args.newStatus === 'done') {
80
+ return;
81
+ }
82
+ if (hasExplicitTruncationMarker(args.updated)) {
83
+ return;
84
+ }
85
+ const originalBodyLength = bodyWithoutFrontmatter(args.original).length;
86
+ const updatedBodyLength = bodyWithoutFrontmatter(args.updated).length;
87
+ const lost = originalBodyLength - updatedBodyLength;
88
+ const ratio = originalBodyLength === 0 ? 0 : lost / originalBodyLength;
89
+ if (lost > SPEC_BODY_SHRINK_ABSOLUTE_THRESHOLD && ratio > SPEC_BODY_SHRINK_RATIO_THRESHOLD) {
90
+ throw new Error(`SPEC_INTEGRITY_WRITE_FAILED: refusing to write ${args.specId} because update_status(${args.newStatus}) would shrink spec.md body by ${String(lost)} characters without an explicit migration marker.`);
91
+ }
92
+ }
70
93
  export async function syncSpecFiles(updatedSpec, currentStatus, newStatus, projectPath) {
71
94
  let warning;
72
95
  if (updatedSpec.specPath) {
@@ -80,6 +103,12 @@ export async function syncSpecFiles(updatedSpec, currentStatus, newStatus, proje
80
103
  updatedContent = stripDoneCriteria(updatedContent);
81
104
  }
82
105
  }
106
+ assertSpecBodyIntegrity({
107
+ original: specContent,
108
+ updated: updatedContent,
109
+ newStatus,
110
+ specId: updatedSpec.id,
111
+ });
83
112
  await writeFile(updatedSpec.specPath, updatedContent, 'utf-8');
84
113
  // SPEC-544: Auto-stage spec.md after writing so it's included in the user's next commit
85
114
  // SPEC-575: Auto-commit staged planu/ docs (idempotent, safe-fail)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planu/cli",
3
- "version": "4.3.2",
3
+ "version": "4.3.3",
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",
@@ -32,14 +32,14 @@
32
32
  "packageName": "@planu/core"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@planu/core-darwin-arm64": "4.3.2",
36
- "@planu/core-darwin-x64": "4.3.2",
37
- "@planu/core-linux-arm64-gnu": "4.3.2",
38
- "@planu/core-linux-arm64-musl": "4.3.2",
39
- "@planu/core-linux-x64-gnu": "4.3.2",
40
- "@planu/core-linux-x64-musl": "4.3.2",
41
- "@planu/core-win32-arm64-msvc": "4.3.2",
42
- "@planu/core-win32-x64-msvc": "4.3.2"
35
+ "@planu/core-darwin-arm64": "4.3.3",
36
+ "@planu/core-darwin-x64": "4.3.3",
37
+ "@planu/core-linux-arm64-gnu": "4.3.3",
38
+ "@planu/core-linux-arm64-musl": "4.3.3",
39
+ "@planu/core-linux-x64-gnu": "4.3.3",
40
+ "@planu/core-linux-x64-musl": "4.3.3",
41
+ "@planu/core-win32-arm64-msvc": "4.3.3",
42
+ "@planu/core-win32-x64-msvc": "4.3.3"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=24.0.0"