requirements-as-code 0.6.2__tar.gz → 0.6.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. {requirements_as_code-0.6.2/requirements_as_code.egg-info → requirements_as_code-0.6.3}/PKG-INFO +43 -18
  2. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/README.md +42 -17
  3. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/artifacts.py +74 -3
  4. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/cli.py +7 -6
  5. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/outputs.py +28 -0
  6. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/schema.py +27 -0
  7. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/stats.py +43 -3
  8. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/validate.py +39 -0
  9. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3/requirements_as_code.egg-info}/PKG-INFO +43 -18
  10. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/SOURCES.txt +5 -1
  11. requirements_as_code-0.6.3/tests/fixtures/design/minimal.md +17 -0
  12. requirements_as_code-0.6.3/tests/fixtures/design/missing_constraints.md +17 -0
  13. requirements_as_code-0.6.3/tests/fixtures/design/valid.md +37 -0
  14. requirements_as_code-0.6.3/tests/test_design.py +413 -0
  15. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_inspect.py +1 -1
  16. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_schema.py +12 -2
  17. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/.github/workflows/python-publish.yml +0 -0
  18. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/.gitignore +0 -0
  19. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/LICENSE +0 -0
  20. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/examples/example_dashboard_v1.md +0 -0
  21. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/examples/example_dashboard_v2.md +0 -0
  22. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-001-markdown-first.md +0 -0
  23. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-002-ai-optional.md +0 -0
  24. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-003-structured-outputs-first.md +0 -0
  25. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-004-artifact-model.md +0 -0
  26. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-005-cli-first.md +0 -0
  27. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-006-ingest-over-rewrite.md +0 -0
  28. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-007-json-contract-stability.md +0 -0
  29. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-008-agent-ready-architecture.md +0 -0
  30. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-009-ai-assisted-development.md +0 -0
  31. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-010-documents-are-not-artifacts.md +0 -0
  32. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-011-file-first-pipeline.md +0 -0
  33. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-012-open-core-strategy.md +0 -0
  34. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-013-leverage-existing-source-control-systems.md +0 -0
  35. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-014-viewer-agnostic-knowledge-artifacts.md +0 -0
  36. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-015-explorer-as-consumer.md +0 -0
  37. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-016-relationships-as-structural-references.md +0 -0
  38. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-017-rac-managed-knowledge-not-work.md +0 -0
  39. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.0-workspace-analysis.md +0 -0
  40. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.1-review-engine.md +0 -0
  41. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.2-mcp-server.md +0 -0
  42. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.4-claude-skills.md +0 -0
  43. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.4-python-sdk.md +0 -0
  44. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/archive/v0.5-decisions.md +0 -0
  45. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/archive/v0.7-prompts.md +0 -0
  46. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.2-stats.md +0 -0
  47. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.3-ingest.md +0 -0
  48. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.3.1-formats.md +0 -0
  49. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.4-inspect.md +0 -0
  50. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.4.1-expansion.md +0 -0
  51. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.4.1-inspect-expansion.md +0 -0
  52. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.4.2-decision-metadata.md +0 -0
  53. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.5.0-artifact-improvement.md +0 -0
  54. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.5.1-guided-improvement.md +0 -0
  55. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.5.2-schema.md +0 -0
  56. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.6.0-roadmap-artifacts.md +0 -0
  57. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.6.1-roadmap-improvement.md +0 -0
  58. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.6.2-prompt-artifact.md +0 -0
  59. /requirements_as_code-0.6.2/planning/roadmap/v0.6.3-deign-artifacts.md → /requirements_as_code-0.6.3/planning/roadmap/v0.6.3-design-artifacts.md +0 -0
  60. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.7.0-relationship-metadata.md +0 -0
  61. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.7.1-relationship-inspection.md +0 -0
  62. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.7.2-relationship-validation.md +0 -0
  63. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.8.0-explorer-foundation.md +0 -0
  64. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.8.1-explorer-experience.md +0 -0
  65. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.8.2-knowledge-operations.md +0 -0
  66. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/pyproject.toml +0 -0
  67. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/__init__.py +0 -0
  68. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/classification.py +0 -0
  69. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/diff.py +0 -0
  70. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/fs.py +0 -0
  71. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/improve.py +0 -0
  72. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/ingest.py +0 -0
  73. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/inspect.py +0 -0
  74. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/models.py +0 -0
  75. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/parser.py +0 -0
  76. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/dependency_links.txt +0 -0
  77. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/entry_points.txt +0 -0
  78. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/requires.txt +0 -0
  79. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/top_level.txt +0 -0
  80. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/setup.cfg +0 -0
  81. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/conftest.py +0 -0
  82. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/bad_category.md +0 -0
  83. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/bad_status.md +0 -0
  84. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/minimal.md +0 -0
  85. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/portfolio/01_accepted_arch.md +0 -0
  86. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/portfolio/02_proposed_process.md +0 -0
  87. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/portfolio/03_no_metadata.md +0 -0
  88. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/with_metadata.md +0 -0
  89. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/diff/new.md +0 -0
  90. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/diff/old.md +0 -0
  91. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/ingest/sample.md +0 -0
  92. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/inspect/ambiguous.md +0 -0
  93. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/inspect/decision.md +0 -0
  94. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/inspect/nested/another_requirement.md +0 -0
  95. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/inspect/requirement.md +0 -0
  96. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/duplicate_ids.md +0 -0
  97. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/empty_req_text.md +0 -0
  98. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/malformed_id.md +0 -0
  99. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/missing_id.md +0 -0
  100. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/missing_problem.md +0 -0
  101. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/missing_requirements.md +0 -0
  102. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/missing_title.md +0 -0
  103. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/multiple_titles.md +0 -0
  104. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/portfolio/broken.md +0 -0
  105. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/portfolio/feature_a.md +0 -0
  106. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/portfolio/feature_b.md +0 -0
  107. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/portfolio/sub/feature_c.md +0 -0
  108. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/prompt/minimal.md +0 -0
  109. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/prompt/missing_output.md +0 -0
  110. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/prompt/valid.md +0 -0
  111. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/roadmap/minimal.md +0 -0
  112. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/roadmap/missing_initiatives.md +0 -0
  113. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/roadmap/valid.md +0 -0
  114. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/valid/bullet_requirements.md +0 -0
  115. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/valid/feature.md +0 -0
  116. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/valid/minimal.md +0 -0
  117. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/valid/warnings.md +0 -0
  118. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_cli.py +0 -0
  119. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_decision_metadata.py +0 -0
  120. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_diff.py +0 -0
  121. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_improve.py +0 -0
  122. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_ingest.py +0 -0
  123. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_parser.py +0 -0
  124. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_prompt.py +0 -0
  125. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_roadmap.py +0 -0
  126. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_stats.py +0 -0
  127. {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_validate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: requirements-as-code
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: RAC — lint and diff product requirements written in Markdown.
5
5
  Author: tcballard
6
6
  License-Expression: MIT
@@ -423,9 +423,12 @@ Counts span all parsed requirement files; a feature with only *warnings* (e.g. n
423
423
  metrics) still counts as valid. Invalid files are listed at the end so they are
424
424
  never silently skipped.
425
425
 
426
- Decision artifacts are aggregated **separately** so they never distort the
427
- requirement totals or averages. When a directory contains decisions, a
428
- `Decisions` section reports the count plus a status and category breakdown:
426
+ Non-requirement artifacts are aggregated **separately** so they never distort the
427
+ requirement totals or averages. Decisions report metadata breakdowns; Roadmaps,
428
+ Prompts, and Designs use lightweight count/valid/invalid summaries.
429
+
430
+ When a directory contains decisions, a `Decisions` section reports the count plus
431
+ a status and category breakdown:
429
432
 
430
433
  ```text
431
434
  Decisions
@@ -444,11 +447,24 @@ Category
444
447
  - Process: 4
445
448
  ```
446
449
 
447
- Add `--json` for machine-readable output (a `decisions` block is included only
448
- when decisions are present). `stats` exits `0` when the directory has at least
449
- one valid feature or decision, `1` if none, and `2` if the path is not a
450
- directory. (A `--strict` flag for failing on *any* invalid file — handy in CI —
451
- is planned.)
450
+ When a directory contains Design artifacts, a lightweight section is shown:
451
+
452
+ ```text
453
+ Designs
454
+ =======
455
+
456
+ Total: 4
457
+ Valid: 3
458
+
459
+ Invalid Designs (1)
460
+ ./planning/design/draft.md — missing-constraints
461
+ ```
462
+
463
+ Add `--json` for machine-readable output. Artifact-specific blocks such as
464
+ `decisions`, `roadmaps`, `prompts`, and `designs` are included only when those
465
+ artifacts are present. `stats` exits `0` when the directory has at least one
466
+ valid known artifact, `1` if none, and `2` if the path is not a directory. (A
467
+ `--strict` flag for failing on *any* invalid file — handy in CI — is planned.)
452
468
 
453
469
  ---
454
470
 
@@ -525,10 +541,10 @@ Missing Sections:
525
541
  ```
526
542
 
527
543
  RAC classifies the document against known artifact schemas (no AI) and reports a
528
- confidence score. v0.4 recognizes **Requirement** and **Decision** artifacts;
529
- anything that doesn't fit well is reported as **Unknown** (a valid, successful
530
- result — not an error). `--json` emits `{ type, confidence, present_sections,
531
- missing_sections }`.
544
+ confidence score. RAC recognizes **Requirement**, **Decision**, **Roadmap**,
545
+ **Prompt**, and **Design** artifacts; anything that doesn't fit well is reported
546
+ as **Unknown** (a valid, successful result — not an error). `--json` emits
547
+ `{ type, confidence, present_sections, missing_sections }`.
532
548
 
533
549
  ### Inspect a directory
534
550
 
@@ -546,7 +562,10 @@ Files Inspected: 23
546
562
 
547
563
  Requirements: 7
548
564
  Decisions: 3
549
- Unknown: 13
565
+ Roadmaps: 2
566
+ Prompts: 4
567
+ Designs: 1
568
+ Unknown: 6
550
569
  ```
551
570
 
552
571
  ### Explain a classification (`--verbose`)
@@ -686,10 +705,10 @@ So you can go straight from `rac inspect requirement.md` to
686
705
  ```
687
706
 
688
707
  `improve` generates suggestions for artifact types with complete schema guidance
689
- coverage. Today that means **Requirement** and **Decision** artifacts. Unknown
690
- documents return a short explanatory message instead. Future artifact types do
691
- not become improvable until their schemas define guidance for every required and
692
- recommended section.
708
+ coverage. Today that means **Requirement**, **Decision**, **Roadmap**, **Prompt**,
709
+ and **Design** artifacts. Unknown documents return a short explanatory message
710
+ instead. Future artifact types do not become improvable until their schemas
711
+ define guidance for every required and recommended section.
693
712
 
694
713
  Guidance is informational metadata only: it does not influence classification,
695
714
  validation, confidence scoring, statistics, or repository analysis.
@@ -709,11 +728,15 @@ rac schema --list
709
728
  rac schema --list --json
710
729
  rac schema requirement
711
730
  rac schema decision --json
731
+ rac schema design
712
732
  rac schema requirement --template
733
+ rac schema design --template
713
734
  ```
714
735
 
715
736
  `rac schema <type>` shows the full schema reference: required, recommended, and
716
737
  optional sections; descriptions; guidance; and metadata values where applicable.
738
+ Supported schemas are `requirement`, `decision`, `roadmap`, `prompt`, and
739
+ `design`.
717
740
 
718
741
  ```text
719
742
  Artifact Type: Decision
@@ -757,6 +780,7 @@ meaningful product knowledge.
757
780
  ```bash
758
781
  rac schema requirement --template > requirement.md
759
782
  rac schema decision --template > decision.md
783
+ rac schema design --template > design.md
760
784
  ```
761
785
 
762
786
  Generated templates are validation-safe:
@@ -764,6 +788,7 @@ Generated templates are validation-safe:
764
788
  ```bash
765
789
  rac schema requirement --template | rac validate -
766
790
  rac schema decision --template | rac validate -
791
+ rac schema design --template | rac validate -
767
792
  ```
768
793
 
769
794
  Unknown schemas fail with exit code `2` and list available schemas. Only
@@ -383,9 +383,12 @@ Counts span all parsed requirement files; a feature with only *warnings* (e.g. n
383
383
  metrics) still counts as valid. Invalid files are listed at the end so they are
384
384
  never silently skipped.
385
385
 
386
- Decision artifacts are aggregated **separately** so they never distort the
387
- requirement totals or averages. When a directory contains decisions, a
388
- `Decisions` section reports the count plus a status and category breakdown:
386
+ Non-requirement artifacts are aggregated **separately** so they never distort the
387
+ requirement totals or averages. Decisions report metadata breakdowns; Roadmaps,
388
+ Prompts, and Designs use lightweight count/valid/invalid summaries.
389
+
390
+ When a directory contains decisions, a `Decisions` section reports the count plus
391
+ a status and category breakdown:
389
392
 
390
393
  ```text
391
394
  Decisions
@@ -404,11 +407,24 @@ Category
404
407
  - Process: 4
405
408
  ```
406
409
 
407
- Add `--json` for machine-readable output (a `decisions` block is included only
408
- when decisions are present). `stats` exits `0` when the directory has at least
409
- one valid feature or decision, `1` if none, and `2` if the path is not a
410
- directory. (A `--strict` flag for failing on *any* invalid file — handy in CI —
411
- is planned.)
410
+ When a directory contains Design artifacts, a lightweight section is shown:
411
+
412
+ ```text
413
+ Designs
414
+ =======
415
+
416
+ Total: 4
417
+ Valid: 3
418
+
419
+ Invalid Designs (1)
420
+ ./planning/design/draft.md — missing-constraints
421
+ ```
422
+
423
+ Add `--json` for machine-readable output. Artifact-specific blocks such as
424
+ `decisions`, `roadmaps`, `prompts`, and `designs` are included only when those
425
+ artifacts are present. `stats` exits `0` when the directory has at least one
426
+ valid known artifact, `1` if none, and `2` if the path is not a directory. (A
427
+ `--strict` flag for failing on *any* invalid file — handy in CI — is planned.)
412
428
 
413
429
  ---
414
430
 
@@ -485,10 +501,10 @@ Missing Sections:
485
501
  ```
486
502
 
487
503
  RAC classifies the document against known artifact schemas (no AI) and reports a
488
- confidence score. v0.4 recognizes **Requirement** and **Decision** artifacts;
489
- anything that doesn't fit well is reported as **Unknown** (a valid, successful
490
- result — not an error). `--json` emits `{ type, confidence, present_sections,
491
- missing_sections }`.
504
+ confidence score. RAC recognizes **Requirement**, **Decision**, **Roadmap**,
505
+ **Prompt**, and **Design** artifacts; anything that doesn't fit well is reported
506
+ as **Unknown** (a valid, successful result — not an error). `--json` emits
507
+ `{ type, confidence, present_sections, missing_sections }`.
492
508
 
493
509
  ### Inspect a directory
494
510
 
@@ -506,7 +522,10 @@ Files Inspected: 23
506
522
 
507
523
  Requirements: 7
508
524
  Decisions: 3
509
- Unknown: 13
525
+ Roadmaps: 2
526
+ Prompts: 4
527
+ Designs: 1
528
+ Unknown: 6
510
529
  ```
511
530
 
512
531
  ### Explain a classification (`--verbose`)
@@ -646,10 +665,10 @@ So you can go straight from `rac inspect requirement.md` to
646
665
  ```
647
666
 
648
667
  `improve` generates suggestions for artifact types with complete schema guidance
649
- coverage. Today that means **Requirement** and **Decision** artifacts. Unknown
650
- documents return a short explanatory message instead. Future artifact types do
651
- not become improvable until their schemas define guidance for every required and
652
- recommended section.
668
+ coverage. Today that means **Requirement**, **Decision**, **Roadmap**, **Prompt**,
669
+ and **Design** artifacts. Unknown documents return a short explanatory message
670
+ instead. Future artifact types do not become improvable until their schemas
671
+ define guidance for every required and recommended section.
653
672
 
654
673
  Guidance is informational metadata only: it does not influence classification,
655
674
  validation, confidence scoring, statistics, or repository analysis.
