@leeovery/claude-technical-workflows 2.0.44 → 2.0.45
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/commands/workflow/start-specification.md +54 -9
- package/package.json +1 -1
- package/scripts/discovery-for-specification.sh +62 -8
- package/scripts/migrate.sh +3 -1
- package/scripts/migrations/003-planning-frontmatter.sh +5 -3
- package/scripts/migrations/004-sources-object-format.sh +204 -0
- package/skills/technical-specification/references/specification-guide.md +44 -0
|
@@ -388,8 +388,41 @@ Present the groupings with FULL status information.
|
|
|
388
388
|
|
|
389
389
|
For each grouping, show:
|
|
390
390
|
- The grouping name
|
|
391
|
-
- Whether a specification already exists for this grouping
|
|
392
|
-
- Each discussion in the grouping and
|
|
391
|
+
- Whether a specification already exists for this grouping (and its effective status)
|
|
392
|
+
- Each discussion in the grouping and its incorporation status
|
|
393
|
+
|
|
394
|
+
### Determining Discussion Status Within a Grouping
|
|
395
|
+
|
|
396
|
+
For each grouping, first check if a grouped specification exists:
|
|
397
|
+
1. Convert the grouping name to kebab-case (lowercase, spaces to hyphens)
|
|
398
|
+
2. Check if `docs/workflow/specification/{kebab-name}.md` exists
|
|
399
|
+
3. If it exists, get its `sources` array from the discovery output
|
|
400
|
+
|
|
401
|
+
The sources array uses object format with explicit status tracking:
|
|
402
|
+
```yaml
|
|
403
|
+
sources:
|
|
404
|
+
- name: topic-a
|
|
405
|
+
status: incorporated
|
|
406
|
+
- name: topic-b
|
|
407
|
+
status: pending
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
**If a grouped spec exists for the grouping:**
|
|
411
|
+
|
|
412
|
+
For each discussion in the grouping:
|
|
413
|
+
1. Look up the discussion in the spec's `sources` array (by `name` field)
|
|
414
|
+
2. If found → use the source's `status` field (`incorporated` or `pending`)
|
|
415
|
+
3. If NOT found → status is `"pending"` (new source not yet added to spec)
|
|
416
|
+
|
|
417
|
+
Calculate the **effective spec status**:
|
|
418
|
+
- If ALL discussions in the grouping have `status: incorporated` → use the spec's actual status from file
|
|
419
|
+
- If ANY discussion has `status: pending` OR is not in sources → effective status is `"needs update"`
|
|
420
|
+
|
|
421
|
+
**If NO grouped spec exists:**
|
|
422
|
+
|
|
423
|
+
For each discussion in the grouping:
|
|
424
|
+
- If the discussion has an individual spec (`has_individual_spec: true`) → status is `"spec: {spec_status}"`
|
|
425
|
+
- If the discussion has no spec → status is `"ready"`
|
|
393
426
|
|
|
394
427
|
**Format:**
|
|
395
428
|
|
|
@@ -398,19 +431,20 @@ For each grouping, show:
|
|
|
398
431
|
|
|
399
432
|
Recommended Groupings:
|
|
400
433
|
|
|
401
|
-
### 1. {Grouping Name}
|
|
434
|
+
### 1. {Grouping Name} (spec: {effective_status})
|
|
402
435
|
| Discussion | Status |
|
|
403
436
|
|------------|--------|
|
|
404
|
-
| {topic-a} |
|
|
405
|
-
| {topic-b} |
|
|
406
|
-
| {topic-c} |
|
|
437
|
+
| {topic-a} | incorporated |
|
|
438
|
+
| {topic-b} | incorporated |
|
|
439
|
+
| {topic-c} | pending |
|
|
407
440
|
|
|
408
441
|
Coupling: {explanation}
|
|
409
442
|
|
|
410
443
|
### 2. {Another Grouping}
|
|
411
444
|
| Discussion | Status |
|
|
412
445
|
|------------|--------|
|
|
413
|
-
| {topic-d} |
|
|
446
|
+
| {topic-d} | ready |
|
|
447
|
+
| {topic-e} | spec: in-progress |
|
|
414
448
|
|
|
415
449
|
Coupling: {explanation}
|
|
416
450
|
|
|
@@ -429,6 +463,13 @@ How would you like to proceed?
|
|
|
429
463
|
(Enter 'refresh' to re-analyze)
|
|
430
464
|
```
|
|
431
465
|
|
|
466
|
+
**Status Legend:**
|
|
467
|
+
- `incorporated` - Discussion content has been woven into the grouped specification
|
|
468
|
+
- `pending` - Discussion is part of the group but not yet incorporated into the spec
|
|
469
|
+
- `ready` - Discussion has no spec yet (ready to be specified)
|
|
470
|
+
- `spec: {status}` - Discussion has its own individual specification
|
|
471
|
+
- `needs update` - Grouped spec exists but has pending sources to incorporate
|
|
472
|
+
|
|
432
473
|
**STOP.** Wait for user to choose.
|
|
433
474
|
|
|
434
475
|
→ Based on choice, proceed to **Step 8**.
|
|
@@ -636,7 +677,9 @@ Proceed? (y/n)
|
|
|
636
677
|
```
|
|
637
678
|
{Creating / Continuing} specification: {topic}
|
|
638
679
|
|
|
639
|
-
|
|
680
|
+
Sources:
|
|
681
|
+
- docs/workflow/discussion/{topic}.md
|
|
682
|
+
|
|
640
683
|
Output: docs/workflow/specification/{topic}.md
|
|
641
684
|
|
|
642
685
|
Proceed? (y/n)
|
|
@@ -680,7 +723,9 @@ Invoke the [technical-specification](../../skills/technical-specification/SKILL.
|
|
|
680
723
|
```
|
|
681
724
|
Specification session for: {topic}
|
|
682
725
|
|
|
683
|
-
|
|
726
|
+
Sources:
|
|
727
|
+
- docs/workflow/discussion/{topic}.md
|
|
728
|
+
|
|
684
729
|
Output: docs/workflow/specification/{topic}.md
|
|
685
730
|
|
|
686
731
|
Additional context: {summary of user's answers from Step 10}
|
package/package.json
CHANGED
|
@@ -35,9 +35,8 @@ extract_array_field() {
|
|
|
35
35
|
local file="$1"
|
|
36
36
|
local field="$2"
|
|
37
37
|
local result
|
|
38
|
-
# Look for field followed by array items (- item),
|
|
39
|
-
result=$(
|
|
40
|
-
grep -v "^---$" | \
|
|
38
|
+
# Look for field followed by array items (- item), within frontmatter only
|
|
39
|
+
result=$(awk 'BEGIN{c=0} /^---$/{c++; if(c==2) exit; next} c==1{print}' "$file" 2>/dev/null | \
|
|
41
40
|
sed -n "/^${field}:/,/^[a-z_]*:/p" | \
|
|
42
41
|
grep "^[[:space:]]*-" | \
|
|
43
42
|
sed 's/^[[:space:]]*-[[:space:]]*//' | \
|
|
@@ -46,6 +45,62 @@ extract_array_field() {
|
|
|
46
45
|
echo "$result"
|
|
47
46
|
}
|
|
48
47
|
|
|
48
|
+
# Helper: Extract sources with status from object format
|
|
49
|
+
# Outputs YAML-formatted source entries with name and status
|
|
50
|
+
# Usage: extract_sources_with_status <file>
|
|
51
|
+
#
|
|
52
|
+
# Note: This only handles the object format. Legacy simple array format
|
|
53
|
+
# is converted by migration 004 before discovery runs.
|
|
54
|
+
extract_sources_with_status() {
|
|
55
|
+
local file="$1"
|
|
56
|
+
local in_sources=false
|
|
57
|
+
local current_name=""
|
|
58
|
+
local current_status=""
|
|
59
|
+
|
|
60
|
+
# Read frontmatter and parse sources block
|
|
61
|
+
while IFS= read -r line; do
|
|
62
|
+
# Detect start of sources block
|
|
63
|
+
if [[ "$line" =~ ^sources: ]]; then
|
|
64
|
+
in_sources=true
|
|
65
|
+
continue
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Detect end of sources block (next top-level field)
|
|
69
|
+
if $in_sources && [[ "$line" =~ ^[a-z_]+: ]] && [[ ! "$line" =~ ^[[:space:]] ]]; then
|
|
70
|
+
# Output last source if pending
|
|
71
|
+
if [ -n "$current_name" ]; then
|
|
72
|
+
echo " - name: \"$current_name\""
|
|
73
|
+
echo " status: \"${current_status:-incorporated}\""
|
|
74
|
+
fi
|
|
75
|
+
break
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if $in_sources; then
|
|
79
|
+
# Object format: " - name: value"
|
|
80
|
+
if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*name:[[:space:]]*(.+)$ ]]; then
|
|
81
|
+
# Output previous source if exists
|
|
82
|
+
if [ -n "$current_name" ]; then
|
|
83
|
+
echo " - name: \"$current_name\""
|
|
84
|
+
echo " status: \"${current_status:-incorporated}\""
|
|
85
|
+
fi
|
|
86
|
+
current_name="${BASH_REMATCH[1]}"
|
|
87
|
+
current_name=$(echo "$current_name" | sed 's/^"//' | sed 's/"$//' | xargs)
|
|
88
|
+
current_status=""
|
|
89
|
+
# Status line: " status: value"
|
|
90
|
+
elif [[ "$line" =~ ^[[:space:]]*status:[[:space:]]*(.+)$ ]]; then
|
|
91
|
+
current_status="${BASH_REMATCH[1]}"
|
|
92
|
+
current_status=$(echo "$current_status" | sed 's/^"//' | sed 's/"$//' | xargs)
|
|
93
|
+
fi
|
|
94
|
+
fi
|
|
95
|
+
done < <(awk 'BEGIN{c=0} /^---$/{c++; if(c==2) exit; next} c==1{print}' "$file" 2>/dev/null)
|
|
96
|
+
|
|
97
|
+
# Output last source if pending (end of frontmatter)
|
|
98
|
+
if [ -n "$current_name" ]; then
|
|
99
|
+
echo " - name: \"$current_name\""
|
|
100
|
+
echo " status: \"${current_status:-incorporated}\""
|
|
101
|
+
fi
|
|
102
|
+
}
|
|
103
|
+
|
|
49
104
|
# Start YAML output
|
|
50
105
|
echo "# Specification Command State Discovery"
|
|
51
106
|
echo "# Generated: $(date -Iseconds)"
|
|
@@ -101,7 +156,6 @@ if [ -d "$SPEC_DIR" ] && [ -n "$(ls -A "$SPEC_DIR" 2>/dev/null)" ]; then
|
|
|
101
156
|
status=${status:-"active"}
|
|
102
157
|
|
|
103
158
|
superseded_by=$(extract_field "$file" "superseded_by")
|
|
104
|
-
sources=$(extract_array_field "$file" "sources")
|
|
105
159
|
|
|
106
160
|
echo " - name: \"$name\""
|
|
107
161
|
echo " status: \"$status\""
|
|
@@ -110,11 +164,11 @@ if [ -d "$SPEC_DIR" ] && [ -n "$(ls -A "$SPEC_DIR" 2>/dev/null)" ]; then
|
|
|
110
164
|
echo " superseded_by: \"$superseded_by\""
|
|
111
165
|
fi
|
|
112
166
|
|
|
113
|
-
|
|
167
|
+
# Extract sources with status (handles both old and new format)
|
|
168
|
+
sources_output=$(extract_sources_with_status "$file")
|
|
169
|
+
if [ -n "$sources_output" ]; then
|
|
114
170
|
echo " sources:"
|
|
115
|
-
|
|
116
|
-
echo " - \"$src\""
|
|
117
|
-
done
|
|
171
|
+
echo "$sources_output"
|
|
118
172
|
fi
|
|
119
173
|
done
|
|
120
174
|
else
|
package/scripts/migrate.sh
CHANGED
|
@@ -116,8 +116,10 @@ for script in "${MIGRATION_SCRIPTS[@]}"; do
|
|
|
116
116
|
MIGRATIONS_RUN=$((MIGRATIONS_RUN + 1))
|
|
117
117
|
done
|
|
118
118
|
|
|
119
|
-
#
|
|
119
|
+
# Report results
|
|
120
120
|
if [ "$FILES_UPDATED" -gt 0 ]; then
|
|
121
121
|
echo ""
|
|
122
122
|
echo "$FILES_UPDATED file(s) migrated. Review with \`git diff\`, then proceed."
|
|
123
|
+
else
|
|
124
|
+
echo "[SKIP] No changes needed"
|
|
123
125
|
fi
|
|
@@ -84,16 +84,18 @@ for file in "$PLAN_DIR"/*.md; do
|
|
|
84
84
|
topic_kebab=$(basename "$file" .md)
|
|
85
85
|
|
|
86
86
|
# Extract format from existing frontmatter (if present)
|
|
87
|
-
|
|
87
|
+
# Use awk to extract only the first frontmatter block (between first pair of --- delimiters)
|
|
88
|
+
# This avoids matching --- horizontal rules in body content
|
|
89
|
+
format_value=$(awk 'BEGIN{c=0} /^---$/{c++; if(c==2) exit; next} c==1{print}' "$file" 2>/dev/null | grep "^format:" | sed 's/^format:[[:space:]]*//' | xargs || echo "")
|
|
88
90
|
if [ -z "$format_value" ]; then
|
|
89
91
|
format_value="MISSING" # No default - missing format is an error
|
|
90
92
|
fi
|
|
91
93
|
|
|
92
94
|
# Extract plan_id from existing frontmatter - could be 'epic' (beads) or 'project' (linear/backlog)
|
|
93
95
|
# These are migrated to a unified 'plan_id' field
|
|
94
|
-
plan_id_value=$(
|
|
96
|
+
plan_id_value=$(awk 'BEGIN{c=0} /^---$/{c++; if(c==2) exit; next} c==1{print}' "$file" 2>/dev/null | grep "^epic:" | sed 's/^epic:[[:space:]]*//' | xargs || echo "")
|
|
95
97
|
if [ -z "$plan_id_value" ]; then
|
|
96
|
-
plan_id_value=$(
|
|
98
|
+
plan_id_value=$(awk 'BEGIN{c=0} /^---$/{c++; if(c==2) exit; next} c==1{print}' "$file" 2>/dev/null | grep "^project:" | sed 's/^project:[[:space:]]*//' | xargs || echo "")
|
|
97
99
|
fi
|
|
98
100
|
|
|
99
101
|
# Extract status from **Status**: Value
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# 004-sources-object-format.sh
|
|
4
|
+
#
|
|
5
|
+
# Migrates specification sources from simple array format to object format
|
|
6
|
+
# with status tracking. Also ensures all specs have a sources field.
|
|
7
|
+
#
|
|
8
|
+
# Previous format (from 002-specification-frontmatter.sh):
|
|
9
|
+
# sources:
|
|
10
|
+
# - topic-a
|
|
11
|
+
# - topic-b
|
|
12
|
+
#
|
|
13
|
+
# New format:
|
|
14
|
+
# sources:
|
|
15
|
+
# - name: topic-a
|
|
16
|
+
# status: incorporated
|
|
17
|
+
# - name: topic-b
|
|
18
|
+
# status: incorporated
|
|
19
|
+
#
|
|
20
|
+
# Status values:
|
|
21
|
+
# - pending: Source selected but content not yet extracted
|
|
22
|
+
# - incorporated: Source content has been fully woven into the specification
|
|
23
|
+
#
|
|
24
|
+
# For existing sources, we assume "incorporated" since they were part of
|
|
25
|
+
# the specification when it was created/worked on.
|
|
26
|
+
#
|
|
27
|
+
# For specs WITHOUT a sources field:
|
|
28
|
+
# - If a matching discussion exists (same filename), add it as incorporated
|
|
29
|
+
# - If no matching discussion, add empty sources: [] and report for user review
|
|
30
|
+
#
|
|
31
|
+
# This script is sourced by migrate.sh and has access to:
|
|
32
|
+
# - is_migrated "filepath" "migration_id"
|
|
33
|
+
# - record_migration "filepath" "migration_id"
|
|
34
|
+
# - report_update "filepath" "description"
|
|
35
|
+
# - report_skip "filepath"
|
|
36
|
+
#
|
|
37
|
+
|
|
38
|
+
MIGRATION_ID="004"
|
|
39
|
+
SPEC_DIR="docs/workflow/specification"
|
|
40
|
+
DISCUSSION_DIR="docs/workflow/discussion"
|
|
41
|
+
|
|
42
|
+
# Skip if no specification directory
|
|
43
|
+
if [ ! -d "$SPEC_DIR" ]; then
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Helper: Extract ONLY the frontmatter content (between first pair of --- delimiters)
|
|
48
|
+
# Documents may contain --- elsewhere (horizontal rules), so sed range matching
|
|
49
|
+
# can return content beyond frontmatter. Use awk for precise first-block extraction.
|
|
50
|
+
extract_frontmatter() {
|
|
51
|
+
local file="$1"
|
|
52
|
+
awk 'BEGIN{c=0} /^---$/{c++; if(c==2) exit; next} c==1{print}' "$file" 2>/dev/null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Helper: Check if sources are already in object format
|
|
56
|
+
# Returns 0 if already migrated (has "name:" entries), 1 if not
|
|
57
|
+
sources_already_object_format() {
|
|
58
|
+
local file="$1"
|
|
59
|
+
# Look for "- name:" pattern within the sources block (frontmatter only)
|
|
60
|
+
# This indicates the new object format
|
|
61
|
+
# Using subshell with || false to ensure proper exit code without pipefail issues
|
|
62
|
+
( extract_frontmatter "$file" | \
|
|
63
|
+
sed -n '/^sources:/,/^[a-z_]*:/p' | \
|
|
64
|
+
grep -q "^[[:space:]]*-[[:space:]]*name:" 2>/dev/null ) || return 1
|
|
65
|
+
return 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Helper: Extract sources array items (simple string format)
|
|
69
|
+
# Returns space-separated list of source names
|
|
70
|
+
extract_simple_sources() {
|
|
71
|
+
local file="$1"
|
|
72
|
+
# Extract sources from frontmatter only, then find the sources block
|
|
73
|
+
extract_frontmatter "$file" | \
|
|
74
|
+
sed -n '/^sources:/,/^[a-z_]*:/p' | \
|
|
75
|
+
grep -v "^sources:" | \
|
|
76
|
+
grep -v "^[a-z_]*:" | \
|
|
77
|
+
{ grep "^[[:space:]]*-[[:space:]]" || true; } | \
|
|
78
|
+
{ grep -v "name:" || true; } | \
|
|
79
|
+
sed 's/^[[:space:]]*-[[:space:]]*//' | \
|
|
80
|
+
sed 's/^"//' | \
|
|
81
|
+
sed 's/"$//' | \
|
|
82
|
+
tr '\n' ' ' | \
|
|
83
|
+
sed 's/[[:space:]]*$//' || true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Process each specification file
|
|
87
|
+
for file in "$SPEC_DIR"/*.md; do
|
|
88
|
+
[ -f "$file" ] || continue
|
|
89
|
+
|
|
90
|
+
# Check if already migrated via tracking
|
|
91
|
+
if is_migrated "$file" "$MIGRATION_ID"; then
|
|
92
|
+
report_skip "$file"
|
|
93
|
+
continue
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Check if file has YAML frontmatter
|
|
97
|
+
if ! head -1 "$file" 2>/dev/null | grep -q "^---$"; then
|
|
98
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
99
|
+
report_skip "$file"
|
|
100
|
+
continue
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
# Check if file has sources field at all
|
|
104
|
+
has_sources_field=false
|
|
105
|
+
if grep -q "^sources:" "$file" 2>/dev/null; then
|
|
106
|
+
has_sources_field=true
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# If sources field exists, check if already in object format
|
|
110
|
+
if $has_sources_field && sources_already_object_format "$file"; then
|
|
111
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
112
|
+
report_skip "$file"
|
|
113
|
+
continue
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
#
|
|
117
|
+
# Build new sources block in object format
|
|
118
|
+
#
|
|
119
|
+
new_sources_block="sources:"
|
|
120
|
+
sources_added=false
|
|
121
|
+
|
|
122
|
+
if $has_sources_field; then
|
|
123
|
+
# Extract existing sources from simple array format
|
|
124
|
+
sources=$(extract_simple_sources "$file")
|
|
125
|
+
|
|
126
|
+
for src in $sources; do
|
|
127
|
+
# Clean the source name (trim whitespace, sed avoids xargs quote issues)
|
|
128
|
+
src=$(echo "$src" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
129
|
+
if [ -n "$src" ]; then
|
|
130
|
+
new_sources_block="${new_sources_block}
|
|
131
|
+
- name: $src
|
|
132
|
+
status: incorporated"
|
|
133
|
+
sources_added=true
|
|
134
|
+
fi
|
|
135
|
+
done
|
|
136
|
+
else
|
|
137
|
+
# No sources field - check for matching discussion by filename
|
|
138
|
+
spec_name=$(basename "$file" .md)
|
|
139
|
+
discussion_file="$DISCUSSION_DIR/${spec_name}.md"
|
|
140
|
+
|
|
141
|
+
if [ -f "$discussion_file" ]; then
|
|
142
|
+
# Matching discussion found - add it as incorporated
|
|
143
|
+
new_sources_block="${new_sources_block}
|
|
144
|
+
- name: $spec_name
|
|
145
|
+
status: incorporated"
|
|
146
|
+
sources_added=true
|
|
147
|
+
fi
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# If no sources were added, use empty array format
|
|
151
|
+
if ! $sources_added; then
|
|
152
|
+
new_sources_block="sources: []"
|
|
153
|
+
# Echo info for Claude to prompt user about unmatched specs
|
|
154
|
+
spec_name=$(basename "$file" .md)
|
|
155
|
+
echo "MIGRATION_INFO: Specification '$spec_name' has no matching discussion. Sources field set to empty - please review and add sources manually."
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
#
|
|
159
|
+
# Update sources block in file
|
|
160
|
+
#
|
|
161
|
+
|
|
162
|
+
# Extract frontmatter (only the first block between --- delimiters)
|
|
163
|
+
frontmatter=$(extract_frontmatter "$file")
|
|
164
|
+
|
|
165
|
+
if $has_sources_field; then
|
|
166
|
+
# Remove old sources block from frontmatter
|
|
167
|
+
# First, remove lines from "sources:" until the next top-level field or end of frontmatter
|
|
168
|
+
new_frontmatter=$(echo "$frontmatter" | awk '
|
|
169
|
+
/^sources:/ { skip=1; next }
|
|
170
|
+
/^[a-z_]+:/ && skip { skip=0 }
|
|
171
|
+
skip == 0 { print }
|
|
172
|
+
')
|
|
173
|
+
else
|
|
174
|
+
# No existing sources field - use frontmatter as-is
|
|
175
|
+
new_frontmatter="$frontmatter"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# Add new sources block at the end
|
|
179
|
+
new_frontmatter="${new_frontmatter}
|
|
180
|
+
${new_sources_block}"
|
|
181
|
+
|
|
182
|
+
# Extract content after frontmatter (everything after the second ---)
|
|
183
|
+
# Uses awk to skip only the first two --- delimiters, preserving any --- in body content
|
|
184
|
+
content=$(awk '/^---$/ && c<2 {c++; next} c>=2 {print}' "$file")
|
|
185
|
+
|
|
186
|
+
# Write new file
|
|
187
|
+
{
|
|
188
|
+
echo "---"
|
|
189
|
+
echo "$new_frontmatter"
|
|
190
|
+
echo "---"
|
|
191
|
+
echo "$content"
|
|
192
|
+
} > "$file"
|
|
193
|
+
|
|
194
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
195
|
+
|
|
196
|
+
# Report appropriate message based on what was done
|
|
197
|
+
if $has_sources_field; then
|
|
198
|
+
report_update "$file" "converted sources to object format"
|
|
199
|
+
elif $sources_added; then
|
|
200
|
+
report_update "$file" "added sources field with matching discussion"
|
|
201
|
+
else
|
|
202
|
+
report_update "$file" "added empty sources field (no matching discussion found)"
|
|
203
|
+
fi
|
|
204
|
+
done
|
|
@@ -148,6 +148,11 @@ topic: {topic-name}
|
|
|
148
148
|
status: in-progress
|
|
149
149
|
type: feature
|
|
150
150
|
date: YYYY-MM-DD # Use today's actual date
|
|
151
|
+
sources:
|
|
152
|
+
- name: discussion-one
|
|
153
|
+
status: incorporated
|
|
154
|
+
- name: discussion-two
|
|
155
|
+
status: pending
|
|
151
156
|
---
|
|
152
157
|
|
|
153
158
|
# Specification: [Topic Name]
|
|
@@ -169,6 +174,45 @@ date: YYYY-MM-DD # Use today's actual date
|
|
|
169
174
|
- **status**: `in-progress` (building) or `concluded` (complete)
|
|
170
175
|
- **type**: `feature` (something to build) or `cross-cutting` (patterns/policies)
|
|
171
176
|
- **date**: Last updated date
|
|
177
|
+
- **sources**: Array of source discussions with incorporation status (see below)
|
|
178
|
+
|
|
179
|
+
### Sources and Incorporation Status
|
|
180
|
+
|
|
181
|
+
**All specifications must track their sources**, even when built from a single discussion. This enables proper tracking when additional discussions are later added to the same grouping.
|
|
182
|
+
|
|
183
|
+
When a specification is built from discussion(s), track each source with its incorporation status:
|
|
184
|
+
|
|
185
|
+
```yaml
|
|
186
|
+
sources:
|
|
187
|
+
- name: auth-flow
|
|
188
|
+
status: incorporated
|
|
189
|
+
- name: api-design
|
|
190
|
+
status: pending
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Status values:**
|
|
194
|
+
- `pending` - Source has been selected for this specification but content extraction is not complete
|
|
195
|
+
- `incorporated` - Source content has been fully extracted and woven into the specification
|
|
196
|
+
|
|
197
|
+
**When to update source status:**
|
|
198
|
+
|
|
199
|
+
1. **When creating the specification**: All sources start as `pending`
|
|
200
|
+
2. **After completing exhaustive extraction from a source**: Mark that source as `incorporated`
|
|
201
|
+
3. **When adding a new source to an existing spec**: Add it with `status: pending`
|
|
202
|
+
|
|
203
|
+
**How to determine if a source is incorporated:**
|
|
204
|
+
|
|
205
|
+
A source is `incorporated` when you have:
|
|
206
|
+
- Performed exhaustive extraction (reviewed ALL content in the source for relevant material)
|
|
207
|
+
- Presented and logged all relevant content from that source
|
|
208
|
+
- No more content from that source needs to be extracted
|
|
209
|
+
|
|
210
|
+
**Important**: The specification's overall `status: concluded` should only be set when:
|
|
211
|
+
- All sources are marked as `incorporated`
|
|
212
|
+
- Both review phases are complete
|
|
213
|
+
- User has signed off
|
|
214
|
+
|
|
215
|
+
If a new source is added to a concluded specification (via grouping analysis), the specification effectively needs updating - even if the file still says `status: concluded`, the presence of `pending` sources indicates work remains.
|
|
172
216
|
|
|
173
217
|
## Specification Types
|
|
174
218
|
|