Skip to content

Conversation

@TabishB
Copy link
Contributor

@TabishB TabishB commented Jan 20, 2026

Summary

  • Add 3-level schema resolution: project-local → user override → package built-in
  • Enable projects to define custom schemas at ./openspec/schemas/<name>/
  • Extend SchemaInfo.source type to include 'project' alongside existing 'user' and 'package'
  • Update CLI commands to display schema source labels for better visibility
  • Add projectRoot to ChangeContext for proper resolution throughout workflow

Test plan

  • Add unit tests for getProjectSchemasDir()
  • Add unit tests for project-local schema resolution priority (project shadows user/package)
  • Add unit tests for backward compatibility (no projectRoot = user + package only)
  • Add unit tests for listSchemas() and listSchemasWithInfo() with project schemas
  • Add integration test with temp project containing local schema
  • Verify all 996 existing tests pass
  • Verify build and lint pass

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Added support for project-local schemas, enabling users to store custom schemas within their project directory for easy sharing and version control
    • Schema resolution now prioritizes project-local schemas over user overrides and built-in schemas
    • Enhanced schema listings to display the source of each schema (project, user, or package origin)

✏️ Tip: You can customize this high-level summary in your review settings.

Add 3-level schema resolution: project-local → user override → package built-in.

- Add `getProjectSchemasDir(projectRoot)` to resolve project schemas at `./openspec/schemas/<name>/`
- Extend `SchemaInfo.source` type to include `'project'`
- Update `getSchemaDir()`, `resolveSchema()`, `listSchemas()`, `listSchemasWithInfo()` with optional `projectRoot` parameter
- Update CLI commands to display schema source labels (project/user/package)
- Add `projectRoot` to `ChangeContext` interface for proper resolution throughout workflow
- Add 17 new tests covering project-local schema resolution

This enables projects to define custom schemas that override user and package schemas,
while maintaining backward compatibility when projectRoot is not provided.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

This pull request introduces project-local schemas as a new highest-priority resolution source in the OpenSpec system. It threads an optional projectRoot parameter through resolver APIs to enable discovery and resolution of schemas from ./openspec/schemas/<name>/ before checking user overrides and package built-ins. The changes include a new getProjectSchemasDir() helper, extended schema metadata with source attribution ('project', 'user', 'package'), and corresponding updates across CLI commands, instruction loading, and utilities while maintaining backward compatibility.

Changes

Cohort / File(s) Summary
Design & Documentation
openspec/changes/project-local-schemas/design.md, openspec/changes/project-local-schemas/specs/schema-resolution/spec.md, openspec/changes/project-local-schemas/tasks.md
Specifies project-local schema resolution design: introduces getProjectSchemasDir(), documents precedence rules (project → user → package), extends SchemaInfo.source to include 'project', and outlines updates to resolver functions to accept optional projectRoot. Plans include CLI annotation of schema sources and comprehensive unit and integration tests.
Core Resolver
src/core/artifact-graph/resolver.ts
Adds getProjectSchemasDir(projectRoot) and extends getSchemaDir, resolveSchema, listSchemas, and listSchemasWithInfo with optional projectRoot parameter. Updates SchemaInfo.source type to include 'project' and prioritizes project-local schemas during resolution and listing; includes error messaging enhancements.
Instruction Loading
src/core/artifact-graph/instruction-loader.ts
Adds projectRoot field to ChangeContext interface, updates loadTemplate, loadChangeContext, generateInstructions, and formatChangeStatus to accept/propagate optional projectRoot. Alters template loading and schema resolution to use project-local scope when available, with fallback logic for optional context.
CLI Commands
src/commands/artifact-workflow.ts
Updates validateSchemaExists signature to accept optional projectRoot; extends TemplateInfo.source from {'user' | 'package'} to {'project' | 'user' | 'package'}. Modifies schemasCommand, templatesCommand, and related flows to compute, propagate, and annotate artifact sources based on project-root-aware resolution.
Config & Utilities
src/core/config-prompts.ts, src/utils/change-metadata.ts, src/utils/change-utils.ts
Extends promptForConfig, validateSchemaName, writeChangeMetadata, and readChangeMetadata with optional projectRoot parameter. Updates call sites to pass projectRoot through schema validation and metadata operations for project-scoped resolution.
Tests
test/core/artifact-graph/resolver.test.ts
Adds 334 lines of test coverage for getProjectSchemasDir, project-local schema precedence, backward compatibility, listSchemas/listSchemasWithInfo with project schemas, and source attribution. Includes integration tests with temporary local projects.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/CLI
    participant Resolver as Schema Resolver
    participant ProjectFS as Project FS
    participant UserFS as User FS
    participant PackageFS as Package FS

    Client->>Resolver: getSchemaDir(name, projectRoot)
    activate Resolver
        Resolver->>ProjectFS: Check ./openspec/schemas/name/
        alt Project Schema Exists
            ProjectFS-->>Resolver: Schema found
        else Project Schema Missing
            Resolver->>UserFS: Check user overrides
            alt User Override Exists
                UserFS-->>Resolver: User schema found
            else User Override Missing
                Resolver->>PackageFS: Check package built-ins
                PackageFS-->>Resolver: Package schema found
            end
        end
    deactivate Resolver
    Resolver-->>Client: Schema path + source
