Skip to content

Commit 070ffd9

Browse files
committed
Specs: add READMEs for flat-structure-migration and template-variable-sync
Gantt: use calendar emoji for planned status; skip empty priority groups earlier and simplify spec rendering loop
1 parent 7725588 commit 070ffd9

File tree

3 files changed

+288
-6
lines changed

3 files changed

+288
-6
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
status: planned
3+
created: '2025-11-03'
4+
tags: [structure, migration, breaking-change, enhancement]
5+
priority: high
6+
---
7+
8+
# Flat Structure Migration
9+
10+
> **Status**: 📅 Planned · **Priority**: High · **Created**: 2025-11-03
11+
12+
**Project**: lean-spec
13+
**Team**: Core Development
14+
15+
## Overview
16+
17+
Migrate the default folder structure from date-based grouping (`{date}/{seq}-{name}/`) to a flat structure (`{seq}-{name}/`). This simplifies the spec organization for most projects while maintaining date-based grouping as an optional pattern for those who need it.
18+
19+
**Why now?**
20+
- Current date-based folders add unnecessary complexity for small/medium projects
21+
- Most users don't need date-based organization
22+
- Flat structure is simpler to navigate and reference
23+
- Other patterns (custom grouping) already available via config
24+
25+
**Current structure**: `specs/20251103/024-flat-structure-migration/`
26+
**Target structure**: `specs/024-flat-structure-migration/`
27+
28+
## Design
29+
30+
### Configuration Changes
31+
32+
**Default config becomes:**
33+
```json
34+
{
35+
"structure": {
36+
"pattern": "flat",
37+
"prefix": "", // No prefix by default
38+
"sequenceDigits": 3,
39+
"defaultFile": "README.md"
40+
}
41+
}
42+
```
43+
44+
**Migration paths:**
45+
1. **New projects** - Use flat structure by default
46+
2. **Existing projects** - Keep current structure, provide migration guide
47+
3. **Date grouping users** - Can opt-in via config:
48+
```json
49+
{
50+
"structure": {
51+
"pattern": "custom",
52+
"groupExtractor": "{YYYYMMDD}"
53+
}
54+
}
55+
```
56+
57+
### Code Changes
58+
59+
1. **Update `DEFAULT_CONFIG` in `src/config.ts`**:
60+
- Change `pattern: 'flat'` (already correct)
61+
- Remove `prefix: '{YYYYMMDD}-'` (set to empty string by default)
62+
63+
2. **Update `lspec init`**:
64+
- New projects get flat structure
65+
- Remove date folder creation from init command
66+
67+
3. **Update docs and templates**:
68+
- README examples show flat structure
69+
- AGENTS.md updated with new default
70+
- Migration guide for existing projects
71+
72+
4. **Spec loader compatibility**:
73+
- Already supports both patterns (tested)
74+
- No breaking changes to loader logic
75+
76+
### Migration Guide for Existing Projects
77+
78+
For users currently on date-based structure who want to migrate:
79+
80+
**Option 1: Keep current structure** (recommended for active projects)
81+
```json
82+
// .lspec/config.json
83+
{
84+
"structure": {
85+
"pattern": "custom",
86+
"groupExtractor": "{YYYYMMDD}"
87+
}
88+
}
89+
```
90+
91+
**Option 2: Migrate to flat**
92+
```bash
93+
# Flatten existing specs
94+
for dir in specs/*/; do
95+
mv "$dir"*/ specs/
96+
done
97+
rmdir specs/202*
98+
99+
# Update config to flat
100+
lspec init --pattern flat --force
101+
```
102+
103+
## Plan
104+
105+
- [ ] Update `DEFAULT_CONFIG` in `src/config.ts` - remove date prefix
106+
- [ ] Update `lspec init` to use flat structure by default
107+
- [ ] Create migration guide document
108+
- [ ] Update README.md with flat structure examples
109+
- [ ] Update AGENTS.md with new default structure
110+
- [ ] Update documentation website
111+
- [ ] Add migration notice to CHANGELOG.md
112+
- [ ] Test new project creation
113+
- [ ] Test existing project compatibility
114+
- [ ] Verify spec loading works for both patterns
115+
116+
## Test
117+
118+
### New Projects
119+
- [ ] `lspec init` creates `specs/` (no date folder)
120+
- [ ] `lspec create test` creates `specs/001-test/`
121+
- [ ] Next spec is `specs/002-another/`
122+
123+
### Existing Projects
124+
- [ ] Projects with date folders continue working
125+
- [ ] Config with `custom` pattern and `{YYYYMMDD}` extractor works
126+
- [ ] `lspec list`, `lspec stats`, etc. work with both structures
127+
128+
### Migration
129+
- [ ] Manual migration steps documented and tested
130+
- [ ] Config update preserves custom fields
131+
- [ ] No data loss during migration
132+
133+
## Notes
134+
135+
**Breaking change**: New projects will have different folder structure than examples in current docs. This is acceptable because:
136+
- Simpler default is better for onboarding
137+
- Date-based grouping still available via config
138+
- Migration path exists for those who want to switch
139+
140+
**Backwards compatibility**: Existing projects continue working without changes. Spec loader already handles both patterns.
141+
142+
**Timeline**: Can be implemented quickly since most infrastructure already exists (flat pattern support is already built-in).
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
status: planned
3+
created: '2025-11-03'
4+
tags: [bug, templates, frontmatter, enhancement]
5+
priority: high
6+
---
7+
8+
# Template Variable Synchronization
9+
10+
> **Status**: 📅 Planned · **Priority**: Medium · **Created**: 2025-11-03
11+
12+
**Project**: lean-spec
13+
**Team**: Core Development
14+
15+
## Overview
16+
17+
When creating a spec with `--field priority=high`, the frontmatter is correctly updated but the template body still shows hardcoded values. For example:
18+
19+
```markdown
20+
---
21+
priority: high # ✓ Correct (from --field)
22+
---
23+
24+
> **Priority**: Medium # ✗ Wrong (hardcoded in template)
25+
```
26+
27+
**Why this matters:**
28+
- Inconsistent data between frontmatter and body content
29+
- Users expect `--field` values to populate throughout the spec
30+
- Manual editing required to fix mismatches
31+
- Affects `priority`, `status`, and potentially other fields
32+
33+
**Root cause:** Templates have hardcoded values in body content instead of using variable placeholders like `{priority}`, `{status}`, etc.
34+
35+
## Design
36+
37+
### Solution 1: Add Frontmatter Variables to Variable Resolver
38+
39+
Extend `variable-resolver.ts` to support frontmatter field variables:
40+
41+
```typescript
42+
export interface VariableContext {
43+
name?: string;
44+
date?: string;
45+
projectName?: string;
46+
gitInfo?: GitInfo;
47+
customVariables?: Record<string, string>;
48+
frontmatter?: Record<string, unknown>; // NEW
49+
}
50+
```
51+
52+
**Variable resolution flow:**
53+
1. Parse template to extract frontmatter
54+
2. Merge frontmatter fields into variable context
55+
3. Resolve variables in body (including `{priority}`, `{status}`, etc.)
56+
4. Support nested fields like `{tags}` (array → comma-separated string)
57+
58+
**Template update:**
59+
```markdown
60+
---
61+
status: planned
62+
priority: medium
63+
tags: []
64+
---
65+
66+
# {name}
67+
68+
> **Status**: {status} · **Priority**: {priority} · **Created**: {date}
69+
```
70+
71+
### Solution 2: Post-Process After Frontmatter Update
72+
73+
Alternative approach - update body content after frontmatter is modified:
74+
75+
```typescript
76+
// In create.ts, after updating frontmatter:
77+
if (options.priority) {
78+
parsed.data.priority = options.priority;
79+
// Also update body content
80+
content = content.replace(/Priority[:\s]+\w+/gi, `Priority: ${capitalize(options.priority)}`);
81+
}
82+
```
83+
84+
**Pros**: Simple, no template changes needed
85+
**Cons**: Fragile (regex-based), doesn't handle all cases
86+
87+
### Recommended: Solution 1
88+
89+
More robust, consistent with existing variable system, enables richer templates.
90+
91+
## Plan
92+
93+
- [ ] Extend `VariableContext` to include frontmatter fields
94+
- [ ] Update `buildVariableContext()` to parse frontmatter from template
95+
- [ ] Update `resolveVariables()` to handle frontmatter field variables
96+
- [ ] Add special handling for arrays (e.g., `{tags}` → comma-separated)
97+
- [ ] Add special handling for status/priority display formatting
98+
- [ ] Update all templates to use variables instead of hardcoded values
99+
- [ ] `templates/standard/spec-template.md`
100+
- [ ] `templates/minimal/spec-template.md`
101+
- [ ] `templates/enterprise/spec-template.md`
102+
- [ ] `.lspec/templates/spec-template.md` (if exists)
103+
- [ ] Update docs to document available frontmatter variables
104+
- [ ] Add tests for frontmatter variable resolution
105+
- [ ] Test with various field types (string, number, boolean, array)
106+
107+
## Test
108+
109+
### Variable Resolution
110+
- [ ] `{priority}` resolves to frontmatter priority value
111+
- [ ] `{status}` resolves to frontmatter status value
112+
- [ ] `{tags}` resolves to comma-separated tag list
113+
- [ ] Custom fields like `{epic}` resolve correctly
114+
- [ ] Missing fields don't break template (empty string or default)
115+
116+
### Integration
117+
- [ ] `lspec create test --field priority=high` shows "Priority: high" in body
118+
- [ ] `lspec create test --field status=in-progress` shows correct status
119+
- [ ] Array fields like tags display correctly
120+
- [ ] Existing specs without variables still work
121+
122+
### Edge Cases
123+
- [ ] Boolean fields (true/false) resolve as strings
124+
- [ ] Number fields resolve as strings
125+
- [ ] Nested objects are handled gracefully
126+
- [ ] Variables in frontmatter values don't cause recursion
127+
128+
## Notes
129+
130+
**Complexity**: Medium - requires careful handling of frontmatter parsing order
131+
132+
**Workaround (current)**: Users can manually edit the body after creation, or update templates locally to match their needs
133+
134+
**Alternative considered**: Use a more sophisticated template engine (Handlebars, Nunjucks), but that adds dependencies and complexity for a simple use case
135+
136+
**Timeline**: Should be implemented before v1.0 release to avoid breaking template changes later

