From 702c1630483358879f877051f3db88009344e2a5 Mon Sep 17 00:00:00 2001 From: Tabish Bidiwale Date: Fri, 26 Dec 2025 23:13:13 +1100 Subject: [PATCH 1/2] proposal: add instruction loader and schema restructure (Slice 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two change proposals for implementing Slice 3 of the artifact POC: 1. restructure-schema-directories - Move schemas from embedded TS objects to self-contained directories - Each schema becomes a directory with schema.yaml + templates/ - Enables co-located templates for user extensibility - 2-level resolution: user override → package built-in 2. add-instruction-loader (depends on #1) - Load templates from schema directories - Enrich templates with change context (dependencies, next steps) - Format change status for CLI output - New instruction-loader capability These proposals complete Slice 3 from docs/artifact_poc.md. --- .../changes/add-instruction-loader/design.md | 149 ++++++++++++++++++ .../add-instruction-loader/proposal.md | 20 +++ .../specs/instruction-loader/spec.md | 65 ++++++++ .../changes/add-instruction-loader/tasks.md | 34 ++++ .../restructure-schema-directories/design.md | 129 +++++++++++++++ .../proposal.md | 20 +++ .../specs/artifact-graph/spec.md | 49 ++++++ .../restructure-schema-directories/tasks.md | 32 ++++ 8 files changed, 498 insertions(+) create mode 100644 openspec/changes/add-instruction-loader/design.md create mode 100644 openspec/changes/add-instruction-loader/proposal.md create mode 100644 openspec/changes/add-instruction-loader/specs/instruction-loader/spec.md create mode 100644 openspec/changes/add-instruction-loader/tasks.md create mode 100644 openspec/changes/restructure-schema-directories/design.md create mode 100644 openspec/changes/restructure-schema-directories/proposal.md create mode 100644 openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md create mode 100644 openspec/changes/restructure-schema-directories/tasks.md diff --git a/openspec/changes/add-instruction-loader/design.md b/openspec/changes/add-instruction-loader/design.md new file mode 100644 index 00000000..332f2db4 --- /dev/null +++ b/openspec/changes/add-instruction-loader/design.md @@ -0,0 +1,149 @@ +## Context + +This is Slice 3 of the artifact-graph POC. We have: +- `ArtifactGraph` class with graph operations (Slice 1) +- `detectCompleted()` for filesystem-based state detection (Slice 1) +- `resolveSchema()` for XDG schema resolution (Slice 1) +- `createChange()` and `validateChangeName()` utilities (Slice 2) + +After `restructure-schema-directories` is implemented, schemas will be self-contained directories: +``` +schemas// +├── schema.yaml +└── templates/ + └── *.md +``` + +This proposal adds template loading and instruction enrichment on top of that structure. + +## Goals / Non-Goals + +**Goals:** +- Load templates from schema directories +- Enrich templates with change-specific context (dependency status) +- Format change status for CLI output + +**Non-Goals:** +- Template authoring UI +- Dynamic template compilation/execution +- Caching (keep it stateless like the rest) + +## Decisions + +### 1. Pure functions over classes + +Follow the pattern in `resolver.ts` and `state.ts`. Use a simple `ChangeContext` interface with pure functions: + +```typescript +interface ChangeContext { + changeName: string; + changeDir: string; + schemaName: string; + graph: ArtifactGraph; + completed: CompletedSet; +} + +function loadChangeContext(projectRoot: string, changeName: string, schemaName?: string): ChangeContext +function loadTemplate(schemaName: string, templatePath: string): string +function getInstructions(artifactId: string, context: ChangeContext): string +function formatStatus(context: ChangeContext): string +``` + +**Why:** Matches existing codebase patterns. Easier to test. No hidden state. + +### 2. Template resolution from schema directory + +Templates are loaded from the schema's `templates/` subdirectory: + +```typescript +function loadTemplate(schemaName: string, templatePath: string): string { + const schemaDir = getSchemaDir(schemaName); // From resolver.ts + const fullPath = path.join(schemaDir, 'templates', templatePath); + return fs.readFileSync(fullPath, 'utf-8'); +} +``` + +Resolution is handled by `getSchemaDir()` which already checks user override → package built-in. + +**Why:** Leverages existing schema resolution. Templates are co-located with schemas. + +### 3. Template path from artifact definition + +The artifact's `template` field is a path relative to the schema's `templates/` directory: + +```yaml +artifacts: + - id: proposal + template: "proposal.md" # → schemas//templates/proposal.md +``` + +**Why:** Explicit, simple, no magic. + +### 4. Minimal context injection + +Templates are markdown. Injection prepends a header section with context: + +```markdown +--- +change: add-auth +artifact: proposal +schema: spec-driven +output: openspec/changes/add-auth/proposal.md +--- + +## Dependencies +- [x] (none - this is a root artifact) + +## Next Steps +After creating this artifact, you can work on: design, specs + +--- + +[original template content...] +``` + +**Why:** Simple string concatenation. No template engine dependency. Clear separation. + +### 5. Status output format + +```markdown +## Change: add-auth (spec-driven) + +| Artifact | Status | Output | +|----------|--------|--------| +| proposal | done | proposal.md | +| specs | ready | specs/*.md | +| design | blocked (needs: proposal) | design.md | +| tasks | blocked (needs: specs, design) | tasks.md | +``` + +**Why:** Markdown table is readable in terminal and docs. Matches CLI output style. + +## File Structure + +``` +src/core/artifact-graph/ +├── index.ts # Add new exports +├── template.ts # NEW: Template loading +├── context.ts # NEW: ChangeContext loading +└── instructions.ts # NEW: Enrichment and formatting +``` + +## Risks / Trade-offs + +**Dependency on restructure-schema-directories:** +- This proposal requires the schema restructure to be done first +- Mitigation: Clear dependency documented, implement in order + +**No template engine:** +- Pro: Zero dependencies, simple code +- Con: Limited expressiveness +- Mitigation: Current use case only needs static templates + header injection + +## Migration Plan + +N/A - new capability, no existing code to migrate. + +## Open Questions + +None. diff --git a/openspec/changes/add-instruction-loader/proposal.md b/openspec/changes/add-instruction-loader/proposal.md new file mode 100644 index 00000000..0112fb3b --- /dev/null +++ b/openspec/changes/add-instruction-loader/proposal.md @@ -0,0 +1,20 @@ +## Why + +Slice 1 (artifact-graph) provides graph operations and state detection. Slice 2 (change-utils) provides change creation. We now need the ability to load templates for artifacts and enrich them with change-specific context so users/agents know what to create next. + +## What Changes + +- Add template resolution from schema directories (uses structure from `restructure-schema-directories`) +- Add instruction enrichment that injects change context into templates +- Add status formatting for CLI output +- New `instruction-loader` capability + +## Dependencies + +- Requires `restructure-schema-directories` to be implemented first (schemas as directories with co-located templates) + +## Impact + +- Affected specs: New `instruction-loader` spec +- Affected code: `src/core/artifact-graph/` (new files) +- Builds on: `artifact-graph` (Slice 1), uses `ArtifactGraph`, `detectCompleted`, `resolveSchema` diff --git a/openspec/changes/add-instruction-loader/specs/instruction-loader/spec.md b/openspec/changes/add-instruction-loader/specs/instruction-loader/spec.md new file mode 100644 index 00000000..261a1ee1 --- /dev/null +++ b/openspec/changes/add-instruction-loader/specs/instruction-loader/spec.md @@ -0,0 +1,65 @@ +## ADDED Requirements + +### Requirement: Template Loading +The system SHALL load templates from schema directories. + +#### Scenario: Template loaded from schema directory +- **WHEN** `loadTemplate(schemaName, templatePath)` is called +- **THEN** the system loads the template from `schemas//templates/` + +#### Scenario: Template not found +- **WHEN** a template file does not exist in the schema's templates directory +- **THEN** the system throws an error with the template path + +### Requirement: Change Context Loading +The system SHALL load change context combining graph and completion state. + +#### Scenario: Load context for existing change +- **WHEN** `loadChangeContext(projectRoot, changeName)` is called for an existing change +- **THEN** the system returns a context with graph, completed set, schema name, and change info + +#### Scenario: Load context with custom schema +- **WHEN** `loadChangeContext(projectRoot, changeName, schemaName)` is called +- **THEN** the system uses the specified schema instead of default + +#### Scenario: Load context for missing change +- **WHEN** `loadChangeContext` is called for a non-existent change directory +- **THEN** the system returns context with empty completed set + +### Requirement: Instruction Enrichment +The system SHALL enrich templates with change-specific context. + +#### Scenario: Header with change info +- **WHEN** instructions are generated for an artifact +- **THEN** the output includes change name, artifact ID, schema name, and output path + +#### Scenario: Dependency status shown +- **WHEN** an artifact has dependencies +- **THEN** the output shows each dependency with completion status (done/missing) + +#### Scenario: Next steps shown +- **WHEN** instructions are generated +- **THEN** the output includes which artifacts become available after this one + +#### Scenario: Root artifact dependencies +- **WHEN** an artifact has no dependencies +- **THEN** the dependency section indicates this is a root artifact + +### Requirement: Status Formatting +The system SHALL format change status as readable output. + +#### Scenario: Format complete change +- **WHEN** all artifacts are completed +- **THEN** status shows all artifacts as "done" + +#### Scenario: Format partial change +- **WHEN** some artifacts are completed +- **THEN** status shows completed as "done", ready as "ready", blocked as "blocked" + +#### Scenario: Show blocked dependencies +- **WHEN** an artifact is blocked +- **THEN** status shows which dependencies are missing + +#### Scenario: Show output paths +- **WHEN** status is formatted +- **THEN** each artifact shows its output path pattern diff --git a/openspec/changes/add-instruction-loader/tasks.md b/openspec/changes/add-instruction-loader/tasks.md new file mode 100644 index 00000000..5184a9c6 --- /dev/null +++ b/openspec/changes/add-instruction-loader/tasks.md @@ -0,0 +1,34 @@ +## 1. Template Loading + +- [ ] 1.1 Create `src/core/artifact-graph/template.ts` +- [ ] 1.2 Implement `loadTemplate(schemaName, templatePath)` using schema directory structure +- [ ] 1.3 Add tests for template loading from schema directory +- [ ] 1.4 Add tests for error when template not found + +## 2. Change Context + +- [ ] 2.1 Create `src/core/artifact-graph/context.ts` +- [ ] 2.2 Define `ChangeContext` interface +- [ ] 2.3 Implement `loadChangeContext()` function +- [ ] 2.4 Add tests for context loading with existing change +- [ ] 2.5 Add tests for context loading with missing change directory + +## 3. Instruction Enrichment + +- [ ] 3.1 Create `src/core/artifact-graph/instructions.ts` +- [ ] 3.2 Implement `getInstructions()` with header injection +- [ ] 3.3 Add dependency status formatting (done/missing) +- [ ] 3.4 Add next steps calculation +- [ ] 3.5 Add tests for enrichment output + +## 4. Status Formatting + +- [ ] 4.1 Implement `formatStatus()` function in instructions.ts +- [ ] 4.2 Format as markdown table with status and output path +- [ ] 4.3 Show blocked dependencies +- [ ] 4.4 Add tests for status formatting + +## 5. Integration + +- [ ] 5.1 Export new functions from `src/core/artifact-graph/index.ts` +- [ ] 5.2 Ensure all tests pass diff --git a/openspec/changes/restructure-schema-directories/design.md b/openspec/changes/restructure-schema-directories/design.md new file mode 100644 index 00000000..f49cc692 --- /dev/null +++ b/openspec/changes/restructure-schema-directories/design.md @@ -0,0 +1,129 @@ +## Context + +Built-in schemas are currently embedded as TypeScript objects: + +```typescript +// src/core/artifact-graph/builtin-schemas.ts +export const SPEC_DRIVEN_SCHEMA: SchemaYaml = { + name: 'spec-driven', + version: 1, + artifacts: [...] +}; +``` + +This doesn't support templates co-located with schemas. The instruction loader (Slice 3) needs templates, and the cleanest approach is self-contained schema directories. + +## Goals / Non-Goals + +**Goals:** +- Schemas as self-contained directories (schema.yaml + templates/) +- User overrides via XDG data directory +- Simple 2-level resolution (user → package) +- Templates co-located with their schema + +**Non-Goals:** +- Shared template fallback (intentionally avoiding complexity) +- Runtime schema compilation +- Schema inheritance + +## Decisions + +### 1. Directory structure + +Each schema is a directory containing `schema.yaml` and `templates/`: + +``` +/schemas/ +├── spec-driven/ +│ ├── schema.yaml +│ └── templates/ +│ ├── proposal.md +│ ├── design.md +│ ├── spec.md +│ └── tasks.md +└── tdd/ + ├── schema.yaml + └── templates/ + ├── spec.md + ├── test.md + ├── implementation.md + └── docs.md +``` + +**Why:** Self-contained like Helm charts. No cross-schema dependencies. Each schema owns its templates. + +### 2. Resolution order (2 levels) + +``` +1. ${XDG_DATA_HOME}/openspec/schemas//schema.yaml # User override +2. /schemas//schema.yaml # Built-in +3. Error (not found) +``` + +**Why:** Simple mental model. User can override entire schema directory or just parts. + +### 3. Template path in schema.yaml + +The `template` field is relative to the schema's `templates/` directory: + +```yaml +# schemas/spec-driven/schema.yaml +artifacts: + - id: proposal + template: "proposal.md" # → schemas/spec-driven/templates/proposal.md +``` + +**Why:** Paths are relative to the schema, not a global templates directory. + +### 4. Resolve package directory via import.meta.url + +```typescript +function getPackageSchemasDir(): string { + const currentFile = fileURLToPath(import.meta.url); + // Navigate from src/core/artifact-graph/ to package root + return path.join(path.dirname(currentFile), '..', '..', '..', 'schemas'); +} +``` + +**Why:** Works in ESM. No hardcoded paths. + +### 5. Keep schema.yaml format unchanged + +The YAML format stays the same - only the storage location changes: + +```yaml +name: spec-driven +version: 1 +description: Specification-driven development +artifacts: + - id: proposal + generates: "proposal.md" + template: "proposal.md" + requires: [] +``` + +**Why:** No breaking changes to schema format. Just moving from TS to YAML files. + +## Migration + +1. Create `schemas/` directory at package root +2. Convert `SPEC_DRIVEN_SCHEMA` to `schemas/spec-driven/schema.yaml` +3. Convert `TDD_SCHEMA` to `schemas/tdd/schema.yaml` +4. Update `resolveSchema()` to load from directories +5. Remove `builtin-schemas.ts` +6. Update `listSchemas()` to scan directories + +## Risks / Trade-offs + +**File I/O at runtime:** +- Previously schemas were in-memory objects +- Now requires reading YAML files +- Mitigation: Schemas are small, loaded once per operation + +**Package distribution:** +- Must ensure `schemas/` directory is included in npm package +- Add to `files` in package.json + +## Open Questions + +None. diff --git a/openspec/changes/restructure-schema-directories/proposal.md b/openspec/changes/restructure-schema-directories/proposal.md new file mode 100644 index 00000000..47fe8719 --- /dev/null +++ b/openspec/changes/restructure-schema-directories/proposal.md @@ -0,0 +1,20 @@ +## Why + +Currently, built-in schemas are embedded as TypeScript objects in `builtin-schemas.ts`. This works for schemas but doesn't support co-located templates. To enable self-contained schema packages (schema + templates together), we need to restructure schemas as directories. + +## What Changes + +- **BREAKING (internal):** Move built-in schemas from embedded TS objects to actual directory structure +- Schemas become directories containing `schema.yaml` + `templates/` +- Update `resolveSchema()` to load from directory structure +- Remove `builtin-schemas.ts` (replaced by file-based schemas) +- Update resolution to check user dir → package dir + +## Impact + +- Affected specs: `artifact-graph` (schema resolution changes) +- Affected code: + - Remove `src/core/artifact-graph/builtin-schemas.ts` + - Update `src/core/artifact-graph/resolver.ts` + - Add `schemas/` directory at package root +- No external API changes (resolution still returns `SchemaYaml`) diff --git a/openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md b/openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md new file mode 100644 index 00000000..be992bb2 --- /dev/null +++ b/openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md @@ -0,0 +1,49 @@ +## MODIFIED Requirements + +### Requirement: Schema Loading +The system SHALL load artifact graph definitions from YAML schema files within schema directories. + +#### Scenario: Valid schema loaded from directory +- **WHEN** a schema directory contains a valid `schema.yaml` file +- **THEN** the system returns an ArtifactGraph with all artifacts and dependencies + +#### Scenario: Invalid schema rejected +- **WHEN** a schema YAML file is missing required fields +- **THEN** the system throws an error with a descriptive message + +#### Scenario: Cyclic dependencies detected +- **WHEN** a schema contains cyclic artifact dependencies +- **THEN** the system throws an error listing the artifact IDs in the cycle + +#### Scenario: Invalid dependency reference +- **WHEN** an artifact's `requires` array references a non-existent artifact ID +- **THEN** the system throws an error identifying the invalid reference + +#### Scenario: Duplicate artifact IDs rejected +- **WHEN** a schema contains multiple artifacts with the same ID +- **THEN** the system throws an error identifying the duplicate + +#### Scenario: Schema directory not found +- **WHEN** resolving a schema name that has no corresponding directory +- **THEN** the system throws an error listing available schemas + +## ADDED Requirements + +### Requirement: Schema Directory Structure +The system SHALL support self-contained schema directories with co-located templates. + +#### Scenario: Schema with templates +- **WHEN** a schema directory contains `schema.yaml` and `templates/` subdirectory +- **THEN** artifacts can reference templates relative to the schema's templates directory + +#### Scenario: User schema override +- **WHEN** a schema directory exists at `${XDG_DATA_HOME}/openspec/schemas//` +- **THEN** the system uses that directory instead of the built-in + +#### Scenario: Built-in schema fallback +- **WHEN** no user override exists for a schema +- **THEN** the system uses the package built-in schema directory + +#### Scenario: List available schemas +- **WHEN** listing schemas +- **THEN** the system returns schema names from both user and package directories diff --git a/openspec/changes/restructure-schema-directories/tasks.md b/openspec/changes/restructure-schema-directories/tasks.md new file mode 100644 index 00000000..dd18908d --- /dev/null +++ b/openspec/changes/restructure-schema-directories/tasks.md @@ -0,0 +1,32 @@ +## 1. Create Schema Directories + +- [ ] 1.1 Create `schemas/` directory at package root +- [ ] 1.2 Create `schemas/spec-driven/schema.yaml` from `SPEC_DRIVEN_SCHEMA` +- [ ] 1.3 Create `schemas/spec-driven/templates/` with placeholder templates +- [ ] 1.4 Create `schemas/tdd/schema.yaml` from `TDD_SCHEMA` +- [ ] 1.5 Create `schemas/tdd/templates/` with placeholder templates + +## 2. Update Schema Resolution + +- [ ] 2.1 Add `getPackageSchemasDir()` function using `import.meta.url` +- [ ] 2.2 Add `getSchemaDir(name)` to resolve schema directory path +- [ ] 2.3 Update `resolveSchema()` to load from directory structure +- [ ] 2.4 Update `listSchemas()` to scan directories instead of object keys +- [ ] 2.5 Add tests for user override resolution +- [ ] 2.6 Add tests for built-in fallback + +## 3. Cleanup + +- [ ] 3.1 Remove `builtin-schemas.ts` +- [ ] 3.2 Update `index.ts` exports (remove `BUILTIN_SCHEMAS`, `SPEC_DRIVEN_SCHEMA`, `TDD_SCHEMA`) +- [ ] 3.3 Update any code that imports removed exports + +## 4. Package Distribution + +- [ ] 4.1 Add `schemas/` to `files` array in `package.json` +- [ ] 4.2 Verify schemas are included in built package + +## 5. Fix Template Paths + +- [ ] 5.1 Update `template` field in schema.yaml files (remove `templates/` prefix) +- [ ] 5.2 Ensure template paths are relative to schema's templates directory From 93c846421afb0a9fa56d6aaf4a14ca0c710382a0 Mon Sep 17 00:00:00 2001 From: Tabish Bidiwale Date: Fri, 26 Dec 2025 23:30:10 +1100 Subject: [PATCH 2/2] fix: include full requirement block in MODIFIED spec Update the Schema Loading requirement to include all original scenarios (modified as needed) per the MODIFIED requirement guidelines. The archiver replaces the entire requirement with the provided content. --- .../restructure-schema-directories/specs/artifact-graph/spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md b/openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md index be992bb2..1568765d 100644 --- a/openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md +++ b/openspec/changes/restructure-schema-directories/specs/artifact-graph/spec.md @@ -3,7 +3,7 @@ ### Requirement: Schema Loading The system SHALL load artifact graph definitions from YAML schema files within schema directories. -#### Scenario: Valid schema loaded from directory +#### Scenario: Valid schema loaded - **WHEN** a schema directory contains a valid `schema.yaml` file - **THEN** the system returns an ArtifactGraph with all artifacts and dependencies