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:

KeywordMeaning
SHALL / MUSTAbsolute requirement
SHALL NOT / MUST NOTAbsolute prohibition
SHOULDRecommended
SHOULD NOTNot recommended
MAYOptional

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:

ConventionExample
Lowercaseuser, order, payment
Kebab-case for multi-wordorder-management, user-auth
Descriptive but conciseauth not authentication-service
Match existing namesIf 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

ScoreMeaning
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:

  1. Check analysis output: .openlore/analysis/repo-structure.json
  2. Verify file scoring in .openlore/analysis/SUMMARY.md
  3. Adjust scoring or explicitly add domains

Incorrect Clustering

If files are grouped wrong:

  1. Review dependency graph: .openlore/analysis/dependency-graph.json
  2. Check for missing imports in source code
  3. 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:

ArtifactCommandWhat it is
Plugin manifestopenlore plugin-manifest emit|validateThe "openspec" key in package.json. The declarative contract the OpenSpec marketplace reads to discover/surface/gate OpenLore.
Federation manifestopenlore 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

  1. Review Before Committing

    • Always review generated specs before committing
    • Check for inaccuracies or missing context
  2. Preserve Manual Edits

    • Use --merge or --no-overwrite to protect edits
    • Consider adding manual sections after "## Technical Notes"
  3. Regular Verification

    • Run openlore verify periodically
    • Address gaps identified in verification
  4. Incremental Updates

    • Generate specific domains when adding features
    • Don't regenerate everything on every change
  5. Version Control

    • Commit .openlore/config.json (project configuration)
    • Don't commit .openlore/analysis/ (regeneratable)
    • Do commit openspec/specs/ (the actual specs)