src/commands/gantt.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const FILLED_BAR_CHAR = '█';
1616
const EMPTY_BAR_CHAR = '░';
1717

1818
const STATUS_CONFIG: Record<SpecStatus, { emoji: string; color: string }> = {
19-
planned: { emoji: '📋', color: 'gray' },
19+
planned: { emoji: '📅', color: 'gray' },
2020
'in-progress': { emoji: '⚡', color: 'yellow' },
2121
complete: { emoji: '✅', color: 'green' },
2222
archived: { emoji: '📦', color: 'gray' },
@@ -159,16 +159,20 @@ export async function ganttCommand(options: {
159159

160160
for (const priority of priorities) {
161161
const specsInGroup = sortSpecs(groupedSpecs[priority]);
162+
163+
// Skip empty priority groups
164+
if (specsInGroup.length === 0) {
165+
continue;
166+
}
167+
162168
const config = PRIORITY_CONFIG[priority];
163169

164-
// Always show priority header with count
170+
// Show priority header with count
165171
console.log(config.colorFn(`${config.emoji} ${config.label} (${specsInGroup.length})`));
166172

167173
// Display specs in this priority group
168-
if (specsInGroup.length > 0) {
169-
for (const spec of specsInGroup) {
170-
renderSpecRow(spec, startDate, endDate, weeks, today);
171-
}
174+
for (const spec of specsInGroup) {
175+
renderSpecRow(spec, startDate, endDate, weeks, today);
172176
}
173177

174178
console.log('');

0 commit comments

Comments
 (0)