create-quiver 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +15 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
  3. package/.github/pull_request_template.md +4 -0
  4. package/.github/workflows/ci.yml +74 -0
  5. package/CHANGELOG.md +24 -0
  6. package/CODE_OF_CONDUCT.md +12 -0
  7. package/CONTRIBUTING.md +15 -0
  8. package/LICENSE +21 -0
  9. package/README.md +118 -0
  10. package/README_FOR_AI.md +90 -0
  11. package/ROADMAP.md +20 -0
  12. package/SECURITY.md +12 -0
  13. package/TEMPLATE.md +108 -0
  14. package/bin/create-quiver.js +8 -0
  15. package/docs/CONTEXTO.md.template +45 -0
  16. package/docs/DOCUMENTATION_GUIDE.md.template +34 -0
  17. package/docs/GITFLOW_PR_GUIDE.md.template +52 -0
  18. package/docs/INDEX.md.template +41 -0
  19. package/docs/MOCK_DATA_GUIDE.md.template +31 -0
  20. package/docs/MULTI_AGENT_WORKFLOW.md.template +31 -0
  21. package/docs/STATUS.md.template +26 -0
  22. package/docs/SUPPORT_MATRIX.md.template +31 -0
  23. package/docs/TESTING_GUIDE_FOR_AI.md.template +42 -0
  24. package/docs/TROUBLESHOOTING.md.template +70 -0
  25. package/docs/UI_STANDARDS.md.template +31 -0
  26. package/docs/WORKFLOW.md.template +56 -0
  27. package/docs/ai/LESSONS.md.template +24 -0
  28. package/docs/ai/PRINCIPLES.md +25 -0
  29. package/docs/ai/RULES.yaml +33 -0
  30. package/i18n/es/README.md +15 -0
  31. package/i18n/es/README_FOR_AI.md +6 -0
  32. package/i18n/es/TEMPLATE.md +18 -0
  33. package/i18n/es/docs/CONTEXTO.md.template +9 -0
  34. package/i18n/es/docs/DOCUMENTATION_GUIDE.md.template +4 -0
  35. package/i18n/es/docs/GITFLOW_PR_GUIDE.md.template +4 -0
  36. package/i18n/es/docs/INDEX.md.template +10 -0
  37. package/i18n/es/docs/MOCK_DATA_GUIDE.md.template +4 -0
  38. package/i18n/es/docs/MULTI_AGENT_WORKFLOW.md.template +4 -0
  39. package/i18n/es/docs/STATUS.md.template +9 -0
  40. package/i18n/es/docs/TESTING_GUIDE_FOR_AI.md.template +7 -0
  41. package/i18n/es/docs/UI_STANDARDS.md.template +4 -0
  42. package/i18n/es/docs/WORKFLOW.md.template +6 -0
  43. package/i18n/es/docs/ai/LESSONS.md.template +3 -0
  44. package/i18n/es/docs/ai/PRINCIPLES.md +7 -0
  45. package/i18n/es/docs/ai/RULES.yaml +7 -0
  46. package/package.json +19 -0
  47. package/package.template.json +10 -0
  48. package/scripts/check-pr-readiness.sh +138 -0
  49. package/scripts/check-scope.sh +150 -0
  50. package/scripts/check-slice-readiness.sh +319 -0
  51. package/scripts/cleanup-slice.sh +177 -0
  52. package/scripts/init-docs.sh +368 -0
  53. package/scripts/migrate-project.sh +218 -0
  54. package/scripts/package-quiver.sh +124 -0
  55. package/scripts/refresh-active-slices.sh +232 -0
  56. package/scripts/release-quiver.sh +77 -0
  57. package/scripts/start-slice.sh +429 -0
  58. package/specs/[project-name]/EVIDENCE_REPORT.md.template +15 -0
  59. package/specs/[project-name]/SPEC.md.template +39 -0
  60. package/specs/[project-name]/STATUS.md.template +22 -0
  61. package/specs/[project-name]/slices/pr.md.template +97 -0
  62. package/specs/[project-name]/slices/slice-template/slice.json +69 -0
  63. package/specs/quiver-v05-readme-adoption-contract/EVIDENCE_REPORT.md +21 -0
  64. package/specs/quiver-v05-readme-adoption-contract/SPEC.md +40 -0
  65. package/specs/quiver-v05-readme-adoption-contract/STATUS.md +24 -0
  66. package/specs/quiver-v05-readme-adoption-contract/slices/slice-01-readme-adoption-contract/slice.json +68 -0
  67. package/specs/quiver-v06-release-readiness/EVIDENCE_REPORT.md +23 -0
  68. package/specs/quiver-v06-release-readiness/SPEC.md +40 -0
  69. package/specs/quiver-v06-release-readiness/STATUS.md +24 -0
  70. package/specs/quiver-v06-release-readiness/slices/slice-01-first-npm-release-readiness/slice.json +71 -0
  71. package/src/create-quiver/index.js +329 -0
@@ -0,0 +1,429 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ usage() {
6
+ cat <<'EOF'
7
+ Uso:
8
+ bash tools/scripts/start-slice.sh [--allow-draft] <ruta-al-slice.json>
9
+
10
+ Lee la metadata git del slice, valida la rama declarada y crea un worktree
11
+ afuera de la raiz trackeada del repo.
12
+
13
+ Variables opcionales:
14
+ SLICE_WORKTREES_DIR Directorio base para los worktrees.
15
+ Default: <repo-parent>/.worktrees/<repo-name>
16
+ --allow-draft Permite bootstraps intencionales de slices en draft.
17
+ EOF
18
+ }
19
+
20
+ append_unique_line() {
21
+ local file_path="$1"
22
+ local line="$2"
23
+
24
+ mkdir -p "$(dirname "$file_path")"
25
+ touch "$file_path"
26
+
27
+ if ! grep -Fxq "$line" "$file_path"; then
28
+ printf '%s\n' "$line" >> "$file_path"
29
+ fi
30
+ }
31
+
32
+ ensure_local_exclude() {
33
+ local workdir="$1"
34
+ local pattern="$2"
35
+ local exclude_path
36
+ local git_dir
37
+
38
+ git_dir="$(git -C "$workdir" rev-parse --absolute-git-dir)"
39
+ exclude_path="$git_dir/info/exclude"
40
+ append_unique_line "$exclude_path" "$pattern"
41
+ }
42
+
43
+ canonicalize_dir() {
44
+ local dir_path="$1"
45
+ (cd "$dir_path" && pwd -P)
46
+ }
47
+
48
+ resolve_base_ref() {
49
+ local base_branch="$1"
50
+
51
+ if git show-ref --verify --quiet "refs/heads/$base_branch"; then
52
+ printf '%s\n' "$base_branch"
53
+ return 0
54
+ fi
55
+
56
+ if git show-ref --verify --quiet "refs/remotes/origin/$base_branch"; then
57
+ printf '%s\n' "origin/$base_branch"
58
+ return 0
59
+ fi
60
+
61
+ if git ls-remote --exit-code --heads origin "$base_branch" >/dev/null 2>&1; then
62
+ if git fetch origin "$base_branch:refs/remotes/origin/$base_branch" >/dev/null 2>&1; then
63
+ printf '%s\n' "origin/$base_branch"
64
+ return 0
65
+ fi
66
+
67
+ echo "Error: origin existe pero no se pudo actualizar '$base_branch'. Revisa conectividad o crea la rama local '$base_branch' antes de correr start-slice.sh." >&2
68
+ return 1
69
+ fi
70
+
71
+ echo "Error: no se encontró '$base_branch' como rama local ni como ref remota 'origin/$base_branch'. Crea la rama base localmente o configura origin antes de correr start-slice.sh." >&2
72
+ return 1
73
+ }
74
+
75
+ slice_alias() {
76
+ node - "$1" <<'NODE'
77
+ const ticket = String(process.argv[2] || '').trim();
78
+ const parts = ticket.split('-').filter(Boolean);
79
+ const prefix = (parts[0] || 'GEN').replace(/[^A-Za-z0-9]/g, '').toUpperCase();
80
+ const suffix = (parts[parts.length - 1] || '00').replace(/[^A-Za-z0-9]/g, '').toUpperCase();
81
+ const short = prefix.length <= 3 ? prefix : prefix.slice(0, 3);
82
+ process.stdout.write(`${short || 'GEN'}-${suffix || '00'}`);
83
+ NODE
84
+ }
85
+
86
+ write_worktree_context() {
87
+ local target_worktree="$1"
88
+ local target_branch="$2"
89
+
90
+ if [[ "$target_worktree" != "$repo_root" ]]; then
91
+ ensure_local_exclude "$target_worktree" "WORKTREE_CONTEXT.md"
92
+ fi
93
+
94
+ node - "$slice_abs" "$target_worktree" "$target_branch" "$spec_family" "$spec_slug" <<'NODE' > "$target_worktree/WORKTREE_CONTEXT.md"
95
+ const fs = require('fs');
96
+
97
+ const [slicePath, worktreePath, branchName, specFamily, specSlug] = process.argv.slice(2);
98
+ const slice = JSON.parse(fs.readFileSync(slicePath, 'utf8'));
99
+
100
+ function toAlias(ticket) {
101
+ const parts = String(ticket || '').split('-').filter(Boolean);
102
+ const prefix = (parts[0] || 'GEN').replace(/[^A-Za-z0-9]/g, '').toUpperCase();
103
+ const suffix = (parts[parts.length - 1] || '00').replace(/[^A-Za-z0-9]/g, '').toUpperCase();
104
+ const short = prefix.length <= 3 ? prefix : prefix.slice(0, 3);
105
+ return `${short || 'GEN'}-${suffix || '00'}`;
106
+ }
107
+
108
+ function listBlock(items) {
109
+ if (!Array.isArray(items) || items.length === 0) {
110
+ return '- n/a';
111
+ }
112
+
113
+ return items.map((item) => `- ${item}`).join('\n');
114
+ }
115
+
116
+ const alias = toAlias(slice.ticket);
117
+ const status = slice.status || 'pending';
118
+ const title = slice.title || slice.slice_id;
119
+ const objective = slice.objective || 'Sin objetivo declarado.';
120
+
121
+ const lines = [
122
+ '# Worktree Context',
123
+ '',
124
+ '> Archivo generado localmente por `tools/scripts/start-slice.sh`.',
125
+ '> No se trackea en git.',
126
+ '',
127
+ `**Alias:** ${alias}`,
128
+ `**Spec:** ${specSlug}`,
129
+ `**Spec family:** ${specFamily}`,
130
+ `**Slice:** ${slice.slice_id}`,
131
+ `**Ticket:** ${slice.ticket}`,
132
+ `**Branch:** ${branchName}`,
133
+ `**Worktree:** ${worktreePath}`,
134
+ `**Status:** ${status}`,
135
+ '',
136
+ '## Title',
137
+ '',
138
+ title,
139
+ '',
140
+ '## Objective',
141
+ '',
142
+ objective,
143
+ '',
144
+ '## Routes',
145
+ '',
146
+ listBlock(slice.ui_scope?.routes),
147
+ '',
148
+ '## Components',
149
+ '',
150
+ listBlock(slice.ui_scope?.components),
151
+ '',
152
+ '## Allowed Files',
153
+ '',
154
+ listBlock(slice.files),
155
+ '',
156
+ '## Constraints',
157
+ '',
158
+ listBlock(slice.not_included),
159
+ '',
160
+ '## Expected Validation',
161
+ '',
162
+ listBlock(slice.acceptance),
163
+ ''
164
+ ];
165
+
166
+ process.stdout.write(`${lines.join('\n')}\n`);
167
+ NODE
168
+ }
169
+
170
+ refresh_active_slices_board() {
171
+ if [[ -x "$repo_root/tools/scripts/refresh-active-slices.sh" ]]; then
172
+ "$repo_root/tools/scripts/refresh-active-slices.sh" >/dev/null 2>&1 || true
173
+ fi
174
+ }
175
+
176
+ allow_draft="0"
177
+ args=()
178
+ for arg in "$@"; do
179
+ case "$arg" in
180
+ -h|--help)
181
+ usage
182
+ exit 0
183
+ ;;
184
+ --allow-draft)
185
+ allow_draft="1"
186
+ ;;
187
+ *)
188
+ args+=("$arg")
189
+ ;;
190
+ esac
191
+ done
192
+
193
+ [[ "${ALLOW_DRAFT_SLICE:-}" == "1" ]] && allow_draft="1"
194
+
195
+ if [[ ${#args[@]} -ne 1 ]]; then
196
+ usage
197
+ exit 1
198
+ fi
199
+
200
+ if ! command -v git >/dev/null 2>&1; then
201
+ echo "Error: git no esta disponible en PATH." >&2
202
+ exit 1
203
+ fi
204
+
205
+ if ! command -v node >/dev/null 2>&1; then
206
+ echo "Error: node no esta disponible en PATH." >&2
207
+ exit 1
208
+ fi
209
+
210
+ slice_input="${args[0]}"
211
+ repo_root="$(canonicalize_dir "$(git rev-parse --show-toplevel)")"
212
+
213
+ if [[ ! -f "$slice_input" ]]; then
214
+ echo "Error: no existe el slice '$slice_input'." >&2
215
+ exit 1
216
+ fi
217
+
218
+ slice_abs="$(canonicalize_dir "$(dirname "$slice_input")")/$(basename "$slice_input")"
219
+ slice_rel="${slice_abs#$repo_root/}"
220
+
221
+ case "$slice_rel" in
222
+ specs-fix/*/slices/*/slice.json)
223
+ spec_family="specs-fix"
224
+ spec_slug="$(printf '%s\n' "$slice_rel" | cut -d/ -f2)"
225
+ ;;
226
+ specs/*/slices/*/slice.json)
227
+ spec_family="specs"
228
+ spec_slug="$(printf '%s\n' "$slice_rel" | cut -d/ -f2)"
229
+ ;;
230
+ *)
231
+ echo "Error: el slice debe vivir dentro de specs/ o specs-fix/." >&2
232
+ exit 1
233
+ ;;
234
+ esac
235
+
236
+ slice_meta=()
237
+ while IFS= read -r line; do
238
+ slice_meta+=("$line")
239
+ done < <(node - "$slice_abs" <<'NODE'
240
+ const fs = require('fs');
241
+
242
+ const slicePath = process.argv[2];
243
+
244
+ function fail(message) {
245
+ console.error(`Error: ${message}`);
246
+ process.exit(1);
247
+ }
248
+
249
+ let json;
250
+ try {
251
+ json = JSON.parse(fs.readFileSync(slicePath, 'utf8'));
252
+ } catch (error) {
253
+ fail(`No se pudo parsear '${slicePath}' como JSON: ${error.message}`);
254
+ }
255
+
256
+ const ticket = typeof json.ticket === 'string' ? json.ticket.trim() : '';
257
+ const git = json.git ?? {};
258
+ const branchType = typeof git.branch_type === 'string' ? git.branch_type.trim() : '';
259
+ const baseBranch = typeof git.base_branch === 'string' ? git.base_branch.trim() : '';
260
+ const branchSlug = typeof git.branch_slug === 'string' ? git.branch_slug.trim() : '';
261
+ const branchName = typeof git.branch_name === 'string' ? git.branch_name.trim() : '';
262
+ const sliceId = typeof json.slice_id === 'string' ? json.slice_id.trim() : '';
263
+
264
+ if (!sliceId) {
265
+ fail('Falta "slice_id" en el slice.');
266
+ }
267
+
268
+ if (!ticket) {
269
+ fail('Falta "ticket" en el slice.');
270
+ }
271
+
272
+ if (!branchType || !baseBranch || !branchSlug || !branchName) {
273
+ fail('El bloque "git" debe incluir "branch_type", "base_branch", "branch_slug" y "branch_name".');
274
+ }
275
+
276
+ const expectedBaseByType = {
277
+ feature: 'develop',
278
+ bugfix: 'develop',
279
+ hotfix: 'main'
280
+ };
281
+
282
+ if (!expectedBaseByType[branchType]) {
283
+ fail(`git.branch_type invalido: "${branchType}". Usa "feature", "bugfix" o "hotfix".`);
284
+ }
285
+
286
+ const expectedBaseBranch = expectedBaseByType[branchType];
287
+ if (baseBranch !== expectedBaseBranch) {
288
+ fail(`git.base_branch invalido para ${branchType}. Esperado: "${expectedBaseBranch}".`);
289
+ }
290
+
291
+ const expectedBranchName = `${branchType}/${ticket}-${branchSlug}`;
292
+ if (branchName !== expectedBranchName) {
293
+ fail(`git.branch_name invalido. Esperado: "${expectedBranchName}".`);
294
+ }
295
+
296
+ console.log(sliceId);
297
+ console.log(ticket);
298
+ console.log(branchType);
299
+ console.log(baseBranch);
300
+ console.log(branchSlug);
301
+ console.log(branchName);
302
+ console.log(String(json.status || 'draft'));
303
+ NODE
304
+ )
305
+
306
+ if [[ ${#slice_meta[@]} -ne 7 ]]; then
307
+ echo "Error: no se pudo leer la metadata git del slice." >&2
308
+ exit 1
309
+ fi
310
+
311
+ slice_id="${slice_meta[0]}"
312
+ ticket="${slice_meta[1]}"
313
+ branch_type="${slice_meta[2]}"
314
+ base_branch="${slice_meta[3]}"
315
+ branch_slug="${slice_meta[4]}"
316
+ branch_name="${slice_meta[5]}"
317
+ slice_status="${slice_meta[6]}"
318
+
319
+ if [[ "$slice_status" == "blocked" ]]; then
320
+ echo "Error: el slice esta bloqueado (status=blocked). Resolve el bloqueante antes de iniciar." >&2
321
+ exit 1
322
+ fi
323
+
324
+ if [[ "$slice_status" == "cancelled" ]]; then
325
+ echo "Error: el slice esta cancelado (status=cancelled)." >&2
326
+ exit 1
327
+ fi
328
+
329
+ if [[ "$slice_status" == "completed" ]]; then
330
+ echo "WARN: el slice ya figura como completed. Si realmente corresponde reejecutarlo, cambia el status a in_progress."
331
+ fi
332
+
333
+ if [[ "$slice_status" == "draft" && "$allow_draft" != "1" ]]; then
334
+ echo "Error: el slice esta en estado 'draft'. Marca el slice como 'ready' o usa --allow-draft para un bootstrap intencional." >&2
335
+ exit 1
336
+ fi
337
+
338
+ if [[ "$slice_status" == "draft" ]]; then
339
+ echo "WARN: bootstrap intencional para un slice en draft."
340
+ fi
341
+
342
+ repo_name="$(basename "$repo_root")"
343
+ repo_parent="$(dirname "$repo_root")"
344
+ worktrees_root="${SLICE_WORKTREES_DIR:-$repo_parent/.worktrees/$repo_name}"
345
+ safe_branch_name="$(printf '%s' "$branch_name" | sed 's#[^A-Za-z0-9._-]#-#g')"
346
+ worktree_path="$worktrees_root/$safe_branch_name"
347
+
348
+ git worktree prune >/dev/null 2>&1 || true
349
+
350
+ existing_worktree_path="$(
351
+ node - "$branch_name" <<'NODE'
352
+ const cp = require('child_process');
353
+
354
+ const branchRef = `refs/heads/${process.argv[2]}`;
355
+ const text = cp.execSync('git worktree list --porcelain', {
356
+ encoding: 'utf8',
357
+ stdio: ['ignore', 'pipe', 'pipe']
358
+ });
359
+
360
+ let currentPath = '';
361
+ for (const line of text.trim().split('\n')) {
362
+ if (line.startsWith('worktree ')) {
363
+ currentPath = line.slice('worktree '.length);
364
+ continue;
365
+ }
366
+
367
+ if (line === `branch ${branchRef}`) {
368
+ process.stdout.write(currentPath);
369
+ break;
370
+ }
371
+ }
372
+ NODE
373
+ )"
374
+
375
+ if [[ -n "$existing_worktree_path" && ! -d "$existing_worktree_path" ]]; then
376
+ existing_worktree_path=""
377
+ fi
378
+
379
+ if [[ -n "$existing_worktree_path" ]]; then
380
+ write_worktree_context "$existing_worktree_path" "$branch_name"
381
+ refresh_active_slices_board
382
+ cat <<EOF
383
+ La rama ya tiene un worktree asociado.
384
+ Alias: $(slice_alias "$ticket")
385
+ Spec: $spec_slug
386
+ Slice: $slice_id
387
+ Ticket: $ticket
388
+ Rama: $branch_name
389
+ Base: $base_branch
390
+ Worktree: $existing_worktree_path
391
+ EOF
392
+ exit 0
393
+ fi
394
+
395
+ if [[ -e "$worktree_path" && ! -f "$worktree_path/.git" && ! -d "$worktree_path/.git" ]]; then
396
+ echo "Error: la ruta '$worktree_path' ya existe y no parece un worktree git." >&2
397
+ exit 1
398
+ fi
399
+
400
+ mkdir -p "$worktrees_root"
401
+
402
+ git fetch origin --prune >/dev/null 2>&1 || true
403
+
404
+ if git show-ref --verify --quiet "refs/heads/$branch_name"; then
405
+ git worktree add "$worktree_path" "$branch_name"
406
+ elif git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then
407
+ git fetch origin "$branch_name:$branch_name" >/dev/null 2>&1
408
+ git worktree add "$worktree_path" "$branch_name"
409
+ else
410
+ base_ref="$(resolve_base_ref "$base_branch")"
411
+ git worktree add -b "$branch_name" "$worktree_path" "$base_ref"
412
+ fi
413
+
414
+ write_worktree_context "$worktree_path" "$branch_name"
415
+ refresh_active_slices_board
416
+
417
+ cat <<EOF
418
+ Slice listo para trabajar.
419
+ Alias: $(slice_alias "$ticket")
420
+ Spec: $spec_slug
421
+ Slice: $slice_id
422
+ Ticket: $ticket
423
+ Tipo de rama: $branch_type
424
+ Base: $base_branch
425
+ Slug: $branch_slug
426
+ Rama: $branch_name
427
+ Worktree: $worktree_path
428
+ Contexto: $worktree_path/WORKTREE_CONTEXT.md
429
+ EOF
@@ -0,0 +1,15 @@
1
+ # {{PROJECT_NAME}} Evidence Report
2
+
3
+ **Spec:** [Spec name]
4
+ **Last updated:** {{FECHA}}
5
+ **Status:** In progress
6
+
7
+ ## Summary
8
+
9
+ | Slice | Acceptance criteria | Status | Evidence |
10
+ |-------|---------------------|--------|----------|
11
+ | slice-01 | [Total] | Pending | - |
12
+
13
+ ## Evidence by Slice
14
+
15
+ _No evidence recorded yet._
@@ -0,0 +1,39 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ **Date:** {{FECHA}}
4
+ **Status:** {{ESTADO}}
5
+
6
+ ## Objective
7
+
8
+ [Describe the project objective in one or two sentences.]
9
+
10
+ ## Scope
11
+
12
+ ### Included
13
+
14
+ - [Feature 1]
15
+ - [Feature 2]
16
+
17
+ ### Excluded
18
+
19
+ - [Out of scope item 1]
20
+ - [Out of scope item 2]
21
+
22
+ ## Timeline
23
+
24
+ | Phase | Description | Target date |
25
+ |-------|-------------|-------------|
26
+ | 1 | [Description] | {{FECHA}} |
27
+ | 2 | [Description] | {{FECHA}} |
28
+
29
+ ## Slices
30
+
31
+ | Slice | Title | Status | Spec |
32
+ |-------|-------|--------|------|
33
+ | 01 | [Name] | Pending | [slice-01](./slices/slice-01/slice.json) |
34
+
35
+ ## Definition of Done
36
+
37
+ - All slices are `completed`
38
+ - No slice is `blocked` or `in_progress`
39
+ - `EVIDENCE_REPORT.md` has evidence for every slice
@@ -0,0 +1,22 @@
1
+ # {{PROJECT_NAME}} Spec Status
2
+
3
+ **Spec:** [Spec name]
4
+ **Last updated:** {{FECHA}}
5
+
6
+ ## Spec Status
7
+
8
+ | Slice | Title | Status | PR | Estimated hours | Actual hours |
9
+ |-------|-------|--------|----|-----------------|--------------|
10
+ | slice-01 | [Name] | Pending | - | 0 | - |
11
+
12
+ ## Progress
13
+
14
+ - Completed slices: 0 / [total]
15
+ - Estimated hours: 0
16
+ - Actual hours: 0
17
+
18
+ ## Blockers
19
+
20
+ | Slice | Blocker | Since | Action needed |
21
+ |-------|---------|-------|---------------|
22
+ | - | - | - | - |
@@ -0,0 +1,97 @@
1
+ # PR - [TICKET] - [Slice Title]
2
+
3
+ ## Title
4
+
5
+ [Short, clear title for the change]
6
+
7
+ ## Summary
8
+
9
+ [Brief summary of what was implemented and why]
10
+
11
+ ## Scope
12
+
13
+ - [Included change 1]
14
+ - [Included change 2]
15
+ - [Relevant exclusion, if any]
16
+
17
+ ## Files
18
+
19
+ - `[file 1]`
20
+ - `[file 2]`
21
+
22
+ ## How to Test (DETAILED - REQUIRED)
23
+
24
+ ### Required Environment
25
+
26
+ - Node: [version, e.g. 20.x]
27
+ - [Additional prerequisite if any]
28
+ - [Environment variable or flag if needed]
29
+
30
+ ### Worktree Access
31
+
32
+ ```bash
33
+ # Navigate to the slice worktree if it already exists
34
+ cd ../.worktrees/[repo-name]/[sanitized-branch-name]
35
+
36
+ # Or create the worktree from scratch
37
+ npm run start:slice -- specs/{{PROJECT_SLUG}}/slices/[slice-id]/slice.json
38
+ ```
39
+
40
+ ### Run the Project
41
+
42
+ ```bash
43
+ # [Project-specific start command]
44
+ ```
45
+
46
+ ### Use Cases
47
+
48
+ #### Case 1: [Descriptive case name]
49
+
50
+ **Prerequisite:** [required initial state]
51
+
52
+ 1. [Exact step 1]
53
+ 2. [Exact step 2]
54
+ 3. [Exact step 3]
55
+
56
+ **Expected result:** [what should happen]
57
+
58
+ ---
59
+
60
+ #### Case 2: [Descriptive case name]
61
+
62
+ **Prerequisite:** [required initial state]
63
+
64
+ 1. [Exact step 1]
65
+ 2. [Exact step 2]
66
+
67
+ **Expected result:** [what should happen]
68
+
69
+ ---
70
+
71
+ ### Technical Verification
72
+
73
+ ```bash
74
+ # Slice validation gate
75
+ npm run check:slice -- specs/{{PROJECT_SLUG}}/slices/[slice-id]/slice.json --gate validation
76
+
77
+ # PR gate
78
+ npm run check:pr -- specs/{{PROJECT_SLUG}}/slices/[slice-id]/slice.json
79
+
80
+ # Whitespace and conflicts
81
+ git diff --check
82
+ ```
83
+
84
+ ## Evidence
85
+
86
+ - [Verifiable signal 1]
87
+ - [Verifiable signal 2]
88
+
89
+ ## Rollback
90
+
91
+ 1. `git revert [commit-hash]`
92
+ 2. [Validate the rollback]
93
+ 3. [Document any follow-up if needed]
94
+
95
+ ## Risks / Notes
96
+
97
+ - [Risk, limitation, or follow-up]
@@ -0,0 +1,69 @@
1
+ {
2
+ "slice_id": "slice-XX-[name]",
3
+ "ticket": "ABC-123",
4
+ "type": "feature",
5
+ "title": "[Slice title]",
6
+ "objective": "[One or two sentences describing what will be implemented.]",
7
+ "description": "[More detail about the slice and why it is needed.]",
8
+ "git": {
9
+ "branch_type": "feature",
10
+ "base_branch": "develop",
11
+ "branch_slug": "[kebab-case-slug]",
12
+ "branch_name": "feature/ABC-123-[kebab-case-slug]"
13
+ },
14
+ "ui_scope": {
15
+ "routes": [
16
+ "/route-1",
17
+ "/route-2"
18
+ ],
19
+ "components": [
20
+ "Component1",
21
+ "Component2"
22
+ ],
23
+ "layouts": [
24
+ "Layout1",
25
+ "Layout2"
26
+ ]
27
+ },
28
+ "must": [
29
+ "[Measurable and verifiable requirement 1]",
30
+ "[Measurable and verifiable requirement 2]",
31
+ "[Measurable and verifiable requirement 3]"
32
+ ],
33
+ "not_included": [
34
+ "[What is not included in this slice]",
35
+ "[What belongs to another slice]"
36
+ ],
37
+ "acceptance": [
38
+ "[Acceptance criterion 1]",
39
+ "[Acceptance criterion 2]",
40
+ "[Acceptance criterion 3]"
41
+ ],
42
+ "files": [
43
+ "app/path/file.ts",
44
+ "lib/module/file.ts",
45
+ "components/Component.tsx"
46
+ ],
47
+ "tests": [
48
+ "tests/e2e/module/test.spec.ts",
49
+ "tests/unit/module/test.test.ts"
50
+ ],
51
+ "documentation": [
52
+ "docs/api/module/README.md",
53
+ "docs/STATUS.md",
54
+ "specs/{{PROJECT_SLUG}}/slices/[slice-id]/pr.md"
55
+ ],
56
+ "dependencies": [
57
+ "slice-XX-previous (if any)"
58
+ ],
59
+ "assumptions": [
60
+ "[Technical or business assumption]"
61
+ ],
62
+ "estimated_hours": 0,
63
+ "actual_hours": 0,
64
+ "status": "draft",
65
+ "blocked_reason": null,
66
+ "ready_at": null,
67
+ "started_at": null,
68
+ "completed_at": null
69
+ }
@@ -0,0 +1,21 @@
1
+ # Quiver v0.5 Evidence Report
2
+
3
+ **Spec:** quiver-v05-readme-adoption-contract
4
+ **Last updated:** 2026-04-21
5
+ **Status:** Completed
6
+
7
+ ## Summary
8
+
9
+ | Slice | Acceptance criteria | Status | Evidence |
10
+ |-------|---------------------|--------|----------|
11
+ | slice-01 | 6 | Completed | README commands and links were checked against the current CLI, package scripts, and repository paths |
12
+
13
+ ## Evidence by Slice
14
+
15
+ - `node bin/create-quiver.js --help`
16
+ - `bash scripts/release-quiver.sh --help`
17
+ - `node -e "const pkg=require('./package.json'); for (const name of ['package:quiver','smoke:create-quiver','release:quiver']) if (!pkg.scripts?.[name]) process.exit(1)"`
18
+ - `test -f README_FOR_AI.md`
19
+ - `test -f docs/SUPPORT_MATRIX.md.template`
20
+ - `test -f docs/TROUBLESHOOTING.md.template`
21
+ - `git diff --check`