@planu/cli 4.3.24 → 4.4.0

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 (69) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/engine/constitution/sdd-rules-registry.d.ts +12 -0
  3. package/dist/engine/constitution/sdd-rules-registry.js +105 -0
  4. package/dist/engine/evidence-index/done-drift.d.ts +6 -0
  5. package/dist/engine/evidence-index/done-drift.js +44 -0
  6. package/dist/engine/evidence-index/index-builder.d.ts +10 -0
  7. package/dist/engine/evidence-index/index-builder.js +138 -0
  8. package/dist/engine/handoff-artifacts/index.d.ts +1 -16
  9. package/dist/engine/handoff-artifacts/io.d.ts +1 -1
  10. package/dist/engine/handoff-artifacts/schemas.d.ts +1 -1
  11. package/dist/engine/handoff-artifacts/validation-result.d.ts +18 -0
  12. package/dist/engine/handoff-artifacts/validation-result.js +3 -0
  13. package/dist/engine/hooks/handlers/on-impl-change.js +3 -2
  14. package/dist/engine/host-rules-templates/templates.js +3 -0
  15. package/dist/engine/spec-format/lean-spec-generator.js +17 -12
  16. package/dist/engine/spec-grounding/contract.d.ts +19 -0
  17. package/dist/engine/spec-grounding/contract.js +186 -0
  18. package/dist/engine/spec-metrics/actionable-metrics.d.ts +10 -0
  19. package/dist/engine/spec-metrics/actionable-metrics.js +69 -0
  20. package/dist/engine/spec-quality/generic-output-gate.d.ts +3 -0
  21. package/dist/engine/spec-quality/generic-output-gate.js +105 -0
  22. package/dist/engine/test-contract-generator.js +41 -39
  23. package/dist/engine/test-scaffold-generator/unit-scaffold.js +10 -10
  24. package/dist/engine/test-spec-generator/criterion-parser.js +5 -24
  25. package/dist/storage/base-store.d.ts +0 -1
  26. package/dist/storage/base-store.js +0 -4
  27. package/dist/tools/create-spec.js +160 -30
  28. package/dist/tools/generate-tests/generators/concurrency-test-generator/java-templates.js +8 -13
  29. package/dist/tools/generate-tests/generators/concurrency-test-generator/js-templates.js +21 -47
  30. package/dist/tools/generate-tests/generators/concurrency-test-generator/python-rust-templates.js +3 -14
  31. package/dist/tools/git/auto-complete-ops.d.ts +18 -0
  32. package/dist/tools/git/auto-complete-ops.js +63 -0
  33. package/dist/tools/git/branch-ops.d.ts +0 -17
  34. package/dist/tools/git/branch-ops.js +0 -54
  35. package/dist/tools/git/cleanup-ops.js +0 -16
  36. package/dist/tools/git/release-ops.js +1 -1
  37. package/dist/tools/init-project/handler.js +1 -1
  38. package/dist/tools/manage-hooks.js +3 -1
  39. package/dist/tools/status-handler.js +1 -1
  40. package/dist/tools/update-status/evidence-gate.js +23 -6
  41. package/dist/tools/update-status/transition-guard.js +49 -0
  42. package/dist/tools/update-status-actions.js +8 -5
  43. package/dist/types/actionable-spec-metrics.d.ts +8 -0
  44. package/dist/types/actionable-spec-metrics.js +2 -0
  45. package/dist/types/clarification-token.d.ts +1 -1
  46. package/dist/types/clarification.d.ts +2 -18
  47. package/dist/types/evidence-gates.d.ts +1 -1
  48. package/dist/types/evidence-index.d.ts +24 -0
  49. package/dist/types/evidence-index.js +2 -0
  50. package/dist/types/hook-status-update.d.ts +10 -0
  51. package/dist/types/hook-status-update.js +3 -0
  52. package/dist/types/index.d.ts +6 -0
  53. package/dist/types/index.js +6 -0
  54. package/dist/types/interactive-question.d.ts +19 -0
  55. package/dist/types/interactive-question.js +3 -0
  56. package/dist/types/sdd-constitution.d.ts +27 -0
  57. package/dist/types/sdd-constitution.js +2 -0
  58. package/dist/types/spec-format.d.ts +7 -0
  59. package/dist/types/spec-grounding.d.ts +22 -0
  60. package/dist/types/spec-grounding.js +2 -0
  61. package/dist/types/spec-quality.d.ts +10 -0
  62. package/dist/types/storage.d.ts +1 -1
  63. package/package.json +9 -9
  64. package/planu-native.json +1 -1
  65. package/planu-plugin.json +1 -1
  66. package/dist/engine/hooks/index.d.ts +0 -20
  67. package/dist/engine/hooks/index.js +0 -25
  68. package/dist/storage/crud-store-factory.d.ts +0 -22
  69. package/dist/storage/crud-store-factory.js +0 -72
@@ -1,5 +1,4 @@
1
1
  import { specStore } from '../../storage/index.js';
2
- import { handleUpdateStatus } from '../update-status/index.js';
3
2
  import { ti } from '../../i18n/index.js';
4
3
  import { git, resolveProjectPath, slugify, mergeConfig, detectGitFlowType, resolveBaseBranch, DEFAULT_BRANCH_PREFIXES, DEFAULT_PROTECTED_BRANCHES, } from './git-helpers.js';
5
4
  import { compactResult, compactError, formatKeyValue, formatTable, formatList, } from '../output-formatter.js';
@@ -20,59 +19,6 @@ function randomSuffix() {
20
19
  .toString(16)
21
20
  .padStart(8, '0');
22
21
  }
23
- export async function autoCompleteSpecs(projectId, projectPath) {
24
- // Try develop first, fall back to main
25
- let mergedOut;
26
- try {
27
- const r = await git(projectPath, ['branch', '--merged', 'develop']);
28
- mergedOut = r.stdout;
29
- }
30
- catch {
31
- const r = await git(projectPath, ['branch', '--merged', 'main']);
32
- mergedOut = r.stdout;
33
- }
34
- const mergedList = mergedOut
35
- .split('\n')
36
- .map((b) => b.replace(/^\*?\s+/, '').trim())
37
- .filter(Boolean);
38
- const mergedSet = new Set(mergedList);
39
- /** Returns true if the spec's branch (exact or by ID pattern) appears in merged branches. */
40
- function isMerged(specId, gitBranch) {
41
- if (gitBranch && mergedSet.has(gitBranch)) {
42
- return true;
43
- }
44
- const num = /SPEC-(\d+)/i.exec(specId)?.[1];
45
- if (!num) {
46
- return false;
47
- }
48
- const pat = new RegExp(`[/\\-]SPEC-${num}([/\\-]|$)`, 'i');
49
- return mergedList.some((b) => pat.test(b));
50
- }
51
- const specs = (await specStore.listSpecs(projectId)).filter((s) => (s.status === 'implementing' || s.status === 'approved') && isMerged(s.id, s.gitBranch));
52
- const completed = [];
53
- const blocked = [];
54
- for (const spec of specs) {
55
- // SPEC-720: Route through handleUpdateStatus so all gates (DoD/validate/QA) run.
56
- // Never call specStore.updateSpec({status}) directly.
57
- const result = await handleUpdateStatus({
58
- specId: spec.id,
59
- status: 'done',
60
- projectId,
61
- projectPath,
62
- trigger: 'git-merge',
63
- actor: 'system',
64
- reviewNotes: 'Auto-completed: branch merged',
65
- });
66
- if (result.isError) {
67
- const reason = result.content[0]?.type === 'text' ? result.content[0].text : 'gate blocked (no message)';
68
- blocked.push({ specId: spec.id, reason });
69
- }
70
- else {
71
- completed.push(spec.id);
72
- }
73
- }
74
- return { completed, blocked };
75
- }
76
22
  export async function handleCreateBranch(projectId, specId, config) {
77
23
  if (!specId) {
78
24
  return compactError('❌ Error: specId is required for create-branch action');
@@ -3,7 +3,6 @@ import { existsSync } from 'node:fs';
3
3
  import { readdir } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
5
  import { git, resolveProjectPath } from './git-helpers.js';
6
- import { autoCompleteSpecs } from './branch-ops.js';
7
6
  import { compactResult, compactError } from '../output-formatter.js';
8
7
  const WORKTREES_DIR = '.claude/worktrees';
9
8
  const STALE_DAYS_THRESHOLD = 7;
@@ -283,23 +282,12 @@ export async function handleCleanup(projectId, config) {
283
282
  stashesDropped = dropped;
284
283
  errors.push(...stashErrors);
285
284
  }
286
- // 6. Auto-complete implementing specs whose branch is merged to develop (SPEC-397)
287
- // SPEC-720: autoCompleteSpecs now returns {completed, blocked}
288
- const autoCompleteResult = await autoCompleteSpecs(projectId, projectPath).catch(() => ({
289
- completed: [],
290
- blocked: [],
291
- }));
292
- const autoCompleted = autoCompleteResult.completed;
293
285
  const messageParts = [
294
286
  `Worktrees removed: ${worktreesRemoved.length}`,
295
287
  `Branches removed: ${branchesRemoved.length}`,
296
288
  `Remote pruned: ${remotePruned ? 'yes' : 'no'}`,
297
289
  `Stale stashes (>${STALE_DAYS_THRESHOLD}d): ${staleStashes.length}`,
298
290
  ...(force ? [`Stashes dropped: ${stashesDropped}`] : ['Use force:true to drop stale stashes']),
299
- ...(autoCompleted.length > 0 ? [`Specs auto-completed: ${autoCompleted.join(', ')}`] : []),
300
- ...(autoCompleteResult.blocked.length > 0
301
- ? [`Specs blocked (gates): ${autoCompleteResult.blocked.map((b) => b.specId).join(', ')}`]
302
- : []),
303
291
  ...(errors.length > 0 ? [`Errors: ${errors.length}`] : []),
304
292
  ];
305
293
  const report = {
@@ -311,7 +299,6 @@ export async function handleCleanup(projectId, config) {
311
299
  stashesDropped,
312
300
  errors,
313
301
  message: messageParts.join(' | '),
314
- ...(autoCompleted.length > 0 ? { autoCompleted } : {}),
315
302
  };
316
303
  const lines = [report.message];
317
304
  if (worktreesRemoved.length > 0) {
@@ -323,9 +310,6 @@ export async function handleCleanup(projectId, config) {
323
310
  if (staleStashes.length > 0) {
324
311
  lines.push('', `**Stale stashes**: ${staleStashes.map((s) => `${s.ref} (${String(s.daysOld)}d)`).join(', ')}`);
325
312
  }
326
- if (autoCompleted.length > 0) {
327
- lines.push('', `**Auto-completed specs**: ${autoCompleted.join(', ')}`);
328
- }
329
313
  if (errors.length > 0) {
330
314
  lines.push('', `**Errors**: ${errors.join('; ')}`);
331
315
  }
@@ -1,6 +1,6 @@
1
1
  import { git, resolveProjectPath, resolveBaseBranch, mergeConfig } from './git-helpers.js';
2
2
  import { specStore } from '../../storage/index.js';
3
- import { autoCompleteSpecs } from './branch-ops.js';
3
+ import { autoCompleteSpecs } from './auto-complete-ops.js';
4
4
  import { compactResult, formatKeyValue, formatList } from '../output-formatter.js';
5
5
  export async function handleRelease(projectId, config) {
6
6
  const projectPath = await resolveProjectPath(projectId);
@@ -13,7 +13,7 @@ import { checkBundledVersionGap } from '../../engine/version-detector/bundled-ve
13
13
  import { checkAndFixBundledVersion } from '../../engine/mcp-config/mcp-config-writer.js';
14
14
  import { buildInitProjectResult } from './result-builder.js';
15
15
  import { fetchRecommendedSkills } from '../suggest-tooling/skills-fetcher.js';
16
- import { autoCompleteSpecs } from '../git/branch-ops.js';
16
+ import { autoCompleteSpecs } from '../git/auto-complete-ops.js';
17
17
  import { regeneratePages } from '../../engine/doc-generator/portal/index.js';
18
18
  import { detectStackPatterns } from './stack-detector.js';
19
19
  import { buildProjectConfig } from './config-builder.js';
@@ -1,6 +1,8 @@
1
1
  // tools/manage-hooks.ts — MCP tool handler for manage_hooks (SPEC-071)
2
2
  import { hooksStore } from '../storage/index.js';
3
- import { dispatchEvent, dryRunDispatch, getTemplate, listTemplateNames, generateGitHooks, formatGitHookInstallGuide, } from '../engine/hooks/index.js';
3
+ import { dispatchEvent, dryRunDispatch } from '../engine/hooks/core.js';
4
+ import { getTemplate, listTemplateNames } from '../engine/hooks/templates.js';
5
+ import { generateGitHooks, formatGitHookInstallGuide } from '../engine/hooks/git-hook-generator.js';
4
6
  import { compactResult, compactError, formatKeyValue, formatTable, formatList, formatConfirmation, } from './output-formatter.js';
5
7
  // ---------------------------------------------------------------------------
6
8
  // Operation handlers
@@ -8,7 +8,7 @@ import { promisify } from 'node:util';
8
8
  import { join } from 'node:path';
9
9
  import { readFile } from 'node:fs/promises';
10
10
  import { shouldSuggestBtw, buildBtwHint } from '../engine/context-advisor/btw-advisor.js';
11
- import { autoCompleteSpecs } from './git/branch-ops.js';
11
+ import { autoCompleteSpecs } from './git/auto-complete-ops.js';
12
12
  import { computeSessionAgeMinutes } from '../engine/session-handoff-generator.js';
13
13
  import { loadSessionState } from '../storage/session-state-store.js';
14
14
  import { consumeUpdateBanner } from '../engine/update-notifier.js';
@@ -4,10 +4,13 @@ import { extractCriteria, extractListItems, extractSection, } from '../../engine
4
4
  import { stripFrontmatter } from '../../engine/frontmatter-parser.js';
5
5
  import { readEvidenceArtifacts } from '../../engine/evidence-gates/artifact-reader.js';
6
6
  import { checkLifecycleEvidenceGate } from '../../engine/evidence-gates/lifecycle-gate.js';
7
+ import { buildSpecEvidenceIndex } from '../../engine/evidence-index/index-builder.js';
8
+ import { checkDoneDriftContract } from '../../engine/evidence-index/done-drift.js';
9
+ import { parseTechnicalReferenceGroundingRecords } from '../../engine/spec-grounding/contract.js';
7
10
  /** SPEC-1054: BDD/SDD lifecycle evidence gate. */
8
11
  export async function checkLifecycleEvidenceTransitionGate(args) {
9
- const [criteria, artifacts] = await Promise.all([
10
- extractLifecycleGateCriteria(args.spec, args.projectPath),
12
+ const [criteriaResult, artifacts] = await Promise.all([
13
+ extractLifecycleGateCriteriaAndBody(args.spec, args.projectPath),
11
14
  readEvidenceArtifacts({
12
15
  spec: args.spec,
13
16
  projectId: args.projectId,
@@ -15,13 +18,26 @@ export async function checkLifecycleEvidenceTransitionGate(args) {
15
18
  projectPath: args.projectPath,
16
19
  }),
17
20
  ]);
21
+ const { criteria, body } = criteriaResult;
18
22
  const result = checkLifecycleEvidenceGate({
19
23
  transition: args.transition,
20
24
  spec: args.spec,
21
25
  criteria,
22
26
  artifacts,
23
27
  });
24
- if (result.passed) {
28
+ if (args.transition === 'done') {
29
+ const index = await buildSpecEvidenceIndex({
30
+ specId: args.specId,
31
+ criteria,
32
+ artifacts,
33
+ projectPath: args.projectPath,
34
+ });
35
+ const groundedTechnicalPaths = body
36
+ ? parseTechnicalReferenceGroundingRecords(body).map((record) => record.path)
37
+ : [];
38
+ result.issues.push(...checkDoneDriftContract({ index, groundedTechnicalPaths }));
39
+ }
40
+ if (result.issues.length === 0) {
25
41
  return null;
26
42
  }
27
43
  return evidenceGateError({
@@ -34,7 +50,7 @@ export async function checkLifecycleEvidenceTransitionGate(args) {
34
50
  requiredContractKinds: result.requiredContractKinds,
35
51
  });
36
52
  }
37
- async function extractLifecycleGateCriteria(spec, projectPath) {
53
+ async function extractLifecycleGateCriteriaAndBody(spec, projectPath) {
38
54
  const specPath = isAbsolute(spec.specPath) || !projectPath ? spec.specPath : join(projectPath, spec.specPath);
39
55
  try {
40
56
  const raw = await readFile(specPath, 'utf-8');
@@ -43,14 +59,14 @@ async function extractLifecycleGateCriteria(spec, projectPath) {
43
59
  if (acceptanceCriteria) {
44
60
  const items = extractListItems(acceptanceCriteria);
45
61
  if (items.length > 0) {
46
- return [...new Set(items)];
62
+ return { criteria: [...new Set(items)], body: raw };
47
63
  }
48
64
  }
49
65
  }
50
66
  catch {
51
67
  // Fall back to the broader validator extractor when the canonical file is unavailable.
52
68
  }
53
- return extractCriteria(spec);
69
+ return { criteria: await extractCriteria(spec), body: null };
54
70
  }
55
71
  function evidenceGateError(args) {
56
72
  return {
@@ -72,6 +88,7 @@ function evidenceGateError(args) {
72
88
  structuredContent: {
73
89
  error: 'lifecycle_evidence_gate_failed',
74
90
  code: 422,
91
+ sddConstitutionRuleId: args.transition === 'done' ? 'sdd.done-evidence-required' : 'sdd.grounded-contract',
75
92
  context: {
76
93
  specId: args.specId,
77
94
  transition: args.transition,
@@ -6,6 +6,8 @@ import { dispatchFeedbackEvent } from '../learn.js';
6
6
  import { scoreAmbiguityFromPath } from '../../engine/ambiguity-scorer.js';
7
7
  import { checkReadinessInternal } from '../../engine/readiness-checker.js';
8
8
  import { validateEnglishOnlySpecText } from '../../engine/spec-language/english-only.js';
9
+ import { checkGroundedSpecContract } from '../../engine/spec-grounding/contract.js';
10
+ import { checkGenericSpecOutput } from '../../engine/spec-quality/generic-output-gate.js';
9
11
  /**
10
12
  * Valid state transitions for spec lifecycle.
11
13
  * draft -> review -> approved -> implementing -> done
@@ -224,6 +226,53 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
224
226
  qualityWarnings: [],
225
227
  };
226
228
  }
229
+ const groundingResult = checkGroundedSpecContract(spec, body);
230
+ if (!groundingResult.passed) {
231
+ return {
232
+ blockResult: {
233
+ content: [
234
+ {
235
+ type: 'text',
236
+ text: `Grounded spec contract blocked transition to ${newStatus}. ` +
237
+ groundingResult.issues.slice(0, 3).join('; '),
238
+ },
239
+ ],
240
+ isError: true,
241
+ structuredContent: {
242
+ error: 'GROUNDED_SPEC_CONTRACT_BLOCKED',
243
+ sddConstitutionRuleId: 'sdd.grounded-contract',
244
+ issues: groundingResult.issues,
245
+ fixHint: 'Add grounding metadata for every contract criterion or move ungrounded criteria to advisory/discovery before review or approval.',
246
+ },
247
+ },
248
+ qualityWarnings: [],
249
+ };
250
+ }
251
+ const genericOutputGate = checkGenericSpecOutput(body);
252
+ if (!genericOutputGate.passed) {
253
+ return {
254
+ blockResult: {
255
+ content: [
256
+ {
257
+ type: 'text',
258
+ text: `Spec quality gate blocked transition to ${newStatus}. ` +
259
+ genericOutputGate.issues
260
+ .slice(0, 3)
261
+ .map((issue) => `${issue.phrase}: ${issue.reason}`)
262
+ .join('; '),
263
+ },
264
+ ],
265
+ isError: true,
266
+ structuredContent: {
267
+ error: 'GENERIC_SPEC_OUTPUT_BLOCKED',
268
+ sddConstitutionRuleId: 'sdd.no-generic-output',
269
+ issues: genericOutputGate.issues,
270
+ fixHint: 'Replace generic criteria or placeholder references with grounded, testable behavior before review or approval.',
271
+ },
272
+ },
273
+ qualityWarnings: [],
274
+ };
275
+ }
227
276
  let score;
228
277
  let warnings;
229
278
  let criteriaCount;
@@ -1,7 +1,5 @@
1
1
  import { specStore, knowledgeStore } from '../storage/index.js';
2
- import { handleCreateBranch } from './git/branch-ops.js';
3
2
  import { checkConstitutionCompliance } from './create-spec-tech.js';
4
- import { cleanupSpecOnDone } from './git/cleanup-ops.js';
5
3
  import { withAudit } from '../engine/autopilot/audit-logger.js';
6
4
  import { hasPending, markPending, clearPending } from '../engine/autopilot/cascade-deduplicator.js';
7
5
  export async function runImplementingActions(projectId, specId) {
@@ -27,10 +25,12 @@ export async function runImplementingActions(projectId, specId) {
27
25
  let branchResult;
28
26
  try {
29
27
  const { resolveProjectPath: resolveBranchPath } = await import('./git/git-helpers.js');
28
+ const { handleCreateBranch } = await import('./git/branch-ops.js');
30
29
  const branchProjectPath = await resolveBranchPath(projectId);
31
30
  branchResult = await withAudit(branchProjectPath, 'update_status(implementing)', 'handleCreateBranch', () => handleCreateBranch(projectId, specId), (r) => ({ isError: r.isError }));
32
31
  }
33
32
  catch {
33
+ const { handleCreateBranch } = await import('./git/branch-ops.js');
34
34
  branchResult = await handleCreateBranch(projectId, specId);
35
35
  }
36
36
  if (!branchResult.isError) {
@@ -224,9 +224,12 @@ export async function runDoneActions(projectId, specId, gitBranch) {
224
224
  // SPEC-491: Sweep planu/ to remove legacy files
225
225
  // SPEC-496: Auto-generate portable session context snapshot
226
226
  void Promise.allSettled([
227
- withAudit(projectPath, 'update_status(done)', 'cleanupSpecOnDone', () => cleanupSpecOnDone(projectPath, specId, gitBranch)).catch(() => {
228
- /* best-effort never blocks status transition */
229
- }),
227
+ (async () => {
228
+ const { cleanupSpecOnDone } = await import('./git/cleanup-ops.js');
229
+ await withAudit(projectPath, 'update_status(done)', 'cleanupSpecOnDone', () => cleanupSpecOnDone(projectPath, specId, gitBranch)).catch(() => {
230
+ /* best-effort — never blocks status transition */
231
+ });
232
+ })(),
230
233
  // SPEC-633: Remove spec from session.json index when done
231
234
  (async () => {
232
235
  const { removeSpecFromSession } = await import('../engine/session-state/writer.js');
@@ -0,0 +1,8 @@
1
+ export type ActionableSpecMetricName = 'groundingCoverage' | 'verificationCoverage' | 'ambiguityCount' | 'driftRisk';
2
+ export interface ActionableSpecMetric {
3
+ name: ActionableSpecMetricName;
4
+ value: number | 'none' | 'low' | 'medium' | 'high';
5
+ inputs: string[];
6
+ nextAction: string;
7
+ }
8
+ //# sourceMappingURL=actionable-spec-metrics.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=actionable-spec-metrics.js.map
@@ -1,4 +1,4 @@
1
- import type { InteractiveQuestion } from './clarification.js';
1
+ import type { InteractiveQuestion } from './interactive-question.js';
2
2
  import type { ToolResult } from './common/primitives.js';
3
3
  /**
4
4
  * Persisted token that blocks a tool from proceeding until the LLM relays
@@ -1,21 +1,5 @@
1
- /** A single option in an interactive question. */
2
- export interface InteractiveOption {
3
- /** Display text — add "(Recommended)" suffix to preferred option. */
4
- label: string;
5
- /** What this option means or what happens if chosen. */
6
- description: string;
7
- }
8
- /** A structured question for the user, presented via the LLM's native UI. */
9
- export interface InteractiveQuestion {
10
- /** The full question text, ending with "?". */
11
- question: string;
12
- /** Short chip/tag label (max 12 chars). E.g. "Auth", "Scope", "Target". */
13
- header: string;
14
- /** 2-4 options to choose from. An "Other" free-text option is added automatically by the LLM. */
15
- options: InteractiveOption[];
16
- /** True for non-exclusive choices (user can pick multiple). */
17
- multiSelect: boolean;
18
- }
1
+ import type { InteractiveQuestion } from './interactive-question.js';
2
+ export type { InteractiveOption, InteractiveQuestion } from './interactive-question.js';
19
3
  /** Tool response when interactive clarification is needed instead of creating a spec. */
20
4
  export interface InteractiveClarificationResponse {
21
5
  needsClarification: true;
@@ -54,7 +54,7 @@ export interface EvidenceArtifacts {
54
54
  invalidArtifacts: string[];
55
55
  }
56
56
  export interface EvidenceGateIssue {
57
- code: 'discovery_missing' | 'discovery_unresolved_questions' | 'task_plan_missing' | 'task_plan_uncovered_criteria' | 'traceability_missing' | 'traceability_uncovered_criteria' | 'traceability_incomplete_rows' | 'contract_validation_missing' | 'contract_validation_failed' | 'evidence_artifact_invalid';
57
+ code: 'discovery_missing' | 'discovery_unresolved_questions' | 'task_plan_missing' | 'task_plan_uncovered_criteria' | 'traceability_missing' | 'traceability_uncovered_criteria' | 'traceability_incomplete_rows' | 'contract_validation_missing' | 'contract_validation_failed' | 'done_drift_uncovered_criteria' | 'done_drift_stale_evidence' | 'done_drift_unapproved_scope' | 'evidence_artifact_invalid';
58
58
  message: string;
59
59
  }
60
60
  export interface EvidenceGateResult {
@@ -0,0 +1,24 @@
1
+ export type EvidenceRecordKind = 'scenario' | 'test' | 'contract' | 'manual' | 'changed-file' | 'validation' | 'reviewer';
2
+ export type EvidenceRecordStatus = 'valid' | 'stale' | 'invalid' | 'missing';
3
+ export interface EvidenceRecord {
4
+ kind: EvidenceRecordKind;
5
+ value: string;
6
+ status: EvidenceRecordStatus;
7
+ source: string;
8
+ command?: string;
9
+ passed?: boolean;
10
+ timestamp?: string;
11
+ reason?: string;
12
+ }
13
+ export interface CriterionEvidenceIndexEntry {
14
+ criterion: string;
15
+ criterionKey: string;
16
+ evidence: EvidenceRecord[];
17
+ }
18
+ export interface SpecEvidenceIndex {
19
+ specId: string;
20
+ generatedAt: string;
21
+ criteria: CriterionEvidenceIndexEntry[];
22
+ diagnostics: string[];
23
+ }
24
+ //# sourceMappingURL=evidence-index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=evidence-index.js.map
@@ -0,0 +1,10 @@
1
+ import type { ToolResult } from './common/primitives.js';
2
+ import type { UpdateStatusInput } from './spec/inputs.js';
3
+ export interface HookUpdateStatusInput extends UpdateStatusInput {
4
+ trigger: 'fs-hook';
5
+ actor: 'system';
6
+ }
7
+ export interface UpdateStatusModule {
8
+ handleUpdateStatus: (input: HookUpdateStatusInput) => Promise<ToolResult>;
9
+ }
10
+ //# sourceMappingURL=hook-status-update.d.ts.map
@@ -0,0 +1,3 @@
1
+ // types/hook-status-update.ts — Runtime bridge types for hook-triggered status transitions
2
+ export {};
3
+ //# sourceMappingURL=hook-status-update.js.map
@@ -25,6 +25,8 @@ export * from './migration/index.js';
25
25
  export * from './migration-advanced.js';
26
26
  export * from './privacy.js';
27
27
  export * from './events.js';
28
+ export * from './evidence-gates.js';
29
+ export * from './evidence-index.js';
28
30
  export * from './analytics.js';
29
31
  export * from './sdd-flow.js';
30
32
  export * from './sdd-model-routing.js';
@@ -48,6 +50,7 @@ export * from './dynamic-migration.js';
48
50
  export * from './multi-agent.js';
49
51
  export * from './ai-native.js';
50
52
  export * from './actuals-tracking.js';
53
+ export * from './actionable-spec-metrics.js';
51
54
  export * from './actuals.js';
52
55
  export * from './risk.js';
53
56
  export * from './context-budget.js';
@@ -57,10 +60,12 @@ export * from './changelog.js';
57
60
  export * from './coverage.js';
58
61
  export * from './ai-cost.js';
59
62
  export * from './hooks.js';
63
+ export * from './hook-status-update.js';
60
64
  export * from './webhook.js';
61
65
  export * from './ci.js';
62
66
  export * from './spec-templates.js';
63
67
  export * from './spec-generator.js';
68
+ export * from './spec-grounding.js';
64
69
  export * from './registry.js';
65
70
  export * from './tool-groups.js';
66
71
  export * from './code-transforms.js';
@@ -103,6 +108,7 @@ export * from './legal.js';
103
108
  export * from './mcp-recommendation.js';
104
109
  export * from './approval.js';
105
110
  export * from './scan-project.js';
111
+ export * from './sdd-constitution.js';
106
112
  export * from './import-spec.js';
107
113
  export * from './error-telemetry.js';
108
114
  export * from './audit-trail.js';
@@ -26,6 +26,8 @@ export * from './migration/index.js';
26
26
  export * from './migration-advanced.js';
27
27
  export * from './privacy.js';
28
28
  export * from './events.js';
29
+ export * from './evidence-gates.js';
30
+ export * from './evidence-index.js';
29
31
  export * from './analytics.js';
30
32
  export * from './sdd-flow.js';
31
33
  export * from './sdd-model-routing.js';
@@ -49,6 +51,7 @@ export * from './dynamic-migration.js';
49
51
  export * from './multi-agent.js';
50
52
  export * from './ai-native.js';
51
53
  export * from './actuals-tracking.js';
54
+ export * from './actionable-spec-metrics.js';
52
55
  export * from './actuals.js';
53
56
  export * from './risk.js';
54
57
  export * from './context-budget.js';
@@ -58,10 +61,12 @@ export * from './changelog.js';
58
61
  export * from './coverage.js';
59
62
  export * from './ai-cost.js';
60
63
  export * from './hooks.js';
64
+ export * from './hook-status-update.js';
61
65
  export * from './webhook.js';
62
66
  export * from './ci.js';
63
67
  export * from './spec-templates.js';
64
68
  export * from './spec-generator.js';
69
+ export * from './spec-grounding.js';
65
70
  export * from './registry.js';
66
71
  export * from './tool-groups.js';
67
72
  export * from './code-transforms.js';
@@ -104,6 +109,7 @@ export * from './legal.js';
104
109
  export * from './mcp-recommendation.js';
105
110
  export * from './approval.js';
106
111
  export * from './scan-project.js';
112
+ export * from './sdd-constitution.js';
107
113
  export * from './import-spec.js';
108
114
  export * from './error-telemetry.js';
109
115
  export * from './audit-trail.js';
@@ -0,0 +1,19 @@
1
+ /** A single option in an interactive question. */
2
+ export interface InteractiveOption {
3
+ /** Display text — add "(Recommended)" suffix to preferred option. */
4
+ label: string;
5
+ /** What this option means or what happens if chosen. */
6
+ description: string;
7
+ }
8
+ /** A structured question for the user, presented via the LLM's native UI. */
9
+ export interface InteractiveQuestion {
10
+ /** The full question text, ending with "?". */
11
+ question: string;
12
+ /** Short chip/tag label (max 12 chars). E.g. "Auth", "Scope", "Target". */
13
+ header: string;
14
+ /** 2-4 options to choose from. An "Other" free-text option is added automatically by the LLM. */
15
+ options: InteractiveOption[];
16
+ /** True for non-exclusive choices (user can pick multiple). */
17
+ multiSelect: boolean;
18
+ }
19
+ //# sourceMappingURL=interactive-question.d.ts.map
@@ -0,0 +1,3 @@
1
+ // types/interactive-question.ts — LLM-native interactive question primitives
2
+ export {};
3
+ //# sourceMappingURL=interactive-question.js.map
@@ -0,0 +1,27 @@
1
+ export type SddConstitutionRuleLevel = 'blocking' | 'advisory';
2
+ export interface SddConstitutionRule {
3
+ id: string;
4
+ title: string;
5
+ level: SddConstitutionRuleLevel;
6
+ category: 'grounding' | 'quality' | 'evidence' | 'approval' | 'metrics';
7
+ description: string;
8
+ nextAction: string;
9
+ }
10
+ export interface SddConstitutionOverride {
11
+ ruleId: string;
12
+ level?: SddConstitutionRuleLevel;
13
+ enabled?: boolean;
14
+ rationale: string;
15
+ }
16
+ export interface EffectiveSddConstitutionRule extends SddConstitutionRule {
17
+ enabled: boolean;
18
+ overrideRationale?: string;
19
+ }
20
+ export interface SddConstitutionViolation {
21
+ ruleId: string;
22
+ level: SddConstitutionRuleLevel;
23
+ message: string;
24
+ evidence: string[];
25
+ nextAction: string;
26
+ }
27
+ //# sourceMappingURL=sdd-constitution.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sdd-constitution.js.map
@@ -1,6 +1,7 @@
1
1
  import type { Spec } from './spec/core.js';
2
2
  import type { Estimation } from './estimation.js';
3
3
  import type { SpecStatus } from './common/index.js';
4
+ import type { CriterionGroundingRecord, TechnicalReferenceGroundingRecord } from './spec-grounding.js';
4
5
  /** A criterion in the lean spec frontmatter. */
5
6
  export interface LeanCriterion {
6
7
  text: string;
@@ -29,6 +30,12 @@ export interface LeanSpecInput {
29
30
  estimation: Estimation;
30
31
  /** SPEC-461 Phase 3: Extra criteria from autopilot pattern detection. */
31
32
  extraCriteria?: string[];
33
+ /** SPEC-1074: Explicit criteria after grounding filters are applied. */
34
+ criteriaOverride?: LeanCriterion[];
35
+ /** SPEC-1074: Criterion-level grounding records for new specs. */
36
+ groundingCriteria?: CriterionGroundingRecord[];
37
+ /** SPEC-1074: Technical reference grounding records for new specs. */
38
+ groundingTechnicalReferences?: TechnicalReferenceGroundingRecord[];
32
39
  /** SPEC-481: Acceptance criteria format. Defaults to 'checkbox'. */
33
40
  acFormat?: 'checkbox' | 'bdd';
34
41
  }
@@ -0,0 +1,22 @@
1
+ export type GroundingSource = 'user_input' | 'project_evidence' | 'documented_assumption' | 'ungrounded_advisory';
2
+ export type GroundingConfidence = 'low' | 'medium' | 'high';
3
+ export interface CriterionGroundingRecord {
4
+ text: string;
5
+ source: GroundingSource;
6
+ evidence: string[];
7
+ confidence: GroundingConfidence;
8
+ }
9
+ export interface TechnicalReferenceGroundingRecord {
10
+ path: string;
11
+ section: 'create' | 'modify' | 'test';
12
+ source: GroundingSource;
13
+ evidence: string[];
14
+ confidence: GroundingConfidence;
15
+ }
16
+ export interface GroundingGateResult {
17
+ passed: boolean;
18
+ required: boolean;
19
+ issues: string[];
20
+ records: CriterionGroundingRecord[];
21
+ }
22
+ //# sourceMappingURL=spec-grounding.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=spec-grounding.js.map
@@ -35,6 +35,16 @@ export interface SpecQualityReport {
35
35
  risk: QualityDimensionDetail;
36
36
  };
37
37
  }
38
+ export type GenericSpecOutputIssueKind = 'generic-criterion' | 'unsupported-verification' | 'placeholder-reference' | 'generic-technical-reference';
39
+ export interface GenericSpecOutputIssue {
40
+ kind: GenericSpecOutputIssueKind;
41
+ phrase: string;
42
+ reason: string;
43
+ }
44
+ export interface GenericSpecOutputGateResult {
45
+ passed: boolean;
46
+ issues: GenericSpecOutputIssue[];
47
+ }
38
48
  /** A single over-engineering signal detected in a spec. SPEC-485 */
39
49
  export interface ComplexitySignal {
40
50
  type: string;
@@ -2,7 +2,7 @@
2
2
  * A minimal CRUD store backed by a single JSON array file.
3
3
  * Items must have an `id: string` field for lookup / update / remove.
4
4
  *
5
- * Created by `createCrudStore()` in `storage/base-store.ts`.
5
+ * JSON-backed stores expose this shape when they implement CRUD operations.
6
6
  */
7
7
  export interface CrudStore<T extends {
8
8
  id: string;