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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ packages = [{ include = "spec_artifacts_process" }]
include = [
{ path = "spec_artifacts_process/manifest.yaml", format = ["sdist", "wheel"] },
{ path = "spec_artifacts_process/schemas/**/*.json", format = ["sdist", "wheel"] },
{ path = "spec_artifacts_process/skeletons/**/*.md", format = ["sdist", "wheel"] },
]

[tool.poetry.dependencies]
Expand Down
21 changes: 21 additions & 0 deletions spec_artifacts_process/examples/spec-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
id: SR-001
title: "Failure-domain review of the quoin spec"
type: SpecReview
analysis: failure-domain
scope: spec/spec.md
review_set: subset
---

## Summary

One review document per analysis skill. The `## Findings` table is validated
by the SpecReview archetype: exact columns, an `FND-NNN` id per row, and a
`Severity` constrained to `low`/`medium`/`high` (quire CR-010 `column_choices`).

## Findings

| ID | Severity | Summary | Refs |
| --- | --- | --- | --- |
| FND-001 | medium | ix-flow spawn failure behavior is undefined | FR-021 |
| FND-002 | low | cycle termination unstated for the graph walk | FR-024 |
39 changes: 39 additions & 0 deletions spec_artifacts_process/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ archetypes:
nav:
top_level: true
order: 12
- kind: spec-review
name: SpecReview
doc_backed: true
supports_membership: false
composition:
nav:
top_level: true
order: 13
- kind: test-matrix
name: Test Matrix
doc_backed: true
Expand All @@ -46,6 +54,7 @@ grammars:
- adr
- plan
- review
- spec-review
- test-matrix
- standard
- finding
Expand Down Expand Up @@ -94,6 +103,35 @@ artifact_types:
- references
examples: []
lint_rules_ref: []
- name: SpecReview
grammar_ref: process-artifacts
frontmatter_schema_ref: schemas/spec-review-frontmatter.schema.json
defaults:
id_pattern: SR-{next:03d}
allowed_links:
- reviews
- references
examples: []
lint_rules_ref: []
body_extraction:
yield_pattern:
match:
summary:
from: section_body
after_heading: Summary
required: true
findings:
from: table_row
under_section: Findings
required: true
multiple: true
assert:
columns: [ID, Severity, Summary, Refs]
min_rows: 1
id_column: ID
id_pattern: '^FND-\d+$'
column_choices:
Severity: [low, medium, high]
- name: Finding
grammar_ref: process-artifacts
frontmatter_schema_ref: schemas/finding-frontmatter.schema.json
Expand Down Expand Up @@ -128,6 +166,7 @@ doc_kinds:
- adr
- plan
- review
- spec-review
- test-matrix
- standard
- finding
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"id",
"title",
"type"
],
"additionalProperties": true,
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Z]{2,4}-[0-9]+$"
},
"title": {
"type": "string",
"minLength": 1
},
"type": {
"const": "SpecReview"
},
"analysis": {
"type": "string",
"enum": [
"base",
"failure-domain",
"integrity",
"dependency",
"evidence",
"risk-complexity",
"scope-boundary"
]
},
"scope": {
"type": "string"
},
"review_set": {
"type": "string",
"enum": [
"base",
"all",
"subset"
]
},
"object": {
"type": "string"
},
"relationships": {
"type": "array",
"items": {
"type": "object",
"required": [
"target",
"type"
],
"additionalProperties": false,
"properties": {
"target": {
"type": "string",
"pattern": "^ix://"
},
"type": {
"type": "string"
},
"cardinality": {
"type": "string"
}
}
}
}
}
}
33 changes: 33 additions & 0 deletions spec_artifacts_process/skeletons/SpecReview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
id: SR-001
title: "<analysis> review of <scope>"
type: SpecReview
analysis: failure-domain
scope: "spec/spec.md"
review_set: subset
---
<!-- SpecReview authoring skeleton (spec-artifacts-process). One SpecReview
document per analysis skill (parallel-safe). Fill every section with
substantive content. Contract (manifest body_extraction asserts,
validated by `quire validate`):
- Frontmatter: `type: SpecReview`; `id` matches ^[A-Z]{2,4}-[0-9]+$
(e.g. SR-001); set `analysis` to this doc's analysis
(base|failure-domain|integrity|dependency|evidence|risk-complexity|
scope-boundary), `scope` to the spec paths/ids reviewed, and
`review_set` to base|all|subset.
- REQUIRED (level 2): Summary, Findings.
- `## Findings` MUST be a table with headers EXACTLY:
ID | Severity | Summary | Refs — with >= 1 data row. The ID column
matches ^FND-\d+$ and Severity is one of low | medium | high.
An analysis that found nothing still records one row, e.g.
FND-001 | low | No issues found | -. -->

## Summary

<!-- One or two sentences: what this analysis examined and what it found. -->

## Findings

| ID | Severity | Summary | Refs |
| ------- | -------- | -------------------------------- | ------ |
| FND-001 | medium | <one-line finding> | FR-001 |
43 changes: 43 additions & 0 deletions tests/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,49 @@ def test_manifest_loads() -> None:
assert manifest["version"]


def test_spec_review_archetype_registered_with_findings_validation() -> None:
"""SpecReview is the per-analysis review archetype: a Summary section plus a
Findings table whose Severity column is constrained to low/medium/high
(quire CR-010 `column_choices`)."""
manifest = yaml.safe_load(MANIFEST_PATH.read_text())

names = {a["name"] for a in manifest["archetypes"]}
assert "SpecReview" in names, "SpecReview archetype must be registered"

sr = next(t for t in manifest["artifact_types"] if t["name"] == "SpecReview")
assert sr["frontmatter_schema_ref"] == "schemas/spec-review-frontmatter.schema.json"

findings = sr["body_extraction"]["yield_pattern"]["match"]["findings"]
assert findings["from"] == "table_row"
assert findings["under_section"] == "Findings"
assert findings["assert"]["columns"] == ["ID", "Severity", "Summary", "Refs"]
assert findings["assert"]["column_choices"]["Severity"] == [
"low",
"medium",
"high",
]
assert findings["assert"]["id_pattern"] == r"^FND-\d+$"

schema_path = pack.PACK_ROOT / "schemas" / "spec-review-frontmatter.schema.json"
assert schema_path.is_file()
schema = json.loads(schema_path.read_text())
assert schema["properties"]["type"]["const"] == "SpecReview"

# An authoring skeleton must exist so `quoin write --types SpecReview`
# emits the authoritative template (catalog resolves skeletons/<Name>.md).
skeleton = pack.PACK_ROOT / "skeletons" / "SpecReview.md"
assert skeleton.is_file()
body = skeleton.read_text()
assert "type: SpecReview" in body
header = next(line for line in body.splitlines() if line.strip().startswith("| ID"))
assert [c.strip() for c in header.strip().strip("|").split("|")] == [
"ID",
"Severity",
"Summary",
"Refs",
]


def test_manifest_validates_against_fr035_schema() -> None:
"""Skip if jsonschema lacks draft 2020-12 (use CI check-jsonschema instead)."""
try:
Expand Down
Loading