Skip to content

Commit 66b72d7

Browse files
author
Marvin Zhang
committed
feat: implement archiving functionality with frontmatter updates and add frontmatter reading helper
1 parent e5486f3 commit 66b72d7

File tree

4 files changed

+58
-10
lines changed

4 files changed

+58
-10
lines changed

specs/050-tool-redesign-first-principles/README.md renamed to specs/archived/050-tool-redesign-first-principles/README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
status: planned
2+
status: archived
33
created: '2025-11-05'
44
tags:
55
- architecture
@@ -15,12 +15,15 @@ related:
1515
- 048-spec-complexity-analysis
1616
- 043-official-launch-02
1717
created_at: '2025-11-05T00:00:00Z'
18-
updated_at: '2025-11-05T05:03:54.951Z'
18+
updated_at: '2025-11-10T07:50:03.476Z'
19+
transitions:
20+
- status: archived
21+
at: '2025-11-10T07:50:03.476Z'
1922
---
2023

2124
# Tool Redesign: First Principles Application
2225

23-
> **Status**: 🗓️ Planned · **Priority**: Medium · **Created**: 2025-11-05 · **Tags**: architecture, cli, mcp, redesign, first-principles, dx, v0.3.0
26+
> **Status**: 📦 Archived · **Priority**: Medium · **Created**: 2025-11-05 · **Tags**: architecture, cli, mcp, redesign, first-principles, dx, v0.3.0
2427
2528
**Project**: lean-spec
2629
**Team**: Core Development
@@ -380,3 +383,18 @@ We'll know this succeeded when:
380383
---
381384

382385
**Remember**: We're not just building better tools. We're proving that LeanSpec philosophy creates superior user experiences when applied systematically.
386+
387+
---
388+
389+
## Archive Note
390+
391+
**Archived**: 2025-11-10
392+
393+
**Reason**: Spec was too broad and many of its proposed improvements have already been implemented incrementally through other specs:
394+
- Spec 039: simplify-viewer-commands
395+
- Spec 045: unified-dashboard
396+
- Spec 046: stats-dashboard-refactor
397+
- Spec 051: docs-system-prompt-principles
398+
- Spec 049: leanspec-first-principles
399+
400+
**Key Learning**: The 7-week comprehensive redesign plan conflicted with LeanSpec's own "Progressive Disclosure" principle. Better to make incremental improvements as pain is felt rather than big-bang redesigns. The current CLI is already well-organized with grouped commands, and the MCP server functions effectively. This spec itself demonstrates why we should practice what we preach: start simple, add complexity when needed.

src/commands.test.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
initTestProject,
99
createTestSpec,
1010
readSpecFile,
11+
readSpecFrontmatter,
1112
dirExists,
1213
getTestDate,
1314
type TestContext,
@@ -136,23 +137,29 @@ describe('archiveSpec', () => {
136137
await expect(archiveSpec(nonExistentPath)).rejects.toThrow();
137138
});
138139

139-
it('should preserve spec content when archiving', async () => {
140+
it('should update metadata when archiving', async () => {
140141
const date = getTestDate();
141142
const specName = '001-test-feature';
142-
const content = '# Test Feature\n\nTest content';
143143
const specDir = await createTestSpec(ctx.tmpDir, date, specName, {
144144
status: 'complete',
145145
created: '2024-11-01',
146-
}, content);
147-
148-
const originalContent = await readSpecFile(specDir);
146+
});
149147

150148
await archiveSpec(specDir);
151149

152150
const archivedPath = path.join(ctx.tmpDir, 'specs', 'archived', specName);
153-
const archivedContent = await readSpecFile(archivedPath);
151+
const fm = await readSpecFrontmatter(archivedPath);
154152

155-
expect(archivedContent).toBe(originalContent);
153+
// Verify status was updated to archived
154+
expect(fm.status).toBe('archived');
155+
156+
// Verify updated_at was set
157+
expect(fm.updated_at).toBeDefined();
158+
159+
// Verify transitions includes the status change
160+
expect(fm.transitions).toBeDefined();
161+
const archiveTransition = fm.transitions?.find(t => t.status === 'archived');
162+
expect(archiveTransition).toBeDefined();
156163
});
157164
});
158165

src/commands/archive.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { loadConfig } from '../config.js';
55
import { resolveSpecPath } from '../utils/path-helpers.js';
66
import { autoCheckIfEnabled } from './check.js';
77
import { sanitizeUserInput } from '../utils/ui.js';
8+
import { getSpecFile, updateFrontmatter } from '../frontmatter.js';
89

910
export async function archiveSpec(specPath: string): Promise<void> {
1011
// Auto-check for conflicts before archive
@@ -21,6 +22,12 @@ export async function archiveSpec(specPath: string): Promise<void> {
2122
throw new Error(`Spec not found: ${sanitizeUserInput(specPath)}`);
2223
}
2324

25+
// Update frontmatter to archived status before moving
26+
const specFile = await getSpecFile(resolvedPath, config.structure.defaultFile);
27+
if (specFile) {
28+
await updateFrontmatter(specFile, { status: 'archived' });
29+
}
30+
2431
// Archive to flat structure in specs/archived/ regardless of original pattern
2532
const archiveDir = path.join(specsDir, 'archived');
2633
await fs.mkdir(archiveDir, { recursive: true });

src/test-helpers.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import * as fs from 'node:fs/promises';
22
import * as path from 'node:path';
33
import * as os from 'node:os';
4+
import matter from 'gray-matter';
5+
import * as yaml from 'js-yaml';
46
import type { LeanSpecConfig } from './config.js';
7+
import type { SpecFrontmatter } from './frontmatter.js';
58

69
/**
710
* Test helpers for setting up isolated test environments
@@ -165,6 +168,19 @@ export async function readSpecFile(specDir: string): Promise<string> {
165168
return await fs.readFile(specFile, 'utf-8');
166169
}
167170

171+
/**
172+
* Read spec frontmatter
173+
*/
174+
export async function readSpecFrontmatter(specDir: string): Promise<SpecFrontmatter> {
175+
const content = await readSpecFile(specDir);
176+
const parsed = matter(content, {
177+
engines: {
178+
yaml: (str) => yaml.load(str, { schema: yaml.FAILSAFE_SCHEMA }) as Record<string, unknown>
179+
}
180+
});
181+
return parsed.data as SpecFrontmatter;
182+
}
183+
168184
/**
169185
* Check if directory exists
170186
*/

0 commit comments

Comments
 (0)