Loading
sequenceDiagram
    participant CLI as CLI Command
    participant Workflow as Artifact Workflow
    participant Resolver as Resolver
    participant Instruction as Instruction Loader
    participant FS as File System

    CLI->>Workflow: schemasCommand(projectRoot)
    activate Workflow
        Workflow->>Resolver: listSchemasWithInfo(projectRoot)
        activate Resolver
            Resolver->>FS: Scan project, user, package locations
            FS-->>Resolver: Schema lists with locations
        deactivate Resolver
        Resolver-->>Workflow: Array<{name, source: 'project'|'user'|'package'}>
    deactivate Workflow
    Workflow->>CLI: Display schemas with source labels
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Hop along the schema trail,
Project schemas now prevail!
Priority flows: local, user, built-in quest,
Threading projectRoot—the best of the best!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(resolver): add project-local schema support' directly and clearly summarizes the main change: adding project-local schema support to the resolver module.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.18.1)
openspec/changes/project-local-schemas/specs/schema-resolution/spec.md

markdownlint-cli2 v0.18.1 (markdownlint v0.38.0)
Finding: openspec/changes/project-local-schemas/specs/schema-resolution/spec.md
Linting: 1 file(s)
Summary: 0 error(s)
Error: EACCES: permission denied, open '/markdownlint-cli2-results.json'
at async open (node:internal/fs/promises:640:25)
at async Object.writeFile (node:internal/fs/promises:1214:14)
at async Promise.all (index 0)
at async outputSummary (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:877:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:1053:25)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:12:22 {
errno: -13,
code: 'EACCES',
syscall: 'open',
path: '/markdownlint-cli2-results.json'
}

openspec/changes/project-local-schemas/design.md

markdownlint-cli2 v0.18.1 (markdownlint v0.38.0)
Finding: openspec/changes/project-local-schemas/design.md
Linting: 1 file(s)
Summary: 1 error(s)
Error: EACCES: permission denied, open '/markdownlint-cli2-results.json'
at async open (node:internal/fs/promises:640:25)
at async Object.writeFile (node:internal/fs/promises:1214:14)
at async Promise.all (index 0)
at async outputSummary (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:877:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:1053:25)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:12:22 {
errno: -13,
code: 'EACCES',
syscall: 'open',
path: '/markdownlint-cli2-results.json'
}

openspec/changes/project-local-schemas/tasks.md

markdownlint-cli2 v0.18.1 (markdownlint v0.38.0)
Finding: openspec/changes/project-local-schemas/tasks.md
Linting: 1 file(s)
Summary: 0 error(s)
Error: EACCES: permission denied, open '/markdownlint-cli2-results.json'
at async open (node:internal/fs/promises:640:25)
at async Object.writeFile (node:internal/fs/promises:1214:14)
at async Promise.all (index 0)
at async outputSummary (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:877:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:1053:25)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:12:22 {
errno: -13,
code: 'EACCES',
syscall: 'open',
path: '/markdownlint-cli2-results.json'
}


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link

greptile-apps bot commented Jan 20, 2026

Greptile Summary

