docassert 0.17.0__tar.gz → 0.18.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 (126) hide show
  1. {docassert-0.17.0/docassert.egg-info → docassert-0.18.0}/PKG-INFO +1 -1
  2. {docassert-0.17.0 → docassert-0.18.0}/docassert/__init__.py +1 -1
  3. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/charter.criteria.yaml +5 -0
  4. {docassert-0.17.0 → docassert-0.18.0}/docassert/bridge/ops.py +17 -0
  5. {docassert-0.17.0 → docassert-0.18.0}/docassert/cli.py +20 -0
  6. {docassert-0.17.0 → docassert-0.18.0}/docassert/status/derive.py +49 -0
  7. docassert-0.18.0/docassert/status/render.py +865 -0
  8. {docassert-0.17.0 → docassert-0.18.0}/docassert/structural.py +16 -0
  9. {docassert-0.17.0 → docassert-0.18.0/docassert.egg-info}/PKG-INFO +1 -1
  10. {docassert-0.17.0 → docassert-0.18.0}/tests/test_badge.py +2 -2
  11. {docassert-0.17.0 → docassert-0.18.0}/tests/test_status.py +10 -8
  12. docassert-0.17.0/docassert/status/render.py +0 -397
  13. {docassert-0.17.0 → docassert-0.18.0}/LICENSE +0 -0
  14. {docassert-0.17.0 → docassert-0.18.0}/NOTICE +0 -0
  15. {docassert-0.17.0 → docassert-0.18.0}/README.md +0 -0
  16. {docassert-0.17.0 → docassert-0.18.0}/docassert/__main__.py +0 -0
  17. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/consistency.yaml +0 -0
  18. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/adr.criteria.yaml +0 -0
  19. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/benefits-realization.criteria.yaml +0 -0
  20. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/brd.criteria.yaml +0 -0
  21. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/business-case.criteria.yaml +0 -0
  22. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/data-migration-plan.criteria.yaml +0 -0
  23. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/frnfr.criteria.yaml +0 -0
  24. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/hypercare-plan.criteria.yaml +0 -0
  25. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/operations.criteria.yaml +0 -0
  26. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/post-implementation-review.criteria.yaml +0 -0
  27. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/prd.criteria.yaml +0 -0
  28. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/project.criteria.yaml +0 -0
  29. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/qa-test-plan.criteria.yaml +0 -0
  30. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/raci-stakeholder.criteria.yaml +0 -0
  31. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/release-cutover-plan.criteria.yaml +0 -0
  32. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/risk-register.criteria.yaml +0 -0
  33. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/rollback-plan.criteria.yaml +0 -0
  34. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/runbook.criteria.yaml +0 -0
  35. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/status-report.criteria.yaml +0 -0
  36. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/test-cases.criteria.yaml +0 -0
  37. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/criteria/user-story.criteria.yaml +0 -0
  38. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/profiles/agile-delivery.yaml +0 -0
  39. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/profiles/lean-startup.yaml +0 -0
  40. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/profiles/operations.yaml +0 -0
  41. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/profiles/regulated-industry.yaml +0 -0
  42. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/adr.schema.json +0 -0
  43. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/benefits-realization.schema.json +0 -0
  44. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/brd.schema.json +0 -0
  45. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/business-case.schema.json +0 -0
  46. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/charter.schema.json +0 -0
  47. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/data-migration-plan.schema.json +0 -0
  48. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/frnfr.schema.json +0 -0
  49. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/hypercare-plan.schema.json +0 -0
  50. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/operations.schema.json +0 -0
  51. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/post-implementation-review.schema.json +0 -0
  52. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/prd.schema.json +0 -0
  53. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/project.schema.json +0 -0
  54. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/qa-test-plan.schema.json +0 -0
  55. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/raci-stakeholder.schema.json +0 -0
  56. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/release-cutover-plan.schema.json +0 -0
  57. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/risk-register.schema.json +0 -0
  58. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/rollback-plan.schema.json +0 -0
  59. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/runbook.schema.json +0 -0
  60. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/status-report.schema.json +0 -0
  61. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/test-cases.schema.json +0 -0
  62. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/schema/user-story.schema.json +0 -0
  63. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/skills/doc-to-pmo/SKILL.md +0 -0
  64. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/adr.template.md +0 -0
  65. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/benefits-realization.template.md +0 -0
  66. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/brd.template.md +0 -0
  67. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/business-case.template.md +0 -0
  68. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/charter.template.md +0 -0
  69. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/data-migration-plan.template.md +0 -0
  70. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/frnfr.template.md +0 -0
  71. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/hypercare-plan.template.md +0 -0
  72. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/operations.template.md +0 -0
  73. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/post-implementation-review.template.md +0 -0
  74. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/prd.template.md +0 -0
  75. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/project.template.md +0 -0
  76. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/qa-test-plan.template.md +0 -0
  77. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/raci-stakeholder.template.md +0 -0
  78. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/release-cutover-plan.template.md +0 -0
  79. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/risk-register.template.md +0 -0
  80. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/rollback-plan.template.md +0 -0
  81. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/runbook.template.md +0 -0
  82. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/status-report.template.md +0 -0
  83. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/test-cases.template.md +0 -0
  84. {docassert-0.17.0 → docassert-0.18.0}/docassert/_data/templates/user-story.template.md +0 -0
  85. {docassert-0.17.0 → docassert-0.18.0}/docassert/bridge/__init__.py +0 -0
  86. {docassert-0.17.0 → docassert-0.18.0}/docassert/bridge/board.py +0 -0
  87. {docassert-0.17.0 → docassert-0.18.0}/docassert/bridge/gh.py +0 -0
  88. {docassert-0.17.0 → docassert-0.18.0}/docassert/bridge/plan.py +0 -0
  89. {docassert-0.17.0 → docassert-0.18.0}/docassert/config.py +0 -0
  90. {docassert-0.17.0 → docassert-0.18.0}/docassert/consistency.py +0 -0
  91. {docassert-0.17.0 → docassert-0.18.0}/docassert/extract.py +0 -0
  92. {docassert-0.17.0 → docassert-0.18.0}/docassert/graph.py +0 -0
  93. {docassert-0.17.0 → docassert-0.18.0}/docassert/loader.py +0 -0
  94. {docassert-0.17.0 → docassert-0.18.0}/docassert/models.py +0 -0
  95. {docassert-0.17.0 → docassert-0.18.0}/docassert/profiles.py +0 -0
  96. {docassert-0.17.0 → docassert-0.18.0}/docassert/projects.py +0 -0
  97. {docassert-0.17.0 → docassert-0.18.0}/docassert/report.py +0 -0
  98. {docassert-0.17.0 → docassert-0.18.0}/docassert/rtm.py +0 -0
  99. {docassert-0.17.0 → docassert-0.18.0}/docassert/scaffold.py +0 -0
  100. {docassert-0.17.0 → docassert-0.18.0}/docassert/semantic.py +0 -0
  101. {docassert-0.17.0 → docassert-0.18.0}/docassert/status/__init__.py +0 -0
  102. {docassert-0.17.0 → docassert-0.18.0}/docassert.egg-info/SOURCES.txt +0 -0
  103. {docassert-0.17.0 → docassert-0.18.0}/docassert.egg-info/dependency_links.txt +0 -0
  104. {docassert-0.17.0 → docassert-0.18.0}/docassert.egg-info/entry_points.txt +0 -0
  105. {docassert-0.17.0 → docassert-0.18.0}/docassert.egg-info/requires.txt +0 -0
  106. {docassert-0.17.0 → docassert-0.18.0}/docassert.egg-info/top_level.txt +0 -0
  107. {docassert-0.17.0 → docassert-0.18.0}/pyproject.toml +0 -0
  108. {docassert-0.17.0 → docassert-0.18.0}/setup.cfg +0 -0
  109. {docassert-0.17.0 → docassert-0.18.0}/tests/test_bridge.py +0 -0
  110. {docassert-0.17.0 → docassert-0.18.0}/tests/test_config.py +0 -0
  111. {docassert-0.17.0 → docassert-0.18.0}/tests/test_consistency.py +0 -0
  112. {docassert-0.17.0 → docassert-0.18.0}/tests/test_defects.py +0 -0
  113. {docassert-0.17.0 → docassert-0.18.0}/tests/test_draft_relaxation.py +0 -0
  114. {docassert-0.17.0 → docassert-0.18.0}/tests/test_extract.py +0 -0
  115. {docassert-0.17.0 → docassert-0.18.0}/tests/test_graph.py +0 -0
  116. {docassert-0.17.0 → docassert-0.18.0}/tests/test_json_report.py +0 -0
  117. {docassert-0.17.0 → docassert-0.18.0}/tests/test_kinds_delivery.py +0 -0
  118. {docassert-0.17.0 → docassert-0.18.0}/tests/test_kinds_governance.py +0 -0
  119. {docassert-0.17.0 → docassert-0.18.0}/tests/test_kinds_operate.py +0 -0
  120. {docassert-0.17.0 → docassert-0.18.0}/tests/test_kinds_reporting.py +0 -0
  121. {docassert-0.17.0 → docassert-0.18.0}/tests/test_profiles.py +0 -0
  122. {docassert-0.17.0 → docassert-0.18.0}/tests/test_projects.py +0 -0
  123. {docassert-0.17.0 → docassert-0.18.0}/tests/test_report_extract.py +0 -0
  124. {docassert-0.17.0 → docassert-0.18.0}/tests/test_scaffold.py +0 -0
  125. {docassert-0.17.0 → docassert-0.18.0}/tests/test_semantic.py +0 -0
  126. {docassert-0.17.0 → docassert-0.18.0}/tests/test_structural.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docassert
3
- Version: 0.17.0
3
+ Version: 0.18.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
@@ -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.17.0"
8
+ __version__ = "0.18.0"
@@ -21,6 +21,11 @@ required_sections:
21
21
  - Approval
22
22
 
23
23
  checks:
24
+ - id: milestones-dated
25
+ type: structural
26
+ blocking: false
27
+ description: "At least one Milestones bullet ends with an ISO date (- <label>: YYYY-MM-DD) so timelines can draw it. Advisory only."
28
+
24
29
  - id: frontmatter-schema
25
30
  type: structural
26
31
  blocking: true
@@ -222,6 +222,19 @@ def status(plan: BridgePlan, gh: G.GhRunner, repo: str) -> dict:
222
222
  "doc": key})
223
223
  out: dict = {"repo": repo, "projects": [],
224
224
  "scope": {"unverified": unverified, "orphaned": orphaned}}
225
+ def facts(key):
226
+ """Real, presentational issue facts (never fed back into derivation)."""
227
+ issue = by_key.get(key) or {}
228
+ return {
229
+ "title": issue.get("title"),
230
+ "state": issue.get("state"),
231
+ "created_at": (issue.get("created_at") or "")[:10] or None,
232
+ "closed_at": (issue.get("closed_at") or "")[:10] or None,
233
+ "url": issue.get("html_url"),
234
+ "assignee": (issue.get("assignee") or {}).get("login"),
235
+ "labels": [lb.get("name") for lb in issue.get("labels") or []],
236
+ }
237
+
225
238
  for p in plan.projects:
