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.
- package/CHANGELOG.md +52 -0
- package/README.md +65 -25
- package/README_FOR_AI.md +36 -29
- package/ROADMAP.md +22 -3
- package/docs/AI_ONBOARDING_PROMPT.md.template +7 -1
- package/docs/COMMANDS.md.template +53 -20
- package/docs/STATUS.md.template +5 -1
- package/docs/WORKFLOW.md.template +13 -11
- package/package.json +10 -3
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EVIDENCE_REPORT.md +293 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EXECUTION_PLAN.md +58 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/SPEC.md +242 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/STATUS.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/pr.md +77 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/slice.json +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/slice.json +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/slice.json +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/EVIDENCE_REPORT.md +208 -0
- package/specs/quiver-v26-0121-smoke-hardening/EXECUTION_PLAN.md +57 -0
- package/specs/quiver-v26-0121-smoke-hardening/SPEC.md +137 -0
- package/specs/quiver-v26-0121-smoke-hardening/STATUS.md +32 -0
- package/specs/quiver-v26-0121-smoke-hardening/pr.md +96 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/slice.json +73 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/slice.json +76 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/slice.json +75 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/slice.json +84 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/slice.json +82 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/slice.json +92 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/AUDIT_V24_V25_V26.md +67 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/COMMAND_CONTRACTS.md +125 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/COVERAGE_MATRIX.md +74 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/EVIDENCE_REPORT.md +179 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/EXECUTION_PLAN.md +71 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/SPEC.md +176 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/STATUS.md +37 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/pr.md +132 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/slice.json +75 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/slice.json +79 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/slice.json +75 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/slice.json +78 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/slice.json +77 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/slice.json +84 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/slice.json +99 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/slice.json +88 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/slice.json +85 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/slice.json +91 -0
- package/src/create-quiver/commands/ai.js +652 -27
- package/src/create-quiver/commands/flow.js +58 -9
- package/src/create-quiver/commands/graph.js +11 -9
- package/src/create-quiver/commands/plan.js +7 -16
- package/src/create-quiver/commands/spec.js +282 -0
- package/src/create-quiver/index.js +409 -31
- package/src/create-quiver/lib/actionable-error.js +27 -0
- package/src/create-quiver/lib/agent-profiles.js +16 -4
- package/src/create-quiver/lib/ai/artifacts.js +318 -0
- package/src/create-quiver/lib/ai/context-packs.js +4 -0
- package/src/create-quiver/lib/ai/execution-plan.js +16 -1
- package/src/create-quiver/lib/ai/executor.js +272 -21
- package/src/create-quiver/lib/ai/export-state.js +679 -0
- package/src/create-quiver/lib/ai/github.js +162 -2
- package/src/create-quiver/lib/ai/onboarding-template.js +215 -2
- package/src/create-quiver/lib/ai/plan-review.js +7 -2
- package/src/create-quiver/lib/ai/providers.js +4 -3
- package/src/create-quiver/lib/ai/run-state.js +414 -0
- package/src/create-quiver/lib/ai/spec-generator.js +84 -13
- package/src/create-quiver/lib/ai/spec-templates.js +150 -21
- package/src/create-quiver/lib/analyze.js +2 -2
- package/src/create-quiver/lib/approvals.js +36 -5
- package/src/create-quiver/lib/demo.js +189 -14
- package/src/create-quiver/lib/doctor.js +154 -0
- package/src/create-quiver/lib/git.js +40 -1
- package/src/create-quiver/lib/handoff.js +123 -12
- package/src/create-quiver/lib/init-docs.js +35 -13
- package/src/create-quiver/lib/init-layout.js +9 -0
- package/src/create-quiver/lib/json.js +53 -3
- package/src/create-quiver/lib/lifecycle.js +52 -3
- package/src/create-quiver/lib/locks.js +134 -0
- package/src/create-quiver/lib/package-safety.js +7 -0
- package/src/create-quiver/lib/paths.js +74 -0
- package/src/create-quiver/lib/project-scan.js +74 -0
- package/src/create-quiver/lib/project-state-resolver.js +236 -0
- package/src/create-quiver/lib/readiness.js +66 -10
- package/src/create-quiver/lib/scope.js +52 -8
- package/src/create-quiver/lib/slice-graph.js +138 -38
- package/src/create-quiver/lib/slice.js +14 -5
- package/src/create-quiver/lib/spec-worktrees.js +129 -32
- 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
|
|
398
|
-
'2. Implement the declared
|
|
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
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
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(
|
|
604
|
-
const
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
const
|
|
608
|
-
const
|
|
609
|
-
const
|
|
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]));
|
|
@@ -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
|
-
|
|
208
|
-
|
|
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} --
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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.
|