This PR adds 3-level schema resolution to OpenSpec, enabling teams to define project-local schemas at ./openspec/schemas/<name>/ that take priority over user overrides and package built-ins.

Key Changes:

  • Project-local schemas have highest priority (project → user → package resolution order)
  • Optional projectRoot parameter added to resolver functions maintains backward compatibility
  • SchemaInfo.source type extended to include 'project' for proper source attribution
  • ChangeContext now includes projectRoot to thread context through the workflow
  • CLI commands updated to display schema source labels ((project), (user override))

Implementation Quality:

  • Well-documented design decisions with clear rationale for trade-offs
  • Comprehensive test coverage (336 new test lines) covering priority, backward compatibility, and edge cases
  • All 996 existing tests passing per PR description
  • Clean optional parameter pattern avoids breaking changes
  • Proper threading of projectRoot through call chain

Architecture:
The implementation follows existing patterns in the codebase where CLI commands obtain process.cwd() and pass it down explicitly rather than using implicit globals. The getProjectSchemasDir() helper mirrors existing getUserSchemasDir() and getPackageSchemasDir() functions. Schema shadowing is intentional and well-documented.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is well-designed with backward compatibility maintained through optional parameters, comprehensive test coverage validates all scenarios including edge cases and priority handling, all tasks in tasks.md are complete, the design follows existing codebase patterns, and proper documentation exists for design decisions and trade-offs
  • No files require special attention

Important Files Changed

Filename Overview
src/core/artifact-graph/resolver.ts Added project-local schema resolution with proper 3-level priority (project → user → package), backward compatible optional parameter design
src/core/artifact-graph/instruction-loader.ts Added projectRoot to ChangeContext and threaded it through template loading and schema resolution
src/commands/artifact-workflow.ts Updated all command handlers to pass projectRoot to schema functions and display project source labels in output
test/core/artifact-graph/resolver.test.ts Comprehensive test coverage for project-local schema resolution, priority, backward compatibility, and source tagging

Sequence Diagram

sequenceDiagram
    participant CLI as CLI Command
    participant Workflow as artifact-workflow.ts
    participant Resolver as resolver.ts
    participant Loader as instruction-loader.ts
    participant Metadata as change-metadata.ts
    
    CLI->>Workflow: openspec schemas
    Workflow->>Workflow: projectRoot = process.cwd()
    Workflow->>Resolver: listSchemasWithInfo(projectRoot)
    Resolver->>Resolver: Check project dir (./openspec/schemas/)
    Resolver->>Resolver: Check user dir (~/.local/share/openspec/schemas/)
    Resolver->>Resolver: Check package dir (npm/schemas/)
    Resolver-->>Workflow: SchemaInfo[] with source tags
    Workflow-->>CLI: Display schemas with (project)/(user) labels
    
    CLI->>Workflow: openspec status --change=my-change
    Workflow->>Workflow: projectRoot = process.cwd()
    Workflow->>Loader: loadChangeContext(projectRoot, changeName)
    Loader->>Metadata: resolveSchemaForChange(changeDir)
    Metadata->>Metadata: projectRoot from changeDir path
    Metadata->>Resolver: resolveSchema(schemaName, projectRoot)
    Resolver->>Resolver: getSchemaDir(name, projectRoot)
    Note over Resolver: Priority: project → user → package
    Resolver-->>Metadata: Schema YAML
    Metadata-->>Loader: Schema name
    Loader->>Resolver: resolveSchema(schemaName, projectRoot)
    Resolver-->>Loader: SchemaYaml object
    Loader-->>Workflow: ChangeContext with projectRoot
    Workflow->>Loader: generateInstructions(context, artifactId)
    Loader->>Loader: loadTemplate(schemaName, templatePath, context.projectRoot)
    Loader->>Resolver: getSchemaDir(schemaName, projectRoot)
    Resolver-->>Loader: Template directory path
    Loader-->>Workflow: Instructions with enriched template
Loading

@TabishB TabishB merged commit adda63e into main Jan 20, 2026
10 checks passed
@TabishB TabishB deleted the feat/project-local-schemas branch January 20, 2026 02:01
@vibe-kanban-cloud
Copy link

Review Complete

Your review story is ready!

