OpenSpec Integration Guide
July 19, 2026 · View on GitHub
This document details how openlore integrates with the OpenSpec ecosystem.
Overview
openlore is designed as the "brownfield on-ramp" for OpenSpec. While openspec init creates empty scaffolding for new projects, openlore reverse-engineers specifications from existing codebases.
┌─────────────────────────────────────────────────────────────────┐
│ OpenSpec Ecosystem │
├─────────────────────────────────────────────────────────────────┤
│ │
│ GREENFIELD (new projects) BROWNFIELD (existing code) │
│ ───────────────────────── ──────────────────────────── │
│ openspec init openlore analyze │
│ │ │ │
│ ▼ ▼ │
│ Empty scaffolding Codebase analysis │
│ │ │ │
│ ▼ ▼ │
│ Manual spec writing openlore generate │
│ │ │ │
│ └──────────┬──────────────────┘ │
│ ▼ │
│ openspec/specs/ (populated) │
│ │ │
│ ▼ │
│ openspec change my-feature │
│ openspec validate │
│ openspec archive │
│ │
└─────────────────────────────────────────────────────────────────┘
Directory Structure
openlore outputs to the standard OpenSpec directory structure:
project-root/
├── openspec/ # OpenSpec root
│ ├── config.yaml # Project configuration
│ ├── specs/ # Source of truth
│ │ ├── overview/
│ │ │ └── spec.md # System overview
│ │ ├── architecture/
│ │ │ └── spec.md # System architecture
│ │ ├── {domain-1}/
│ │ │ └── spec.md # Domain specification
│ │ ├── {domain-2}/
│ │ │ └── spec.md
│ │ └── api/
│ │ └── spec.md # API specification
│ ├── changes/ # Proposed modifications
│ │ └── archive/ # Completed changes
│ └── schemas/ # Custom workflow schemas
├── .openlore/ # openlore working directory
│ ├── config.json # openlore configuration (commit this)
│ ├── analysis/ # Analysis artifacts (regeneratable)
│ ├── outputs/ # Generation reports
│ ├── verification/ # Verification results
│ ├── backups/ # Backed up specs
│ ├── runs/ # Run metadata
│ └── logs/ # LLM request/response logs
└── .gitignore # Updated to include .openlore/
Spec File Format
Generated specs follow OpenSpec's standard format:
# {Domain} Specification
> Generated by openlore v{version} on {date}
> Source files: {list of analyzed files}
> Confidence: {percentage}%
## Purpose
{High-level description of what this domain covers}
## Requirements
### Requirement: {RequirementName}
{The system SHALL/MUST/SHOULD do X...}
#### Scenario: {ScenarioName}
- **GIVEN** {precondition or initial state}
- **WHEN** {action or trigger occurs}
- **THEN** {expected outcome or result}
- **AND** {additional assertion if needed}
### Requirement: {RequirementName2}
{Another requirement...}
#### Scenario: {AnotherScenario}
- **GIVEN** ...
- **WHEN** ...
- **THEN** ...
## Technical Notes
- **Implementation**: `{file paths}`
- **Dependencies**: {list of related domains/services}
- **Patterns**: {observed architectural patterns}
RFC 2119 Keywords
All requirements use RFC 2119 keywords:
| Keyword | Meaning |
|---|---|
| SHALL / MUST | Absolute requirement |
| SHALL NOT / MUST NOT | Absolute prohibition |
| SHOULD | Recommended |
| SHOULD NOT | Not recommended |
| MAY | Optional |
Scenario Format
Scenarios MUST use exactly 4 hashtags (####) per OpenSpec convention:
#### Scenario: ValidUserLogin
- **GIVEN** a registered user with valid credentials
- **WHEN** the user submits the login form
- **THEN** the user is authenticated
- **AND** a session token is created
config.yaml Integration
openlore updates openspec/config.yaml while preserving user content:
# User-defined content (preserved)
schema: spec-driven
context: |
This is an e-commerce platform built for enterprise clients.
Key requirements include PCI compliance and high availability.
rules:
proposal:
- Must include cost analysis
specs:
- All requirements must be testable
# Auto-generated by openlore (merged)
openlore:
version: "1.0.0"
generatedAt: "2024-01-15T10:30:00Z"
domains:
- user
- order
- auth
- api
confidence: 0.85
sourceAnalysis: ".openlore/analysis/repo-structure.json"
Context Injection
If existing context is present, openlore appends detected information:
context: |
# User-provided context (preserved)
This is an e-commerce platform for enterprise clients.
# Auto-detected by openlore (appended)
Tech stack: Node.js 20, TypeScript 5.3, Express 4.18, TypeORM 0.3
Architecture: Layered (routes → controllers → services → repositories)
Detected domains: user, order, product, payment, notification
Key patterns: Repository pattern, Dependency injection
OpenSpec CLI Compatibility
Generated specs work with all OpenSpec commands:
Validation
openspec validate --all
openlore ensures:
- Valid markdown structure
- Correct heading hierarchy
- RFC 2119 keywords in requirements
- Proper scenario format (#### heading, GIVEN/WHEN/THEN)
- No delta markers (ADDED, MODIFIED, REMOVED)
Listing
openspec list --specs
Shows all generated domain specs.
Viewing
openspec show specs/user
Displays the user domain specification.
Changes
openspec change add-payment-feature
Works normally - generated specs serve as the baseline for proposed changes.
Merge Strategies
When specs already exist, openlore offers three strategies:
1. Replace (Default)
openlore generate
- Creates backup in
.openlore/backups/{timestamp}/ - Replaces existing specs with new generation
- Logs what was replaced
2. Merge
openlore generate --merge
- Preserves all existing content
- Appends "## Generated Analysis" section
- Never removes user-written requirements
3. Skip
openlore generate --no-overwrite
- Only writes specs for new domains
- Skips any existing spec files
- Useful for incremental generation
Workflow Examples
Initial Brownfield Adoption
# 1. Initialize OpenSpec (if not already done)
openspec init
# 2. Run openlore to reverse-engineer
openlore
# 3. Review generated specs
openspec list --specs
openspec show specs/user
# 4. Validate structure
openspec validate --all
# 5. Verify accuracy
openlore verify
# 6. Start spec-driven development
openspec change my-first-feature
Adding a New Domain
# 1. Code is added to codebase (e.g., new payment module)
# 2. Re-run analysis
openlore analyze --force
# 3. Generate only the new domain
openlore generate --domains payment --no-overwrite
# 4. Review and refine
openspec show specs/payment
Refreshing After Major Refactor
# 1. Re-analyze codebase
openlore analyze --force
# 2. Generate with merge to preserve manual edits
openlore generate --merge
# 3. Review differences
# (check the "## Generated Analysis" sections)
# 4. Manually reconcile if needed
Continuous Drift Detection
# 1. Install pre-commit hook (runs static mode — fast, no API key)
openlore drift --install-hook
# 2. Check drift on a feature branch
openlore drift --base main
# 3. Use LLM to filter false positives
openlore drift --use-llm
# 4. CI/CD integration
openlore drift --json --fail-on error
Using Custom LLM Endpoints
For enterprise teams or local model servers:
# Generate specs with a local vLLM server
openlore generate --api-base http://localhost:8000/v1
# Verify with an internal endpoint and self-signed cert
openlore verify --api-base https://llm.internal.corp/v1 --insecure
# Or configure once in .openlore/config.json:
# { "llm": { "apiBase": "http://localhost:8000/v1", "sslVerify": false } }
Domain Naming Conventions
openlore follows OpenSpec conventions for domain names:
| Convention | Example |
|---|---|
| Lowercase | user, order, payment |
| Kebab-case for multi-word | order-management, user-auth |
| Descriptive but concise | auth not authentication-service |
| Match existing names | If openspec/specs/user/ exists, use user |
Avoided names:
misc,other,utils(too generic)service,module(describes structure, not domain)- Implementation-specific names
Technical Notes Format
Each spec includes technical notes linking to implementation:
## Technical Notes
- **Implementation**: `src/services/user-service.ts`, `src/repositories/user-repo.ts`
- **Dependencies**: auth, notification, database
- **Patterns**: Repository pattern, Service layer
- **External Integrations**: Stripe (payment processing), SendGrid (email)
This helps developers:
- Find source code for requirements
- Understand dependencies between domains
- Identify integration points
Confidence Scores
openlore provides confidence scores at multiple levels:
Overall Confidence
In openspec/config.yaml:
openlore:
confidence: 0.85
Per-Domain Confidence
In each spec file header:
> Confidence: 82%
Interpretation
| Score | Meaning |
|---|---|
| 90-100% | High confidence, minimal review needed |
| 75-89% | Good coverage, review recommended |
| 50-74% | Partial coverage, significant review needed |
| <50% | Low confidence, consider manual writing |
Troubleshooting
Validation Failures
openspec validate --all
# Error: Invalid scenario format in specs/user/spec.md
Check:
- Scenario headings have exactly 4 hashtags (####)
- GIVEN/WHEN/THEN keywords are present
- No delta markers (ADDED, MODIFIED, REMOVED)
Missing Domains
If expected domains aren't generated:
- Check analysis output:
.openlore/analysis/repo-structure.json - Verify file scoring in
.openlore/analysis/SUMMARY.md - Adjust scoring or explicitly add domains
Incorrect Clustering
If files are grouped wrong:
- Review dependency graph:
.openlore/analysis/dependency-graph.json - Check for missing imports in source code
- Manually specify domains:
openlore generate --domains user,order,auth
OpenSpec Plugin Marketplace
OpenSpec is adding a plugin marketplace so optional, heavyweight "engines" can extend it without bloating the core. OpenLore is the inaugural engine and the reference plugin. This section documents the OpenLore side of that contract. OpenSpec discovers, surfaces, gates, and invokes OpenLore as a subprocess — it never imports OpenLore's code.
openspec init
openspec lore generate # delegates to OpenLore: code archaeology → specs
openspec validate --specs # core OpenSpec takes over
Two manifests — do not confuse them
OpenLore ships two unrelated "manifest" artifacts, with distinct names so they never collide:
| Artifact | Command | What it is |
|---|---|---|
| Plugin manifest | openlore plugin-manifest emit|validate | The "openspec" key in package.json. The declarative contract the OpenSpec marketplace reads to discover/surface/gate OpenLore. |
| Federation manifest | openlore manifest emit|validate | .well-known/openlore.json — a repo's public-symbol self-description for cross-repo federation. Unrelated to the marketplace. |
The plugin manifest is the "openspec" key in OpenLore's own package.json (the single source of
truth — scannable from node_modules with zero extra files). Discovery needs no command; OpenSpec
reads the static key. openlore plugin-manifest exists only for CI validation and non-npm
distribution:
openlore plugin-manifest emit --json # print the manifest (stdout only)
openlore plugin-manifest validate # schema + semantic check; exit 0 valid / 1 invalid / 2 not found
The manifest declares: manifestVersion, id, namespace (lore), bin (openlore, with an
npx binArgs fallback), openspecCompat, the help-only commands[], contributed skills[], and
ownsConfigKeys (["openlore"]).
Surfaced commands
commands[] is help/completion only — it does not route execution (the host passes everything
after the namespace verbatim to the bin). OpenLore surfaces the spec-relevant, externally-useful
subcommands and omits internal/experimental (panic-*, gryph-watch, serve, view, telemetry)
and host-owned lifecycle (install, connect, setup):
generate · drift · verify · analyze · orient · digest · decisions
Each is safe to run as a non-interactive child process: deterministic exit codes, no blocking prompts
when stdin/stdout is not a TTY, machine output (--json) on stdout only with logs on stderr,
and project-root resolution from the spawn directory.
Node-version guard
OpenLore requires Node ≥22.13 (the first line where the built-in node:sqlite is available
without runtime flags); OpenSpec requires only ≥20.19. A user on Node 20/21 can run
openspec lore generate, which spawns openlore under an unsupported Node. engines is advisory at
install time and does not protect the spawn, so OpenLore fails fast at runtime: one legible
stderr line naming the required and actual versions, and a stable exit code 78 — never a stack
trace or a partial run, so the host propagates a legible failure.
Config-key ownership
OpenLore declares ownsConfigKeys: ["openlore"] and writes only the openlore key in
openspec/config.yaml. When OpenSpec already created the config (host-owned keys such as version,
profile, delivery, workflows, featureFlags, plugins are present), OpenLore splices in only
its own block and leaves every other key and comment byte-for-byte unchanged — CRLF line endings,
inline-comment spacing, and folded scalars are all preserved, because the write is a top-level-block
text splice, not a YAML re-serialization. It never introduces or overwrites a host-owned key, and it
skips context auto-injection (the host owns context). A host config that is not valid YAML is
refused with a clear error, never clobbered. When no config exists, standalone OpenLore is the
legitimate creator and may seed schema/context as before.
The MCP server stays separately wired
Subprocess delegation fits OpenLore's batch/one-shot commands. OpenLore's persistent MCP server
(openlore mcp, the long-lived process agents talk to continuously) is a different shape and is
not modeled as a per-call delegated subcommand — it is wired into the agent's MCP configuration
independently (today via .mcp.json) and stays there. The plugin surfaces one-shot orient for the
openspec lore orient "task" ergonomic; the continuous orientation runtime is out of the delegation
path by design. (A future mcp-capability advertisement is a Phase 2 idea.)
Recommended registry.json entry (for the OpenSpec side)
When the OpenSpec curated registry lands, the inaugural OpenLore listing should be:
{
"id": "openlore",
"package": "openlore",
"namespace": "lore",
"summary": "Reverse-engineer living OpenSpec specs from existing code, then keep code and specs in sync.",
"homepage": "https://github.com/clay-good/openlore#readme",
"repository": "https://github.com/clay-good/openlore",
"openspecCompat": ">=0.1.0"
}
openspecCompat is kept canonical with the @fission-ai/openspec peer-dependency range (a CI guard
asserts they agree) and will be pinned to the first OpenSpec release that ships the loader.
Status
Phase 1 (manifest, Node-version guard, delegation-safety guarantees, config-key ownership, CI
coherence guard, docs) is implemented. Handing skill/workflow distribution fully to OpenSpec and
retiring/gating the OpenLore installer, plus the onboard-from-code workflow and optional mcp
wiring, are Phase 2 and land with the host loader.
Best Practices
-
Review Before Committing
- Always review generated specs before committing
- Check for inaccuracies or missing context
-
Preserve Manual Edits
- Use
--mergeor--no-overwriteto protect edits - Consider adding manual sections after "## Technical Notes"
- Use
-
Regular Verification
- Run
openlore verifyperiodically - Address gaps identified in verification
- Run
-
Incremental Updates
- Generate specific domains when adding features
- Don't regenerate everything on every change
-
Version Control
- Commit
.openlore/config.json(project configuration) - Don't commit
.openlore/analysis/(regeneratable) - Do commit
openspec/specs/(the actual specs)
- Commit