Every ambiguity in your spec
is a token your agent burns.

Then a clarifying question mid-build. Then a fix after it ships. Then the context you keep re-explaining.

SpecScore is an open, lintable file format for specifications β€” like JSON for data. With a CLI your agents call to query and edit them β€” like jq for JSON.

A format for the specs you already write.

SpecScore is a Markdown + YAML format for features, requirements, and acceptance criteria. It defines a structure your team and your AI agents both understand β€” and it's just files in your repo.

A SpecScore project is three artifact kinds along a lifecycle: an Idea captures a half-formed direction; aFeature spec freezes the agreed behavior; aPlan decomposes the feature into ordered tasks that each cite the acceptance criteria they satisfy.

Why your agent cares. Because the structure is machine-readable, AI agents can query the spec through the CLI instead of reading every file. Ask "what should I work on next?" and the CLI returns open Features and unanswered questions. Same shape your team reviews; far less context the agent has to load.

Feature β†’ Requirements β†’ Acceptance Criteria β†’ Scenarios.
EXAMPLEideas/apply-discount.md
# Idea: Discount codes at checkout

**Status:** Approved
**Date:** 2026-05-19
**Owner:** cart-team
**Promotes To:** features/apply-discount

## Problem Statement

How might we let returning customers redeem a
single promo code at checkout without breaking
the existing cart math or the site-wide promotion
engine?

## Recommended Direction

A registered user can apply **one** valid code per cart.
Codes are time-bounded and replaceable. Stacking
with site-wide promotions follows a deterministic
tiebreak rule (higher discount wins; site-wide
wins on tie).
EXAMPLEfeatures/checkout/apply-discount.md
---
status: approved
owners: [cart-team]
---

# Feature: Apply Discount

A registered user can apply one valid discount code to their cart at checkout.

## Requirements

### R1 β€” Single-code limit

- **AC-1** The cart accepts at most one discount code at a time.
- **AC-2** Applying a second code replaces the first, with a visible confirmation.
- **AC-3** Removing the code restores the original cart total.

### R2 β€” Code validity

- **AC-1** A code is valid only if `valid_from` ≀ now ≀ `valid_until`.
- **AC-2** Invalid codes show a specific error message.

### R3 β€” Discount application

- **AC-1** Percentage discounts apply to subtotal, not shipping.
- **AC-2** Discount totals round down to the nearest cent.
- **AC-3** Stacking with site-wide promotions follows the higher-of rule.

## Scenario: Apply a valid percent-off code

**GIVEN** a cart with subtotal $50.00 and shipping $10.00
**WHEN** the user applies code `SAVE20` (20% off)
**THEN** the cart shows subtotal $40.00, shipping $10.00, total $50.00
EXAMPLEplans/apply-discount/README.md
# Plan: Apply Discount

**Status:** approved
**Features:**
  - [apply-discount](../../features/apply-discount.md)
**Author:** cart-team
**Effort:** M
**Impact:** high

## Context

The apply-discount Feature is Approved. This plan
breaks it into ordered tasks; each task cites the
acceptance criteria it satisfies.

## Tasks

### T1 β€” Accept a `code` field on the cart
Cites: R1.AC-1, R1.AC-2, R1.AC-3

### T2 β€” Validate code time-window
Cites: R2.AC-1, R2.AC-2

### T3 β€” Apply percentage to subtotal, round down
Cites: R3.AC-1, R3.AC-2

### T4 β€” Higher-of tiebreak vs site-wide promo
Cites: R3.AC-3

### T5 β€” Confirmation toast on apply / replace
Cites: R1.AC-2

Specs are hierarchical. So your spec tree is too.

Flat list at fifty Features becomes a dump. SpecScore keeps the tree navigable: parents own scope, children own detail, and the directory shape mirrors the product shape your team already reasons about.

Open questions roll up. Unresolved questions at any depth surface in every parent's index. You see what is blocking a release from the top of the tree, without spelunking to find it.

Most spec tools assume a flat list works. It does β€” until it doesn't. SpecScore is built for the moment when "where does this Feature live?" starts to matter.

See it live in specscore-cli β†’

