Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/CPS-VALIDATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# CPS Validation Rules

This document describes all validation rules applied to Cardano Problem Statement (CPS) documents.

These validations are to be ran automatically via Github workflow using [`/scripts/validate-cps.py`](./scripts/validate-cps.py).

These attempt to codify the guidance described within [CIP-9999 | Cardano Problem Statements](../CIP-9999/README.md).

## File-Level Validations

| Validation | Description |
| ---------- | ----------- |
| File path | Must be in a `CPS-*` directory |
| Line endings | Must use UNIX line endings (LF), not Windows (CRLF) or old Mac (CR) |
| Frontmatter | Must have valid YAML frontmatter between `---` delimiters |
| No H1 headings | H1 (`#`) headings are not allowed in the document body |

## Header Field Validations

All 9 fields are **required**,
must appear in order,
and no extra fields are allowed.

| Field | Order | Validation Rules |
| ----- | ----- | ---------------- |
| **CPS** | 1 | Positive integer (`1`, `42`) or `?`/`??`/etc. for unassigned. No leading zeros. |
| **Title** | 2 | 1-100 characters, no backticks (`` ` ``) |
| **Category** | 3 | One of: `Meta`, `Wallets`, `Tokens`, `Metadata`, `Tools`, `Plutus`, `Ledger`, `Consensus`, `Network`, `?` |
| **Status** | 4 | `Open`, `Solved`, or `Inactive` (optionally with reason, e.g., `Inactive (Superseded)`) |
| **Authors** | 5 | Non-empty list, each entry: `Name <email>` |
| **Proposed Solutions** | 6 | List (can be empty), each entry: `Label: URL` |
| **Discussions** | 7 | Non-empty list, each entry: `Label: URL` |
| **Created** | 8 | Date in `YYYY-MM-DD` format |
| **License** | 9 | `CC-BY-4.0` or `Apache-2.0` |

## CIP Label Validation

For the **Proposed Solutions** and **Discussions** fields,
when an entry label matches the `CIP-NNNN` pattern,
extra validation applies:

| Rule | Description |
| ---- | ----------- |
| GitHub URL required | Must be `https://github.com/cardano-foundation/CIPs/...` |
| Valid URL types | `/pull/NNN` (PR) or `/tree/{branch}/CIP-NNNN` or `/blob/{branch}/CIP-NNNN` (merged) |
| `?` suffix for PRs | `CIP-0030?` required when linking to a pull request (candidate) |
| No `?` for merged | `CIP-0030` (without `?`) required when linking to a merged CIP |

Non-CIP labels (e.g., `Forum Post`, `Pull Request`) are allowed with any valid URL.

## Required Sections (H2 Headers)

The following sections must exist in this order with **exact capitalization**.

**No other H2 sections are allowed** except the optional sections listed below.

| Order | Section |
| ----- | ------- |
| 1 | `Abstract` |
| 2 | `Problem` |
| 3 | `Use Cases` |
| 4 | `Goals` |
| 5 | `Open Questions` |
| 6 | `Copyright` |

## Optional Sections

The following sections are allowed with exact capitalization.
They **must** appear after `Open Questions` and before `Copyright`:

- `References`
- `Appendices`
- `Acknowledgments` / `Acknowledgements`

Optional sections appearing before any required section (other than `Copyright`) will cause validation to fail.
141 changes: 141 additions & 0 deletions .github/schemas/cps-header.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/cardano-foundation/CIPs/.github/schemas/cps-header.schema.json",
"title": "CPS Header Schema",
"description": "JSON Schema for validating YAML frontmatter headers in Cardano Problem Statements (CPSs)",
"type": "object",
"required": [
"CPS",
"Title",
"Category",
"Status",
"Authors",
"Proposed Solutions",
"Discussions",
"Created",
"License"
],
"properties": {
"CPS": {
"description": "The CPS number (without leading 0), or one or more '?' before number has been assigned",
"oneOf": [
{
"type": "string",
"pattern": "^\\?+$"
},
{
"type": "integer",
"minimum": 1
},
{
"type": "string",
"pattern": "^[1-9]\\d*$"
}
]
},
"Title": {
"description": "A succinct and descriptive title. Don't use backticks (`) in titles since they disrupt formatting in other contexts. Must be less than 100 characters.",
"type": "string",
"minLength": 1,
"maxLength": 100,
"not": {
"pattern": "`"
}
},
"Category": {
"description": "One of the editorially accepted categories covering one area of the ecosystem",
"type": "string",
"enum": [
"Meta",
"Wallets",
"Tokens",
"Metadata",
"Tools",
"Plutus",
"Ledger",
"Consensus",
"Network",
"?"
]
},
"Status": {
"description": "Status of the CPS: Open, Solved, or Inactive (with optional reason)",
"type": "string",
"pattern": "^(Open|Solved|Inactive(?:\\s+\\(.*\\))?)$"
},
"Authors": {
"description": "A list of authors' real names and email addresses (e.g. John Doe <john.doe@email.domain>)",
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"pattern": "^.+\\s+<.+>$",
"description": "Author entry in format: Name <email@domain.com>"
}
},
"Proposed Solutions": {
"description": "A list of CIPs addressing the problem, if any. Can be an empty array []. Each entry is 'Label: URL'. When label is CIP-NNNN, URL must be a GitHub CIPs repo link; use '?' suffix for PRs (candidates).",
"type": "array",
"items": {
"oneOf": [
{
"type": "string",
"minLength": 1,
"pattern": "^.+:\\s+https?://.+$",
"description": "Entry in format: 'Label: URL'"
},
{
"type": "object",
"minProperties": 1,
"maxProperties": 1,
"additionalProperties": {
"type": "string",
"format": "uri"
},
"description": "Dictionary format: {'Label': 'URL'}"
}
]
}
},
"Discussions": {
"description": "A list of links where major technical discussions regarding this CPS happened. Must include a link to the pull request that created the CPS. Each entry must have a label followed by a colon and URL. Can be a string 'Label: URL' or a dictionary {'Label': 'URL'}.",
"type": "array",
"minItems": 1,
"items": {
"oneOf": [
{
"type": "string",
"pattern": "^.+:\\s+https?://.+$",
"description": "Entry in format: 'Label: URL'"
},
{
"type": "object",
"minProperties": 1,
"maxProperties": 1,
"additionalProperties": {
"type": "string",
"format": "uri"
},
"description": "Dictionary format: {'Label': 'URL'}"
}
]
}
},
"Created": {
"description": "Date created on, in ISO 8601 (YYYY-MM-DD) format",
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"format": "date"
},
"License": {
"description": "Abbreviation of an approved license",
"type": "string",
"enum": [
"CC-BY-4.0",
"Apache-2.0"
]
}
},
"additionalProperties": false
}

Loading