|
| 1 | +# Schema Loading |
| 2 | + |
| 3 | +## Requirement |
| 4 | + |
| 5 | +The system SHALL load artifact graph definitions from YAML schema files. |
| 6 | + |
| 7 | +## Scenarios |
| 8 | + |
| 9 | +### Scenario: Valid schema loaded |
| 10 | +- **WHEN** a valid schema YAML file is provided |
| 11 | +- **THEN** the system returns an ArtifactGraph with all artifacts and dependencies |
| 12 | + |
| 13 | +### Scenario: Invalid schema rejected |
| 14 | +- **WHEN** a schema YAML file is missing required fields |
| 15 | +- **THEN** the system throws an error with a descriptive message |
| 16 | + |
| 17 | +### Scenario: Cyclic dependencies detected |
| 18 | +- **WHEN** a schema contains cyclic artifact dependencies |
| 19 | +- **THEN** the system throws an error listing the artifact IDs in the cycle |
| 20 | + |
| 21 | +### Scenario: Invalid dependency reference |
| 22 | +- **WHEN** an artifact's `requires` array references a non-existent artifact ID |
| 23 | +- **THEN** the system throws an error identifying the invalid reference |
| 24 | + |
| 25 | +### Scenario: Duplicate artifact IDs rejected |
| 26 | +- **WHEN** a schema contains multiple artifacts with the same ID |
| 27 | +- **THEN** the system throws an error identifying the duplicate |
| 28 | + |
| 29 | +## Design |
| 30 | + |
| 31 | +### Decision: Zod for Schema Validation |
| 32 | +Use Zod for validating YAML schema structure and deriving TypeScript types. |
| 33 | + |
| 34 | +**Rationale:** |
| 35 | +- Already a project dependency (v4.0.17) used in `src/core/schemas/` |
| 36 | +- Type inference via `z.infer<>` - single source of truth for types |
| 37 | +- Runtime validation with detailed error messages |
| 38 | +- Consistent with existing project patterns (`base.schema.ts`, `config-schema.ts`) |
| 39 | + |
| 40 | +**Alternatives considered:** |
| 41 | +- Manual validation: More code, error-prone, no type inference |
| 42 | +- JSON Schema: Would require additional dependency, less TypeScript integration |
| 43 | +- io-ts: Not already in project, steeper learning curve |
| 44 | + |
| 45 | +### Decision: Two-Level Schema Resolution |
| 46 | +Schemas resolve from global user config, falling back to package built-ins. |
| 47 | + |
| 48 | +**Resolution order:** |
| 49 | +1. `~/.config/openspec/schemas/<name>.yaml` - Global user override |
| 50 | +2. `<package>/schemas/<name>.yaml` - Built-in defaults |
| 51 | + |
| 52 | +**Rationale:** |
| 53 | +- Follows common CLI patterns (ESLint, Prettier, Git, npm) |
| 54 | +- Built-ins baked into package, never auto-copied |
| 55 | +- Users customize by creating files in global config dir |
| 56 | +- Simple - no project-level overrides (can add later if needed) |
| 57 | + |
| 58 | +**Alternatives considered:** |
| 59 | +- Project-level overrides: Added complexity, not needed initially |
| 60 | +- Auto-copy to user space: Creates drift, harder to update defaults |
| 61 | + |
| 62 | +### Decision: Cycle Error Format |
| 63 | +Cycle errors list all artifact IDs in the cycle for easy debugging. |
| 64 | + |
| 65 | +**Format:** `"Cyclic dependency detected: A → B → C → A"` |
| 66 | + |
| 67 | +**Rationale:** |
| 68 | +- Shows the full cycle path, not just that a cycle exists |
| 69 | +- Actionable - developer can see exactly which artifacts to fix |
| 70 | +- Consistent with Kahn's algorithm which naturally identifies cycle participants |
| 71 | + |
| 72 | +### Decision: Template Field Parsed But Not Resolved |
| 73 | +The `template` field is required in schema YAML for completeness, but template resolution is deferred to Slice 3. |
| 74 | + |
| 75 | +**Rationale:** |
| 76 | +- Slice 1 focuses on "What's Ready?" - dependency and completion queries only |
| 77 | +- Template paths are validated syntactically (non-empty string) but not resolved |
| 78 | +- Keeps Slice 1 focused and independently testable |
| 79 | + |
| 80 | +## Tasks |
| 81 | + |
| 82 | +### Type Definitions |
| 83 | +- [ ] 1.1 Create `src/core/artifact-graph/types.ts` with Zod schemas (`ArtifactSchema`, `SchemaYamlSchema`) and inferred types via `z.infer<>` |
| 84 | +- [ ] 1.2 Define `CompletedSet` (Set<string>), `BlockedArtifacts`, and `ArtifactGraphResult` types for runtime state |
| 85 | + |
| 86 | +### Schema Parser |
| 87 | +- [ ] 2.1 Create `src/core/artifact-graph/schema.ts` with YAML loading and Zod validation via `.safeParse()` |
| 88 | +- [ ] 2.2 Implement dependency reference validation (ensure `requires` references valid artifact IDs) |
| 89 | +- [ ] 2.3 Implement duplicate artifact ID detection |
| 90 | +- [ ] 2.4 Add cycle detection during schema load (error format: "Cyclic dependency detected: A → B → C → A") |
| 91 | + |
| 92 | +### Schema Resolution |
| 93 | +- [ ] 6.1 Create `src/core/artifact-graph/resolver.ts` with schema resolution logic |
| 94 | +- [ ] 6.2 Implement `resolveSchema(name)` - global (`~/.config/openspec/schemas/`) → built-in fallback |
| 95 | +- [ ] 6.3 Use existing `getGlobalConfigDir()` from `src/core/global-config.ts` |
| 96 | + |
| 97 | +### Built-in Schemas |
| 98 | +- [ ] 7.1 Create `src/core/artifact-graph/schemas/spec-driven.yaml` (default: proposal → specs → design → tasks) |
| 99 | +- [ ] 7.2 Create `src/core/artifact-graph/schemas/tdd.yaml` (alternative: tests → implementation → docs) |
| 100 | + |
| 101 | +### Tests |
| 102 | +- [ ] 9.1 Test: Parse valid schema YAML returns correct artifact graph |
| 103 | +- [ ] 9.2 Test: Parse invalid schema (missing fields) throws descriptive error |
| 104 | +- [ ] 9.3 Test: Duplicate artifact IDs throws error |
| 105 | +- [ ] 9.4 Test: Invalid `requires` reference throws error identifying the invalid ID |
| 106 | +- [ ] 9.5 Test: Cycle in schema throws error listing cycle path (e.g., "A → B → C → A") |
| 107 | +- [ ] 9.18 Test: Schema resolution finds global override before built-in |
| 108 | +- [ ] 9.19 Test: Schema resolution falls back to built-in when no global |
0 commit comments