FROMspecscore-cli/spec/features/cli/
spec/features/cli/
β”œβ”€β”€ agent/
β”‚   └── setup/
β”œβ”€β”€ feature/
β”‚   β”œβ”€β”€ change-status/
β”‚   β”œβ”€β”€ deps/
β”‚   β”œβ”€β”€ info/
β”‚   β”œβ”€β”€ list/
β”‚   β”œβ”€β”€ new/
β”‚   β”œβ”€β”€ refs/
β”‚   └── tree/
β”œβ”€β”€ idea/
β”‚   β”œβ”€β”€ change-status/
β”‚   β”œβ”€β”€ new/
β”‚   └── relocate/
β”œβ”€β”€ spec/
β”‚   └── lint/
β”‚       β”œβ”€β”€ issue-rules/     ← sub-sub-Feature
β”‚       └── plan-rules/
β”œβ”€β”€ task/
β”‚   β”œβ”€β”€ info/
β”‚   β”œβ”€β”€ list/
β”‚   └── new/
└── … 10 more top-level Features

What goes wrong without it.

  • Specs drift.

    The spec in the doc, the thread in Slack, and the code in the PR quietly disagree for weeks. Nobody can find which version of the conversation is canonical.

    The fix

    One canonical spec, in the repo, versioned with the code.specscore lint runs in CI on every push; git history is the audit trail. The spec, the thread, and the PR can't quietly disagree β€” they share the same file under review.

  • AC stay vague.

    "Handles errors gracefully" was supposed to mean"show a toast and roll back." Or was it"log and retry?" The agent has to guess.

    The fix

    The linter catches ambiguous criteria before any code is written. The verb fails lint because it has no concrete object or condition. Fixing the warning forces you to write what "handles" actually means.

  • Agents ship the wrong thing.

    Humans interpolate, ask, clarify. AI agents don't β€” they implement whatever pattern was most common in their training data. The code passes review because it doessomething sensible. It's just not what you meant.

    The fix

    Structured input. AI agents parse REQ IDs, AC IDs andGIVEN / WHEN / THENscenarios deterministically β€” there's nothing to interpolate. Plans must cite the AC IDs each task satisfies, so what was built is always checkable against what was specified.

  • You pay twice for a vague spec.

    The agent builds something from an ambiguous brief, you notice it's wrong, and you re-prompt β€” four different ways. Every half-built feature and every retry is billed in tokens. The vaguer the spec, the more rounds before it lands.

    The fix

    Lint the spec before the agent runs. specscore lintrejects vague verbs and missing acceptance criteria up front, so the agent implements a concrete brief once instead of guessing across retries. The cheapest agent run is the one you don't repeat.

  • Agents pay to re-read your specs.

    An AI agent answering "what depends on this feature?"reads the whole feature README. Then every parent index. Then sibling features just in case. Every query is a re-parse. Tokens evaporate; latency rises; the bill rises with them.

    The fix

    Structured queries instead of file reads.specscore feature tree returns the hierarchy as YAML; feature deps walks the transitive dependency chain; feature info exposes status, owner, and open questions β€” each in one call. The agent navigates the graph, not the prose.

  • Code forgets its spec.

    Six months later, you're debugging a function and nobody remembers which feature it implements. The link between code and spec lived in someone's head, and that person left. The PR that introduced it is in the archive.

    The fix

    Source annotations. Comments like// specscore: features/checkout/apply-discount/R1.AC-2make the link machine-readable and survive every refactor.specscore code deps answers"what spec does this file implement?" in one call; the linter flags references that point at deleted specs.

  • Status changes leak.

    You move a feature from Draft to Approvedin its README, but the parent features-index still lists it as Draft. Three weeks later, an agent reading the index thinks it's still in flux. The status lives in two places now, and they disagree.

    The fix

    specscore feature change-status rewrites the feature's Status field and re-syncs the features-index row in one atomic step β€” lint failure rolls the rewrite back. The status exists in one canonical place; the index is a generated view of it.

  • Specs forget what they don't know.

    A spec reads as if it's settled β€” until you remember the"and what about timezones?" question got lost in a Slack thread two months ago and never made it into the doc. Now you're in implementation, and nobody can say what the answer was.

    The fix

    Every SpecScore feature has a mandatoryOutstanding Questions section. The linter rejects features that omit it β€” empty is fine ("None at this time."), but missing isn't. Unresolved questions are visible in the doc, queryable from the CLI, and impossible to forget.

The easiest way to start: SpecStudio Skills for AI agents.

