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
@@ -5,9 +5,11 @@ const cp = require('node:child_process');
5
5
  const { buildContextPackMetadata, normalizeRole } = require('./context-packs');
6
6
  const { buildProviderInvocation, runProvider } = require('./providers');
7
7
  const { resolveProfileProvider } = require('../agent-profiles');
8
- const { runGit } = require('../git');
8
+ const { currentBranch, runGit } = require('../git');
9
+ const { redactSecrets, truncateText } = require('../evidence');
9
10
  const { captureWorktreeSnapshot, validateScopeSnapshot } = require('../scope');
10
11
  const { resolveSliceContext } = require('../slice');
12
+ const { validateProjectRelativePaths } = require('../paths');
11
13
 
12
14
  const DEFAULT_EXECUTE_PROVIDER = 'codex';
13
15
  const DEFAULT_EXECUTE_ROLE = 'executor';
@@ -77,6 +79,14 @@ function formatList(items) {
77
79
  return items.map((item) => `- ${item}`);
78
80
  }
79
81
 
82
+ function uniqueList(items) {
83
+ return Array.from(new Set((Array.isArray(items) ? items : []).map((item) => String(item)).filter(Boolean)));
84
+ }
85
+
86
+ function escapeRegex(value) {
87
+ return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
88
+ }
89
+
80
90
  function extractMarkdownHeading(text) {
81
91
  const match = String(text || '').match(/^#\s+(.+)$/m);
82
92
  return match ? match[1].trim() : '';
@@ -189,6 +199,9 @@ function buildManualExecutorPrompt({ repoRoot, slicePath, role, context, tokenLi
189
199
  `- Source: ${specExcerpt.path}`,
190
200
  ...specExcerpt.lines,
191
201
  '',
202
+ 'Expected read paths:',
203
+ ...formatList(executorContext.expectedReadPaths),
204
+ '',
192
205
  'Allowed files:',
193
206
  ...formatList(executorContext.allowedFiles),
194
207
  '',
@@ -201,6 +214,9 @@ function buildManualExecutorPrompt({ repoRoot, slicePath, role, context, tokenLi
201
214
  'Validation commands:',
202
215
  ...formatList(executorContext.validationCommands),
203
216
  '',
217
+ 'Validation hints:',
218
+ ...formatList(executorContext.validationHints),
219
+ '',
204
220
  'Exact deliverable expected:',
205
221
  '- Implement only this slice.',
206
222
  '- Keep the change inside the allowed files.',
@@ -288,9 +304,11 @@ function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
288
304
  });
289
305
  const relativeSlicePath = toRelativePath(canonicalRepoRoot, slice.sliceAbs);
290
306
  const relativeBriefPath = toRelativePath(canonicalRepoRoot, briefPath);
291
- const allowedFiles = Array.isArray(slice.files) ? slice.files.map((file) => String(file)) : [];
307
+ const allowedFiles = validateProjectRelativePaths(Array.isArray(slice.files) ? slice.files.map((file) => String(file)) : [], 'slice write scope');
308
+ const expectedReadPaths = validateProjectRelativePaths(Array.isArray(slice.expectedReadPaths) ? slice.expectedReadPaths.map((file) => String(file)) : [], 'slice read scope');
292
309
  const acceptance = Array.isArray(slice.acceptance) ? slice.acceptance.map((item) => String(item)) : [];
293
310
  const validationCommands = Array.isArray(slice.tests) ? slice.tests.map((item) => String(item)) : [];
311
+ const validationHints = Array.isArray(slice.validationHints) ? slice.validationHints.map((item) => String(item)) : [];
294
312
  const mustItems = Array.isArray(slice.json.must) ? slice.json.must.map((item) => String(item)) : [];
295
313
  const excludedItems = Array.isArray(slice.json.not_included) ? slice.json.not_included.map((item) => String(item)) : [];
296
314
 
@@ -301,6 +319,8 @@ function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
301
319
  `Spec: ${slice.specSlug}`,
302
320
  `Slice file: ${relativeSlicePath}`,
303
321
  `Execution brief: ${relativeBriefPath}`,
322
+ 'Expected read paths:',
323
+ ...formatList(expectedReadPaths),
304
324
  'Allowed files:',
305
325
  ...formatList(allowedFiles),
306
326
  'Acceptance criteria:',
@@ -317,6 +337,10 @@ function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
317
337
  sections.push('Not included:', ...formatList(excludedItems));
318
338
  }
319
339
 
340
+ if (validationHints.length > 0) {
341
+ sections.push('Validation hints:', ...formatList(validationHints));
342
+ }
343
+
320
344
  sections.push(
321
345
  'Constraints:',
322
346
  '- Do not commit manually. Quiver can create the slice commit after scope and validation pass when the user enables --commit.',
@@ -332,8 +356,10 @@ function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
332
356
  briefPath: relativeBriefPath,
333
357
  briefText,
334
358
  context: pack,
359
+ expectedReadPaths,
335
360
  prompt: sections.join('\n\n'),
336
361
  slice,
362
+ validationHints,
337
363
  validationCommands,
338
364
  };
339
365
  }
@@ -407,18 +433,18 @@ function runValidationCommand(command, repoRoot) {
407
433
  stdio: ['ignore', 'pipe', 'pipe'],
408
434
  });
409
435
  return {
410
- command,
436
+ command: redactSecrets(command),
411
437
  ok: true,
412
- stdout,
438
+ stdout: redactSecrets(stdout),
413
439
  stderr: '',
414
440
  exitCode: 0,
415
441
  };
416
442
  } catch (error) {
417
443
  return {
418
- command,
444
+ command: redactSecrets(command),
419
445
  ok: false,
420
- stdout: error.stdout ? String(error.stdout) : '',
421
- stderr: error.stderr ? String(error.stderr) : '',
446
+ stdout: redactSecrets(error.stdout ? String(error.stdout) : ''),
447
+ stderr: redactSecrets(error.stderr ? String(error.stderr) : ''),
422
448
  exitCode: Number.isInteger(error.status) ? error.status : 1,
423
449
  error,
424
450
  };
@@ -428,7 +454,13 @@ function runValidationCommand(command, repoRoot) {
428
454
  function runValidationCommands(repoRoot, commands, runner = runValidationCommand) {
429
455
  const results = [];
430
456
  for (const command of commands) {
431
- const result = runner(command, repoRoot);
457
+ const rawResult = runner(command, repoRoot);
458
+ const result = {
459
+ ...rawResult,
460
+ command: redactSecrets(rawResult.command || command),
461
+ stderr: redactSecrets(rawResult.stderr || ''),
462
+ stdout: redactSecrets(rawResult.stdout || ''),
463
+ };
432
464
  results.push(result);
433
465
  if (!result.ok) {
434
466
  const details = [
@@ -492,11 +524,196 @@ function commitSliceChanges(repoRoot, slice, changedFiles, options = {}) {
492
524
  };
493
525
  }
494
526
 
527
+ function assertCorrectSliceWorktree(repoRoot, slice, options = {}) {
528
+ if (options.skipWorktreeBranchCheck === true) {
529
+ return null;
530
+ }
531
+
532
+ const expectedBranch = String(slice.branchName || slice.json.git?.branch_name || '').trim();
533
+ if (!expectedBranch) {
534
+ return null;
535
+ }
536
+
537
+ const actualBranch = currentBranch(repoRoot);
538
+ if (actualBranch !== expectedBranch) {
539
+ const error = new Error(formatError(`ai execute-slice must run from the slice worktree branch. Current branch: ${actualBranch || '(detached or unavailable)'}. Expected: ${expectedBranch}.`));
540
+ error.code = 'WRONG_WORKTREE';
541
+ error.details = {
542
+ actualBranch,
543
+ expectedBranch,
544
+ slice: slice.sliceRel,
545
+ };
546
+ throw error;
547
+ }
548
+
549
+ return {
550
+ actualBranch,
551
+ expectedBranch,
552
+ };
553
+ }
554
+
555
+ function sliceLifecycleArtifactPaths(repoRoot, slice) {
556
+ const closureAbs = path.join(path.dirname(slice.sliceAbs), 'CLOSURE_BRIEF.md');
557
+ return {
558
+ closure: toRelativePath(repoRoot, closureAbs),
559
+ commandLog: toRelativePath(repoRoot, path.join(slice.specDirAbs, 'COMMAND_LOG.md')),
560
+ evidence: toRelativePath(repoRoot, path.join(slice.specDirAbs, 'EVIDENCE_REPORT.md')),
561
+ sliceJson: toRelativePath(repoRoot, slice.sliceAbs),
562
+ status: toRelativePath(repoRoot, path.join(slice.specDirAbs, 'STATUS.md')),
563
+ };
564
+ }
565
+
566
+ function renderClosureBrief({ slice, changedFiles, validationResults, completedAt }) {
567
+ const criteria = Array.isArray(slice.acceptance) ? slice.acceptance : [];
568
+ const validationLines = Array.isArray(validationResults) && validationResults.length > 0
569
+ ? validationResults.map((result) => `- [x] \`${result.command}\` exited ${result.exitCode}`)
570
+ : ['- [x] No validation commands declared.'];
571
+
572
+ return `${[
573
+ `# CLOSURE BRIEF - ${slice.sliceId}: ${slice.json.title || slice.sliceId}`,
574
+ '',
575
+ '## Summary of Work',
576
+ '',
577
+ `Executed controlled slice closure at ${completedAt}. Quiver validated scope, validation commands, and lifecycle evidence for this slice.`,
578
+ '',
579
+ '## Validation Against Acceptance Criteria',
580
+ '',
581
+ ...(criteria.length > 0 ? criteria.map((item) => `- [x] ${item}`) : ['- [x] Slice execution completed with scope validation.']),
582
+ '',
583
+ '## Relevant Changes',
584
+ '',
585
+ ...formatList(changedFiles),
586
+ '',
587
+ '## Validation Commands',
588
+ '',
589
+ ...validationLines,
590
+ '',
591
+ '## Pending',
592
+ '',
593
+ 'None recorded by Quiver.',
594
+ '',
595
+ '## Remaining Risks',
596
+ '',
597
+ 'None recorded by Quiver.',
598
+ '',
599
+ '## Future Recommendations',
600
+ '',
601
+ 'Review the evidence report and commit diff before opening the PR.',
602
+ '',
603
+ ].join('\n')}\n`;
604
+ }
605
+
606
+ function appendSection(filePath, fallbackTitle, section) {
607
+ const current = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8').trimEnd() : fallbackTitle;
608
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
609
+ fs.writeFileSync(filePath, `${current}\n\n${section.trimEnd()}\n`);
610
+ }
611
+
612
+ function updateStatusMarkdown(filePath, slice, completedAt) {
613
+ const fallback = `# Status - ${slice.specSlug}\n`;
614
+ let text = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : fallback;
615
+ const rowRegex = new RegExp(`(\\|\\s*${escapeRegex(slice.sliceId)}\\s*\\|\\s*)[^|\\n]+(\\|[^\\n]*\\|)`);
616
+ if (rowRegex.test(text)) {
617
+ text = text.replace(rowRegex, '$1Completed $2');
618
+ }
619
+ text = text.replace(/\*\*Current slice:\*\*\s*[^\n]*/i, `**Current slice:** ${slice.sliceId} completed`);
620
+ if (!text.endsWith('\n')) {
621
+ text += '\n';
622
+ }
623
+ const section = [
624
+ '',
625
+ `## Execution Update - ${slice.sliceId}`,
626
+ '',
627
+ `- Status: Completed`,
628
+ `- Completed at: ${completedAt}`,
629
+ `- Source: \`npx create-quiver ai execute-slice --slice ${slice.sliceRel}\``,
630
+ ].join('\n');
631
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
632
+ fs.writeFileSync(filePath, `${text}${section}\n`);
633
+ }
634
+
635
+ function updateSliceJson(filePath, completedAt) {
636
+ const json = JSON.parse(fs.readFileSync(filePath, 'utf8'));
637
+ json.status = 'completed';
638
+ json.completed_at = completedAt;
639
+ fs.writeFileSync(filePath, `${JSON.stringify(json, null, 2)}\n`);
640
+ }
641
+
642
+ function writeExecutionArtifacts(repoRoot, executorContext, details) {
643
+ const { slice } = executorContext;
644
+ const completedAt = details.completedAt || new Date().toISOString();
645
+ const artifacts = sliceLifecycleArtifactPaths(repoRoot, slice);
646
+ const changedFiles = uniqueList(details.changedFiles);
647
+ const closurePath = path.join(repoRoot, artifacts.closure);
648
+ const evidencePath = path.join(repoRoot, artifacts.evidence);
649
+ const commandLogPath = path.join(repoRoot, artifacts.commandLog);
650
+ const statusPath = path.join(repoRoot, artifacts.status);
651
+ const validationResults = Array.isArray(details.validationResults) ? details.validationResults : [];
652
+ const providerStdout = truncateText(redactSecrets(details.providerOutput?.stdout || ''), 1200).text;
653
+ const providerStderr = truncateText(redactSecrets(details.providerOutput?.stderr || ''), 1200).text;
654
+
655
+ fs.mkdirSync(path.dirname(closurePath), { recursive: true });
656
+ fs.writeFileSync(closurePath, renderClosureBrief({
657
+ slice,
658
+ changedFiles,
659
+ validationResults,
660
+ completedAt,
661
+ }));
662
+
663
+ const validationLines = validationResults.length > 0
664
+ ? validationResults.map((result) => `- \`${result.command}\` -> exit ${result.exitCode}`)
665
+ : ['- No validation commands declared.'];
666
+ appendSection(evidencePath, `# Evidence Report - ${slice.specSlug}`, [
667
+ `## ${slice.sliceId} - Execution Evidence`,
668
+ '',
669
+ `- Completed at: ${completedAt}`,
670
+ `- Changed files: ${changedFiles.length}`,
671
+ ...changedFiles.map((file) => ` - \`${file}\``),
672
+ `- Scope validation: passed`,
673
+ `- Provider stdout redacted: ${providerStdout ? 'yes' : 'n/a'}`,
674
+ `- Provider stderr redacted: ${providerStderr ? 'yes' : 'n/a'}`,
675
+ '',
676
+ '### Validation',
677
+ '',
678
+ ...validationLines,
679
+ '',
680
+ '### Provider Output',
681
+ '',
682
+ '```text',
683
+ providerStdout || 'n/a',
684
+ providerStderr ? `\n${providerStderr}` : '',
685
+ '```',
686
+ ].join('\n'));
687
+
688
+ const commandLogRows = [
689
+ `| ${completedAt} | ${slice.sliceId} | \`npx create-quiver ai execute-slice --slice ${slice.sliceRel}\` | passed |`,
690
+ ...validationResults.map((result) => `| ${completedAt} | ${slice.sliceId} | \`${result.command}\` | exit ${result.exitCode} |`),
691
+ ];
692
+ const commandLogHeader = [
693
+ '# Command Log',
694
+ '',
695
+ '| Timestamp | Slice | Command | Result |',
696
+ '|---|---|---|---|',
697
+ ].join('\n');
698
+ const currentCommandLog = fs.existsSync(commandLogPath) ? fs.readFileSync(commandLogPath, 'utf8').trimEnd() : commandLogHeader;
699
+ fs.mkdirSync(path.dirname(commandLogPath), { recursive: true });
700
+ fs.writeFileSync(commandLogPath, `${currentCommandLog}\n${commandLogRows.join('\n')}\n`);
701
+
702
+ updateStatusMarkdown(statusPath, slice, completedAt);
703
+ updateSliceJson(path.join(repoRoot, artifacts.sliceJson), completedAt);
704
+
705
+ return {
706
+ completedAt,
707
+ files: Object.values(artifacts),
708
+ };
709
+ }
710
+
495
711
  async function runExecuteSlice(repoRoot, options = {}) {
712
+ const canonicalRepoRoot = canonicalizeRepoRoot(repoRoot);
496
713
  const role = normalizeRole(options.role || DEFAULT_EXECUTE_ROLE);
497
714
  const provider = options.providerExplicit === true || (options.provider && options.providerExplicit !== false)
498
715
  ? String(options.provider || DEFAULT_EXECUTE_PROVIDER).trim().toLowerCase()
499
- : resolveProfileProvider(repoRoot, role, DEFAULT_EXECUTE_PROVIDER);
716
+ : resolveProfileProvider(canonicalRepoRoot, role, DEFAULT_EXECUTE_PROVIDER);
500
717
  const context = options.context || DEFAULT_EXECUTE_CONTEXT;
501
718
  const timeoutMs = normalizeTimeout(options.timeout);
502
719
 
@@ -505,7 +722,7 @@ async function runExecuteSlice(repoRoot, options = {}) {
505
722
  }
506
723
 
507
724
  const executorContext = buildExecuteSliceContext({
508
- repoRoot,
725
+ repoRoot: canonicalRepoRoot,
509
726
  slicePath: options.slice,
510
727
  role,
511
728
  context,
@@ -517,7 +734,7 @@ async function runExecuteSlice(repoRoot, options = {}) {
517
734
  try {
518
735
  invocation = buildProviderInvocation(provider, {
519
736
  prompt,
520
- cwd: repoRoot,
737
+ cwd: canonicalRepoRoot,
521
738
  timeoutMs,
522
739
  });
523
740
  } catch (error) {
@@ -551,7 +768,16 @@ async function runExecuteSlice(repoRoot, options = {}) {
551
768
  return report;
552
769
  }
553
770
 
554
- const beforeSnapshot = captureWorktreeSnapshot(repoRoot);
771
+ try {
772
+ assertCorrectSliceWorktree(canonicalRepoRoot, executorContext.slice, options);
773
+ } catch (error) {
774
+ throw appendRecovery(error, executorContext.slice);
775
+ }
776
+
777
+ const beforeSnapshot = captureWorktreeSnapshot(canonicalRepoRoot);
778
+ if (beforeSnapshot.files.length > 0 && options.commit === true) {
779
+ throw appendRecovery(new Error(formatError(`ai execute-slice --commit requires a clean worktree before running. Commit or stash first: ${beforeSnapshot.files.join(', ')}`)), executorContext.slice);
780
+ }
555
781
  if (beforeSnapshot.files.length > 0 && options.allowDirty !== true) {
556
782
  throw appendRecovery(new Error(formatError(`ai execute-slice requires a clean worktree before running. Commit or stash first: ${beforeSnapshot.files.join(', ')}`)), executorContext.slice);
557
783
  }
@@ -560,7 +786,7 @@ async function runExecuteSlice(repoRoot, options = {}) {
560
786
  try {
561
787
  result = await (options.runProviderFn || runProvider)(provider, {
562
788
  prompt,
563
- cwd: repoRoot,
789
+ cwd: canonicalRepoRoot,
564
790
  timeoutMs,
565
791
  dryRun: false,
566
792
  probe: options.probe,
@@ -574,17 +800,27 @@ async function runExecuteSlice(repoRoot, options = {}) {
574
800
  }
575
801
 
576
802
  if (result.stdout) {
577
- process.stdout.write(result.stdout);
803
+ process.stdout.write(redactSecrets(result.stdout));
578
804
  }
579
805
  if (result.stderr) {
580
- process.stderr.write(result.stderr);
806
+ process.stderr.write(redactSecrets(result.stderr));
581
807
  }
582
808
 
583
809
  if (!result.ok) {
584
810
  throw appendRecovery(annotateProviderError(result.error || new Error('provider run failed'), 'execute-slice'), executorContext.slice);
585
811
  }
586
812
 
587
- const afterSnapshot = captureWorktreeSnapshot(repoRoot);
813
+ const providerOutput = {
814
+ stdout: redactSecrets(result.stdout || ''),
815
+ stderr: redactSecrets(result.stderr || ''),
816
+ };
817
+ const sanitizedResult = {
818
+ ...result,
819
+ stdout: providerOutput.stdout,
820
+ stderr: providerOutput.stderr,
821
+ };
822
+
823
+ const afterSnapshot = captureWorktreeSnapshot(canonicalRepoRoot);
588
824
  let scopeResult;
589
825
  try {
590
826
  scopeResult = validateScopeSnapshot({
@@ -596,11 +832,16 @@ async function runExecuteSlice(repoRoot, options = {}) {
596
832
  } catch (error) {
597
833
  throw appendRecovery(error, executorContext.slice);
598
834
  }
835
+ if (scopeResult.changedFiles.length === 0) {
836
+ const error = new Error(formatError('provider produced no changed files; slice closure was not updated.'));
837
+ error.code = 'NO_CHANGES_TO_CLOSE';
838
+ throw appendRecovery(error, executorContext.slice);
839
+ }
599
840
 
600
841
  let validationResults = [];
601
842
  try {
602
843
  validationResults = runValidationCommands(
603
- repoRoot,
844
+ canonicalRepoRoot,
604
845
  executorContext.validationCommands,
605
846
  options.runValidationCommandFn,
606
847
  );
@@ -608,11 +849,18 @@ async function runExecuteSlice(repoRoot, options = {}) {
608
849
  throw appendRecovery(error, executorContext.slice);
609
850
  }
610
851
 
611
- const finalSnapshot = captureWorktreeSnapshot(repoRoot);
852
+ const artifacts = writeExecutionArtifacts(canonicalRepoRoot, executorContext, {
853
+ changedFiles: scopeResult.changedFiles,
854
+ completedAt: new Date().toISOString(),
855
+ providerOutput,
856
+ validationResults,
857
+ });
858
+
859
+ const finalSnapshot = captureWorktreeSnapshot(canonicalRepoRoot);
612
860
  let finalScopeResult;
613
861
  try {
614
862
  finalScopeResult = validateScopeSnapshot({
615
- allowedFiles: executorContext.allowedFiles,
863
+ allowedFiles: uniqueList([...executorContext.allowedFiles, ...artifacts.files]),
616
864
  beforeSnapshot,
617
865
  afterSnapshot: finalSnapshot,
618
866
  strict: true,
@@ -624,7 +872,7 @@ async function runExecuteSlice(repoRoot, options = {}) {
624
872
  let commitResult = null;
625
873
  if (options.commit === true) {
626
874
  try {
627
- commitResult = commitSliceChanges(repoRoot, executorContext.slice, finalScopeResult.changedFiles, {
875
+ commitResult = commitSliceChanges(canonicalRepoRoot, executorContext.slice, finalScopeResult.changedFiles, {
628
876
  message: options.commitMessage,
629
877
  });
630
878
  } catch (error) {
@@ -649,12 +897,13 @@ async function runExecuteSlice(repoRoot, options = {}) {
649
897
  slice: executorContext.slice.sliceId,
650
898
  specSlug: executorContext.slice.specSlug,
651
899
  invocation,
652
- result,
900
+ result: sanitizedResult,
653
901
  beforeSnapshot,
654
902
  afterSnapshot: finalSnapshot,
655
903
  scopeResult: finalScopeResult,
656
904
  validationResults,
657
905
  commitResult,
906
+ artifacts,
658
907
  };
659
908
  }
660
909
 
@@ -672,6 +921,8 @@ module.exports = {
672
921
  commitSliceChanges,
673
922
  formatExecuteSliceDryRunReport,
674
923
  formatExecuteSliceResult,
924
+ assertCorrectSliceWorktree,
925
+ writeExecutionArtifacts,
675
926
  runValidationCommand,
676
927
  runValidationCommands,
677
928
  normalizeTimeout,