create-quiver 0.12.0 → 0.13.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 (158) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +65 -25
  3. package/README_FOR_AI.md +36 -29
  4. package/ROADMAP.md +22 -3
  5. package/docs/AI_ONBOARDING_PROMPT.md.template +7 -1
  6. package/docs/COMMANDS.md.template +53 -20
  7. package/docs/STATUS.md.template +5 -1
  8. package/docs/WORKFLOW.md.template +13 -11
  9. package/package.json +10 -3
  10. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EVIDENCE_REPORT.md +293 -0
  11. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EXECUTION_PLAN.md +58 -0
  12. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/SPEC.md +242 -0
  13. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/STATUS.md +35 -0
  14. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/pr.md +77 -0
  15. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +34 -0
  16. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
  17. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/slice.json +52 -0
  18. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/CLOSURE_BRIEF.md +36 -0
  19. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/EXECUTION_BRIEF.md +52 -0
  20. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/slice.json +56 -0
  21. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/CLOSURE_BRIEF.md +43 -0
  22. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/EXECUTION_BRIEF.md +54 -0
  23. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/slice.json +52 -0
  24. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/CLOSURE_BRIEF.md +35 -0
  25. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/EXECUTION_BRIEF.md +53 -0
  26. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/slice.json +54 -0
  27. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/CLOSURE_BRIEF.md +34 -0
  28. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/EXECUTION_BRIEF.md +54 -0
  29. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/slice.json +52 -0
  30. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/CLOSURE_BRIEF.md +34 -0
  31. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/EXECUTION_BRIEF.md +54 -0
  32. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/slice.json +53 -0
  33. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/CLOSURE_BRIEF.md +33 -0
  34. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/EXECUTION_BRIEF.md +56 -0
  35. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/slice.json +55 -0
  36. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/CLOSURE_BRIEF.md +33 -0
  37. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/EXECUTION_BRIEF.md +54 -0
  38. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/slice.json +52 -0
  39. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/CLOSURE_BRIEF.md +39 -0
  40. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/EXECUTION_BRIEF.md +56 -0
  41. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/slice.json +53 -0
  42. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/CLOSURE_BRIEF.md +38 -0
  43. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/EXECUTION_BRIEF.md +57 -0
  44. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/slice.json +52 -0
  45. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/CLOSURE_BRIEF.md +39 -0
  46. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/EXECUTION_BRIEF.md +55 -0
  47. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/slice.json +56 -0
  48. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/CLOSURE_BRIEF.md +36 -0
  49. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/EXECUTION_BRIEF.md +54 -0
  50. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/slice.json +53 -0
  51. package/specs/quiver-v26-0121-smoke-hardening/EVIDENCE_REPORT.md +208 -0
  52. package/specs/quiver-v26-0121-smoke-hardening/EXECUTION_PLAN.md +57 -0
  53. package/specs/quiver-v26-0121-smoke-hardening/SPEC.md +137 -0
  54. package/specs/quiver-v26-0121-smoke-hardening/STATUS.md +32 -0
  55. package/specs/quiver-v26-0121-smoke-hardening/pr.md +96 -0
  56. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/CLOSURE_BRIEF.md +35 -0
  57. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/EXECUTION_BRIEF.md +55 -0
  58. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/slice.json +73 -0
  59. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/CLOSURE_BRIEF.md +38 -0
  60. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/EXECUTION_BRIEF.md +51 -0
  61. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/slice.json +76 -0
  62. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/CLOSURE_BRIEF.md +37 -0
  63. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/EXECUTION_BRIEF.md +52 -0
  64. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/slice.json +75 -0
  65. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/CLOSURE_BRIEF.md +37 -0
  66. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/EXECUTION_BRIEF.md +53 -0
  67. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/slice.json +77 -0
  68. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/CLOSURE_BRIEF.md +35 -0
  69. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/EXECUTION_BRIEF.md +52 -0
  70. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/slice.json +77 -0
  71. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/CLOSURE_BRIEF.md +34 -0
  72. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/EXECUTION_BRIEF.md +54 -0
  73. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/slice.json +84 -0
  74. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/CLOSURE_BRIEF.md +35 -0
  75. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/EXECUTION_BRIEF.md +53 -0
  76. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/slice.json +82 -0
  77. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/CLOSURE_BRIEF.md +35 -0
  78. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/EXECUTION_BRIEF.md +55 -0
  79. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/slice.json +92 -0
  80. package/specs/quiver-v27-reliability-ai-workflow-hardening/AUDIT_V24_V25_V26.md +67 -0
  81. package/specs/quiver-v27-reliability-ai-workflow-hardening/COMMAND_CONTRACTS.md +125 -0
  82. package/specs/quiver-v27-reliability-ai-workflow-hardening/COVERAGE_MATRIX.md +74 -0
  83. package/specs/quiver-v27-reliability-ai-workflow-hardening/EVIDENCE_REPORT.md +179 -0
  84. package/specs/quiver-v27-reliability-ai-workflow-hardening/EXECUTION_PLAN.md +71 -0
  85. package/specs/quiver-v27-reliability-ai-workflow-hardening/SPEC.md +176 -0
  86. package/specs/quiver-v27-reliability-ai-workflow-hardening/STATUS.md +37 -0
  87. package/specs/quiver-v27-reliability-ai-workflow-hardening/pr.md +132 -0
  88. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/CLOSURE_BRIEF.md +36 -0
  89. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/EXECUTION_BRIEF.md +56 -0
  90. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/slice.json +75 -0
  91. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/CLOSURE_BRIEF.md +37 -0
  92. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/EXECUTION_BRIEF.md +54 -0
  93. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/slice.json +79 -0
  94. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/CLOSURE_BRIEF.md +34 -0
  95. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/EXECUTION_BRIEF.md +54 -0
  96. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/slice.json +75 -0
  97. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/CLOSURE_BRIEF.md +36 -0
  98. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/EXECUTION_BRIEF.md +55 -0
  99. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/slice.json +78 -0
  100. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/CLOSURE_BRIEF.md +31 -0
  101. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/EXECUTION_BRIEF.md +55 -0
  102. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/slice.json +77 -0
  103. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/CLOSURE_BRIEF.md +31 -0
  104. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/EXECUTION_BRIEF.md +55 -0
  105. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/slice.json +84 -0
  106. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/CLOSURE_BRIEF.md +32 -0
  107. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/EXECUTION_BRIEF.md +57 -0
  108. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/slice.json +99 -0
  109. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/CLOSURE_BRIEF.md +31 -0
  110. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/EXECUTION_BRIEF.md +57 -0
  111. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/slice.json +88 -0
  112. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/CLOSURE_BRIEF.md +31 -0
  113. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/EXECUTION_BRIEF.md +56 -0
  114. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/slice.json +85 -0
  115. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/CLOSURE_BRIEF.md +32 -0
  116. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/EXECUTION_BRIEF.md +56 -0
  117. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/slice.json +91 -0
  118. package/src/create-quiver/commands/ai.js +652 -27
  119. package/src/create-quiver/commands/flow.js +58 -9
  120. package/src/create-quiver/commands/graph.js +11 -9
  121. package/src/create-quiver/commands/plan.js +7 -16
  122. package/src/create-quiver/commands/spec.js +282 -0
  123. package/src/create-quiver/index.js +409 -31
  124. package/src/create-quiver/lib/actionable-error.js +27 -0
  125. package/src/create-quiver/lib/agent-profiles.js +16 -4
  126. package/src/create-quiver/lib/ai/artifacts.js +318 -0
  127. package/src/create-quiver/lib/ai/context-packs.js +4 -0
  128. package/src/create-quiver/lib/ai/execution-plan.js +16 -1
  129. package/src/create-quiver/lib/ai/executor.js +272 -21
  130. package/src/create-quiver/lib/ai/export-state.js +679 -0
  131. package/src/create-quiver/lib/ai/github.js +162 -2
  132. package/src/create-quiver/lib/ai/onboarding-template.js +215 -2
  133. package/src/create-quiver/lib/ai/plan-review.js +7 -2
  134. package/src/create-quiver/lib/ai/providers.js +4 -3
  135. package/src/create-quiver/lib/ai/run-state.js +414 -0
  136. package/src/create-quiver/lib/ai/spec-generator.js +84 -13
  137. package/src/create-quiver/lib/ai/spec-templates.js +150 -21
  138. package/src/create-quiver/lib/analyze.js +2 -2
  139. package/src/create-quiver/lib/approvals.js +36 -5
  140. package/src/create-quiver/lib/demo.js +189 -14
  141. package/src/create-quiver/lib/doctor.js +154 -0
  142. package/src/create-quiver/lib/git.js +40 -1
  143. package/src/create-quiver/lib/handoff.js +123 -12
  144. package/src/create-quiver/lib/init-docs.js +35 -13
  145. package/src/create-quiver/lib/init-layout.js +9 -0
  146. package/src/create-quiver/lib/json.js +53 -3
  147. package/src/create-quiver/lib/lifecycle.js +52 -3
  148. package/src/create-quiver/lib/locks.js +134 -0
  149. package/src/create-quiver/lib/package-safety.js +7 -0
  150. package/src/create-quiver/lib/paths.js +74 -0
  151. package/src/create-quiver/lib/project-scan.js +74 -0
  152. package/src/create-quiver/lib/project-state-resolver.js +236 -0
  153. package/src/create-quiver/lib/readiness.js +66 -10
  154. package/src/create-quiver/lib/scope.js +52 -8
  155. package/src/create-quiver/lib/slice-graph.js +138 -38
  156. package/src/create-quiver/lib/slice.js +14 -5
  157. package/src/create-quiver/lib/spec-worktrees.js +129 -32
  158. package/src/create-quiver/lib/statuses.js +115 -0