SpecStudio Skillsis a Claude Code plugin that turns the SpecScore lifecycle into four slash commands. Install it once, then run them in order β€” from raw idea to executable plan.

$ /plugin marketplace add specscore/ai-marketplace
$ /plugin install specstudio@specscore
Claude Codeautocomplete
> /specstudio:
   /specstudio:ideate     Refine a raw idea into a SpecScore Idea
   /specstudio:specify    Turn an approved Idea into a Feature spec
   /specstudio:plan       Decompose a Feature into a Plan of tasks
   /specstudio:implement  Execute the next task in a Plan
Claude Code/ideate
> /ideate Let's think about how the basket screen should look for the checkout process.
Claude Code/specify
> /specify Let's define spec/ideas/checkout-basket.md
Claude Code/plan
> /plan Implement spec/features/checkout/basket/README.md
Claude Code/implement
> /implement Execute spec/plans/checkout-basket/README.md

Free for public and open-source repositories. The plugin source is atspecscore/specstudio-skills β€” MIT-licensed.

Check your spec in two passes.

A fast deterministic linter, then a deeper AI review β€” run against the spec you just saw. Two passes, two kinds of certainty.

Run the linter.

deterministic

Same result every run. Free, fast, runs on every commit.

Review & score with AI.

non-deterministic

Reads for meaning, like a human reviewer.

Here's what it won't catch.

A linter that claims it catches everything is lying. SpecScore is a layered system β€” a deterministic linter, an AI reviewer, and your own judgment β€” and each layer has a real edge. We wrote them all down: what lint catches, what the AI catches, and what's still on you because no tool can do it yet.

What the linter won't catch β†’

What's in the box.

The format

Markdown + YAML. Works in any editor that opens text. No new tooling required to start writing.

The linter

Go CLI. Validates structure, references, and completeness. Exits non-zero on failure β€” works in your terminal and your CI.

Bring your own everything.

SpecScore is just files in your repo. No SaaS to sign up for. No orchestrator required. Use it with Linear, Jira, GitHub Projects, raw Markdown in a folder, or no project management at all. The format and the linter don't care where your tickets live.

Prefer the CLI?

Don't use AI agents, or need spec validation in your CI pipeline? There's a CLI for that. Install it in 60 seconds β€” no account, no telemetry, works on any directory of Markdown specs.

$ curl -fsSL https://specscore.md/install/get-cli | sh

Lint the example spec that ships with the binary:

$ specscore lint examples/checkout/apply-discount.md

See the output:

  examples/checkout/apply-discount.md
    βœ“ R1  has acceptance criteria
    βœ“ R2  has acceptance criteria
    βœ— R3  no acceptance criteria
    βœ— link     "Related Feature: discount-tiers" not found
    βœ— section  "Open Questions" missing

  3 problems   exit 1

Read the full docsContribute on GitHub

Built on SpecScore.

Got questions?

Here are the questions people keep asking. Each links to the full answer on the FAQ page.

How is SpecScore different from spec-kit?

spec-kit is a methodology; SpecScore is a storage format for the specs you write, plus a CLI. They compose β€” a spec-kit extension that writes SpecScore is in flight.

Full answer β†’
How are SpecStudio Skills different from Superpowers and agent-skills?

Similar in spirit β€” all three guide AI agents through ideate β†’ specify β†’ plan β†’ implement. SpecStudio Skills writes artifacts in the SpecScore format and uses thespecscore CLI for queries and status changes; the others read and write files directly. Pick SpecStudio when token consumption matters.

Full answer β†’
Why a CLI β€” can I not just install skills?

Skills cannot reliably enforce referential integrity, return filtered queries without reading every file, or mutate the right status field at the right time. The CLI does each in one call. Skills read every file to do the same.

Full answer β†’
Why "SpecScore" β€” what does it score?

Two readings, both intended. Partitura β€” a structured score of requirements. And a literal quality grade β€” the CLI linter is the deterministic score, and the/score skill is the AI-judged A–F readiness grade.

Full answer β†’
Where are the token-savings numbers?

A measured side-by-side comparison is in flight before the Habr post. Structurally: the CLI returns a small structured list; a vanilla agent reads N documents instead.

Full answer β†’

Specs that pass.

or read the docs

β˜… Star us on GitHub β€” it helps others find the project.GitHub stars

Have an idea how to improve this page? Let us know.