@@ -669,11 +688,15 @@ rac schema --list
669
688
  rac schema --list --json
670
689
  rac schema requirement
671
690
  rac schema decision --json
691
+ rac schema design
672
692
  rac schema requirement --template
693
+ rac schema design --template
673
694
  ```
674
695
 
675
696
  `rac schema <type>` shows the full schema reference: required, recommended, and
676
697
  optional sections; descriptions; guidance; and metadata values where applicable.
698
+ Supported schemas are `requirement`, `decision`, `roadmap`, `prompt`, and
699
+ `design`.
677
700
 
678
701
  ```text
679
702
  Artifact Type: Decision
@@ -717,6 +740,7 @@ meaningful product knowledge.
717
740
  ```bash
718
741
  rac schema requirement --template > requirement.md
719
742
  rac schema decision --template > decision.md
743
+ rac schema design --template > design.md
720
744
  ```
721
745
 
722
746
  Generated templates are validation-safe:
@@ -724,6 +748,7 @@ Generated templates are validation-safe:
724
748
  ```bash
725
749
  rac schema requirement --template | rac validate -
726
750
  rac schema decision --template | rac validate -
751
+ rac schema design --template | rac validate -
727
752
  ```
728
753
 
729
754
  Unknown schemas fail with exit code `2` and list available schemas. Only
@@ -9,11 +9,12 @@ there is a single source of truth.
9
9
  Section names are normalized (stripped + casefolded) for matching; ``display``
10
10
  holds the human-facing label.
11
11
 
12
- Four artifact types have a concrete schema today: Requirement (RAC's own format /
12
+ Five artifact types have a concrete schema today: Requirement (RAC's own format /
13
13
  validator), Decision (the ADR format used in this repository), Roadmap (outcome- and
14
14
  initiative-focused knowledge, added in v0.6.0), and Prompt (structured AI prompts as
15
- knowledge, added in v0.6.2). Meeting is intentionally deferred until its schema is
16
- formalized — see planning/roadmap/.
15
+ knowledge, added in v0.6.2), and Design (UX and interaction knowledge, added in
16
+ v0.6.3). Meeting is intentionally deferred until its schema is formalized — see
17
+ planning/roadmap/.
17
18
  """
18
19
 
19
20
  from __future__ import annotations
@@ -241,6 +242,76 @@ ARTIFACT_SPECS: tuple[ArtifactSpec, ...] = (
241
242
  "input specification": "input",
242
243
  },
243
244
  ),
245
+ ArtifactSpec(
246
+ name="design",
247
+ display="Design",
248
+ required=("context", "user need", "design", "constraints"),
249
+ recommended=(
250
+ "rationale",
251
+ "alternatives",
252
+ "accessibility",
253
+ "style guidance",
254
+ "open questions",
255
+ ),
256
+ # Relationship sections are recognized but never scored or templated; they
257
+ # let a Design reference other artifacts as text without RAC analyzing
258
+ # those links (relationship analysis is v0.7.x).
259
+ optional=(
260
+ "related requirements",
261
+ "related decisions",
262
+ "related roadmaps",
263
+ "related prompts",
264
+ ),
265
+ descriptions={
266
+ "context": "The product area, situation, or experience this design addresses",
267
+ "user need": "The user, audience, task, pain point, or goal this design supports",
268
+ "design": "The proposed experience, interaction, layout, flow, or behavior",
269
+ "constraints": "Technical, product, accessibility, platform, or implementation constraints",
270
+ "rationale": "Why this design approach was chosen",
271
+ "alternatives": "Other approaches considered and why they were not chosen",
272
+ "accessibility": "Accessibility needs and expectations for the design",
273
+ "style guidance": "Visual, tone, layout, or interaction style guidance",
274
+ "open questions": "Unresolved design questions to validate or decide later",
275
+ },
276
+ guidance={
277
+ "context": (
278
+ "What situation, product area, or user experience does this design address?",
279
+ "Why is this design needed now?",
280
+ ),
281
+ "user need": (
282
+ "Who is the user or audience?",
283
+ "What task, pain point, or goal does this design support?",
284
+ ),
285
+ "design": (
286
+ "What is the proposed design?",
287
+ "How should the experience work?",
288
+ ),
289
+ "constraints": (
290
+ "What constraints shape this design?",
291
+ "What must the design respect or avoid?",
292
+ ),
293
+ "rationale": (
294
+ "Why is this the preferred approach?",
295
+ "What trade-offs does this design make?",
296
+ ),
297
+ "alternatives": (
298
+ "What other approaches were considered?",
299
+ "Why were they not chosen?",
300
+ ),
301
+ "accessibility": (
302
+ "What accessibility needs should this design support?",
303
+ "Are there keyboard, contrast, readability, or screen-reader considerations?",
304
+ ),
305
+ "style guidance": (
306
+ "What visual or interaction style should be followed?",
307
+ "What patterns should remain consistent?",
308
+ ),
309
+ "open questions": (
310
+ "What still needs to be decided?",
311
+ "What should be validated or explored further?",
312
+ ),
313
+ },
314
+ ),
244
315
  )
245
316
 
246
317
 
@@ -11,8 +11,8 @@ Commands:
11
11
 
12
12
  Exit codes:
13
13
  0 success (incl. inspect/improve reporting Unknown)
14
- 1 validate: errors found; stats: no valid features or decisions;
15
- ingest: conversion failed
14
+ 1 validate: errors found; stats: no valid known artifacts; ingest:
15
+ conversion failed
16
16
  2 usage / IO error (file not found, not a directory, unsupported type,
17
17
  refuse-to-overwrite)
18
18
  """
@@ -90,14 +90,15 @@ def cmd_stats(args: argparse.Namespace) -> int:
90
90
  else:
91
91
  print(outputs.render_stats_human(stats))
92
92
  # Success as long as the portfolio has analysable content: at least one valid
93
- # feature, one decision, one valid roadmap, or one valid prompt. Invalid files
94
- # are reported but don't fail the run on their own. (A future --strict flag will
95
- # fail the run if *any* file is invalid, for CI use.)
93
+ # feature, one decision, one valid roadmap, one valid prompt, or one valid
94
+ # design. Invalid files are reported but don't fail the run on their own. (A
95
+ # future --strict flag will fail the run if *any* file is invalid, for CI use.)
96
96
  has_content = (
97
97
  stats.valid_features > 0
98
98
  or stats.decision_count > 0
99
99
  or stats.valid_roadmaps > 0
100
100
  or stats.valid_prompts > 0
101
+ or stats.valid_designs > 0
101
102
  )
102
103
  return EXIT_OK if has_content else EXIT_VALIDATION_FAILED
103
104
 
@@ -368,7 +369,7 @@ def build_parser() -> argparse.ArgumentParser:
368
369
  p_schema.add_argument(
369
370
  "schema",
370
371
  nargs="?",
371
- help="Schema name, e.g. requirement, decision, roadmap, or prompt.",
372
+ help="Schema name, e.g. requirement, decision, roadmap, prompt, or design.",
372
373
  )