@@ -31,6 +31,46 @@ function bullets(items, fallback = '- n/a') {
31
31
  return list.map((item) => `- ${item}`);
32
32
  }
33
33
 
34
+ function normalizeStringArray(items) {
35
+ return Array.isArray(items) ? items.map((item) => String(item).trim()).filter(Boolean) : [];
36
+ }
37
+
38
+ function resolveExpectedReadPaths(manifest, slice) {
39
+ const explicit = normalizeStringArray(slice.expected_read_paths);
40
+ if (explicit.length > 0) {
41
+ return explicit;
42
+ }
43
+
44
+ if (slice.slice_id === SPEC_FOUNDATION_SLICE_ID) {
45
+ return [manifest.sourcePath];
46
+ }
47
+
48
+ return [
49
+ manifest.sourcePath,
50
+ `specs/${manifest.slug}/SPEC.md`,
51
+ `specs/${manifest.slug}/slices/${slice.slice_id}/slice.json`,
52
+ `specs/${manifest.slug}/slices/${slice.slice_id}/EXECUTION_BRIEF.md`,
53
+ ];
54
+ }
55
+
56
+ function resolveAllowedWritePaths(slice) {
57
+ const explicit = normalizeStringArray(slice.allowed_write_paths);
58
+ if (explicit.length > 0) {
59
+ return explicit;
60
+ }
61
+
62
+ return normalizeStringArray(slice.files);
63
+ }
64
+
65
+ function resolveValidationHints(slice) {
66
+ const explicit = normalizeStringArray(slice.validation_hints);
67
+ if (explicit.length > 0) {
68
+ return explicit;
69
+ }
70
+
71
+ return normalizeStringArray(slice.tests);
72
+ }
73
+
34
74
  function displayStatus(status, defaultStatus = 'draft') {
35
75
  const normalized = String(status || defaultStatus).trim().toLowerCase();
36
76
  if (normalized === 'completed') {
@@ -325,6 +365,9 @@ function buildSliceJson(manifest, slice, index) {
325
365
  const dependsOn = isFoundation ? [] : Array.from(new Set(['slice-00-spec-foundation', ...(slice.depends_on || [])]));
326
366
  const branchSlug = slice.git?.branch_slug || slugify(slice.slice_id);
327
367
  const ticket = slice.ticket || manifest.ticket;
368
+ const expectedReadPaths = resolveExpectedReadPaths(manifest, slice);
369
+ const allowedWritePaths = resolveAllowedWritePaths(slice);
370
+ const validationHints = resolveValidationHints(slice);
328
371
 
329
372
  return {
330
373
  slice_id: slice.slice_id,
@@ -343,12 +386,15 @@ function buildSliceJson(manifest, slice, index) {
343
386
  not_included: Array.isArray(slice.not_included) ? slice.not_included : [],
344
387
  acceptance: Array.isArray(slice.acceptance) ? slice.acceptance : [],
345
388
  files: Array.isArray(slice.files) ? slice.files : [],
389
+ expected_read_paths: expectedReadPaths,
390
+ allowed_write_paths: allowedWritePaths,
346
391
  depends_on: dependsOn,
347
392
  parallel_safe: slice.parallel_safe || (isFoundation ? 'never' : 'after_dependencies'),
348
393
  parallel_safe_reason: slice.parallel_safe_reason || (isFoundation
349
394
  ? 'slice-00 is the mandatory documentation foundation and must land before every implementation slice.'
350
395
  : 'Can run after slice-00 and the approved input has been staged.'),
351
396
  tests: Array.isArray(slice.tests) ? slice.tests : [],
397
+ validation_hints: validationHints,
352
398
  documentation: [
353
399
  `specs/${manifest.slug}/slices/${slice.slice_id}/EXECUTION_BRIEF.md`,
354
400
  `specs/${manifest.slug}/slices/${slice.slice_id}/CLOSURE_BRIEF.md`,
@@ -365,6 +411,9 @@ function buildSliceJson(manifest, slice, index) {
365
411
  }
366
412
 
367
413
  function buildExecutionBrief(manifest, slice) {
414
+ const expectedReadPaths = resolveExpectedReadPaths(manifest, slice);
415
+ const allowedWritePaths = resolveAllowedWritePaths(slice);
416
+ const validationHints = resolveValidationHints(slice);
368
417
  const lines = [
369
418
  `# EXECUTION BRIEF - ${slice.slice_id}`,
370
419
  '',
@@ -392,11 +441,23 @@ function buildExecutionBrief(manifest, slice) {
392
441
  '',
393
442
  `Follow the approved input staged in \`${manifest.sourcePath}\` and keep the slice inside its declared files.`,
394
443
  '',
444
+ '## Expected read paths',
445
+ '',
446
+ ...bullets(expectedReadPaths, '- No explicit read paths declared.'),
447
+ '',
448
+ '## Allowed write paths',
449
+ '',
450
+ ...bullets(allowedWritePaths, '- No explicit write paths declared.'),
451
+ '',
452
+ '## Validation hints',
453
+ '',
454
+ ...bullets(validationHints, '- No explicit validation commands declared.'),
455
+ '',
395
456
  '## Pasos sugeridos de ejecucion',
396
457
  '',
397
- '1. Read the approved input.',
398
- '2. Implement the declared files.',
399
- '3. Run the declared validation commands.',
458
+ '1. Read only the expected paths unless a blocker requires more context.',
459
+ '2. Implement only the declared allowed write paths.',
460
+ '3. Run the declared validation commands or document why they are not available.',
400
461
  '',
401
462
  '## Restricciones',
402
463
  '',
@@ -454,6 +515,16 @@ function normalizeSliceName(sliceId) {
454
515
  return String(sliceId || '').trim() || SPEC_FOUNDATION_SLICE_ID;
455
516
  }
456
517
 
518
+ function dependencySliceId(dependency) {
519
+ const value = String(dependency || '').trim();
520
+ if (!value) {
521
+ return '';
522
+ }
523
+
524
+ const slashIndex = value.lastIndexOf('/');
525
+ return slashIndex === -1 ? value : value.slice(slashIndex + 1);
526
+ }
527
+
457
528
  function buildDefaultImplementationSlice(manifest) {
458
529
  const title = `${manifest.title} implementation`;
459
530
  return {
@@ -501,17 +572,11 @@ function buildManifest(source, options = {}) {
501
572
  const assumptions = Array.isArray(specSource.assumptions) ? specSource.assumptions.slice() : Array.isArray(source.assumptions) ? source.assumptions.slice() : [];
502
573
 
503
574
  const rawSlices = Array.isArray(specSource.slices) ? specSource.slices : Array.isArray(source.slices) ? source.slices : [];
504
- const normalizedSlices = rawSlices.length > 0
505
- ? rawSlices.map((slice) => normalizeImplementationSlice(slice, ticket))
506
- : [buildDefaultImplementationSlice({
507
- acceptance,
508
- assumptions,
509
- objective,
510
- scope,
511
- sourcePath,
512
- ticket,
513
- title,
514
- })];
575
+ if (rawSlices.length === 0) {
576
+ throw new Error('approved technical plan must include a structured slices array. Expected JSON: { "spec": { "slices": [{ "slice_id": "slice-01-name", "title": "...", "objective": "...", "files": [] }] } }');
577
+ }
578
+
579
+ const normalizedSlices = rawSlices.map((slice) => normalizeImplementationSlice(slice, ticket));
515
580
 
516
581
  const slices = [
517
582
  {
@@ -561,6 +626,8 @@ function buildManifest(source, options = {}) {
561
626
  ...normalizedSlices,
562
627
  ].map((slice, index) => normalizeSliceManifest(slice, index, ticket));
563
628
 
629
+ validateSliceManifestGraph(slices);
630
+
564
631
  const executionOrder = orderSlices(slices);
565
632
  const executionGroups = buildExecutionGroups(slices);
566
633
 
@@ -600,13 +667,18 @@ function normalizeSliceManifest(slice, index, fallbackTicket) {
600
667
  const description = String(slice.description || '').trim() || (index === 0
601
668
  ? 'Document the approved planning input.'
602
669
  : `Implement the approved plan captured in the spec source.`);
603
- const dependsOn = Array.isArray(slice.depends_on) ? slice.depends_on.map((dep) => String(dep).trim()).filter(Boolean) : [];
604
- const files = Array.isArray(slice.files) ? slice.files.map((file) => String(file).trim()).filter(Boolean) : [];
605
- const must = Array.isArray(slice.must) ? slice.must.map((item) => String(item).trim()).filter(Boolean) : [];
606
- const notIncluded = Array.isArray(slice.not_included) ? slice.not_included.map((item) => String(item).trim()).filter(Boolean) : [];
607
- const acceptance = Array.isArray(slice.acceptance) ? slice.acceptance.map((item) => String(item).trim()).filter(Boolean) : [];
608
- const tests = Array.isArray(slice.tests) ? slice.tests.map((item) => String(item).trim()).filter(Boolean) : [];
609
- const assumptions = Array.isArray(slice.assumptions) ? slice.assumptions.map((item) => String(item).trim()).filter(Boolean) : [];
670
+ const dependsOn = Array.isArray(slice.depends_on) ? slice.depends_on.map(dependencySliceId).filter(Boolean) : [];
671
+ const explicitAllowedWritePaths = normalizeStringArray(slice.allowed_write_paths || slice.allowedWritePaths || slice.write_paths);
672
+ const declaredFiles = normalizeStringArray(slice.files);
673
+ const files = declaredFiles.length > 0 ? declaredFiles : explicitAllowedWritePaths;
674
+ const must = normalizeStringArray(slice.must);
675
+ const notIncluded = normalizeStringArray(slice.not_included);
676
+ const acceptance = normalizeStringArray(slice.acceptance);
677
+ const tests = normalizeStringArray(slice.tests);
678
+ const assumptions = normalizeStringArray(slice.assumptions);
679
+ const expectedReadPaths = normalizeStringArray(slice.expected_read_paths || slice.expectedReadPaths || slice.read_paths || slice.reads);
680
+ const allowedWritePaths = explicitAllowedWritePaths;
681
+ const validationHints = normalizeStringArray(slice.validation_hints || slice.validationHints);
610
682
  const normalizedDependsOn = index === 0
611
683
  ? dependsOn
612
684
  : Array.from(new Set([SPEC_FOUNDATION_SLICE_ID, ...dependsOn]));
@@ -628,10 +700,13 @@ function normalizeSliceManifest(slice, index, fallbackTicket) {
628
700
  not_included: notIncluded,
629
701
  acceptance,
630
702
  files,
703
+ expected_read_paths: expectedReadPaths,
704
+ allowed_write_paths: allowedWritePaths,
631
705
  depends_on: normalizedDependsOn,
632
706
  parallel_safe: slice.parallel_safe,
633
707
  parallel_safe_reason: slice.parallel_safe_reason,
634
708
  tests,
709
+ validation_hints: validationHints,
635
710
  assumptions,
636
711
  estimated_hours: Number.isFinite(Number(slice.estimated_hours)) ? Number(slice.estimated_hours) : 0,
637
712
  status: slice.status || (index === 0 ? 'ready' : 'draft'),
@@ -642,6 +717,60 @@ function normalizeSliceManifest(slice, index, fallbackTicket) {
642
717
  };
643
718
  }
644
719
 
720
+ function validateSliceManifestGraph(slices) {
721
+ const ids = new Set();
722
+
723
+ for (const slice of slices) {
724
+ if (!String(slice.slice_id || '').startsWith('slice-')) {
725
+ throw new Error(`invalid slice_id '${slice.slice_id}'. Slice ids must start with 'slice-'.`);
726
+ }
727
+
728
+ if (ids.has(slice.slice_id)) {
729
+ throw new Error(`duplicate slice_id '${slice.slice_id}' in approved technical plan.`);
730
+ }
731
+
732
+ ids.add(slice.slice_id);
733
+ }
734
+
735
+ for (const slice of slices) {
736
+ for (const dependency of slice.depends_on || []) {
737
+ if (!ids.has(dependency)) {
738
+ throw new Error(`slice '${slice.slice_id}' depends on missing slice '${dependency}'.`);
739
+ }
740
+ }
741
+ }
742
+
743
+ const visiting = new Set();
744
+ const visited = new Set();
745
+ const stack = [];
746
+
747
+ function visit(sliceId) {
748
+ if (visited.has(sliceId)) {
749
+ return;
750
+ }
751
+
752
+ if (visiting.has(sliceId)) {
753
+ const start = stack.indexOf(sliceId);
754
+ const cycle = start === -1 ? [sliceId] : [...stack.slice(start), sliceId];
755
+ throw new Error(`approved technical plan contains a dependency cycle: ${cycle.join(' -> ')}.`);
756
+ }
757
+
758
+ visiting.add(sliceId);
759
+ stack.push(sliceId);
760
+ const slice = slices.find((item) => item.slice_id === sliceId);
761
+ for (const dependency of slice?.depends_on || []) {
762
+ visit(dependency);
763
+ }
764
+ stack.pop();
765
+ visiting.delete(sliceId);
766
+ visited.add(sliceId);
767
+ }
768
+
769
+ for (const slice of slices) {
770
+ visit(slice.slice_id);
771
+ }
772
+ }
773
+
645
774
  function orderSlices(slices) {
646
775
  const ordered = [];
647
776
  const remaining = new Map(slices.map((slice) => [slice.slice_id, slice]));
@@ -1,7 +1,7 @@
1
1
  const { runAnalyze } = require('../index');
2
2
 
3
- function analyzeProject(targetDir) {
4
- return runAnalyze(targetDir);
3
+ function analyzeProject(targetDir, options = {}) {
4
+ return runAnalyze(targetDir, options);
5
5
  }
6
6
 
7
7
  module.exports = {
@@ -95,6 +95,18 @@ function findDraftVersion(meta, version) {
95
95
  return normalizeDrafts(meta).find((item) => Number(item.version) === parsed) || null;
96
96
  }
97
97
 
98
+ function latestDraftVersion(meta) {
99
+ const draftVersion = Number(meta?.draft?.version || 0);
100
+ if (Number.isInteger(draftVersion) && draftVersion > 0) {
101
+ return draftVersion;
102
+ }
103
+
104
+ const versions = normalizeDrafts(meta)
105
+ .map((item) => Number(item.version))
106
+ .filter((value) => Number.isInteger(value) && value > 0);
107
+ return versions.length > 0 ? Math.max(...versions) : null;
108
+ }
109
+
98
110
  function readPhaseApproval(projectRoot, phase) {
99
111
  const normalizedPhase = normalizePhase(phase);
100
112
  const draftPath = approvalDraftPath(projectRoot, normalizedPhase);
@@ -191,12 +203,23 @@ function writeApprovalArtifacts(projectRoot, phase, kind, sourceFile, contents,
191
203
  };
192
204
  let finalContents = `${contents}`;
193
205
  let version = null;
206
+ let rawArtifactPath = options.rawArtifactPath || null;
207
+ let outputSource = options.outputSource || null;
208
+ let inputCompaction = options.inputCompaction || null;
209
+
210
+ if (kind === 'approved' && !options.version) {
211
+ throw new Error(formatError(`${normalizedPhase} approval requires a concrete draft version. Use --version <n>.`));
212
+ }
194
213
 
195
214
  if (kind === 'approved' && options.version) {
215
+ const latestVersion = latestDraftVersion(current);
196
216
  const selectedDraft = findDraftVersion(current, options.version);
197
217
  if (!selectedDraft) {
198
218
  throw new Error(formatError(`missing ${normalizedPhase} draft version ${options.version}`));
199
219
  }
220
+ if (latestVersion && Number(selectedDraft.version) !== latestVersion) {
221
+ throw new Error(formatError(`${normalizedPhase} draft version ${options.version} is not current; latest draft version is ${latestVersion}. Approve the latest version or revise again.`));
222
+ }
200
223
  const draftPath = path.resolve(projectRoot, selectedDraft.path);
201
224
  if (!fs.existsSync(draftPath)) {
202
225
  throw new Error(formatError(`missing ${normalizedPhase} draft artifact: ${selectedDraft.path}`));
@@ -204,8 +227,9 @@ function writeApprovalArtifacts(projectRoot, phase, kind, sourceFile, contents,
204
227
  finalContents = fs.readFileSync(draftPath, 'utf8');
205
228
  sourceFile = selectedDraft.path;
206
229
  version = Number(selectedDraft.version);
207
- } else if (kind === 'approved' && current.draft?.version) {
208
- version = Number(current.draft.version);
230
+ rawArtifactPath = rawArtifactPath || selectedDraft.raw_artifact_path || null;
231
+ outputSource = outputSource || selectedDraft.output_source || null;
232
+ inputCompaction = inputCompaction || selectedDraft.input_compaction || null;
209
233
  }
210
234
 
211
235
  if (kind === 'draft') {
@@ -220,6 +244,9 @@ function writeApprovalArtifacts(projectRoot, phase, kind, sourceFile, contents,
220
244
  source_file: toRelativePosix(projectRoot, path.resolve(projectRoot, sourceFile)),
221
245
  path: toRelativePosix(projectRoot, versionPath),
222
246
  created_at: now,
247
+ raw_artifact_path: rawArtifactPath,
248
+ output_source: outputSource,
249
+ input_compaction: inputCompaction,
223
250
  });
224
251
  }
225
252
 
@@ -231,6 +258,9 @@ function writeApprovalArtifacts(projectRoot, phase, kind, sourceFile, contents,
231
258
  path: toRelativePosix(projectRoot, filePath),
232
259
  version,
233
260
  created_at: now,
261
+ raw_artifact_path: rawArtifactPath,
262
+ output_source: outputSource,
263
+ input_compaction: inputCompaction,
234
264
  ...(kind === 'approved' ? { approved_at: now } : {}),
235
265
  };
236
266
 
@@ -252,8 +282,8 @@ function writeApprovalArtifacts(projectRoot, phase, kind, sourceFile, contents,
252
282
  };
253
283
  }
254
284
 
255
- function savePlannerDraft(projectRoot, phase, sourceFile, contents) {
256
- return writeApprovalArtifacts(projectRoot, phase, 'draft', sourceFile, contents);
285
+ function savePlannerDraft(projectRoot, phase, sourceFile, contents, options = {}) {
286
+ return writeApprovalArtifacts(projectRoot, phase, 'draft', sourceFile, contents, options);
257
287
  }
258
288
 
259
289
  function approvePlannerPhase(projectRoot, phase, sourceFile, contents, options = {}) {
@@ -274,7 +304,7 @@ function resolveApprovedPlannerInput(projectRoot, phase, explicitInput) {
274
304
 
275
305
  const approval = readPhaseApproval(projectRoot, dependencyPhase);
276
306
  if (approval.status !== 'approved') {
277
- throw new Error(formatError(`ai plan phase '${normalizedPhase}' requires approved ${dependencyPhase} input; current status: ${approval.status}. Run \`npx create-quiver ai approve --phase ${dependencyPhase} --input <file>\`.`));
307
+ throw new Error(formatError(`ai plan phase '${normalizedPhase}' requires approved ${dependencyPhase} input; current status: ${approval.status}. Run \`npx create-quiver ai approve --phase ${dependencyPhase} --version <n>\`.`));
278
308
  }
279
309
 
280
310
  const approvedPath = approval.approved?.path ? path.resolve(projectRoot, approval.approved.path) : '';
@@ -341,6 +371,7 @@ module.exports = {
341
371
  approvalMetaPath,
342
372
  approvePlannerPhase,
343
373
  findDraftVersion,
374
+ latestDraftVersion,
344
375
  normalizePhase,
345
376
  readPhaseApproval,
346
377
  renderApprovalStatus,
@@ -1,6 +1,12 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { toProjectSlug } = require('./init-layout');
3
+ const {
4
+ buildQuiverConfig,
5
+ buildQuiverInternalGitignore,
6
+ resolveInitPackageScripts,
7
+ toProjectSlug,
8
+ } = require('./init-layout');
9
+ const cliPackageJson = require('../../../package.json');
4
10
 
5
11
  const SPEC_VIEWER_DEMO = 'spec-viewer';
6
12
  const SPEC_VIEWER_PROJECT_NAME = 'Quiver Spec Viewer';
@@ -11,6 +17,14 @@ function createJson(data) {
11
17
  }
12
18
 
13
19
  function createSpecViewerFiles() {
20
+ const quiverScripts = {
21
+ ...resolveInitPackageScripts('default'),
22
+ 'quiver:plan': 'npx create-quiver plan --spec quiver-spec-viewer',
23
+ 'quiver:graph': 'npx create-quiver graph --spec quiver-spec-viewer',
24
+ 'quiver:next': 'npx create-quiver next --spec quiver-spec-viewer',
25
+ 'quiver:evidence': 'npx create-quiver evidence',
26
+ };
27
+
14
28
  return [
15
29
  {
16
30
  path: 'package.json',
@@ -22,13 +36,63 @@ function createSpecViewerFiles() {
22
36
  scripts: {
23
37
  start: 'node server.js',
24
38
  validate: 'node scripts/validate-demo.js',
25
- 'quiver:plan': 'npx create-quiver plan --spec quiver-spec-viewer',
26
- 'quiver:graph': 'npx create-quiver graph --spec quiver-spec-viewer',
27
- 'quiver:next': 'npx create-quiver next --spec quiver-spec-viewer',
28
- 'quiver:evidence': 'npx create-quiver evidence',
39
+ ...quiverScripts,
29
40
  },
30
41
  }),
31
42
  },
43
+ {
44
+ path: '.quiver/state.json',
45
+ content: createJson({
46
+ quiver_version: cliPackageJson.version || '0.0.0',
47
+ project_name: SPEC_VIEWER_PROJECT_NAME,
48
+ initialized_version: cliPackageJson.version || '0.0.0',
49
+ migrated_version: null,
50
+ last_initialized_at: '2026-05-23T00:00:00.000Z',
51
+ last_migration_at: null,
52
+ last_analysis_at: null,
53
+ demo: SPEC_VIEWER_DEMO,
54
+ }),
55
+ },
56
+ {
57
+ path: '.quiver/config.json',
58
+ content: createJson(buildQuiverConfig()),
59
+ },
60
+ {
61
+ path: '.quiver/.gitignore',
62
+ content: buildQuiverInternalGitignore(),
63
+ },
64
+ {
65
+ path: 'AGENTS.md',
66
+ content: `# Agents
67
+
68
+ Purpose
69
+
70
+ This demo is a small Quiver project used to inspect specs and slices.
71
+
72
+ ## Reading Budget
73
+
74
+ Start with README.md and the demo spec. Do not read generated or dependency folders.
75
+
76
+ ## Reading Order
77
+
78
+ 1. README.md
79
+ 2. docs/AI_ONBOARDING_PROMPT.md
80
+ 3. specs/quiver-spec-viewer/SPEC.md
81
+
82
+ ## Output Policy
83
+
84
+ Keep reports short and cite touched files.
85
+
86
+ ## Slice Execution Rules
87
+
88
+ Execute only the selected slice and keep changes inside its declared files.
89
+
90
+ ## Links
91
+
92
+ - docs/AI_ONBOARDING_PROMPT.md
93
+ - specs/quiver-spec-viewer/SPEC.md
94
+ `,
95
+ },
32
96
  {
33
97
  path: 'README.md',
34
98
  content: `# ${SPEC_VIEWER_PROJECT_NAME}
@@ -42,7 +106,19 @@ npm run validate
42
106
  npm start
43
107
  \`\`\`
44
108
 
45
- Open http://127.0.0.1:4173 after starting the server.
109
+ Open the URL printed by the server. It starts at http://127.0.0.1:4173 and automatically tries the next ports if that port is occupied.
110
+
111
+ To request a specific starting port:
112
+
113
+ \`\`\`bash
114
+ PORT=4300 npm start
115
+ \`\`\`
116
+
117
+ On Windows PowerShell:
118
+
119
+ \`\`\`powershell
120
+ $env:PORT = "4300"; npm start
121
+ \`\`\`
46
122
 
47
123
  ## Quiver workflow
48
124
 
@@ -51,6 +127,52 @@ Open http://127.0.0.1:4173 after starting the server.
51
127
  - The first implementation slice is \`slice-01-static-spec-viewer\`.
52
128
  - Use \`npm run quiver:plan\`, \`npm run quiver:graph\`, and \`npm run quiver:next\` to inspect execution order.
53
129
  - Use \`npm run quiver:evidence -- run -- npm run validate\` to capture validation evidence.
130
+ `,
131
+ },
132
+ {
133
+ path: 'docs/AI_CONTEXT.md',
134
+ content: `# Quiver Spec Viewer AI Context
135
+
136
+ This demo is a small static Quiver project. It exists to exercise Quiver specs, slices, validation, and diagnostics with minimal product code.
137
+
138
+ ## Relevant paths
139
+
140
+ - \`specs/quiver-spec-viewer/SPEC.md\`
141
+ - \`specs/quiver-spec-viewer/slices/\`
142
+ - \`src/\`
143
+ `,
144
+ },
145
+ {
146
+ path: 'docs/AI_ONBOARDING_PROMPT.md',
147
+ content: `# AI Onboarding Prompt
148
+
149
+ Read README.md, AGENTS.md, and specs/quiver-spec-viewer/SPEC.md.
150
+
151
+ Do not modify product files unless a slice explicitly asks for it.
152
+ `,
153
+ },
154
+ {
155
+ path: 'docs/COMMANDS.md',
156
+ content: `# Commands
157
+
158
+ | Command | Purpose |
159
+ |---|---|
160
+ | \`npm run validate\` | Validate required demo files. |
161
+ | \`npm start\` | Start the dependency-free static server. |
162
+ | \`npm run quiver:doctor\` | Run Quiver diagnostics. |
163
+ | \`npm run quiver:plan\` | Show demo slice execution order. |
164
+ | \`npm run quiver:graph\` | Show the demo slice graph. |
165
+ | \`npm run quiver:next\` | Suggest the next demo slice. |
166
+ `,
167
+ },
168
+ {
169
+ path: 'docs/WORKFLOW.md',
170
+ content: `# Workflow
171
+
172
+ 1. Validate the demo with \`npm run validate\`.
173
+ 2. Inspect the Quiver workflow with \`npm run quiver:plan\`, \`npm run quiver:graph\`, and \`npm run quiver:next\`.
174
+ 3. Execute only one slice at a time.
175
+ 4. Keep \`slice-00-docs-foundation\` as the documentary baseline.
54
176
  `,
55
177
  },
56
178
  {
@@ -60,14 +182,20 @@ const http = require('node:http');
60
182
  const path = require('node:path');
61
183
 
62
184
  const root = path.join(__dirname, 'src');
63
- const port = Number(process.env.PORT || 4173);
185
+ const startingPort = Number.parseInt(process.env.PORT || '4173', 10);
186
+ const maxPortAttempts = 10;
64
187
  const types = {
65
188
  '.css': 'text/css; charset=utf-8',
66
189
  '.html': 'text/html; charset=utf-8',
67
190
  '.js': 'text/javascript; charset=utf-8',
68
191
  };
69
192
 
70
- const server = http.createServer((request, response) => {
193
+ if (!Number.isInteger(startingPort) || startingPort < 1 || startingPort > 65535) {
194
+ console.error('Invalid PORT value. Use a number between 1 and 65535.');
195
+ process.exit(1);
196
+ }
197
+
198
+ function handleRequest(request, response) {
71
199
  const requestedPath = request.url === '/' ? '/index.html' : request.url;
72
200
  const filePath = path.normalize(path.join(root, requestedPath));
73
201
 
@@ -87,11 +215,31 @@ const server = http.createServer((request, response) => {
87
215
  response.writeHead(200, { 'Content-Type': types[path.extname(filePath)] || 'text/plain; charset=utf-8' });
88
216
  response.end(content);
89
217
  });
90
- });
218
+ }
91
219
 
92
- server.listen(port, '127.0.0.1', () => {
93
- console.log(\`Quiver Spec Viewer running at http://127.0.0.1:\${port}\`);
94
- });
220
+ function startServer(port, attempt = 0) {
221
+ const server = http.createServer(handleRequest);
222
+
223
+ server.on('error', (error) => {
224
+ if (error.code === 'EADDRINUSE' && attempt < maxPortAttempts) {
225
+ startServer(port + 1, attempt + 1);
226
+ return;
227
+ }
228
+
229
+ if (error.code === 'EADDRINUSE') {
230
+ console.error(\`Port \${port} is in use. Set PORT to a free port, for example: PORT=4300 npm start\`);
231
+ } else {
232
+ console.error(error.message);
233
+ }
234
+ process.exit(1);
235
+ });
236
+
237
+ server.listen(port, '127.0.0.1', () => {
238
+ console.log(\`Quiver Spec Viewer running at http://127.0.0.1:\${port}\`);
239
+ });
240
+ }
241
+
242
+ startServer(startingPort);
95
243
  `,
96
244
  },
97
245
  {
@@ -363,7 +511,16 @@ renderContent();
363
511
  const path = require('node:path');
364
512
 
365
513
  const required = [
514
+ '.quiver/config.json',
515
+ '.quiver/state.json',
516
+ '.quiver/.gitignore',
517
+ 'AGENTS.md',
366
518
  'README.md',
519
+ 'docs/AI_CONTEXT.md',
520
+ 'docs/AI_ONBOARDING_PROMPT.md',
521
+ 'docs/COMMANDS.md',
522
+ 'docs/WORKFLOW.md',
523
+ 'package.json',
367
524
  'server.js',
368
525
  'src/index.html',
369
526
  'src/styles.css',
@@ -380,6 +537,14 @@ if (missing.length > 0) {
380
537
  process.exit(1);
381
538
  }
382
539
 
540
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
541
+ for (const scriptName of ['start', 'validate', 'quiver:doctor', 'quiver:plan', 'quiver:graph', 'quiver:next', 'quiver:evidence']) {
542
+ if (typeof packageJson.scripts?.[scriptName] !== 'string') {
543
+ console.error(\`Missing package script: \${scriptName}\`);
544
+ process.exit(1);
545
+ }
546
+ }
547
+
383
548
  console.log('Quiver Spec Viewer demo validated');
384
549
  `,
385
550
  },
@@ -477,7 +642,11 @@ This slice publishes the demo planning artifacts.
477
642
 
478
643
  Keep the Quiver Spec Viewer spec, status, evidence, and PR body available in the repo.
479
644
 
480
- ## Checklist
645
+ ## Acceptance Criteria
646
+
647
+ - Demo documentation artifacts exist.
648
+
649
+ ## Completion Checklist
481
650
 
482
651
  - [ ] Verify spec docs exist.
483
652
  - [ ] Run \`npm run validate\`.
@@ -535,7 +704,13 @@ The demo is intentionally static and dependency-free.
535
704
 
536
705
  Implement a small viewer for mocked Quiver specs and slices.
537
706
 
538
- ## Checklist
707
+ ## Acceptance Criteria
708
+
709
+ - Viewer shows title, spec list, and detail.
710
+ - Viewer supports loading, empty, error, and content states.
711
+ - Demo validates with npm run validate.
712
+
713
+ ## Completion Checklist
539
714
 
540
715
  - [ ] Keep the UI simple and readable.
541
716
  - [ ] Do not add heavy dependencies.