Validatable spec-driven development (inspired by openspec and kiro)
Tired of your specs disappearing like a ghost? spectr archive is your friend - it merges your change deltas into spec files so nothing gets lost.
Built with Go
- Overview
- Key Features
- Supported AI Tools
- Installation
- CI Integration
- Quick Start
- Command Reference
- Architecture & Development
- Contributing
- Advanced Topics
- Troubleshooting
- Links & Resources
Spectr is a CLI tool for validatable spec-driven development. It helps teams manage specifications and changes through a structured three-stage workflow:
- Creating Changes: Write proposals with delta specs showing what SHOULD change
- Implementing Changes: Follow the implementation checklist in
tasks.md - Archiving Changes: Merge deltas into specs, preserving history
Spectr enforces a clear separation between current truth (spectr/specs/ - what IS built) and proposed changes (spectr/changes/ - what SHOULD change), ensuring all modifications are intentional, documented, and validated.
- Structured Workflow: Propose, validate, implement, and archive changes systematically
- Delta Specifications: Track proposed changes separately from current specs
- Strict Validation: Enforce requirements format, scenarios, and spec consistency
- Interactive TUI: Beautiful terminal UI for wizards and selection flows
- Archive Merging: Automatically merge change deltas into spec files with
spectr archive - Clean Architecture: Well-organized codebase with clear separation of concerns
- Comprehensive Testing: Table-driven tests with high coverage
- Nix Integration: First-class Nix flake support for reproducible builds
Spectr integrates with popular AI coding assistants to provide spec-driven development workflows:
| Tool | Website |
|---|---|
| Claude Code | Anthropic's CLI AI coding agent |
| Gemini CLI | Google's Gemini CLI |
| Cursor | AI-powered code editor |
| Windsurf | AI code editor by Codeium |
| Aider | AI pair programming in terminal |
| Cline | Autonomous coding agent for VS Code |
| Continue | Open-source AI code assistant |
| Codex CLI | OpenAI's Codex CLI |
| Crush | A glamorous CLI AI coding agent by Charmbracelet |
| Kilocode | AI coding assistant |
| Antigravity | AI coding assistant |
| Qwen Code | Alibaba's Qwen Code assistant |
| CoStrict | AI coding assistant |
| Qoder | AI coding assistant |
Run spectr init to configure Spectr for your preferred AI tools.
The recommended way to install Spectr is via Nix flakes:
# Run directly without installing
nix run github:connerohnesorge/spectr
# Install to your profile
nix profile install github:connerohnesorge/spectr
# Add to your flake.nix inputs
{
inputs.spectr.url = "github:connerohnesorge/spectr";
}If you prefer to build from source:
# Clone the repository
git clone https://github.com/connerohnesorge/spectr.git
cd spectr
# Build with Go
go build -o spectr
# Or use Nix
nix build
# Install to your PATH
mv spectr /usr/local/bin/ # or any directory in your PATH- Go 1.25+ (if building from source)
- Nix with flakes enabled (optional, for Nix installation)
- Git (for project version control)
Spectr can be integrated into your GitHub Actions workflows using spectr-action to automatically validate specifications and changes on every push or pull request.
Add the following to your workflow file (e.g., .github/workflows/ci.yml):
name: CI
on:
push:
branches: ["**"]
pull_request:
branches: ["**"]
jobs:
spectr-validate:
name: Validate Specs
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate with Spectr
uses: connerohnesorge/spectr-action@v1This workflow will:
- Run on every push and pull request
- Check out your repository with full git history
- Validate all specifications and active changes
- Fail the build if validation errors are found
For strict validation mode and additional options, see the spectr-action documentation.
Start by initializing Spectr in your project:
# Initialize with interactive wizard
spectr init
# Or specify a path
spectr init /path/to/project
# Non-interactive mode with defaults
spectr init --non-interactiveThis creates the following structure:
your-project/
└── spectr/
├── project.md # Project conventions and context
├── specs/ # Current specifications (truth)
│ └── [capability]/ # One directory per capability
│ ├── spec.md # Requirements and scenarios
│ └── design.md # Technical patterns (optional)
└── changes/ # Proposed changes
└── archive/ # Completed changes
Let's create a simple "Hello World" change:
# 1. List current state
spectr list # See active changes
spectr list --specs # See existing capabilities
# 2. Create a change directory
mkdir -p spectr/changes/add-hello-world/specs/greeting
# 3. Write a proposal
cat > spectr/changes/add-hello-world/proposal.md << 'EOF'
# Change: Add Hello World Greeting
## Why
We need a simple greeting capability to welcome users.
## What Changes
- Add new `greeting` capability with hello world functionality
## Impact
- Affected specs: greeting (new)
- Affected code: None (example)
EOF
# 4. Create delta spec
cat > spectr/changes/add-hello-world/specs/greeting/spec.md << 'EOF'
## ADDED Requirements
### Requirement: Hello World Greeting
The system SHALL provide a greeting function that returns "Hello, World!".
#### Scenario: Greet successfully
- **WHEN** the greeting function is called
- **THEN** it SHALL return "Hello, World!"
EOF
# 5. Create tasks checklist
cat > spectr/changes/add-hello-world/tasks.md << 'EOF'
## 1. Implementation
- [ ] 1.1 Create greeting.go file
- [ ] 1.2 Implement HelloWorld() function
- [ ] 1.3 Write tests for greeting
- [ ] 1.4 Update documentation
EOF
# 6. Validate the change
spectr validate add-hello-world --strict
# 7. After implementation, archive it
spectr archive add-hello-worldUnderstanding the directory structure is crucial:
spectr/
├── project.md # Project-wide conventions
├── specs/ # CURRENT TRUTH - what IS built
│ └── [capability]/
│ ├── spec.md # Requirements with scenarios
│ └── design.md # Technical patterns (optional)
├── changes/ # PROPOSALS - what SHOULD change
│ ├── [change-id]/
│ │ ├── proposal.md # Why, what, impact
│ │ ├── tasks.md # Implementation checklist
│ │ ├── design.md # Technical decisions (optional)
│ │ └── specs/ # Delta changes
│ │ └── [capability]/
│ │ └── spec.md # ADDED/MODIFIED/REMOVED requirements
│ └── archive/ # Completed changes (history)
│ └── YYYY-MM-DD-[change-id]/
Key Concepts:
- spectr/specs/: The source of truth for what's currently built
- spectr/changes/: Proposed modifications, kept separate until approved
- spectr/changes/archive/: Historical record of all changes with timestamps
- Delta Specs: Use
## ADDED,## MODIFIED,## REMOVED, or## RENAMED Requirementsheaders
Initialize Spectr in a project directory.
Usage:
spectr init [PATH] [FLAGS]Flags:
--tools <tools>: Comma-separated list of tools to include (e.g.,git,github)--non-interactive: Skip interactive wizard, use defaults--path <path>: Project directory (default: current directory)
Examples:
# Interactive initialization (recommended)
spectr init
# Initialize specific directory with Git integration
spectr init /path/to/project --tools git
# Non-interactive with defaults
spectr init --non-interactiveOutput:
✓ Created spectr/ directory
✓ Created specs/ directory
✓ Created changes/ directory
✓ Created project.md
✓ Spectr initialized successfully!
List active changes or specifications.
Usage:
spectr list [FLAGS]Flags:
--specs: List specifications instead of changes--json: Output in JSON format--long: Show detailed information--no-interactive: Disable interactive selection
Examples:
# List all active changes
spectr list
# List all specifications
spectr list --specs
# Get detailed JSON output
spectr list --json --long
# List specs with full details
spectr list --specs --longExample Output:
Active Changes:
add-two-factor-auth Add 2FA authentication support
refactor-validation Improve validation error messages
Run 'spectr view <change>' for details
Validate changes or specifications against rules.
Usage:
spectr validate [ITEM] [FLAGS]Flags:
--strict: Enable strict validation (warnings become errors)--type <change|spec>: Disambiguate when name conflicts exist--json: Output validation results as JSON--no-interactive: Skip interactive mode
Examples:
# Validate a specific change (strict mode recommended)
spectr validate add-two-factor-auth --strict
# Validate all changes interactively
spectr validate
# Validate a specification
spectr validate auth --type spec
# Get JSON validation results
spectr validate add-2fa --jsonValidation Rules:
- Every requirement MUST have at least one scenario
- Scenarios MUST use
#### Scenario:format (4 hashtags) - Purpose sections MUST be at least 50 characters
- MODIFIED requirements MUST include complete updated content
- Change directories MUST contain at least one delta spec
Example Output:
Validating change: add-two-factor-auth
✓ Proposal file exists
✓ Delta specs found
✓ All requirements have scenarios
✓ Scenario formatting correct
✓ All validations passed!
Accept a change proposal and convert tasks.md to tasks.jsonc format for stable machine-readable task tracking.
Usage:
spectr accept <CHANGE-ID> [FLAGS]Flags:
--dry-run: Preview conversion without writing files--no-interactive: Disable interactive prompts
What It Does:
- Validates the change before accepting
- Parses
tasks.mdand extracts task sections, IDs, descriptions, and status - Writes
tasks.jsoncwith structured task data - Removes
tasks.mdto prevent drift (JSON becomes single source of truth)
Example:
# Accept a change (with validation)
spectr accept add-two-factor-auth
# Preview conversion without making changes
spectr accept add-two-factor-auth --dry-runtasks.jsonc Format:
{
"version": 1,
"tasks": [
{
"id": "1.1",
"section": "Implementation",
"description": "Create database schema",
"status": "pending"
}
]
}Status Values:
pending: Task not startedin_progress: Task being worked oncompleted: Task finished
Why JSON? Based on Anthropic's research on effective harnesses for long-running agents, JSON task lists are more stable for AI agents:
- Structural validation catches corruption immediately
- Atomic field updates prevent accidental overwrites
- Machine-readable format eliminates parsing errors
Archive a completed change, merging deltas into specs.
Usage:
spectr archive <CHANGE-ID> [FLAGS]Flags:
--skip-specs: Archive without updating specs (for tooling-only changes)--yes/-y: Skip confirmation prompts (non-interactive)--no-interactive: Disable interactive mode
Partial ID Matching:
You don't need to type the full change ID. Spectr supports intelligent partial matching:
# Instead of typing the full ID:
spectr archive refactor-unified-interactive-tui
# Use a prefix:
spectr archive refactor
# Output: Resolved 'refactor' -> 'refactor-unified-interactive-tui'
# Or a substring:
spectr archive unified
# Output: Resolved 'unified' -> 'refactor-unified-interactive-tui'
# Case-insensitive:
spectr archive REFACTOR
# Output: Resolved 'REFACTOR' -> 'refactor-unified-interactive-tui'The matching algorithm:
- Exact match: Used directly (no resolution message)
- Prefix match: Finds IDs starting with your input (case-insensitive)
- Substring match: Finds IDs containing your input (fallback if no prefix match)
If multiple changes match, you'll get an error listing the ambiguous matches.
Examples:
# Archive with interactive confirmation
spectr archive add-two-factor-auth
# Archive without updating specs
spectr archive fix-typo --skip-specs
# Non-interactive archive (for CI/CD)
spectr archive add-feature --yes
# Use partial ID for long change names
spectr archive refactor --yesWhat It Does:
- Validates the change before archiving
- Merges delta specs into
specs/(unless--skip-specs) - Moves
changes/[name]→changes/archive/YYYY-MM-DD-[name] - Preserves complete history in archive
Example Output:
Archiving change: add-two-factor-auth
✓ Validation passed
✓ Merging deltas into specs/auth/spec.md
- Added 2 requirements
- Modified 1 requirement
✓ Moving to archive/2025-11-18-add-two-factor-auth/
✓ Archive complete!
Display detailed information about a change or spec.
Usage:
spectr view [ITEM] [FLAGS]Flags:
--type <change|spec>: Specify item type--json: Output in JSON format--deltas-only: Show only delta specifications (changes only)
Examples:
# View a change interactively
spectr view
# View specific change
spectr view add-two-factor-auth
# View spec details
spectr view auth --type spec
# Debug delta parsing
spectr view add-2fa --json --deltas-onlyExample Output:
Change: add-two-factor-auth
Status: Active
Proposal:
Add two-factor authentication support via OTP
Affected Specs:
- auth
- notifications
Tasks: 4 total, 2 completed
Delta Summary:
auth:
- ADDED: 2 requirements
- MODIFIED: 1 requirement
Spectr follows Clean Architecture principles with clear separation of concerns:
spectr/
├── cmd/ # CLI command definitions (thin layer)
│ ├── root.go # Kong CLI framework setup
│ ├── init.go # Init command handler
│ ├── list.go # List command handler
│ ├── validate.go # Validate command handler
│ ├── archive.go # Archive command handler
│ └── view.go # View command handler
├── internal/ # Core business logic (not importable externally)
│ ├── init/ # Initialization wizard and setup
│ ├── validation/ # Spec and change validation rules
│ ├── parsers/ # Requirement and delta parsing
│ ├── archive/ # Archive workflow and spec merging
│ ├── list/ # Listing and formatting logic
│ ├── discovery/ # File discovery utilities
│ └── view/ # Display and formatting
├── main.go # Application entry point
└── testdata/ # Test fixtures and integration tests
Design Principles:
- Thin CLI Layer: Commands delegate to internal packages
- No Circular Dependencies: Strict dependency flow from cmd → internal
- Single Responsibility: Each package has one focused purpose
- Testability: Logic separated from I/O for easy testing
| Package | Purpose | Key Types |
|---|---|---|
cmd/ |
CLI command handlers using Kong framework | Command structs |
internal/init/ |
Project initialization wizard and templates | Wizard, Executor, Templates |
internal/validation/ |
Validation rules for specs and changes | Validator, Rule, ValidationResult |
internal/parsers/ |
Parse requirements, scenarios, and deltas | RequirementParser, DeltaParser |
internal/archive/ |
Archive changes and merge deltas into specs | Archiver, SpecMerger |
internal/list/ |
List changes and specs with formatting | Lister, Formatter |
internal/discovery/ |
Discover spec and change files | Discoverer, FileInfo |
internal/view/ |
Display detailed information with TUI | Dashboard, ProgressTracker |
# Clone the repository
git clone https://github.com/connerohnesorge/spectr.git
cd spectr
# Enter development shell (provides all tools)
nix develop
# Available tools:
# - go_1_25: Go compiler and runtime
# - air: Live reload during development
# - gopls: Language server for IDE integration
# - golangci-lint: Comprehensive linting
# - gotestsum: Enhanced test output
# - delve: Debugger# Install Go 1.25+
# Download from https://go.dev/dl/
# Clone repository
git clone https://github.com/connerohnesorge/spectr.git
cd spectr
# Install dependencies
go mod download
# Build
go build -o spectr
# Run
./spectr --helpSpectr uses table-driven tests with high coverage:
# Run all tests
go test ./...
# Run with coverage
go test ./... -cover
# Run with race detector
go test ./... -race
# Run with enhanced output (if gotestsum installed)
gotestsum --format testname
# Run specific package tests
go test ./internal/validation/...
# Run with verbose output
go test -v ./internal/parsers/...Test Organization:
- Unit Tests: Co-located with source files (
*_test.go) - Table-Driven: Subtests with
t.Run()for different scenarios - Integration Tests: Located in
testdata/integration/ - Test Fixtures: Stored in
testdata/directory
Example Test Structure:
func TestValidator_ValidateSpec(t *testing.T) {
tests := []struct {
name string
spec *Spec
wantErr bool
}{
{"valid spec", validSpec, false},
{"missing scenarios", specNoScenarios, true},
{"invalid format", malformedSpec, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validator.ValidateSpec(tt.spec)
if (err != nil) != tt.wantErr {
t.Errorf("got error = %v, wantErr = %v", err, tt.wantErr)
}
})
}
}We welcome contributions! Please follow these guidelines to ensure smooth collaboration.
-
Fork the Repository
# Click "Fork" on GitHub, then clone your fork git clone https://github.com/YOUR-USERNAME/spectr.git cd spectr
-
Create a Feature Branch
git checkout -b add-new-feature
-
Make Changes
- Follow code style guidelines
- Write tests for new functionality
- Update documentation as needed
-
Run Tests and Linting
go test ./... golangci-lint run -
Commit Your Changes
git add . git commit -m "Add new validation rule for scenarios"
-
Push and Create Pull Request
git push origin add-new-feature # Create PR on GitHub
- Formatting: Use
gofmt(orgofumptfor stricter formatting) - Linting: All code must pass
golangci-lint run - Naming Conventions:
- Packages: lowercase, single-word (e.g.,
validation,parsers) - Interfaces: Descriptive nouns (e.g.,
Validator,Parser) - Exported functions: Clear, verb-led names (e.g.,
ValidateSpec,ParseRequirement)
- Packages: lowercase, single-word (e.g.,
- Comments: All exported types and functions MUST have doc comments
- Error Handling: Use explicit error returns with context via
fmt.Errorfwrapping
Example:
// ValidateSpec checks if a specification meets all validation rules.
// It returns an error if any rule is violated.
func ValidateSpec(spec *Spec) error {
if spec == nil {
return fmt.Errorf("spec cannot be nil")
}
// validation logic...
}Use clear, descriptive commit messages:
<type>: <short summary>
<optional body>
<optional footer>
Types:
feat: New featurefix: Bug fixrefactor: Code refactoringtest: Adding or updating testsdocs: Documentation changeschore: Maintenance tasks
Examples:
feat: add strict validation mode
Implement --strict flag for validate command that treats
warnings as errors. Useful for CI/CD pipelines.
fix: correct scenario header parsing
Scenarios with extra whitespace were not being recognized.
Updated regex to trim whitespace before matching.
docs: update README with archive examples
All contributions MUST include appropriate tests:
- New Features: Add unit tests covering success and error cases
- Bug Fixes: Add regression test demonstrating the fix
- Refactoring: Ensure existing tests still pass
- Test Coverage: Aim for high coverage (current: >80%)
Running Tests Before PR:
# Run all tests
go test ./...
# Check coverage
go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out
# Run linting
golangci-lint run
# Format code
go fmt ./...Spectr implements a three-stage workflow for managing changes:
Create a proposal when you need to:
- Add features or functionality
- Make breaking changes (API, schema)
- Change architecture or patterns
- Optimize performance (changes behavior)
- Update security patterns
Skip proposals for:
- Bug fixes (restore intended behavior)
- Typos, formatting, comments
- Dependency updates (non-breaking)
- Tests for existing behavior
- Read
proposal.md- Understand what's being built - Read
design.md(if exists) - Review technical decisions - Read
tasks.md- Get implementation checklist - Run
spectr accept <id>- Convert to stable JSON format - Implement tasks sequentially
- Update task status in
tasks.jsoncwith values:pending,in_progress,completed - Approval gate: Do not implement until proposal is approved
After deployment:
- Run
spectr validate <change> --strictto ensure quality - Run
spectr archive <change>to merge deltas into specs - Changes move to
archive/YYYY-MM-DD-<change>/ - Specs in
specs/are updated with merged requirements
Delta specs describe proposed changes using operation headers:
## ADDED Requirements
### Requirement: New Feature
The system SHALL provide new functionality.
#### Scenario: Success case
- **WHEN** condition occurs
- **THEN** expected result
## MODIFIED Requirements
### Requirement: Existing Feature
[Complete modified requirement with all scenarios]
## REMOVED Requirements
### Requirement: Deprecated Feature
**Reason**: Why removing
**Migration**: How to handle existing usage
## RENAMED Requirements
- FROM: `### Requirement: Old Name`
- TO: `### Requirement: New Name`Key Rules:
- ADDED: New capabilities that stand alone
- MODIFIED: Changes to existing requirements (include FULL updated content)
- REMOVED: Deprecated features (provide reason and migration path)
- RENAMED: Name-only changes (use with MODIFIED if behavior changes too)
Spectr enforces strict validation rules to maintain quality:
| Rule | Description | Severity |
|---|---|---|
| Requirement Scenarios | Every requirement MUST have ≥1 scenario | Error |
| Scenario Format | Scenarios MUST use #### Scenario: (4 hashtags) |
Error |
| Purpose Length | Purpose sections MUST be ≥50 characters | Warning |
| MODIFIED Complete | MODIFIED requirements MUST be complete, not partial | Error |
| Delta Presence | Changes MUST have ≥1 delta spec | Error |
| Scenario Structure | Scenarios SHOULD have WHEN/THEN bullets | Warning |
| Header Matching | Operation headers use trim() - whitespace ignored | Info |
Strict Mode:
# Treat warnings as errors
spectr validate <change> --strictDebugging Validation:
# See detailed validation output
spectr validate <change> --json | jq '.errors'
# Check delta parsing
spectr view <change> --json --deltas-onlyThe spectr archive command performs an atomic operation:
-
Pre-Archive Validation
- Validates change structure
- Checks all delta specs
- Ensures requirements have scenarios
-
Delta Merging
- Reads each delta spec in
spectr/changes/<id>/specs/ - For each capability:
- ADDED: Appends to
specs/<capability>/spec.md - MODIFIED: Replaces entire requirement block
- REMOVED: Removes requirement (keeps comment)
- RENAMED: Updates requirement header
- ADDED: Appends to
- Reads each delta spec in
-
Archive Move
- Creates
spectr/changes/archive/YYYY-MM-DD-<id>/ - Moves entire change directory
- Preserves all history (proposal, tasks, design, deltas)
- Creates
-
Post-Archive Verification
- Validates updated specs
- Ensures merge was successful
- Reports summary
Example Archive:
$ spectr archive add-two-factor-auth
Archiving change: add-two-factor-auth
✓ Validation passed
✓ Merging deltas:
- specs/auth/spec.md: +2 ADDED, 1 MODIFIED
- specs/notifications/spec.md: +1 ADDED
✓ Moved to archive/2025-11-18-add-two-factor-auth/
✓ Archive complete!Problem: Validation fails because no delta specs found.
Solution:
- Ensure
changes/<name>/specs/directory exists - Create at least one
.mdfile with delta operations - Verify files have
## ADDED,## MODIFIED,## REMOVED, or## RENAMED Requirementsheaders
# Check delta structure
ls -la changes/my-change/specs/
cat changes/my-change/specs/*/spec.md | grep "^## "Problem: Requirement found without scenarios.
Solution: Use the exact format for scenarios (4 hashtags, specific text):
### Requirement: My Feature
The system SHALL do something.
#### Scenario: Success case
- **WHEN** user does X
- **THEN** system does YCommon Mistakes:
- Using
###(3 hashtags) instead of####(4 hashtags) - Using bold
**Scenario:**instead of header#### - Using bullets
- Scenario:instead of header
Problem: --strict flag causes warnings to fail.
Solution:
- Review warning messages carefully
- Fix underlying issues (often scenario structure or purpose length)
- Use non-strict mode during development:
spectr validate <change> - Use strict mode before archiving:
spectr validate <change> --strict
Problem: Multiple changes modify the same requirement.
Solution:
- Archive changes sequentially, not in parallel
- Resolve conflicts manually in
specs/after first archive - Validate the second change after first is archived
- Consider combining related changes into a single proposal
Yes. The approval gate is intentional. Changes should be reviewed and approved before implementation begins. This prevents wasted effort on changes that may be rejected or need significant revision.
Create multiple delta specs, one per capability:
spectr/changes/add-2fa-notifications/
├── proposal.md
├── tasks.md
└── specs/
├── auth/
│ └── spec.md # Auth-related deltas
└── notifications/
└── spec.md # Notification-related deltas
- specs/[capability]/design.md: Current technical patterns for a capability
- changes/[name]/design.md: Design decisions for a proposed change
The change's design.md explains new architectural decisions. After archiving, relevant design details may be added to capability design docs.
For minor fixes only: typos, formatting, clarifications that don't change meaning.
For everything else: Create a change proposal. This ensures:
- Changes are reviewed and approved
- History is preserved in archive
- Validation catches errors before merging
Use JSON output to see parsed structure:
# Check what was parsed
spectr view <change> --json --deltas-only | jq '.deltas[].requirements[].scenarios'
# Verify scenario count
spectr validate <change> --json | jq '.errors[] | select(.rule == "RequirementScenarios")'Archives accumulate but remain organized by date:
spectr/changes/archive/
├── 2025-11-15-add-auth/
├── 2025-11-16-fix-validation/
├── 2025-11-18-add-notifications/
└── ...
Periodically, you may:
- Compress old archives
- Move ancient archives to separate storage
- Keep 6-12 months in active repository
- GitHub Repository: github.com/connerohnesorge/spectr
- GitHub Action for CI/CD: connerohnesorge/spectr-action
- Specification Documentation: See
spectr/specs/for detailed capability specs - AI Agents Documentation: See spectr/AGENTS.md for AI assistant instructions
- Project Conventions: See spectr/project.md
- Issue Tracker: GitHub Issues
- Discussions: GitHub Discussions




