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.
- {requirements_as_code-0.6.2/requirements_as_code.egg-info → requirements_as_code-0.6.3}/PKG-INFO +43 -18
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/README.md +42 -17
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/artifacts.py +74 -3
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/cli.py +7 -6
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/outputs.py +28 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/schema.py +27 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/stats.py +43 -3
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/validate.py +39 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3/requirements_as_code.egg-info}/PKG-INFO +43 -18
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/SOURCES.txt +5 -1
- requirements_as_code-0.6.3/tests/fixtures/design/minimal.md +17 -0
- requirements_as_code-0.6.3/tests/fixtures/design/missing_constraints.md +17 -0
- requirements_as_code-0.6.3/tests/fixtures/design/valid.md +37 -0
- requirements_as_code-0.6.3/tests/test_design.py +413 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_inspect.py +1 -1
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_schema.py +12 -2
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/.github/workflows/python-publish.yml +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/.gitignore +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/LICENSE +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/examples/example_dashboard_v1.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/examples/example_dashboard_v2.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-001-markdown-first.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-002-ai-optional.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-003-structured-outputs-first.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-004-artifact-model.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-005-cli-first.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-006-ingest-over-rewrite.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-007-json-contract-stability.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-008-agent-ready-architecture.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-009-ai-assisted-development.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-010-documents-are-not-artifacts.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-011-file-first-pipeline.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-012-open-core-strategy.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-013-leverage-existing-source-control-systems.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-014-viewer-agnostic-knowledge-artifacts.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-015-explorer-as-consumer.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-016-relationships-as-structural-references.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/adr/adr-017-rac-managed-knowledge-not-work.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.0-workspace-analysis.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.1-review-engine.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.2-mcp-server.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.4-claude-skills.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/future/v1.4-python-sdk.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/archive/v0.5-decisions.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/archive/v0.7-prompts.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.2-stats.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.3-ingest.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.3.1-formats.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.4-inspect.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.4.1-expansion.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.4.1-inspect-expansion.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.4.2-decision-metadata.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.5.0-artifact-improvement.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.5.1-guided-improvement.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.5.2-schema.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.6.0-roadmap-artifacts.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.6.1-roadmap-improvement.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.6.2-prompt-artifact.md +0 -0
- /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
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.7.0-relationship-metadata.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.7.1-relationship-inspection.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.7.2-relationship-validation.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.8.0-explorer-foundation.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.8.1-explorer-experience.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/planning/roadmap/v0.8.2-knowledge-operations.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/pyproject.toml +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/__init__.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/classification.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/diff.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/fs.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/improve.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/ingest.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/inspect.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/models.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/rac/parser.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/dependency_links.txt +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/entry_points.txt +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/requires.txt +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/requirements_as_code.egg-info/top_level.txt +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/setup.cfg +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/conftest.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/bad_category.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/bad_status.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/minimal.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/portfolio/01_accepted_arch.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/portfolio/02_proposed_process.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/portfolio/03_no_metadata.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/decision/with_metadata.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/diff/new.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/diff/old.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/ingest/sample.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/inspect/ambiguous.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/inspect/decision.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/inspect/nested/another_requirement.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/inspect/requirement.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/duplicate_ids.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/empty_req_text.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/malformed_id.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/missing_id.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/missing_problem.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/missing_requirements.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/missing_title.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/invalid/multiple_titles.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/portfolio/broken.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/portfolio/feature_a.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/portfolio/feature_b.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/portfolio/sub/feature_c.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/prompt/minimal.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/prompt/missing_output.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/prompt/valid.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/roadmap/minimal.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/roadmap/missing_initiatives.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/roadmap/valid.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/valid/bullet_requirements.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/valid/feature.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/valid/minimal.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/fixtures/valid/warnings.md +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_cli.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_decision_metadata.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_diff.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_improve.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_ingest.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_parser.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_prompt.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_roadmap.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_stats.py +0 -0
- {requirements_as_code-0.6.2 → requirements_as_code-0.6.3}/tests/test_validate.py +0 -0
{requirements_as_code-0.6.2/requirements_as_code.egg-info → requirements_as_code-0.6.3}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: requirements-as-code
|
|
3
|
-
Version: 0.6.
|
|
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
|
-
|
|
427
|
-
requirement totals or averages.
|
|
428
|
-
|
|
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
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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.
|
|
529
|
-
anything that doesn't fit well is reported
|
|
530
|
-
result — not an error). `--json` emits
|
|
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
|
-
|
|
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
|
|
690
|
-
documents return a short explanatory message
|
|
691
|
-
not become improvable until their schemas
|
|
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
|
-
|
|
387
|
-
requirement totals or averages.
|
|
388
|
-
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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.
|
|
489
|
-
anything that doesn't fit well is reported
|
|
490
|
-
result — not an error). `--json` emits
|
|
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
|
-
|
|
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
|
|
650
|
-
documents return a short explanatory message
|
|
651
|
-
not become improvable until their schemas
|
|
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
|
-
|
|
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)
|
|
16
|
-
formalized — see
|
|
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
|
|
15
|
-
|
|
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,
|
|
94
|
-
# are reported but don't fail the run on their own. (A
|
|
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
|
|
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
|
|
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
|
|
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] = []
|