docassert 0.5.0__tar.gz → 0.7.0__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 (111) hide show
  1. {docassert-0.5.0/docassert.egg-info → docassert-0.7.0}/PKG-INFO +9 -3
  2. {docassert-0.5.0 → docassert-0.7.0}/README.md +6 -2
  3. {docassert-0.5.0 → docassert-0.7.0}/docassert/__init__.py +1 -1
  4. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/adr.criteria.yaml +6 -2
  5. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/benefits-realization.criteria.yaml +6 -2
  6. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/brd.criteria.yaml +5 -1
  7. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/business-case.criteria.yaml +5 -1
  8. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/charter.criteria.yaml +7 -3
  9. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/data-migration-plan.criteria.yaml +6 -2
  10. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/frnfr.criteria.yaml +5 -1
  11. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/hypercare-plan.criteria.yaml +6 -2
  12. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/post-implementation-review.criteria.yaml +5 -1
  13. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/prd.criteria.yaml +5 -1
  14. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/project.criteria.yaml +5 -1
  15. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/qa-test-plan.criteria.yaml +6 -2
  16. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/raci-stakeholder.criteria.yaml +6 -2
  17. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/release-cutover-plan.criteria.yaml +6 -2
  18. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/risk-register.criteria.yaml +6 -2
  19. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/rollback-plan.criteria.yaml +6 -2
  20. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/runbook.criteria.yaml +6 -2
  21. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/status-report.criteria.yaml +6 -2
  22. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/test-cases.criteria.yaml +5 -1
  23. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/criteria/user-story.criteria.yaml +6 -2
  24. {docassert-0.5.0 → docassert-0.7.0}/docassert/cli.py +9 -1
  25. {docassert-0.5.0 → docassert-0.7.0}/docassert/consistency.py +4 -2
  26. {docassert-0.5.0 → docassert-0.7.0}/docassert/profiles.py +1 -1
  27. {docassert-0.5.0 → docassert-0.7.0}/docassert/semantic.py +1 -1
  28. {docassert-0.5.0 → docassert-0.7.0}/docassert/status.py +10 -2
  29. {docassert-0.5.0 → docassert-0.7.0}/docassert/structural.py +51 -9
  30. {docassert-0.5.0 → docassert-0.7.0/docassert.egg-info}/PKG-INFO +9 -3
  31. {docassert-0.5.0 → docassert-0.7.0}/docassert.egg-info/SOURCES.txt +2 -0
  32. {docassert-0.5.0 → docassert-0.7.0}/docassert.egg-info/requires.txt +2 -0
  33. {docassert-0.5.0 → docassert-0.7.0}/pyproject.toml +18 -1
  34. docassert-0.7.0/tests/test_badge.py +26 -0
  35. docassert-0.7.0/tests/test_draft_relaxation.py +138 -0
  36. {docassert-0.5.0 → docassert-0.7.0}/LICENSE +0 -0
  37. {docassert-0.5.0 → docassert-0.7.0}/NOTICE +0 -0
  38. {docassert-0.5.0 → docassert-0.7.0}/docassert/__main__.py +0 -0
  39. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/consistency.yaml +0 -0
  40. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/profiles/agile-delivery.yaml +0 -0
  41. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/profiles/lean-startup.yaml +0 -0
  42. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/profiles/regulated-industry.yaml +0 -0
  43. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/adr.schema.json +0 -0
  44. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/benefits-realization.schema.json +0 -0
  45. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/brd.schema.json +0 -0
  46. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/business-case.schema.json +0 -0
  47. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/charter.schema.json +0 -0
  48. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/data-migration-plan.schema.json +0 -0
  49. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/frnfr.schema.json +0 -0
  50. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/hypercare-plan.schema.json +0 -0
  51. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/post-implementation-review.schema.json +0 -0
  52. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/prd.schema.json +0 -0
  53. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/project.schema.json +0 -0
  54. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/qa-test-plan.schema.json +0 -0
  55. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/raci-stakeholder.schema.json +0 -0
  56. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/release-cutover-plan.schema.json +0 -0
  57. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/risk-register.schema.json +0 -0
  58. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/rollback-plan.schema.json +0 -0
  59. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/runbook.schema.json +0 -0
  60. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/status-report.schema.json +0 -0
  61. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/test-cases.schema.json +0 -0
  62. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/schema/user-story.schema.json +0 -0
  63. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/skills/doc-to-pmo/SKILL.md +0 -0
  64. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/adr.template.md +0 -0
  65. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/benefits-realization.template.md +0 -0
  66. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/brd.template.md +0 -0
  67. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/business-case.template.md +0 -0
  68. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/charter.template.md +0 -0
  69. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/data-migration-plan.template.md +0 -0
  70. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/frnfr.template.md +0 -0
  71. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/hypercare-plan.template.md +0 -0
  72. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/post-implementation-review.template.md +0 -0
  73. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/prd.template.md +0 -0
  74. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/project.template.md +0 -0
  75. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/qa-test-plan.template.md +0 -0
  76. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/raci-stakeholder.template.md +0 -0
  77. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/release-cutover-plan.template.md +0 -0
  78. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/risk-register.template.md +0 -0
  79. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/rollback-plan.template.md +0 -0
  80. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/runbook.template.md +0 -0
  81. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/status-report.template.md +0 -0
  82. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/test-cases.template.md +0 -0
  83. {docassert-0.5.0 → docassert-0.7.0}/docassert/_data/templates/user-story.template.md +0 -0
  84. {docassert-0.5.0 → docassert-0.7.0}/docassert/config.py +0 -0
  85. {docassert-0.5.0 → docassert-0.7.0}/docassert/extract.py +0 -0
  86. {docassert-0.5.0 → docassert-0.7.0}/docassert/graph.py +0 -0
  87. {docassert-0.5.0 → docassert-0.7.0}/docassert/loader.py +0 -0
  88. {docassert-0.5.0 → docassert-0.7.0}/docassert/models.py +0 -0
  89. {docassert-0.5.0 → docassert-0.7.0}/docassert/projects.py +0 -0
  90. {docassert-0.5.0 → docassert-0.7.0}/docassert/report.py +0 -0
  91. {docassert-0.5.0 → docassert-0.7.0}/docassert/rtm.py +0 -0
  92. {docassert-0.5.0 → docassert-0.7.0}/docassert/scaffold.py +0 -0
  93. {docassert-0.5.0 → docassert-0.7.0}/docassert.egg-info/dependency_links.txt +0 -0
  94. {docassert-0.5.0 → docassert-0.7.0}/docassert.egg-info/entry_points.txt +0 -0
  95. {docassert-0.5.0 → docassert-0.7.0}/docassert.egg-info/top_level.txt +0 -0
  96. {docassert-0.5.0 → docassert-0.7.0}/setup.cfg +0 -0
  97. {docassert-0.5.0 → docassert-0.7.0}/tests/test_config.py +0 -0
  98. {docassert-0.5.0 → docassert-0.7.0}/tests/test_consistency.py +0 -0
  99. {docassert-0.5.0 → docassert-0.7.0}/tests/test_defects.py +0 -0
  100. {docassert-0.5.0 → docassert-0.7.0}/tests/test_extract.py +0 -0
  101. {docassert-0.5.0 → docassert-0.7.0}/tests/test_graph.py +0 -0
  102. {docassert-0.5.0 → docassert-0.7.0}/tests/test_json_report.py +0 -0
  103. {docassert-0.5.0 → docassert-0.7.0}/tests/test_kinds_delivery.py +0 -0
  104. {docassert-0.5.0 → docassert-0.7.0}/tests/test_kinds_governance.py +0 -0
  105. {docassert-0.5.0 → docassert-0.7.0}/tests/test_kinds_operate.py +0 -0
  106. {docassert-0.5.0 → docassert-0.7.0}/tests/test_kinds_reporting.py +0 -0
  107. {docassert-0.5.0 → docassert-0.7.0}/tests/test_profiles.py +0 -0
  108. {docassert-0.5.0 → docassert-0.7.0}/tests/test_projects.py +0 -0
  109. {docassert-0.5.0 → docassert-0.7.0}/tests/test_scaffold.py +0 -0
  110. {docassert-0.5.0 → docassert-0.7.0}/tests/test_status.py +0 -0
  111. {docassert-0.5.0 → docassert-0.7.0}/tests/test_structural.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docassert
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: Unit testing for business documents — validate structured Markdown docs against a configurable audit standard.
5
5
  Author: C4G Enterprises Inc.
