docassert 0.16.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.16.0/docassert.egg-info → docassert-0.18.0}/PKG-INFO +3 -1
  2. {docassert-0.16.0 → docassert-0.18.0}/README.md +2 -0
  3. {docassert-0.16.0 → docassert-0.18.0}/docassert/__init__.py +1 -1
  4. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/charter.criteria.yaml +5 -0
  5. {docassert-0.16.0 → docassert-0.18.0}/docassert/bridge/ops.py +17 -0
  6. {docassert-0.16.0 → docassert-0.18.0}/docassert/cli.py +20 -0
  7. {docassert-0.16.0 → docassert-0.18.0}/docassert/status/derive.py +49 -0
  8. docassert-0.18.0/docassert/status/render.py +865 -0
  9. {docassert-0.16.0 → docassert-0.18.0}/docassert/structural.py +16 -0
  10. {docassert-0.16.0 → docassert-0.18.0/docassert.egg-info}/PKG-INFO +3 -1
  11. {docassert-0.16.0 → docassert-0.18.0}/docassert.egg-info/SOURCES.txt +1 -0
  12. {docassert-0.16.0 → docassert-0.18.0}/pyproject.toml +1 -1
  13. {docassert-0.16.0 → docassert-0.18.0}/tests/test_badge.py +2 -2
  14. docassert-0.18.0/tests/test_report_extract.py +70 -0
  15. {docassert-0.16.0 → docassert-0.18.0}/tests/test_status.py +10 -8
  16. docassert-0.16.0/docassert/status/render.py +0 -397
  17. {docassert-0.16.0 → docassert-0.18.0}/LICENSE +0 -0
  18. {docassert-0.16.0 → docassert-0.18.0}/NOTICE +0 -0
  19. {docassert-0.16.0 → docassert-0.18.0}/docassert/__main__.py +0 -0
  20. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/consistency.yaml +0 -0
  21. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/adr.criteria.yaml +0 -0
  22. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/benefits-realization.criteria.yaml +0 -0
  23. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/brd.criteria.yaml +0 -0
  24. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/business-case.criteria.yaml +0 -0
  25. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/data-migration-plan.criteria.yaml +0 -0
  26. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/frnfr.criteria.yaml +0 -0
  27. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/hypercare-plan.criteria.yaml +0 -0
  28. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/operations.criteria.yaml +0 -0
  29. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/post-implementation-review.criteria.yaml +0 -0
  30. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/prd.criteria.yaml +0 -0
  31. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/project.criteria.yaml +0 -0
  32. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/qa-test-plan.criteria.yaml +0 -0
  33. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/raci-stakeholder.criteria.yaml +0 -0
  34. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/release-cutover-plan.criteria.yaml +0 -0
  35. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/risk-register.criteria.yaml +0 -0
  36. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/rollback-plan.criteria.yaml +0 -0
  37. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/runbook.criteria.yaml +0 -0
  38. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/status-report.criteria.yaml +0 -0
  39. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/test-cases.criteria.yaml +0 -0
  40. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/criteria/user-story.criteria.yaml +0 -0
  41. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/profiles/agile-delivery.yaml +0 -0
  42. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/profiles/lean-startup.yaml +0 -0
  43. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/profiles/operations.yaml +0 -0
  44. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/profiles/regulated-industry.yaml +0 -0
  45. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/adr.schema.json +0 -0
  46. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/benefits-realization.schema.json +0 -0
  47. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/brd.schema.json +0 -0
  48. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/business-case.schema.json +0 -0
  49. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/charter.schema.json +0 -0
  50. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/data-migration-plan.schema.json +0 -0
  51. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/frnfr.schema.json +0 -0
  52. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/hypercare-plan.schema.json +0 -0
  53. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/operations.schema.json +0 -0
  54. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/post-implementation-review.schema.json +0 -0
  55. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/prd.schema.json +0 -0
  56. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/project.schema.json +0 -0
  57. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/qa-test-plan.schema.json +0 -0
  58. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/raci-stakeholder.schema.json +0 -0
  59. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/release-cutover-plan.schema.json +0 -0
  60. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/risk-register.schema.json +0 -0
  61. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/rollback-plan.schema.json +0 -0
  62. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/runbook.schema.json +0 -0
  63. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/status-report.schema.json +0 -0
  64. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/test-cases.schema.json +0 -0
  65. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/schema/user-story.schema.json +0 -0
  66. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/skills/doc-to-pmo/SKILL.md +0 -0
  67. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/adr.template.md +0 -0
  68. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/benefits-realization.template.md +0 -0
  69. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/brd.template.md +0 -0
  70. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/business-case.template.md +0 -0
  71. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/charter.template.md +0 -0
  72. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/data-migration-plan.template.md +0 -0
  73. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/frnfr.template.md +0 -0
  74. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/hypercare-plan.template.md +0 -0
  75. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/operations.template.md +0 -0
  76. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/post-implementation-review.template.md +0 -0
  77. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/prd.template.md +0 -0
  78. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/project.template.md +0 -0
  79. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/qa-test-plan.template.md +0 -0
  80. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/raci-stakeholder.template.md +0 -0
  81. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/release-cutover-plan.template.md +0 -0
  82. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/risk-register.template.md +0 -0
  83. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/rollback-plan.template.md +0 -0
  84. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/runbook.template.md +0 -0
  85. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/status-report.template.md +0 -0
  86. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/test-cases.template.md +0 -0
  87. {docassert-0.16.0 → docassert-0.18.0}/docassert/_data/templates/user-story.template.md +0 -0
  88. {docassert-0.16.0 → docassert-0.18.0}/docassert/bridge/__init__.py +0 -0
  89. {docassert-0.16.0 → docassert-0.18.0}/docassert/bridge/board.py +0 -0
  90. {docassert-0.16.0 → docassert-0.18.0}/docassert/bridge/gh.py +0 -0
  91. {docassert-0.16.0 → docassert-0.18.0}/docassert/bridge/plan.py +0 -0
  92. {docassert-0.16.0 → docassert-0.18.0}/docassert/config.py +0 -0
  93. {docassert-0.16.0 → docassert-0.18.0}/docassert/consistency.py +0 -0
  94. {docassert-0.16.0 → docassert-0.18.0}/docassert/extract.py +0 -0
  95. {docassert-0.16.0 → docassert-0.18.0}/docassert/graph.py +0 -0
  96. {docassert-0.16.0 → docassert-0.18.0}/docassert/loader.py +0 -0
  97. {docassert-0.16.0 → docassert-0.18.0}/docassert/models.py +0 -0
  98. {docassert-0.16.0 → docassert-0.18.0}/docassert/profiles.py +0 -0
  99. {docassert-0.16.0 → docassert-0.18.0}/docassert/projects.py +0 -0
  100. {docassert-0.16.0 → docassert-0.18.0}/docassert/report.py +0 -0
  101. {docassert-0.16.0 → docassert-0.18.0}/docassert/rtm.py +0 -0
  102. {docassert-0.16.0 → docassert-0.18.0}/docassert/scaffold.py +0 -0
  103. {docassert-0.16.0 → docassert-0.18.0}/docassert/semantic.py +0 -0
  104. {docassert-0.16.0 → docassert-0.18.0}/docassert/status/__init__.py +0 -0
  105. {docassert-0.16.0 → docassert-0.18.0}/docassert.egg-info/dependency_links.txt +0 -0
  106. {docassert-0.16.0 → docassert-0.18.0}/docassert.egg-info/entry_points.txt +0 -0
  107. {docassert-0.16.0 → docassert-0.18.0}/docassert.egg-info/requires.txt +0 -0
  108. {docassert-0.16.0 → docassert-0.18.0}/docassert.egg-info/top_level.txt +0 -0
  109. {docassert-0.16.0 → docassert-0.18.0}/setup.cfg +0 -0
  110. {docassert-0.16.0 → docassert-0.18.0}/tests/test_bridge.py +0 -0
  111. {docassert-0.16.0 → docassert-0.18.0}/tests/test_config.py +0 -0
  112. {docassert-0.16.0 → docassert-0.18.0}/tests/test_consistency.py +0 -0
  113. {docassert-0.16.0 → docassert-0.18.0}/tests/test_defects.py +0 -0
  114. {docassert-0.16.0 → docassert-0.18.0}/tests/test_draft_relaxation.py +0 -0
  115. {docassert-0.16.0 → docassert-0.18.0}/tests/test_extract.py +0 -0
  116. {docassert-0.16.0 → docassert-0.18.0}/tests/test_graph.py +0 -0
  117. {docassert-0.16.0 → docassert-0.18.0}/tests/test_json_report.py +0 -0
  118. {docassert-0.16.0 → docassert-0.18.0}/tests/test_kinds_delivery.py +0 -0
  119. {docassert-0.16.0 → docassert-0.18.0}/tests/test_kinds_governance.py +0 -0
  120. {docassert-0.16.0 → docassert-0.18.0}/tests/test_kinds_operate.py +0 -0
  121. {docassert-0.16.0 → docassert-0.18.0}/tests/test_kinds_reporting.py +0 -0
  122. {docassert-0.16.0 → docassert-0.18.0}/tests/test_profiles.py +0 -0
  123. {docassert-0.16.0 → docassert-0.18.0}/tests/test_projects.py +0 -0
  124. {docassert-0.16.0 → docassert-0.18.0}/tests/test_scaffold.py +0 -0
  125. {docassert-0.16.0 → docassert-0.18.0}/tests/test_semantic.py +0 -0
  126. {docassert-0.16.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.16.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
@@ -102,6 +102,8 @@ flagged as TODOs, never invented). The skill's source is
102
102
  | `docassert extract <file>` | Extract plain text from a source `.docx` / `.pdf` / `.md` / `.txt` (the first step of doc-to-pmo conversion). Needs the `convert` extra: `pip install "docassert[convert]"`. |
103
103
 
104
104
  Every document-reading command accepts `--documents-dir` (default `documents/`).
105
+ The full flag-by-flag reference, and what 1.x does and does not promise to
106
+ keep stable, live in [STABILITY.md](STABILITY.md).
105
107
  AI alignment grades at most `alignment_limit` links per run (default 25; set it
106
108
  in `consistency.yaml`, `0` = no cap) so API cost stays bounded on large graphs.
107
109
 
@@ -62,6 +62,8 @@ flagged as TODOs, never invented). The skill's source is
62
62
  | `docassert extract <file>` | Extract plain text from a source `.docx` / `.pdf` / `.md` / `.txt` (the first step of doc-to-pmo conversion). Needs the `convert` extra: `pip install "docassert[convert]"`. |
63
63
 
64
64
  Every document-reading command accepts `--documents-dir` (default `documents/`).
65
+ The full flag-by-flag reference, and what 1.x does and does not promise to
66
+ keep stable, live in [STABILITY.md](STABILITY.md).
65
67
  AI alignment grades at most `alignment_limit` links per run (default 25; set it
66
68
  in `consistency.yaml`, `0` = no cap) so API cost stays bounded on large graphs.
67
69
 
@@ -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.16.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
  })