View Story

Comment !reviewfast on this PR to re-generate the story.

TabishB added a commit that referenced this pull request Jan 20, 2026
Update documentation to reflect implemented schema management features:
- Document schema CLI commands (which, validate, fork, init)
- Update gap summary to show completed phases (PR #522, #525)
- Improve custom schema examples with actual CLI usage
- Update resolution order documentation
TabishB added a commit that referenced this pull request Jan 20, 2026
Update documentation to reflect implemented schema management features:
- Document schema CLI commands (which, validate, fork, init)
- Update gap summary to show completed phases (PR #522, #525)
- Improve custom schema examples with actual CLI usage
- Update resolution order documentation
TabishB added a commit that referenced this pull request Jan 20, 2026
…526)

* docs: update workflow docs for schema management CLI

Update documentation to reflect implemented schema management features:
- Document schema CLI commands (which, validate, fork, init)
- Update gap summary to show completed phases (PR #522, #525)
- Improve custom schema examples with actual CLI usage
- Update resolution order documentation

* feat(cli): mark schema commands as experimental

Add [experimental] tag to help description and runtime warning
for schema management commands to indicate they may change.
StrayDragon pushed a commit to StrayDragon/OpenSpec that referenced this pull request Jan 20, 2026
Add 3-level schema resolution: project-local → user override → package built-in.

- Add `getProjectSchemasDir(projectRoot)` to resolve project schemas at `./openspec/schemas/<name>/`
- Extend `SchemaInfo.source` type to include `'project'`
- Update `getSchemaDir()`, `resolveSchema()`, `listSchemas()`, `listSchemasWithInfo()` with optional `projectRoot` parameter
- Update CLI commands to display schema source labels (project/user/package)
- Add `projectRoot` to `ChangeContext` interface for proper resolution throughout workflow
- Add 17 new tests covering project-local schema resolution

This enables projects to define custom schemas that override user and package schemas,
while maintaining backward compatibility when projectRoot is not provided.
StrayDragon pushed a commit to StrayDragon/OpenSpec that referenced this pull request Jan 20, 2026
…ission-AI#526)

* docs: update workflow docs for schema management CLI

Update documentation to reflect implemented schema management features:
- Document schema CLI commands (which, validate, fork, init)
- Update gap summary to show completed phases (PR Fission-AI#522, Fission-AI#525)
- Improve custom schema examples with actual CLI usage
- Update resolution order documentation

* feat(cli): mark schema commands as experimental

Add [experimental] tag to help description and runtime warning
for schema management commands to indicate they may change.
harikrishnan83 added a commit to intent-driven-dev/OpenSpec that referenced this pull request Jan 21, 2026
# By Tabish Bidiwale (57) and others
# Via GitHub
* main: (67 commits)
  fix(ci): use workflow_dispatch for polish release notes (Fission-AI#533)
  fix(changelog): convert markdown headers to bold text for proper formatting (Fission-AI#532)
  Version Packages (Fission-AI#531)
  Add changeset for project config and schema commands (Fission-AI#530)
  fix(config): handle null rules field in project config (Fission-AI#529)
  docs: update workflow docs and mark schema commands as experimental (Fission-AI#526)
  feat(cli): add schema management commands (Fission-AI#525)
  fix: Windows path compatibility in resolver tests (Fission-AI#524)
  change(schema-management-cli): proposal for schema management commands (Fission-AI#523)
  feat(resolver): add project-local schema support (Fission-AI#522)
  docs: add project-config demo guide (Fission-AI#521)
  feat(config): add project-level configuration via openspec/config.yaml (Fission-AI#499)
  fix: auto-trigger polish release notes on release publish (Fission-AI#519)
  perf: add path filtering to Nix validation CI job (Fission-AI#518)
  Version Packages (Fission-AI#517)
  Add changeset for v0.21 release (Fission-AI#516)
  fix: prevent implementation during explore mode (Fission-AI#515)
  OPSX apply: infer target change (Fission-AI#513)
  Refine opsx archive sync assessment (Fission-AI#514)
  feat: add nix flake support (sorry for this duplicate) (Fission-AI#459)
  ...

# Conflicts:
#	src/core/templates/slash-command-templates.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants