@planu/cli 5.3.4 → 5.3.5

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 (49) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/engine/execution/validate-job-executor.js +25 -10
  3. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
  4. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +4 -4
  5. package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
  6. package/dist/engine/planu-core.darwin-x64.node.sbom.json +4 -4
  7. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
  8. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +4 -4
  9. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
  10. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +4 -4
  11. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
  12. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +4 -4
  13. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
  14. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +4 -4
  15. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
  16. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +4 -4
  17. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
  18. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +4 -4
  19. package/dist/engine/reconcile/apply-changes.d.ts +0 -12
  20. package/dist/engine/reconcile/apply-changes.js +11 -33
  21. package/dist/engine/reconcile/verify-write.js +2 -3
  22. package/dist/engine/rules-reconciler.js +21 -2
  23. package/dist/engine/spec-format/acceptance-criteria.js +7 -1
  24. package/dist/engine/universal-rules/catalog.js +6 -0
  25. package/dist/engine/universal-rules/rules/agent-teams.js +0 -2
  26. package/dist/engine/universal-rules/rules/planu-approval-gates.js +0 -1
  27. package/dist/engine/universal-rules/rules/planu-bdd-criteria.js +0 -1
  28. package/dist/engine/universal-rules/rules/planu-clean-code-no-comments.d.ts +3 -0
  29. package/dist/engine/universal-rules/rules/planu-clean-code-no-comments.js +30 -0
  30. package/dist/engine/universal-rules/rules/planu-debate-review.d.ts +3 -0
  31. package/dist/engine/universal-rules/rules/planu-debate-review.js +38 -0
  32. package/dist/engine/universal-rules/rules/planu-dogfood-bugs.js +0 -2
  33. package/dist/engine/universal-rules/rules/planu-english-specs.js +0 -1
  34. package/dist/engine/universal-rules/rules/planu-minimal-change.js +0 -2
  35. package/dist/engine/universal-rules/rules/planu-modes.js +0 -2
  36. package/dist/engine/universal-rules/rules/planu-release-policy.js +0 -1
  37. package/dist/engine/universal-rules/rules/planu-revert-proof-tests.d.ts +3 -0
  38. package/dist/engine/universal-rules/rules/planu-revert-proof-tests.js +34 -0
  39. package/dist/engine/universal-rules/rules/planu-sdd-model-routing.js +0 -1
  40. package/dist/engine/universal-rules/rules/planu-workflow.js +0 -2
  41. package/dist/engine/validator/validation-report-writer.js +1 -1
  42. package/dist/tools/reconcile-spec.d.ts +1 -1
  43. package/dist/tools/reconcile-spec.js +303 -178
  44. package/dist/tools/update-status/dod-gates.js +3 -3
  45. package/dist/types/reconcile.d.ts +5 -17
  46. package/dist/types/reconcile.js +1 -7
  47. package/package.json +9 -9
  48. package/planu-native.json +1 -1
  49. package/planu-plugin.json +1 -1
@@ -1,19 +1,9 @@
1
- // engine/reconcile/apply-changes.ts — SPEC-1011 Bug D: Apply approved text changes to spec.md
2
- // Replacement is section-anchored to prevent silent cross-section mutations:
3
- // the originalValue is searched only inside the named section's body (between its
4
- // heading and the next heading at the same or shallower level), and the change is
5
- // applied only when the match is unique within those bounds.
6
1
  import { readFile } from 'node:fs/promises';
7
2
  import { atomicWriteFile } from '../safety/atomic-write-file.js';
8
- import { RECONCILE_METADATA_SECTIONS, } from '../../types/index.js';
3
+ import { SECTIONS_WITHOUT_LITERAL_BODY_TEXT, } from '../../types/index.js';
9
4
  function escapeRegex(str) {
10
5
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11
6
  }
12
- /**
13
- * Find the body bounds of a markdown section by heading name.
14
- * Returns the [start, end) offsets of the section body (after the heading line,
15
- * up to the next heading at the same or shallower level — or EOF).
16
- */
17
7
  function findSectionBounds(content, section) {
18
8
  const headingRe = new RegExp(`^(#{1,6})\\s+${escapeRegex(section)}\\s*$`, 'mi');
19
9
  const match = headingRe.exec(content);
@@ -22,16 +12,12 @@ function findSectionBounds(content, section) {
22
12
  }
23
13
  const headingLevel = match[1]?.length ?? 0;
24
14
  const start = match.index + match[0].length;
25
- // Find next heading at same-or-shallower level
26
- const nextRe = new RegExp(`^#{1,${String(headingLevel)}}\\s+\\S`, 'gm');
27
- nextRe.lastIndex = start;
28
- const next = nextRe.exec(content);
15
+ const nextHeadingAtSameOrShallowerLevelRe = new RegExp(`^#{1,${String(headingLevel)}}\\s+\\S`, 'gm');
16
+ nextHeadingAtSameOrShallowerLevelRe.lastIndex = start;
17
+ const next = nextHeadingAtSameOrShallowerLevelRe.exec(content);
29
18
  return { start, end: next ? next.index : content.length };
30
19
  }
31
- /**
32
- * Count non-overlapping occurrences of `needle` in `haystack`.
33
- */
34
- function countOccurrences(haystack, needle) {
20
+ function countNonOverlappingOccurrences(haystack, needle) {
35
21
  if (needle.length === 0) {
36
22
  return 0;
37
23
  }
@@ -45,20 +31,8 @@ function countOccurrences(haystack, needle) {
45
31
  }
46
32
  return count;
47
33
  }
48
- /**
49
- * Apply approved text changes to the spec.md body.
50
- *
51
- * For each non-metadata approved change:
52
- * 1. Locate the named section's body via heading anchor.
53
- * 2. Require exactly one occurrence of `originalValue` inside that section.
54
- * 3. Replace it in-place at the resolved absolute offset.
55
- *
56
- * Skips (with reason) when the section heading is absent, the value is missing
57
- * inside the section, or the value appears multiple times (ambiguous match).
58
- * Writes only when at least one substitution succeeded.
59
- */
60
34
  export async function applyChangesToSpec(specPath, changes) {
61
- const approvedTextChanges = changes.filter((c) => c.approved && !RECONCILE_METADATA_SECTIONS.has(c.section));
35
+ const approvedTextChanges = changes.filter((c) => c.approved && !SECTIONS_WITHOUT_LITERAL_BODY_TEXT.has(c.section));
62
36
  if (approvedTextChanges.length === 0) {
63
37
  return { applied: 0, skipped: [], fileWritten: false };
64
38
  }
@@ -74,6 +48,7 @@ export async function applyChangesToSpec(specPath, changes) {
74
48
  section: c.section,
75
49
  originalValue: c.originalValue,
76
50
  reason: `Cannot read spec.md: ${reason}`,
51
+ reasonCategory: 'skipped',
77
52
  })),
78
53
  fileWritten: false,
79
54
  };
@@ -88,16 +63,18 @@ export async function applyChangesToSpec(specPath, changes) {
88
63
  section: change.section,
89
64
  originalValue: change.originalValue,
90
65
  reason: `Section heading "${change.section}" not found in spec.md`,
66
+ reasonCategory: 'skipped',
91
67
  });
92
68
  continue;
93
69
  }
94
70
  const sectionBody = modified.slice(bounds.start, bounds.end);
95
- const occurrences = countOccurrences(sectionBody, change.originalValue);
71
+ const occurrences = countNonOverlappingOccurrences(sectionBody, change.originalValue);
96
72
  if (occurrences === 0) {
97
73
  skipped.push({
98
74
  section: change.section,
99
75
  originalValue: change.originalValue,
100
76
  reason: `originalValue not found in section "${change.section}"`,
77
+ reasonCategory: 'skipped',
101
78
  });
102
79
  continue;
103
80
  }
@@ -106,6 +83,7 @@ export async function applyChangesToSpec(specPath, changes) {
106
83
  section: change.section,
107
84
  originalValue: change.originalValue,
108
85
  reason: `originalValue is ambiguous (${String(occurrences)} matches in section "${change.section}")`,
86
+ reasonCategory: 'ambiguous',
109
87
  });
110
88
  continue;
111
89
  }
@@ -1,7 +1,7 @@
1
1
  // engine/reconcile/verify-write.ts — SPEC-1011 Bug D: Post-write integrity verification
2
2
  // After reconcile_spec writes changes, re-reads the file to assert each newValue is present.
3
3
  import { readFile } from 'node:fs/promises';
4
- import { RECONCILE_METADATA_SECTIONS, } from '../../types/index.js';
4
+ import { SECTIONS_WITHOUT_LITERAL_BODY_TEXT, } from '../../types/index.js';
5
5
  /**
6
6
  * Re-reads the file at `filePath` and checks that each approved change's `newValue`
7
7
  * is present somewhere in the file content.
@@ -30,8 +30,7 @@ export async function verifyWriteSucceeded(filePath, changes) {
30
30
  }
31
31
  const missingChanges = [];
32
32
  for (const change of approvedChanges) {
33
- // Skip metadata-only sections that do not produce text in spec.md
34
- if (RECONCILE_METADATA_SECTIONS.has(change.section)) {
33
+ if (SECTIONS_WITHOUT_LITERAL_BODY_TEXT.has(change.section)) {
35
34
  continue;
36
35
  }
37
36
  if (!content.includes(change.newValue)) {
@@ -67,7 +67,23 @@ function buildCoverageMap(rulesFiles, allRules) {
67
67
  *
68
68
  * A file is "stale" when it has NO categories in common with CLAUDE.md categories.
69
69
  */
70
- function detectStaleRules(rulesFiles, allRules, claudeCategories) {
70
+ function manifestOwnedRuleFiles(projectPath) {
71
+ const raw = readFileSafe(join(projectPath, '.claude', 'rules', '.planu-rules-manifest.json'));
72
+ if (!raw) {
73
+ return new Set();
74
+ }
75
+ try {
76
+ const manifest = JSON.parse(raw);
77
+ return new Set((manifest.rules ?? [])
78
+ .map((rule) => rule.path)
79
+ .filter((path) => typeof path === 'string')
80
+ .map((path) => basename(path)));
81
+ }
82
+ catch {
83
+ return new Set();
84
+ }
85
+ }
86
+ function detectStaleRules(rulesFiles, allRules, claudeCategories, manifestOwned) {
71
87
  // Build per-file category sets
72
88
  const fileCategories = new Map();
73
89
  for (const r of allRules) {
@@ -80,6 +96,9 @@ function detectStaleRules(rulesFiles, allRules, claudeCategories) {
80
96
  }
81
97
  const stale = [];
82
98
  for (const filePath of rulesFiles) {
99
+ if (manifestOwned.has(basename(filePath))) {
100
+ continue;
101
+ }
83
102
  const cats = fileCategories.get(filePath);
84
103
  if (!cats || cats.size === 0) {
85
104
  // No parseable conventions found — candidate for stale
@@ -162,7 +181,7 @@ export function reconcileRules(projectPath) {
162
181
  }
163
182
  }
164
183
  // 5. Detect stale rules files
165
- const staleRules = detectStaleRules(rulesFiles, allRules, claudeCategories);
184
+ const staleRules = detectStaleRules(rulesFiles, allRules, claudeCategories, manifestOwnedRuleFiles(projectPath));
166
185
  // 6. Auto-patch: generate new rules files for uncovered categories
167
186
  const updatedRules = [];
168
187
  for (const category of newRulesNeeded) {
@@ -5,7 +5,7 @@ import { stripFencedBlocks } from './text-fences.js';
5
5
  import { createCriterionIdentity, normalizeCriterionText } from '../criterion-identity.js';
6
6
  const BDD_STEP = /^(GIVEN|WHEN|THEN|AND|DADO|CUANDO|ENTONCES|Y)\b/i;
7
7
  const BDD_START = /^(GIVEN|DADO)\b/i;
8
- const TECHNICAL_MARKER = /^(FILES|FUNCTIONS|TEST|OWNER):/i;
8
+ const TECHNICAL_MARKER = /^(FILES|FUNCTIONS|TEST|OWNER|NOTE):/i;
9
9
  const EXPLICIT_CRITERION_HEADING = /^#{3,6}\s+((?:AC|AB)\s*-?\s*\d+)\b/i;
10
10
  function extractBddBlocks(section) {
11
11
  const lines = section.split('\n').map((line) => line.trim());
@@ -38,6 +38,12 @@ function extractBddBlocks(section) {
38
38
  current.push(withoutListMarker);
39
39
  continue;
40
40
  }
41
+ // Wrapped continuation of the step above: keep it, or the criterion identity
42
+ // truncates at the physical Markdown wrap boundary (SPEC-1253). A line that
43
+ // carried its own list marker is a sibling item, never a wrap.
44
+ if (current.length > 0 && withoutListMarker === line) {
45
+ current.push(withoutListMarker);
46
+ }
41
47
  }
42
48
  if (current.length > 0) {
43
49
  bddBlocks.push(current.join(' '));
@@ -10,6 +10,9 @@ import { planuApprovalGatesRule } from './rules/planu-approval-gates.js';
10
10
  import { planuReleasePolicyRule } from './rules/planu-release-policy.js';
11
11
  import { planuSddModelRoutingRule } from './rules/planu-sdd-model-routing.js';
12
12
  import { planuMinimalChangeRule } from './rules/planu-minimal-change.js';
13
+ import { planuDebateReviewRule } from './rules/planu-debate-review.js';
14
+ import { planuRevertProofTestsRule } from './rules/planu-revert-proof-tests.js';
15
+ import { planuCleanCodeNoCommentsRule } from './rules/planu-clean-code-no-comments.js';
13
16
  /**
14
17
  * The full catalog of universal Planu rules.
15
18
  * Order matters: rules are installed in catalog order.
@@ -25,5 +28,8 @@ export const UNIVERSAL_RULES = [
25
28
  planuModesRule,
26
29
  agentTeamsRule,
27
30
  planuDogfoodBugsRule,
31
+ planuDebateReviewRule,
32
+ planuRevertProofTestsRule,
33
+ planuCleanCodeNoCommentsRule,
28
34
  ];
29
35
  //# sourceMappingURL=catalog.js.map
@@ -1,5 +1,3 @@
1
- // engine/universal-rules/rules/agent-teams.ts — SPEC-779
2
- // Universal rule: Agent Teams (migrated from rules-generator.ts)
3
1
  function buildBody() {
4
2
  return `# Agent Teams (MANDATORY when 3+ specs in parallel)
5
3
 
@@ -1,4 +1,3 @@
1
- // engine/universal-rules/rules/planu-approval-gates.ts — Universal rule: approval and done gates
2
1
  function buildBody() {
3
2
  return `# Planu Approval and Done Gates
4
3
 
@@ -1,4 +1,3 @@
1
- // engine/universal-rules/rules/planu-bdd-criteria.ts — Universal rule: BDD acceptance criteria
2
1
  function buildBody() {
3
2
  return `# Planu BDD Acceptance Criteria
4
3
 
@@ -0,0 +1,3 @@
1
+ import type { UniversalRule } from '../../../types/universal-rules/index.js';
2
+ export declare const planuCleanCodeNoCommentsRule: UniversalRule;
3
+ //# sourceMappingURL=planu-clean-code-no-comments.d.ts.map
@@ -0,0 +1,30 @@
1
+ function buildBody() {
2
+ return `# Planu Clean Code — No Comments
3
+
4
+ Auto-generated by \`init_project\`. Do not edit manually.
5
+
6
+ Code must be self-documenting. A comment is allowed only when the behavior cannot be expressed through naming or structure — for example a non-obvious language semantic. Everything else is a naming problem.
7
+
8
+ This rule assumes \`planu-dogfood-bugs\` already prohibits deferred-work markers such as TODO/FIXME and does not restate that prohibition.
9
+
10
+ ## The Rule
11
+
12
+ Prohibited: narrative comments that restate the code, \`(SPEC-NNNN: ...)\` rationale blocks whose history belongs in the spec and in git blame, step-by-step or section-divider comments inside a function, and JSDoc that only repeats the signature.
13
+
14
+ Replacement action: Extract a helper whose name is the comment, promote a magic value to a named constant, or introduce a named type when a shape needs explaining.
15
+
16
+ ## Enforcement
17
+
18
+ Implementation agents add narrative comments by default. Stripping them before accepting subagent output is part of reviewing that output, not an optional cleanup pass.
19
+ `;
20
+ }
21
+ export const planuCleanCodeNoCommentsRule = {
22
+ id: 'planu-clean-code-no-comments',
23
+ name: 'Planu Clean Code — No Comments',
24
+ description: 'Prohibits narrative comments and rationale blocks, requiring named helpers or constants instead.',
25
+ category: 'quality',
26
+ applicableHosts: ['all'],
27
+ defaultEnabled: true,
28
+ buildContent: (_host) => buildBody(),
29
+ };
30
+ //# sourceMappingURL=planu-clean-code-no-comments.js.map
@@ -0,0 +1,3 @@
1
+ import type { UniversalRule } from '../../../types/universal-rules/index.js';
2
+ export declare const planuDebateReviewRule: UniversalRule;
3
+ //# sourceMappingURL=planu-debate-review.d.ts.map
@@ -0,0 +1,38 @@
1
+ function buildBody() {
2
+ return `# Planu Debate Review
3
+
4
+ Auto-generated by \`init_project\`. Do not edit manually.
5
+
6
+ This rule assumes \`planu-approval-gates\` (reviewer independence, self-approval prohibitions) and \`planu-sdd-model-routing\` (arbitration) and restates neither.
7
+
8
+ ## Reviewer Plurality And Provider Diversity
9
+
10
+ Every spec and every implementation is reviewed by two independent reviewers from DIFFERENT providers, in ONE parallel round.
11
+
12
+ Prohibited: running a single reviewer, or running two reviewers from the same provider, and calling the result a debate round.
13
+
14
+ Replacement action: launch both reviewers in the same message so they run concurrently, and pick two different providers (for example one Claude-family model and one external model).
15
+
16
+ ## One Parallel Round, No Serial Confirmation Loops
17
+
18
+ Prohibited: re-launching a reviewer for pass 2, pass 3, or a "confirm the fix" loop after the first round already produced verdicts.
19
+
20
+ Replacement action: cross the two verdicts once — coincident findings apply directly, divergent findings are arbitrated against the source code — then close the round. Re-open a reviewer only for a specific disagreement the arbiter cannot resolve by reading the code directly.
21
+
22
+ ## Degradation Ladder
23
+
24
+ Prohibited: silently reviewing with only one provider when a second is unavailable and presenting it as a debate round.
25
+
26
+ Replacement action: when only one provider is available, run two isolated sessions of the same model instead of one, so two independent verdicts still exist before arbitration.
27
+ `;
28
+ }
29
+ export const planuDebateReviewRule = {
30
+ id: 'planu-debate-review',
31
+ name: 'Planu Debate Review',
32
+ description: 'Requires two-provider parallel review rounds with no serial confirmation loops and a single-provider degradation ladder.',
33
+ category: 'quality',
34
+ applicableHosts: ['all'],
35
+ defaultEnabled: true,
36
+ buildContent: (_host) => buildBody(),
37
+ };
38
+ //# sourceMappingURL=planu-debate-review.js.map
@@ -1,5 +1,3 @@
1
- // engine/universal-rules/rules/planu-dogfood-bugs.ts — SPEC-779
2
- // Universal rule: Planu Dogfood Bugs (mandatory for all Planu-using projects)
3
1
  /** Shared markdown body (same for all hosts). */
4
2
  function buildBody() {
5
3
  return `# Planu Dogfood Bugs (MANDATORY — STRICT)
@@ -1,4 +1,3 @@
1
- // engine/universal-rules/rules/planu-english-specs.ts — Universal rule: specs are written in English
2
1
  function buildBody() {
3
2
  return `# Planu Artifacts Must Be Written in English
4
3
 
@@ -1,5 +1,3 @@
1
- // engine/universal-rules/rules/planu-minimal-change.ts
2
- // Universal rule: minimal-change implementation discipline.
3
1
  function buildBody() {
4
2
  return `# Planu Minimal Change
5
3
 
@@ -1,5 +1,3 @@
1
- // engine/universal-rules/rules/planu-modes.ts — SPEC-779
2
- // Universal rule: Claude Code Mode Hints per spec phase (SPEC-494)
3
1
  function buildBody(host) {
4
2
  if (host === 'claude-code') {
5
3
  return `# Claude Code Mode Hints (MANDATORY — Planu SDD)
@@ -1,4 +1,3 @@
1
- // engine/universal-rules/rules/planu-release-policy.ts — Universal rule: release decision
2
1
  function buildBody() {
3
2
  return `# Planu Release Policy
4
3
 
@@ -0,0 +1,3 @@
1
+ import type { UniversalRule } from '../../../types/universal-rules/index.js';
2
+ export declare const planuRevertProofTestsRule: UniversalRule;
3
+ //# sourceMappingURL=planu-revert-proof-tests.d.ts.map
@@ -0,0 +1,34 @@
1
+ function buildBody() {
2
+ return `# Planu Revert-Proof Tests
3
+
4
+ Auto-generated by \`init_project\`. Do not edit manually.
5
+
6
+ A new test that never fails against the unfixed source proves nothing.
7
+
8
+ ## The Rule
9
+
10
+ Prohibited: reporting a new test as passing evidence without ever having watched it fail. This includes an assertion that compares a value to itself, a mock that echoes back what it was given, and a negative assertion that holds vacuously regardless of the source under test.
11
+
12
+ Replacement action: before reporting the test as evidence, revert only the source change it covers — invert the specific guard line in place, or run the test against the pre-change body read with \`git show <ref>:<path>\` — run the test, and observe it fail. Then restore the source and confirm the test passes again. Report the observed failure output, not just the final green run.
13
+
14
+ Prohibited: using \`git stash\`, \`git checkout\` or \`git restore\` on a shared checkout to stage the revert. Stashes are a process-wide LIFO stack, so a concurrent agent can pop work that is not its own.
15
+
16
+ A guard's regression test must fail against the unguarded variant specifically, not merely against an unrelated point in history such as HEAD.
17
+
18
+ ## Hard Blocks
19
+
20
+ - Do not accept a test whose only run is against already-fixed source.
21
+ - Do not accept an assertion that compares a value to itself.
22
+ - Do not accept an assertion that cannot fail for any input.
23
+ `;
24
+ }
25
+ export const planuRevertProofTestsRule = {
26
+ id: 'planu-revert-proof-tests',
27
+ name: 'Planu Revert-Proof Tests',
28
+ description: 'Requires new tests to be proven to fail against the unfixed source before being reported as evidence.',
29
+ category: 'quality',
30
+ applicableHosts: ['all'],
31
+ defaultEnabled: true,
32
+ buildContent: (_host) => buildBody(),
33
+ };
34
+ //# sourceMappingURL=planu-revert-proof-tests.js.map
@@ -1,4 +1,3 @@
1
- // engine/universal-rules/rules/planu-sdd-model-routing.ts — Universal rule: model routing + context continuity
2
1
  function buildBody() {
3
2
  return `# Planu SDD Model Routing and Context Guarantee
4
3
 
@@ -1,5 +1,3 @@
1
- // engine/universal-rules/rules/planu-workflow.ts — SPEC-779
2
- // Universal rule: Planu Workflow (migrated from rules-generator.ts)
3
1
  function buildBody() {
4
2
  return `# Planu SDD Workflow (MANDATORY)
5
3
 
@@ -39,7 +39,7 @@ export async function writeImplementationReviewReport(input) {
39
39
  return {
40
40
  written: true,
41
41
  path: artifact.path,
42
- sha: artifact.sha,
42
+ sha: `sha256:${artifact.sha}`,
43
43
  reviewer,
44
44
  passed,
45
45
  gates: mergedGates,
@@ -1,4 +1,4 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { ReconcileSpecInput, ToolResult } from '../types/index.js';
2
+ import { type ReconcileSpecInput, type ToolResult } from '../types/index.js';
3
3
  export declare function handleReconcileSpec(params: ReconcileSpecInput, server?: McpServer): Promise<ToolResult>;
4
4
  //# sourceMappingURL=reconcile-spec.d.ts.map