373
374
  p_schema.add_argument(
374
375
  "--list",
@@ -263,6 +263,24 @@ def render_stats_human(s: PortfolioStats) -> str:
263
263
  reasons = ", ".join(p.error_codes) or "unknown"
264
264
  lines.append(f" {_red(p.path)} — {reasons}")
265
265
 
266
+ # Designs are reported separately and lightly (count + invalid only); the
267
+ # section is omitted entirely when there are none.
268
+ if s.designs:
269
+ lines += [
270
+ "",
271
+ _bold("Designs"),
272
+ "=======",
273
+ "",
274
+ f"Total: {s.design_count}",
275
+ f"Valid: {s.valid_designs}",
276
+ ]
277
+ invalid_designs = s.invalid_designs
278
+ if invalid_designs:
279
+ lines += ["", _bold(f"Invalid Designs ({len(invalid_designs)})")]
280
+ for d in invalid_designs:
281
+ reasons = ", ".join(d.error_codes) or "unknown"
282
+ lines.append(f" {_red(d.path)} — {reasons}")
283
+
266
284
  return "\n".join(lines)
267
285
 
268
286
 
@@ -320,6 +338,16 @@ def render_stats_json(s: PortfolioStats) -> str:
320
338
  {"file": p.path, "errors": p.error_codes} for p in s.invalid_prompts
321
339
  ],
322
340
  }
341
+ # Additive: only present when the portfolio contains designs. Lightweight by
342
+ # design — count and validity only (no design quality or rendering metrics).
343
+ if s.designs:
344
+ payload["designs"] = {
345
+ "count": s.design_count,
346
+ "valid": s.valid_designs,
347
+ "invalid": [
348
+ {"file": d.path, "errors": d.error_codes} for d in s.invalid_designs
349
+ ],
350
+ }
323
351
  return json.dumps(payload, indent=2)
324
352
 
325
353
 
@@ -115,6 +115,8 @@ def _starter_body(
115
115
  return _metadata_default(section, metadata_values)
116
116
  if ref.type == "requirement" and section == "requirements":
117
117
  return "- [REQ-001] TODO: describe a required system behaviour."
118
+ if ref.type == "design":
119
+ return _design_free_text_todo(section)
118
120
  return _free_text_todo(section)
119
121
 
120
122
 
@@ -163,5 +165,30 @@ def _free_text_todo(section: str) -> str:
163
165
  return messages.get(section, f"TODO: describe {section}.")
164
166
 
165
167
 
168
+ def _design_free_text_todo(section: str) -> str:
169
+ messages = {
170
+ "context": "TODO: describe the design context and why this design exists.",
171
+ "user need": (
172
+ "TODO: describe who this design is for and what they need to accomplish."
173
+ ),
174
+ "design": (
175
+ "TODO: describe the proposed experience, interaction, layout, flow, "
176
+ "or system behavior."
177
+ ),
178
+ "constraints": (
179
+ "TODO: describe technical, product, accessibility, platform, or "
180
+ "implementation constraints."
181
+ ),
182
+ "rationale": "TODO: explain why this design approach was chosen.",
183
+ "alternatives": "TODO: describe alternatives that were considered.",
184
+ "accessibility": "TODO: describe accessibility considerations.",
185
+ "style guidance": (
186
+ "TODO: describe visual, tone, layout, or interaction style guidance."
187
+ ),
188
+ "open questions": "TODO: list unresolved design questions.",
189
+ }
190
+ return messages.get(section, _free_text_todo(section))
191
+
192
+
166
193
  def _snake(section: str) -> str:
167
194
  return section.replace(" ", "_")
@@ -4,10 +4,10 @@
4
4
  each one, and aggregates the results. Like the rest of RAC, it works on the
5
5
  Product AST: every `.md` is parsed into a :class:`~rac.models.Product`.
6
6
 
7
- Requirement, Decision, Roadmap, and Prompt artifacts are aggregated separately so
7
+ Requirement, Decision, Roadmap, Prompt, and Design artifacts are aggregated separately so
8
8
  that one never distorts another: requirement totals/averages span only requirement
9
9
  files, decisions get their own status/category breakdown, and roadmaps and prompts
10
- each get a lightweight count of how many exist and how many are valid.
10
+ and designs each get a lightweight count of how many exist and how many are valid.
11
11
 
12
12
  Counting basis: requirement totals, averages, and the per-feature breakdown span
13
13
  *all* parsed requirement files (including ones that fail validation). A file
@@ -79,6 +79,20 @@ class PromptStat:
79
79
  error_codes: list[str]
80
80
 
81
81
 
82
+ @dataclass
83
+ class DesignStat:
84
+ """Per-file result for a Design artifact (kept separate from features).
85
+
86
+ Lightweight by design (v0.6.3): identity plus validity and error codes only.
87
+ No section-completeness, visual, or design-system metrics.
88
+ """
89
+
90
+ path: str
91
+ name: str # the design title, or the filename stem if it has none
92
+ valid: bool
93
+ error_codes: list[str]
94
+
95
+
82
96
  @dataclass
83
97
  class PortfolioStats:
84
98
  """Aggregate view over all discovered requirement files."""
@@ -88,6 +102,7 @@ class PortfolioStats:
88
102
  decisions: list[DecisionStat] = field(default_factory=list)
89
103
  roadmaps: list[RoadmapStat] = field(default_factory=list)
90
104
  prompts: list[PromptStat] = field(default_factory=list)
105
+ designs: list[DesignStat] = field(default_factory=list)
91
106
 
92
107
  # --- counts (requirement features) ---
93
108
  @property
@@ -198,6 +213,19 @@ class PortfolioStats:
198
213
  def invalid_prompts(self) -> list[PromptStat]:
199
214
  return [p for p in self.prompts if not p.valid]
200
215
 
216
+ # --- designs ---
217
+ @property
218
+ def design_count(self) -> int:
219
+ return len(self.designs)
220
+
221
+ @property
222
+ def valid_designs(self) -> int:
223
+ return sum(1 for d in self.designs if d.valid)
224
+
225
+ @property
226
+ def invalid_designs(self) -> list[DesignStat]:
227
+ return [d for d in self.designs if not d.valid]
228
+
201
229
 
202
230
  def _bucket(decisions: list[DecisionStat], attr: str, metadata_key: str) -> dict[str, int]:
203
231
  """Count ``decisions`` by ``attr`` in the artifact spec's declared order."""
@@ -224,7 +252,7 @@ def _neg_name(name: str) -> tuple[int, ...]:
224
252
  def collect_stats(directory: str) -> PortfolioStats:
225
253
  """Parse and classify every Markdown file under ``directory``.
226
254
 
227
- Decisions, roadmaps, and prompts are each routed to their own aggregate;
255
+ Decisions, roadmaps, prompts, and designs are each routed to their own aggregate;
228
256
  everything else is treated as a requirement feature (preserving prior behavior
229
257
  for requirement repositories).
230
258
  """
@@ -268,6 +296,18 @@ def collect_stats(directory: str) -> PortfolioStats:
268
296
  )