6
6
  License: Apache-2.0
@@ -33,7 +33,9 @@ Requires-Dist: python-docx>=1.1; extra == "convert"
33
33
  Requires-Dist: pypdf>=4.0; extra == "convert"
34
34
  Provides-Extra: dev
35
35
  Requires-Dist: pytest>=8.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
36
37
  Requires-Dist: ruff>=0.6; extra == "dev"
38
+ Requires-Dist: mypy>=1.10; extra == "dev"
37
39
  Dynamic: license-file
38
40
 
39
41
  # docassert
@@ -49,7 +51,8 @@ semantic checks that advise. Requirements trace end to end, and project status i
49
51
  derived from the documents rather than self-reported.
50
52
 
51
53
  docassert is the reference implementation of **[PMO as Code](https://c4g-john.github.io/pmo-as-code/)** —
52
- a vendor-neutral standard for running a PMO from version-controlled, declarative files.
54
+ a vendor-neutral standard for running a PMO from version-controlled, declarative
55
+ files. It implements the [PMO as Code specification](https://github.com/c4g-john/pmo-as-code-spec) **v0.1**.
53
56
 
54
57
  ## Install
55
58
 
@@ -89,7 +92,7 @@ flagged as TODOs, never invented). The skill's source is
89
92
  | `docassert consistency` | Cross-document checks: referential integrity, coverage, required links, profile completeness. Reports: `--junit` / `--markdown` / `--json`. |
90
93
  | `docassert rtm [--project ID]` | Requirements traceability matrix (Markdown or CSV). |
91
94
  | `docassert status [--project ID] [--index]` | Derived project status (md / json / html). |
92
- | `docassert pages --out DIR` | Build the portfolio site (index + a page per project). |
95
+ | `docassert pages --out DIR` | Build the portfolio site (index + a page per project + shields.io badge endpoints `badge.json` / `badges/<ID>.json`). |
93
96
  | `docassert projects [--out] [--check]` | Generate / verify the project registry. |
94
97
  | `docassert new <kind> --project ID` | Scaffold a document from its template with identity filled in (`new project --code XYZ` auto-numbers the id); suggests the next free item ids. |
95
98
  | `docassert init [DIR]` | Scaffold the default config into a repo. |
@@ -114,6 +117,9 @@ kind is adding a trio — no code for the common cases.
114
117
  - **Structural — deterministic, blocking.** Required fields and sections,
115
118
  measurable success criteria, risks with owner + mitigation, resolving
116
119
  references, unique ids. Plain Python, reliable enough to gate a merge.
120
+ Within this tier, *integrity* checks (malformed items, bad types, duplicate
121
+ ids) block at any status, while *completeness* checks relax to advisory on
122
+ `status: draft` and gate once a document is proposed — WIP is never punished.
117
123
  - **Semantic — AI-graded, advisory.** Scored via the Anthropic API and posted to
118
124
  the PR — never blocking. Set `ANTHROPIC_API_KEY` to enable; skipped otherwise.
119
125
 
@@ -11,7 +11,8 @@ semantic checks that advise. Requirements trace end to end, and project status i
11
11
  derived from the documents rather than self-reported.
12
12
 
13
13
  docassert is the reference implementation of **[PMO as Code](https://c4g-john.github.io/pmo-as-code/)** —
14
- a vendor-neutral standard for running a PMO from version-controlled, declarative files.
14
+ a vendor-neutral standard for running a PMO from version-controlled, declarative
15
+ files. It implements the [PMO as Code specification](https://github.com/c4g-john/pmo-as-code-spec) **v0.1**.
15
16
 
16
17
  ## Install
17
18
 
@@ -51,7 +52,7 @@ flagged as TODOs, never invented). The skill's source is
51
52
  | `docassert consistency` | Cross-document checks: referential integrity, coverage, required links, profile completeness. Reports: `--junit` / `--markdown` / `--json`. |
52
53
  | `docassert rtm [--project ID]` | Requirements traceability matrix (Markdown or CSV). |
53
54
  | `docassert status [--project ID] [--index]` | Derived project status (md / json / html). |
54
- | `docassert pages --out DIR` | Build the portfolio site (index + a page per project). |
55
+ | `docassert pages --out DIR` | Build the portfolio site (index + a page per project + shields.io badge endpoints `badge.json` / `badges/<ID>.json`). |
55
56
  | `docassert projects [--out] [--check]` | Generate / verify the project registry. |
56
57
  | `docassert new <kind> --project ID` | Scaffold a document from its template with identity filled in (`new project --code XYZ` auto-numbers the id); suggests the next free item ids. |
57
58
  | `docassert init [DIR]` | Scaffold the default config into a repo. |
@@ -76,6 +77,9 @@ kind is adding a trio — no code for the common cases.
76
77
  - **Structural — deterministic, blocking.** Required fields and sections,
77
78
  measurable success criteria, risks with owner + mitigation, resolving
78
79
  references, unique ids. Plain Python, reliable enough to gate a merge.
80
+ Within this tier, *integrity* checks (malformed items, bad types, duplicate
81
+ ids) block at any status, while *completeness* checks relax to advisory on
82
+ `status: draft` and gate once a document is proposed — WIP is never punished.
79
83
  - **Semantic — AI-graded, advisory.** Scored via the Anthropic API and posted to
80
84
  the PR — never blocking. Set `ANTHROPIC_API_KEY` to enable; skipped otherwise.
81
85
 
@@ -5,4 +5,4 @@ standard: deterministic structural checks that gate a merge, plus optional
5
5
  AI-graded semantic checks that advise.
6
6
  """
7
7
 
8
- __version__ = "0.5.0"
8
+ __version__ = "0.7.0"
@@ -18,9 +18,13 @@ checks:
18
18
  type: structural
19
19
  blocking: true
20
20
  description: Frontmatter is valid against schema/adr.schema.json.
21
+ - id: frontmatter-complete
22
+ type: structural
23
+ blocking: once-proposed
24
+ description: Every schema-required frontmatter field is present (advisory while draft).
21
25
  - id: required-sections
22
26
  type: structural
23
- blocking: true
27
+ blocking: once-proposed
24
28
  description: Every required section is present and non-empty.
25
29
  - id: items-well-formed
26
30
  type: structural
@@ -28,7 +32,7 @@ checks:
28
32
  description: Every decision is a valid **ADR-###** item.
29
33
  - id: adr-items-have-status
30
34
  type: structural
31
- blocking: true
35
+ blocking: once-proposed
32
36
  description: Every decision declares a valid Status (proposed | accepted | superseded | deprecated | rejected).
33
37
  - id: unique-id
34
38
  type: structural
@@ -16,13 +16,17 @@ checks:
16
16
  type: structural
17
17
  blocking: true
18
18
  description: Frontmatter is valid against schema/benefits-realization.schema.json.
19
+ - id: frontmatter-complete
20
+ type: structural
21
+ blocking: once-proposed
22
+ description: Every schema-required frontmatter field is present (advisory while draft).
19
23
  - id: required-sections
20
24
  type: structural
21
- blocking: true
25
+ blocking: once-proposed
22
26
  description: Every required section is present and non-empty.
23
27
  - id: measurable-items
24
28
  type: structural
25
- blocking: true
29
+ blocking: once-proposed
26
30
  description: Every benefit states a measurable target.
27
31
  - id: unique-id
28
32
  type: structural
@@ -16,9 +16,13 @@ checks:
16
16
  type: structural
17
17
  blocking: true
18
18
  description: Frontmatter is valid against schema/brd.schema.json.
19
+ - id: frontmatter-complete
20
+ type: structural
21
+ blocking: once-proposed
22
+ description: Every schema-required frontmatter field is present (advisory while draft).
19
23
  - id: required-sections
20
24
  type: structural
21
- blocking: true
25
+ blocking: once-proposed
22
26
  description: Every required section is present and non-empty.
23
27
  - id: items-well-formed
24
28
  type: structural
@@ -13,9 +13,13 @@ checks:
13
13
  type: structural
14
14
  blocking: true
15
15
  description: Frontmatter is valid against schema/business-case.schema.json.
16
+ - id: frontmatter-complete
17
+ type: structural
18
+ blocking: once-proposed
19
+ description: Every schema-required frontmatter field is present (advisory while draft).
16
20
  - id: required-sections
17
21
  type: structural
18
- blocking: true
22
+ blocking: once-proposed
19
23
  description: Every required section is present and non-empty.
20
24
  - id: unique-id
21
25
  type: structural
@@ -26,19 +26,23 @@ checks:
26
26
  blocking: true
27
27
  description: Frontmatter is present and valid against schema/charter.schema.json.
28
28
 
29
+ - id: frontmatter-complete
30
+ type: structural
31
+ blocking: once-proposed
32
+ description: Every schema-required frontmatter field is present (advisory while draft).
29
33
  - id: required-sections
30
34
  type: structural
31
- blocking: true
35
+ blocking: once-proposed
32
36
  description: Every required section is present and non-empty.
33
37
 
34
38
  - id: measurable-success-criteria
35
39
  type: structural
36
- blocking: true
40
+ blocking: once-proposed
37
41
  description: Every Success Criteria bullet states a measurable threshold (number, %, currency, or date).
38
42
 
39
43
  - id: risks-have-owner-and-mitigation
40
44
  type: structural
41
- blocking: true
45
+ blocking: once-proposed
42
46
  description: Every Risks bullet names an Owner and a Mitigation.
43
47
 
44
48
  - id: dates-consistent
@@ -14,13 +14,17 @@ checks:
14
14
  type: structural
15
15
  blocking: true
16
16
  description: Frontmatter is valid against schema/data-migration-plan.schema.json.
17
+ - id: frontmatter-complete
18
+ type: structural
19
+ blocking: once-proposed
20
+ description: Every schema-required frontmatter field is present (advisory while draft).
17
21
  - id: required-sections
18
22
  type: structural
19
- blocking: true
23
+ blocking: once-proposed
20
24
  description: Every required section is present and non-empty.
21
25
  - id: mapping-table
22
26
  type: structural
23
- blocking: true
27
+ blocking: once-proposed
24
28
  description: The Field Mapping section contains a mapping table.
25
29
  - id: unique-id
26
30
  type: structural
@@ -17,9 +17,13 @@ checks:
17
17
  type: structural
18
18
  blocking: true
19
19
  description: Frontmatter is valid against schema/frnfr.schema.json.
20
+ - id: frontmatter-complete
21
+ type: structural
22
+ blocking: once-proposed
23
+ description: Every schema-required frontmatter field is present (advisory while draft).
20
24
  - id: required-sections
21
25
  type: structural
22
- blocking: true
26
+ blocking: once-proposed
23
27
  description: Every required section is present and non-empty.
24
28
  - id: items-well-formed
25
29
  type: structural
@@ -13,13 +13,17 @@ checks:
13
13
  type: structural
14
14
  blocking: true
15
15
  description: Frontmatter is valid against schema/hypercare-plan.schema.json.
16
+ - id: frontmatter-complete
17
+ type: structural
18
+ blocking: once-proposed
19
+ description: Every schema-required frontmatter field is present (advisory while draft).
16
20
  - id: required-sections
17
21
  type: structural
18
- blocking: true
22
+ blocking: once-proposed
19
23
  description: Every required section is present and non-empty.
20
24
  - id: measurable-exit-criteria
21
25
  type: structural
22
- blocking: true
26
+ blocking: once-proposed
23
27
  description: Every hypercare exit criterion states a measurable threshold.
24
28
  - id: unique-id
25
29
  type: structural
@@ -14,9 +14,13 @@ checks:
14
14
  type: structural
15
15
  blocking: true
16
16
  description: Frontmatter is valid against schema/post-implementation-review.schema.json.
17
+ - id: frontmatter-complete
18
+ type: structural
19
+ blocking: once-proposed
20
+ description: Every schema-required frontmatter field is present (advisory while draft).
17
21
  - id: required-sections
18
22
  type: structural
19
- blocking: true
23
+ blocking: once-proposed
20
24
  description: Every required section is present and non-empty.
21
25
  - id: unique-id
22
26
  type: structural
@@ -17,9 +17,13 @@ checks:
17
17
  type: structural
18
18
  blocking: true
19
19
  description: Frontmatter is valid against schema/prd.schema.json.
20
+ - id: frontmatter-complete
21
+ type: structural
22
+ blocking: once-proposed
23
+ description: Every schema-required frontmatter field is present (advisory while draft).
20
24
  - id: required-sections
21
25
  type: structural
22
- blocking: true
26
+ blocking: once-proposed
23
27
  description: Every required section is present and non-empty.
24
28
  - id: items-well-formed
25
29
  type: structural
@@ -16,6 +16,10 @@ checks:
16
16
  blocking: true
17
17
  description: Frontmatter is present and valid against schema/project.schema.json.
18
18
 
19
+ - id: frontmatter-complete
20
+ type: structural
21
+ blocking: once-proposed
22
+ description: Every schema-required frontmatter field is present (advisory while draft).
19
23
  - id: project-id-format
20
24
  type: structural
21
25
  blocking: true
@@ -23,7 +27,7 @@ checks:
23
27
 
24
28
  - id: required-sections
25
29
  type: structural
26
- blocking: true
30
+ blocking: once-proposed
27
31
  description: Every required section is present and non-empty.
28
32
 
29
33
  - id: unique-id
@@ -13,13 +13,17 @@ checks:
13
13
  type: structural
14
14
  blocking: true
15
15
  description: Frontmatter is valid against schema/qa-test-plan.schema.json.
16
+ - id: frontmatter-complete
17
+ type: structural
18
+ blocking: once-proposed
19
+ description: Every schema-required frontmatter field is present (advisory while draft).
16
20
  - id: required-sections
17
21
  type: structural
18
- blocking: true
22
+ blocking: once-proposed
19
23
  description: Every required section is present and non-empty.
20
24
  - id: measurable-exit-criteria
21
25
  type: structural
22
- blocking: true
26
+ blocking: once-proposed
23
27
  description: Every exit criterion states a measurable threshold.
24
28
  - id: unique-id
25
29
  type: structural
@@ -10,13 +10,17 @@ checks:
10
10
  type: structural
11
11
  blocking: true
12
12
  description: Frontmatter is valid against schema/raci-stakeholder.schema.json.
13
+ - id: frontmatter-complete
14
+ type: structural
15
+ blocking: once-proposed
16
+ description: Every schema-required frontmatter field is present (advisory while draft).
13
17
  - id: required-sections
14
18
  type: structural
15
- blocking: true
19
+ blocking: once-proposed
16
20
  description: Every required section is present and non-empty.
17
21
  - id: raci-one-accountable
18
22
  type: structural
19
- blocking: true
23
+ blocking: once-proposed
20
24
  description: Every activity in the RACI Matrix has exactly one Accountable (A) role.
21
25
  - id: unique-id
22
26
  type: structural
@@ -16,13 +16,17 @@ checks:
16
16
  type: structural
17
17
  blocking: true
18
18
  description: Frontmatter is valid against schema/release-cutover-plan.schema.json.
19
+ - id: frontmatter-complete
20
+ type: structural
21
+ blocking: once-proposed
22
+ description: Every schema-required frontmatter field is present (advisory while draft).
19
23
  - id: required-sections
20
24
  type: structural
21
- blocking: true
25
+ blocking: once-proposed
22
26
  description: Every required section is present and non-empty.
23
27
  - id: numbered-steps
24
28
  type: structural
25
- blocking: true
29
+ blocking: once-proposed
26
30
  description: Cutover Steps is an ordered list of at least two numbered steps.
27
31
  - id: unique-id
28
32
  type: structural
@@ -14,9 +14,13 @@ checks:
14
14
  type: structural
15
15
  blocking: true
16
16
  description: Frontmatter is valid against schema/risk-register.schema.json.
17
+ - id: frontmatter-complete
18
+ type: structural
19
+ blocking: once-proposed
20
+ description: Every schema-required frontmatter field is present (advisory while draft).
17
21
  - id: required-sections
18
22
  type: structural
19
- blocking: true
23
+ blocking: once-proposed
20
24
  description: Every required section is present and non-empty.
21
25
  - id: items-well-formed
22
26
  type: structural
@@ -24,7 +28,7 @@ checks:
24
28
  description: Every risk is a valid **RISK-###** item.
25
29
  - id: risk-items-complete
26
30
  type: structural
27
- blocking: true
31
+ blocking: once-proposed
28
32
  description: Every risk states a Probability, Impact, Owner, and Response.
29
33
  - id: unique-id
30
34
  type: structural
@@ -15,13 +15,17 @@ checks:
15
15
  type: structural
16
16
  blocking: true
17
17
  description: Frontmatter is valid against schema/rollback-plan.schema.json.
18
+ - id: frontmatter-complete
19
+ type: structural
20
+ blocking: once-proposed
21
+ description: Every schema-required frontmatter field is present (advisory while draft).
18
22
  - id: required-sections
19
23
  type: structural
20
- blocking: true
24
+ blocking: once-proposed
21
25
  description: Every required section is present and non-empty.
22
26
  - id: numbered-steps
23
27
  type: structural
24
- blocking: true
28
+ blocking: once-proposed
25
29
  description: Rollback Steps is an ordered list of at least two numbered steps.
26
30
  - id: unique-id
27
31
  type: structural
@@ -16,13 +16,17 @@ checks:
16
16
  type: structural
17
17
  blocking: true
18
18
  description: Frontmatter is valid against schema/runbook.schema.json.
19
+ - id: frontmatter-complete
20
+ type: structural
21
+ blocking: once-proposed
22
+ description: Every schema-required frontmatter field is present (advisory while draft).
19
23
  - id: required-sections
20
24
  type: structural
21
- blocking: true
25
+ blocking: once-proposed
22
26
  description: Every required section is present and non-empty.
23
27
  - id: numbered-steps
24
28
  type: structural
25
- blocking: true
29
+ blocking: once-proposed
26
30
  description: Procedures is an ordered list of at least two numbered steps.
27
31
  - id: unique-id
28
32
  type: structural
@@ -12,13 +12,17 @@ checks:
12
12
  type: structural
13
13
  blocking: true
14
14
  description: Frontmatter is valid (includes a period date and a green/amber/red rag).
15
+ - id: frontmatter-complete
16
+ type: structural
17
+ blocking: once-proposed
18
+ description: Every schema-required frontmatter field is present (advisory while draft).
15
19
  - id: required-sections
16
20
  type: structural
17
- blocking: true
21
+ blocking: once-proposed
18
22
  description: Every required section is present and non-empty.
19
23
  - id: references-risk
20
24
  type: structural
21
- blocking: true
25
+ blocking: once-proposed
22
26
  description: The Risks & Issues section cites at least one RISK-### from the register.
23
27
  - id: unique-id
24
28
  type: structural
@@ -14,9 +14,13 @@ checks:
14
14
  type: structural
15
15
  blocking: true
16
16
  description: Frontmatter is valid against schema/test-cases.schema.json.
17
+ - id: frontmatter-complete
18
+ type: structural
19
+ blocking: once-proposed
20
+ description: Every schema-required frontmatter field is present (advisory while draft).
17
21
  - id: required-sections
18
22
  type: structural
19
- blocking: true
23
+ blocking: once-proposed
20
24
  description: Every required section is present and non-empty.
21
25
  - id: items-well-formed
22
26
  type: structural
@@ -14,9 +14,13 @@ checks:
14
14
  type: structural
15
15
  blocking: true
16
16
  description: Frontmatter is valid against schema/user-story.schema.json.
17
+ - id: frontmatter-complete
18
+ type: structural
19
+ blocking: once-proposed
20
+ description: Every schema-required frontmatter field is present (advisory while draft).
17
21
  - id: required-sections
18
22
  type: structural
19
- blocking: true
23
+ blocking: once-proposed
20
24
  description: Every required section is present and non-empty.
21
25
  - id: items-well-formed
22
26
  type: structural
@@ -24,7 +28,7 @@ checks:
24
28
  description: Every story is a valid **US-###** item.
25
29
  - id: story-format
26
30
  type: structural
27
- blocking: true
31
+ blocking: once-proposed
28
32
  description: Every story follows "As a … I want … so that …".
29
33
  - id: unique-id
30
34
  type: structural
@@ -226,13 +226,21 @@ def cmd_pages(args: argparse.Namespace) -> int:
226
226
  index = status_mod.build_index(docs_dir)
227
227
  (out / "index.html").write_text(status_mod.render_index_html(index))
228
228
 
229
+ # shields.io endpoint badges: one for the portfolio, one per project
230
+ # (https://img.shields.io/endpoint?url=<site>/badge.json)
231
+ (out / "badge.json").write_text(
232
+ status_mod.render_badge_json(index["overall"]["rag"]))
233
+ (out / "badges").mkdir(exist_ok=True)
234
+
229
235
  plist = projects_mod.load_projects(docs_dir)
230
236
  for p in plist:
231
237
  model = status_mod.build_status(docs_dir, project=p["id"])
232
238
  (out / f"{p['id']}.html").write_text(status_mod.render_html(model))
239
+ (out / "badges" / f"{p['id']}.json").write_text(
240
+ status_mod.render_badge_json(model["rag"], label=p["code"].lower()))
233
241
 
234
242
  (out / "RTM.md").write_text(rtm.render_markdown(build_graph(docs_dir)))
235
- print(f"docassert: wrote {out}/ — index + {len(plist)} project page(s) + RTM.md "
243
+ print(f"docassert: wrote {out}/ — index + {len(plist)} project page(s) + badges + RTM.md "
236
244
  f"(portfolio: {index['overall']['rag']})")
237
245
  return 0
238
246
 
@@ -63,7 +63,8 @@ def check_referential_integrity(graph) -> CheckResult:
63
63
 
64
64
  def check_required_links(graph, config) -> CheckResult:
65
65
  required = config.get("required_links", {})
66
- approved_orphans, draft_orphans = [], []
66
+ approved_orphans: list[str] = []
67
+ draft_orphans: list[str] = []
67
68
  for item in graph.all_items():
68
69
  relation = required.get(item.type)
69
70
  if relation and not item.targets(relation):
@@ -79,7 +80,8 @@ def check_required_links(graph, config) -> CheckResult:
79
80
 
80
81
 
81
82
  def check_coverage(graph, config) -> CheckResult:
82
- approved_gaps, draft_gaps = [], []
83
+ approved_gaps: list[str] = []
84
+ draft_gaps: list[str] = []
83
85
  for rule in config.get("coverage", []):
84
86
  parent_prefix, relation = rule["parent"], rule["relation"]
85
87
  by_prefix = rule.get("by_prefix")
@@ -73,7 +73,7 @@ def completeness(profile: dict, documents: list[dict], project_status: str) -> d
73
73
  """
74
74
  by_kind: dict[str, list[dict]] = {}
75
75
  for d in documents:
76
- by_kind.setdefault(d.get("kind"), []).append(d)
76
+ by_kind.setdefault(str(d.get("kind") or ""), []).append(d)
77
77
 
78
78
  required = [{"kind": k, "state": _kind_state(k, by_kind)} for k in profile["required"]]
79
79
  recommended = [{"kind": k, "state": _kind_state(k, by_kind)} for k in profile["recommended"]]
@@ -69,7 +69,7 @@ def _grade(prompt: str, content: str, model: str) -> dict:
69
69
  "content": f"AUDIT CRITERION:\n{prompt}\n\nDOCUMENT:\n{content}",
70
70
  }],
71
71
  )
72
- text = "".join(block.text for block in message.content
72
+ text = "".join(getattr(block, "text", "") for block in message.content
73
73
  if getattr(block, "type", None) == "text").strip()
74
74
  # tolerate models that wrap JSON in prose or fences
75
75
  start, end = text.find("{"), text.rfind("}")
@@ -128,9 +128,9 @@ def build_status(documents_dir=DOCUMENTS_DIR, project: str | None = None) -> dic
128
128
  else:
129
129
  docs = all_docs
130
130
 
131
- id_index = {}
131
+ id_index: dict[str, list[str]] = {}
132
132
  for d in all_docs: # uniqueness is always global
133
- id_index.setdefault(d.id, []).append(d.path)
133
+ id_index.setdefault(d.id or "", []).append(d.path)
134
134
 
135
135
  documents = [{
136
136
  "kind": d.kind,
@@ -460,6 +460,14 @@ def render_html(model) -> str:
460
460
 
461
461
 
462
462
  _RAG_COLOR = {"green": "var(--ok)", "amber": "var(--amber)", "red": "var(--bad)"}
463
+ _BADGE_COLOR = {"green": "brightgreen", "amber": "orange", "red": "red"}
464
+
465
+
466
+ def render_badge_json(rag: str, label: str = "pmo docs") -> str:
467
+ """A shields.io endpoint payload (https://shields.io/badges/endpoint-badge),
468
+ so a README can carry a live derived-status badge."""
469
+ return json.dumps({"schemaVersion": 1, "label": label,
470
+ "message": rag, "color": _BADGE_COLOR[rag]}) + "\n"
463
471
 
464
472
 
465
473
  def _index_card(p, esc) -> str: