Skip to content

Commit ab47cc6

Browse files
authored
feat: restructure schemas as directories with templates (#411)
* feat: restructure schemas as directories with templates Move built-in schemas from embedded TypeScript objects to a file-based directory structure. This enables co-located templates alongside schemas. Changes: - Remove builtin-schemas.ts (replaced by file-based schemas) - Add schemas/ directory at package root with spec-driven and tdd schemas - Update resolveSchema() to load from directory structure - Resolution checks user dir → package dir * chore: archive restructure-schema-directories change * docs: update artifact_poc.md for directory-based schema structure Update documentation to reflect the new schema structure where schemas are directories containing schema.yaml and co-located templates/ rather than single .yaml files with separate template directories.
1 parent 8dcd170 commit ab47cc6

File tree

21 files changed

+434
-243
lines changed

21 files changed

+434
-243
lines changed

docs/artifact_poc.md

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ Schemas can vary across multiple dimensions:
5252
Schemas follow the XDG Base Directory Specification with a 2-level resolution:
5353

5454
```
55-
1. ${XDG_DATA_HOME}/openspec/schemas/<name>.yaml # Global user override
56-
2. <package>/schemas/<name>.yaml # Built-in defaults
55+
1. ${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml # Global user override
56+
2. <package>/schemas/<name>/schema.yaml # Built-in defaults
5757
```
5858

5959
**Platform-specific paths:**
@@ -69,25 +69,23 @@ Schemas follow the XDG Base Directory Specification with a 2-level resolution:
6969

7070
### Template Inheritance (2 Levels Max)
7171

72-
Templates also use 2-level resolution (to be implemented in Slice 3):
72+
Templates are co-located with schemas in a `templates/` subdirectory:
7373

7474
```
75-
1. ${XDG_DATA_HOME}/openspec/schemas/<schema>/templates/<artifact>.md # Schema-specific
76-
2. ${XDG_DATA_HOME}/openspec/templates/<artifact>.md # Shared
77-
3. <package>/templates/<artifact>.md # Built-in fallback
75+
1. ${XDG_DATA_HOME}/openspec/schemas/<schema>/templates/<artifact>.md # User override
76+
2. <package>/schemas/<schema>/templates/<artifact>.md # Built-in
7877
```
7978

8079
**Rules:**
81-
- Schema-specific templates override shared templates
82-
- Shared templates override package built-ins
80+
- User overrides take precedence over package built-ins
8381
- A CLI command shows resolved paths (no guessing)
8482
- No inheritance between schemas (copy if you need to diverge)
85-
- Max 2 levels - no deeper inheritance chains
83+
- Templates are always co-located with their schema
8684

8785
**Why this matters:**
8886
- Avoids "where does this come from?" debugging
8987
- No implicit magic that works until it doesn't
90-
- Clear boundaries between shared and specific
88+
- Schema + templates form a cohesive unit
9189

9290
---
9391

@@ -333,21 +331,19 @@ This separation means:
333331
### 3. XDG-Compliant Schema Resolution
334332

335333
```
336-
${XDG_DATA_HOME}/openspec/schemas/<name>.yaml # User override
334+
${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml # User override
337335
↓ (not found)
338-
<package>/schemas/<name>.yaml # Built-in
336+
<package>/schemas/<name>/schema.yaml # Built-in
339337
↓ (not found)
340338
Error (schema not found)
341339
```
342340

343-
### 4. Two-Level Template Fallback (Slice 3)
341+
### 4. Two-Level Template Fallback
344342

345343
```
346-
${XDG_DATA_HOME}/openspec/schemas/<schema>/templates/<artifact>.md # Schema-specific
344+
${XDG_DATA_HOME}/openspec/schemas/<schema>/templates/<artifact>.md # User override
347345
↓ (not found)
348-
${XDG_DATA_HOME}/openspec/templates/<artifact>.md # Shared
349-
↓ (not found)
350-
<package>/templates/<artifact>.md # Built-in
346+
<package>/schemas/<schema>/templates/<artifact>.md # Built-in
351347
↓ (not found)
352348
Error (no silent fallback to avoid confusion)
353349
```
@@ -487,21 +483,29 @@ Structured as **vertical slices** - each slice is independently testable.
487483
# Global (XDG paths - user overrides)
488484
~/.local/share/openspec/ # Unix/macOS ($XDG_DATA_HOME/openspec/)
489485
%LOCALAPPDATA%/openspec/ # Windows
490-
├── schemas/ # Schema overrides
491-
│ └── custom-workflow.yaml # User-defined schema
492-
└── templates/ # Template overrides (Slice 3)
493-
└── proposal.md # Custom proposal template
486+
└── schemas/ # Schema overrides
487+
└── custom-workflow/ # User-defined schema directory
488+
├── schema.yaml # Schema definition
489+
└── templates/ # Co-located templates
490+
└── proposal.md
494491
495492
# Package (built-in defaults)
496493
<package>/
497-
├── schemas/ # Built-in schema definitions
498-
│ ├── spec-driven.yaml # Default: proposal → specs → design → tasks
499-
│ └── tdd.yaml # TDD: tests → implementation → docs
500-
└── templates/ # Built-in templates (Slice 3)
501-
├── proposal.md
502-
├── design.md
503-
├── specs.md
504-
└── tasks.md
494+
└── schemas/ # Built-in schema definitions
495+
├── spec-driven/ # Default: proposal → specs → design → tasks
496+
│ ├── schema.yaml
497+
│ └── templates/
498+
│ ├── proposal.md
499+
│ ├── design.md
500+
│ ├── spec.md
501+
│ └── tasks.md
502+
└── tdd/ # TDD: tests → implementation → docs
503+
├── schema.yaml
504+
└── templates/
505+
├── test.md
506+
├── implementation.md
507+
├── spec.md
508+
└── docs.md
505509
506510
# Project (change instances)
507511
openspec/
@@ -528,8 +532,8 @@ openspec/
528532
## Schema YAML Format
529533

530534
```yaml
531-
# Built-in: <package>/schemas/spec-driven.yaml
532-
# Or user override: ~/.local/share/openspec/schemas/spec-driven.yaml
535+
# Built-in: <package>/schemas/spec-driven/schema.yaml
536+
# Or user override: ~/.local/share/openspec/schemas/spec-driven/schema.yaml
533537
name: spec-driven
534538
version: 1
535539
description: Specification-driven development
@@ -538,7 +542,7 @@ artifacts:
538542
- id: proposal
539543
generates: "proposal.md"
540544
description: "Create project proposal document"
541-
template: "proposal.md" # resolves via 2-level fallback (Slice 3)
545+
template: "proposal.md" # resolves from co-located templates/ directory
542546
requires: []
543547

544548
- id: specs

openspec/changes/restructure-schema-directories/design.md renamed to openspec/changes/archive/2025-12-28-restructure-schema-directories/design.md

File renamed without changes.

openspec/changes/restructure-schema-directories/proposal.md renamed to openspec/changes/archive/2025-12-28-restructure-schema-directories/proposal.md

File renamed without changes.

openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md renamed to openspec/changes/archive/2025-12-28-restructure-schema-directories/specs/artifact-graph/spec.md

File renamed without changes.

openspec/changes/restructure-schema-directories/tasks.md renamed to openspec/changes/archive/2025-12-28-restructure-schema-directories/tasks.md

File renamed without changes.

openspec/specs/artifact-graph/spec.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
TBD - created by archiving change add-artifact-graph-core. Update Purpose after archive.
55
## Requirements
66
### Requirement: Schema Loading
7-
The system SHALL load artifact graph definitions from YAML schema files.
7+
The system SHALL load artifact graph definitions from YAML schema files within schema directories.
88

99
#### Scenario: Valid schema loaded
10-
- **WHEN** a valid schema YAML file is provided
10+
- **WHEN** a schema directory contains a valid `schema.yaml` file
1111
- **THEN** the system returns an ArtifactGraph with all artifacts and dependencies
1212

1313
#### Scenario: Invalid schema rejected
@@ -26,6 +26,10 @@ The system SHALL load artifact graph definitions from YAML schema files.
2626
- **WHEN** a schema contains multiple artifacts with the same ID
2727
- **THEN** the system throws an error identifying the duplicate
2828

29+
#### Scenario: Schema directory not found
30+
- **WHEN** resolving a schema name that has no corresponding directory
31+
- **THEN** the system throws an error listing available schemas
32+
2933
### Requirement: Build Order Calculation
3034
The system SHALL compute a valid topological build order for artifacts.
3135

@@ -105,3 +109,22 @@ The system SHALL identify which artifacts are blocked and return all their unmet
105109
- **WHEN** artifact C requires A and B, and neither is complete
106110
- **THEN** getBlocked() returns `{ C: ['A', 'B'] }`
107111

112+
### Requirement: Schema Directory Structure
113+
The system SHALL support self-contained schema directories with co-located templates.
114+
115+
#### Scenario: Schema with templates
116+
- **WHEN** a schema directory contains `schema.yaml` and `templates/` subdirectory
117+
- **THEN** artifacts can reference templates relative to the schema's templates directory
118+
119+
#### Scenario: User schema override
120+
- **WHEN** a schema directory exists at `${XDG_DATA_HOME}/openspec/schemas/<name>/`
121+
- **THEN** the system uses that directory instead of the built-in
122+
123+
#### Scenario: Built-in schema fallback
124+
- **WHEN** no user override exists for a schema
125+
- **THEN** the system uses the package built-in schema directory
126+
127+
#### Scenario: List available schemas
128+
- **WHEN** listing schemas
129+
- **THEN** the system returns schema names from both user and package directories
130+

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"files": [
3333
"dist",
3434
"bin",
35+
"schemas",
3536
"scripts/postinstall.js",
3637
"!dist/**/*.test.js",
3738
"!dist/**/__tests__",

schemas/spec-driven/schema.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: spec-driven
2+
version: 1
3+
description: Default OpenSpec workflow - proposal → specs → design → tasks
4+
artifacts:
5+
- id: proposal
6+
generates: proposal.md
7+
description: Initial proposal document outlining the change
8+
template: proposal.md
9+
requires: []
10+
- id: specs
11+
generates: "specs/*.md"
12+
description: Detailed specifications for the change
13+
template: spec.md
14+
requires:
15+
- proposal
16+
- id: design
17+
generates: design.md
18+
description: Technical design document with implementation details
19+
template: design.md
20+
requires:
21+
- proposal
22+
- id: tasks
23+
generates: tasks.md
24+
description: Implementation tasks derived from specs and design
25+
template: tasks.md
26+
requires:
27+
- specs
28+
- design
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## Context
2+
3+
<!-- Background and current state -->
4+
5+
## Goals / Non-Goals
6+
7+
**Goals:**
8+
<!-- What this design aims to achieve -->
9+
10+
**Non-Goals:**
11+
<!-- What is explicitly out of scope -->
12+
13+
## Decisions
14+
15+
<!-- Key design decisions and rationale -->
16+
17+
## Risks / Trade-offs
18+
19+
<!-- Known risks and trade-offs -->
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## Why
2+
3+
<!-- Explain the motivation for this change -->
4+
5+
## What Changes
6+
7+
<!-- Describe what will change -->
8+
9+
## Impact
10+
11+
<!-- List affected areas -->

0 commit comments

Comments
 (0)