226
239
  feats = []
227
240
  for f in p["features"]:
@@ -229,6 +242,10 @@ def status(plan: BridgePlan, gh: G.GhRunner, repo: str) -> dict:
229
242
  feats.append({
230
243
  "id": f.id,
231
244
  "issue": (by_key.get(f.id) or {}).get("number"),
245
+ "facts": facts(f.id),
246
+ "stories": [{"id": s.id,
247
+ "issue": (by_key.get(s.id) or {}).get("number"),
248
+ "facts": facts(s.id)} for s in f.stories],
232
249
  "stories_total": len(f.stories),
233
250
  "stories_closed": sum(1 for s in states if s == "closed"),
234
251
  "closed": (by_key.get(f.id) or {}).get("state") == "closed",
@@ -240,9 +240,29 @@ def cmd_pages(args: argparse.Namespace) -> int:
240
240
  execution[proj["id"]] = {**proj, "scope": data.get("scope"),
241
241
  "repo": proj.get("repo") or data.get("repo")}
242
242
 
243
+ def _activity(project_id: str) -> list[dict]:
244
+ """Recent documents/ commits for one project (best-effort, git only)."""
245
+ import subprocess
246
+ try:
247
+ out = subprocess.run(
248
+ ["git", "log", "-n", "6", "--date=short",
249
+ "--pretty=%ad\x1f%s", "--", f"{docs_dir}/{project_id}"],
250
+ capture_output=True, text=True, timeout=10)
251
+ if out.returncode != 0:
252
+ return []
253
+ rows = []
254
+ for line in out.stdout.strip().splitlines():
255
+ when, _, text = line.partition("\x1f")
256
+ if text:
257
+ rows.append({"when": when, "text": text})
258
+ return rows
259
+ except Exception:
260
+ return []
261
+
243
262
  plist = projects_mod.load_projects(docs_dir)
244
263
  for p in plist:
245
264
  model = status_mod.build_status(docs_dir, project=p["id"])
265
+ model["activity"] = _activity(p["id"])
246
266
  if p["id"] in execution:
247
267
  model["execution"] = execution[p["id"]]
248
268
  (out / f"{p['id']}.html").write_text(status_mod.render_html(model))
@@ -113,6 +113,37 @@ def _broken_references(graph, code=None):
113
113
  return broken
114
114
 
115
115
 
116
+ _MS_RE = re.compile(r"^(?P<label>.+?)[:\u2014\u2013-]\s*(?P<date>\d{4}-\d{2}-\d{2})\.?\s*$")
117
+
118
+
119
+ def _milestones(docs):
120
+ """Dated charter milestones as temporal facts (never completion claims)."""
121
+ import datetime
122
+ today = datetime.date.today()
123
+ out = []
124
+ for d in docs:
125
+ if d.kind != "charter":
126
+ continue
127
+ section = d.section("Milestones")
128
+ if section is None:
129
+ continue
130
+ for bullet in section.list_items:
131
+ m = _MS_RE.match(bullet.strip())
132
+ if not m:
133
+ continue
134
+ try:
135
+ due = datetime.date.fromisoformat(m.group("date"))
136
+ except ValueError:
137
+ continue
138
+ delta = (due - today).days
139
+ out.append({"label": m.group("label").strip(),
140
+ "date": due.isoformat(),
141
+ "days": delta,
142
+ "when": ("today" if delta == 0 else
143
+ "elapsed" if delta < 0 else "upcoming")})
144
+ return sorted(out, key=lambda x: x["date"])
145
+
146
+
116
147
  def _operations(docs):
117
148
  """Operations documents with their review state."""
118
149
  import datetime
@@ -199,6 +230,7 @@ def build_status(documents_dir=DOCUMENTS_DIR, project: str | None = None) -> dic
199
230
  "coverage": _coverage(graph, cfg, code),
200
231
  "risks": _risks(graph, code),
201
232
  "operations": _operations(docs),
233
+ "milestones": _milestones(docs),
202
234
  "broken_references": _broken_references(graph, code),
203
235
  "latest_report": _latest_report(docs),
204
236
  "completeness": completeness,
@@ -231,6 +263,19 @@ def completeness_report(documents_dir=DOCUMENTS_DIR) -> list[dict]:
231
263
  return out
232
264
 
233
265
 
266
+ def _next_dated(m) -> str | None:
267
+ """The nearest upcoming dated marker (milestone or operations review)."""
268
+ import datetime
269
+ cands = [x["date"] for x in m.get("milestones", []) if x["days"] >= 0]
270
+ for o in m.get("operations", []):
271
+ try:
272
+ if datetime.date.fromisoformat(o["review_by"]) >= datetime.date.today():
273
+ cands.append(o["review_by"])
274
+ except ValueError:
275
+ pass
276
+ return min(cands) if cands else None
277
+
278
+
234
279
  def build_index(documents_dir=DOCUMENTS_DIR) -> dict:
235
280
  """The multi-project view: each project's derived RAG + headline signals,
236
281
  plus the whole-repo rollup."""
@@ -246,6 +291,10 @@ def build_index(documents_dir=DOCUMENTS_DIR) -> dict:
246
291
  "failing": m["counts"]["failing"],
247
292
  "risks": len([r for r in m["risks"] if r.get("disposition", "open") == "open"]),
248
293
  "coverage_gaps": sum(len(c["gaps"]) for c in m["coverage"]),
294
+ "coverage_pct": (round(100 * sum(c["covered"] for c in m["coverage"])
295
+ / max(1, sum(c["total"] for c in m["coverage"])))
296
+ if any(c["total"] for c in m["coverage"]) else 100),
297
+ "next": _next_dated(m),
249
298
  "broken": len(m["broken_references"]),
250
299
  "completeness": m.get("completeness"),
251
300
  })