SEED_SPEC_SCHEMA
Seed Spec Schema — Structure and field definitions for the immutable core spec document, and how jev_judge serializes it for Jev
The Seed Spec is a constitutional document created before the PRD. After locking (LOCKED), it can only be changed through the explicit evolution protocol.
It is also the state that Jev judges. skills/jev-gate/scripts/jev_judge.py parse
turns this
markdown into JSON deterministically — keep the headings and table columns exactly as below, or the
parser reports the section as UNVERIFIED (which blocks the gate; it is never scored as a pass).
Document Structure#
# Seed: {project-name} | v{major.minor.patch} | Status: {DRAFT|LOCKED}
## Metadata
- Created: {YYYY-MM-DD}
- Author: {author}
- Locked: {YYYY-MM-DD or N/A}
- Ambiguity Score: {0.0-1.0 | Not evaluated}
- Gate: {NOT-EVALUATED | PASS | CONDITIONAL | FAIL | UNVERIFIED} ({model} · {YYYY-MM-DD} · {spec_hash_short})
## 1. Core Problem
> Immutable after locking. Cannot evolve.
{Core problem defined in a single sentence, without assumptions}
### Problem Rationale
- {Rationale based on data or user interviews 1}
- {Rationale 2}
## 2. Immutable Constraints
> Immutable after locking. Each item requires rationale.
| ID | Constraint | Rationale | Type |
|----|-----------|-----------|------|
| C-01 | {Constraint} | {Why this constraint is needed} | Technical/Business/Legal |
| C-02 | {Constraint} | {Rationale} | {Type} |
## 3. Domain Entities
> After locking, only additions are allowed (existing entities cannot be modified)
### {Entity Name}
- **Attributes**: {Attribute list}
- **Relationships**: {Relationships with other entities}
- **Invariants**: {Conditions that must always hold true}
## 4. Acceptance Boundaries
> One flat table. Each row is a single acceptance criterion with its own ID and Status.
> FLAT only — no parent_id / no nesting. Nested AC decomposition (sub-criteria, trees) is OUT OF SCOPE.
| ID | Type (Must/Must-Not/May) | Acceptance Criterion | Status |
|----|--------------------------|----------------------|--------|
| AC-01 | Must | {Requirement that must be satisfied} | PENDING |
| AC-02 | Must-Not | {Thing that must never be done} | PENDING |
| AC-03 | May | {Thing that may or may not be implemented} | PENDING |
- **ID**: `AC-01`, `AC-02`, ... (sequential, stable; never reused after removal).
- **Type**: exactly one of `Must` / `Must-Not` / `May`.
- **Status**: one of `PENDING` / `IN-PROGRESS` / `MET` / `FAILED` / `WAIVED`. Defaults to `PENDING` at create time.
- `/cc-spec:status` echoes Status **verbatim from the parsed table** (no re-judgment). Whether a piece of
work actually meets a row is a separate **Jev noul judgment** (`met_AC-NN` / `violates_AC-NN` in
`questions/drift.json`) — never lexical matching (Korean criterion text has no word boundaries and uses josa).
## 5. Exposed Assumptions
> Assumptions confirmed in the /cc-spec:seed assumption pass (at least 3)
| ID | Assumption | Verification Status | Verification Method |
|----|-----------|-------------------|-------------------|
| A-01 | {Assumption content} | Verified/Unverified/Disproved | {Verification method} |
## 6. Evolution Log
> Append-only. Records change history after locking.
| Version | Date | Changes | Reason | Approver |
|---------|------|---------|--------|----------|
| v1.0.0 | {Date} | Initial version | Creation | {Approver} |
Serialization for Jev (jev_judge.py parse)#
Jev does not process markdown tables well, so the parser sends arrays of objects, never raw markdown:
| Section | JSON key | Row shape |
|---|---|---|
| header | project, version, status |
strings |
| Metadata | metadata |
{created, author, locked, ambiguity_score, gate} |
| S1 | s1_core_problem, s1_rationale | string, string[] |
| S2 | s2_constraints |
{id, constraint, rationale, type} |
| S3 | s3_entities |
{name, attributes, relationships, invariants} |
| S4 | s4_acceptance | {id, type, criterion, status} |
| S5 | s5_assumptions |
{id, assumption, status, method} |
| S6 | s6_evolution |
{version, date, changes, reason, approver} |
A section whose heading exists but whose table cannot be parsed lands in _unparseable and its
mechanical checks report UNVERIFIED — treated exactly like FAIL.
Field Rules#
State Transitions#
DRAFT ──(Jev gate PASS + --lock | user confirmation)──▶ LOCKED
LOCKED ──(evolution protocol, Jev drift LOW/MEDIUM-confirmed)──▶ LOCKED (v+1)
- DRAFT: Freely modifiable
- LOCKED: Evolution protocol required (
/cc-spec:evolve)
Version Rules (SemVer)#
| Change Type | Version Change | Example |
|---|---|---|
| Domain entity addition | MINOR | v1.0.0 → v1.1.0 |
| Acceptance boundary May item change | MINOR | v1.1.0 → v1.2.0 |
| Assumption verification status update | PATCH | v1.2.0 → v1.2.1 |
| Core problem change | Not allowed | — Requires new Seed creation |
| Immutable constraint change | Not allowed | — Requires new Seed creation |
Lock Policy#
-
Can be locked only when
/cc-spec:verifyreports Gate: PASS — mechanical M-01..M-10 all PASS, Jev ambiguity score ≤ 0.2, panel blocking = 0, and the verdict stamp matches the current file hash. UNVERIFIED (no API key, API error, missing answer) can never be locked. - After locking, Core Problem (S1) and Immutable Constraints (S2) cannot be modified
- Domain Entities (S3) can only be added (existing ones cannot be modified)
-
In Acceptance Boundaries (S4), only rows whose Type is
Maycan be changed (Must / Must-Not rows are immutable; their Status may still advance via the evolution protocol) - Maximum 3 evolutions per cycle
File Location#
docs/seed-spec-{project-slug}.md