269
297
  )
270
298
  continue
299
+ if result.type == "design":
300
+ issues = validate(product)
301
+ error_codes = [i.code for i in issues if i.severity == "error"]
302
+ stats.designs.append(
303
+ DesignStat(
304
+ path=str(path),
305
+ name=name,
306
+ valid=not error_codes,
307
+ error_codes=error_codes,
308
+ )
309
+ )
310
+ continue
271
311
  issues = validate(product)
272
312
  error_codes = [i.code for i in issues if i.severity == "error"]
273
313
  stats.features.append(
@@ -45,6 +45,8 @@ def validate(product: Product) -> list[Issue]:
45
45
  return _validate_roadmap(product)
46
46
  if artifact_type == "prompt":
47
47
  return _validate_prompt(product)
48
+ if artifact_type == "design":
49
+ return _validate_design(product)
48
50
  return _validate_requirement(product)
49
51
 
50
52
 
@@ -187,6 +189,43 @@ def _validate_prompt(product: Product) -> list[Issue]:
187
189
  return issues
188
190
 
189
191
 
192
+ def _validate_design(product: Product) -> list[Issue]:
193
+ """Validate a Design artifact (v0.6.3).
194
+
195
+ Required sections (Context, User Need, Design, Constraints) must be present;
196
+ missing recommended/optional sections never fail or warn. Designs carry no
197
+ metadata and are knowledge artifacts, not UI renderings or component systems.
198
+ """
199
+ spec = spec_for("design")
200
+ assert spec is not None # the design spec always exists
201
+ issues: list[Issue] = []
202
+
203
+ if not product.title:
204
+ issues.append(Issue("error", "missing-title", "File has no top-level # title."))
205
+
206
+ if product.extra_title_lines:
207
+ issues.append(
208
+ Issue(
209
+ "error",
210
+ "multiple-titles",
211
+ "File has more than one top-level # title; expected exactly one.",
212
+ product.extra_title_lines[0],
213
+ )
214
+ )
215
+
216
+ for section in spec.required:
217
+ if section not in product.sections:
218
+ issues.append(
219
+ Issue(
220
+ "error",
221
+ f"missing-{section.replace(' ', '-')}",
222
+ f"Design is missing a ## {section.title()} section.",
223
+ )
224
+ )
225
+
226
+ return issues
227
+
228
+
190
229
  def _validate_requirement(product: Product) -> list[Issue]:
191
230
  """Check ``product`` and return all structural and quality findings."""
192
231
  issues: list[Issue] = []