docassert 0.9.0__tar.gz → 0.10.1__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.
- {docassert-0.9.0/docassert.egg-info → docassert-0.10.1}/PKG-INFO +1 -1
- {docassert-0.9.0 → docassert-0.10.1}/docassert/__init__.py +1 -1
- {docassert-0.9.0 → docassert-0.10.1}/docassert/bridge/ops.py +22 -3
- {docassert-0.9.0 → docassert-0.10.1}/docassert/bridge/plan.py +19 -3
- {docassert-0.9.0 → docassert-0.10.1}/docassert/cli.py +11 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/status/render.py +35 -0
- {docassert-0.9.0 → docassert-0.10.1/docassert.egg-info}/PKG-INFO +1 -1
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_badge.py +22 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_bridge.py +29 -0
- {docassert-0.9.0 → docassert-0.10.1}/LICENSE +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/NOTICE +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/README.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/__main__.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/consistency.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/adr.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/benefits-realization.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/brd.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/business-case.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/charter.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/data-migration-plan.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/frnfr.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/hypercare-plan.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/post-implementation-review.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/prd.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/project.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/qa-test-plan.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/raci-stakeholder.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/release-cutover-plan.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/risk-register.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/rollback-plan.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/runbook.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/status-report.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/test-cases.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/user-story.criteria.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/profiles/agile-delivery.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/profiles/lean-startup.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/profiles/regulated-industry.yaml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/adr.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/benefits-realization.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/brd.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/business-case.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/charter.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/data-migration-plan.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/frnfr.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/hypercare-plan.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/post-implementation-review.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/prd.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/project.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/qa-test-plan.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/raci-stakeholder.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/release-cutover-plan.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/risk-register.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/rollback-plan.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/runbook.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/status-report.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/test-cases.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/user-story.schema.json +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/skills/doc-to-pmo/SKILL.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/adr.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/benefits-realization.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/brd.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/business-case.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/charter.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/data-migration-plan.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/frnfr.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/hypercare-plan.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/post-implementation-review.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/prd.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/project.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/qa-test-plan.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/raci-stakeholder.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/release-cutover-plan.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/risk-register.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/rollback-plan.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/runbook.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/status-report.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/test-cases.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/user-story.template.md +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/bridge/__init__.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/bridge/board.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/bridge/gh.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/config.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/consistency.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/extract.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/graph.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/loader.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/models.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/profiles.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/projects.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/report.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/rtm.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/scaffold.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/semantic.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/status/__init__.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/status/derive.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert/structural.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert.egg-info/SOURCES.txt +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert.egg-info/dependency_links.txt +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert.egg-info/entry_points.txt +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert.egg-info/requires.txt +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/docassert.egg-info/top_level.txt +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/pyproject.toml +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/setup.cfg +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_config.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_consistency.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_defects.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_draft_relaxation.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_extract.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_graph.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_json_report.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_kinds_delivery.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_kinds_governance.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_kinds_operate.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_kinds_reporting.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_profiles.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_projects.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_scaffold.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_status.py +0 -0
- {docassert-0.9.0 → docassert-0.10.1}/tests/test_structural.py +0 -0
|
@@ -171,9 +171,28 @@ def reconcile(plan: BridgePlan, gh: G.GhRunner, repo: str) -> tuple[list[str], i
|
|
|
171
171
|
|
|
172
172
|
|
|
173
173
|
def status(plan: BridgePlan, gh: G.GhRunner, repo: str) -> dict:
|
|
174
|
-
"""Read-only: delivery progress per feature
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
"""Read-only: delivery progress per feature, plus scope classification.
|
|
175
|
+
|
|
176
|
+
Unlike reconcile, this never labels or comments; it only reports, so it is
|
|
177
|
+
safe to run from the dashboard build.
|
|
178
|
+
"""
|
|
179
|
+
issues = G.list_issues(gh, repo)
|
|
180
|
+
by_key = _index(issues)
|
|
181
|
+
managed = plan.managed_ids
|
|
182
|
+
unverified, orphaned = [], []
|
|
183
|
+
for issue in issues:
|
|
184
|
+
if issue.get("state") != "open":
|
|
185
|
+
continue
|
|
186
|
+
key = _marker_of(issue)
|
|
187
|
+
if key == SCOPE_REPORT_KEY:
|
|
188
|
+
continue
|
|
189
|
+
if key is None:
|
|
190
|
+
unverified.append({"number": issue["number"], "title": issue["title"]})
|
|
191
|
+
elif key not in managed:
|
|
192
|
+
orphaned.append({"number": issue["number"], "title": issue["title"],
|
|
193
|
+
"doc": key})
|
|
194
|
+
out: dict = {"repo": repo, "projects": [],
|
|
195
|
+
"scope": {"unverified": unverified, "orphaned": orphaned}}
|
|
177
196
|
for p in plan.projects:
|
|
178
197
|
feats = []
|
|
179
198
|
for f in p["features"]:
|
|
@@ -7,6 +7,7 @@ anywhere in the bridge, writes to documents/.
|
|
|
7
7
|
"""
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import os
|
|
10
11
|
from dataclasses import dataclass, field
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
|
|
@@ -54,6 +55,18 @@ class BridgePlan:
|
|
|
54
55
|
return {f.id for f in self.features} | {s.id for s in self.stories}
|
|
55
56
|
|
|
56
57
|
|
|
58
|
+
def _repo_rel(path: str, documents_dir: Path) -> str:
|
|
59
|
+
"""Path relative to the docs repo root (the parent of documents_dir).
|
|
60
|
+
|
|
61
|
+
Issue bodies link Source as {docs_url}/{doc_path}, and docs_url points at
|
|
62
|
+
the docs repository. When CI checks that repository out into a
|
|
63
|
+
subdirectory, load paths carry the checkout prefix; stripping down to the
|
|
64
|
+
repo root keeps the links valid wherever the docs are checked out.
|
|
65
|
+
"""
|
|
66
|
+
rel = os.path.relpath(path, documents_dir.parent)
|
|
67
|
+
return path if rel.startswith("..") else Path(rel).as_posix()
|
|
68
|
+
|
|
69
|
+
|
|
57
70
|
def _title(item_text: str, limit: int = 64) -> str:
|
|
58
71
|
"""First sentence of the item text, trimmed for an issue title."""
|
|
59
72
|
first = item_text.split(". ")[0].rstrip(".")
|
|
@@ -126,14 +139,17 @@ def build_bridge_plan(documents_dir: str | Path = "documents") -> BridgePlan:
|
|
|
126
139
|
feat = features.get(parent.id)
|
|
127
140
|
if feat is None:
|
|
128
141
|
feat = Feature(id=parent.id, text=parent.text,
|
|
129
|
-
doc_path=parent.doc_path,
|
|
142
|
+
doc_path=_repo_rel(parent.doc_path,
|
|
143
|
+
documents_dir),
|
|
144
|
+
project=proj_id)
|
|
130
145
|
feat.acceptance = [(a.id, a.text) for a in
|
|
131
146
|
graph.children(parent.id, "verifies", "AC")]
|
|
132
147
|
for aid, _ in feat.acceptance:
|
|
133
148
|
feat.tests += [t.id for t in graph.children(aid, "tests", "TC")]
|
|
134
149
|
features[parent.id] = feat
|
|
135
|
-
feat.stories.append(Story(
|
|
136
|
-
|
|
150
|
+
feat.stories.append(Story(
|
|
151
|
+
id=s.id, text=s.text, feature_id=parent.id,
|
|
152
|
+
doc_path=_repo_rel(s.doc_path, documents_dir)))
|
|
137
153
|
if problems:
|
|
138
154
|
plan.skipped.append({"id": proj_id,
|
|
139
155
|
"reason": "; ".join(problems)})
|
|
@@ -232,9 +232,19 @@ def cmd_pages(args: argparse.Namespace) -> int:
|
|
|
232
232
|
status_mod.render_badge_json(index["overall"]["rag"]))
|
|
233
233
|
(out / "badges").mkdir(exist_ok=True)
|
|
234
234
|
|
|
235
|
+
execution = {}
|
|
236
|
+
if getattr(args, "execution", None):
|
|
237
|
+
import json as _json
|
|
238
|
+
data = _json.loads(Path(args.execution).read_text())
|
|
239
|
+
for proj in data.get("projects", []):
|
|
240
|
+
execution[proj["id"]] = {**proj, "scope": data.get("scope"),
|
|
241
|
+
"repo": data.get("repo")}
|
|
242
|
+
|
|
235
243
|
plist = projects_mod.load_projects(docs_dir)
|
|
236
244
|
for p in plist:
|
|
237
245
|
model = status_mod.build_status(docs_dir, project=p["id"])
|
|
246
|
+
if p["id"] in execution:
|
|
247
|
+
model["execution"] = execution[p["id"]]
|
|
238
248
|
(out / f"{p['id']}.html").write_text(status_mod.render_html(model))
|
|
239
249
|
(out / "badges" / f"{p['id']}.json").write_text(
|
|
240
250
|
status_mod.render_badge_json(model["rag"], label=p["code"].lower()))
|
|
@@ -398,6 +408,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
398
408
|
|
|
399
409
|
pg = sub.add_parser("pages", help="Build the full Pages site (portfolio index + a page per project).")
|
|
400
410
|
pg.add_argument("--out", default="_site", help="Output directory (default: _site).")
|
|
411
|
+
pg.add_argument("--execution", help="Optional bridge-status JSON; adds Delivery and Scope panels to project pages.")
|
|
401
412
|
docs_dir_opt(pg)
|
|
402
413
|
pg.set_defaults(func=cmd_pages)
|
|
403
414
|
|
|
@@ -205,6 +205,40 @@ def render_html(model) -> str:
|
|
|
205
205
|
f'{note}<table><thead><tr><th>Kind</th><th>State</th></tr></thead>'
|
|
206
206
|
f'<tbody>{req_rows}{sep}{rec_rows}</tbody></table></section>')
|
|
207
207
|
|
|
208
|
+
execution_html = ""
|
|
209
|
+
ex = model.get("execution")
|
|
210
|
+
if ex:
|
|
211
|
+
feat_rows: list[str] = []
|
|
212
|
+
for f in ex.get("features", []):
|
|
213
|
+
total = f.get("stories_total", 0) or 0
|
|
214
|
+
done = f.get("stories_closed", 0) or 0
|
|
215
|
+
pct = (100 * done // total) if total else 0
|
|
216
|
+
mark = "✓" if f.get("closed") else ""
|
|
217
|
+
issue = f' · <a href="https://github.com/{esc(ex.get("repo", ""))}/issues/{f["issue"]}">#{f["issue"]}</a>' if f.get("issue") else ""
|
|
218
|
+
feat_rows.append(
|
|
219
|
+
f'<div class="sig"><div class="sig-h"><span>{esc(f["id"])} {mark}</span>'
|
|
220
|
+
f'<span>{done}/{total} stories{issue}</span></div>'
|
|
221
|
+
f'<div class="bar"><div class="bar-f" style="width:{pct}%;background:var(--ok)"></div></div></div>')
|
|
222
|
+
overall_t = ex.get("stories_total", 0) or 0
|
|
223
|
+
overall_d = ex.get("stories_closed", 0) or 0
|
|
224
|
+
execution_html = (
|
|
225
|
+
f'<section><h2>Delivery · {overall_d}/{overall_t} stories closed</h2>'
|
|
226
|
+
'<p style="color:var(--muted);font-size:13px;margin:0 0 10px">'
|
|
227
|
+
"Read from the bridge-managed issues. Scope lives in these documents; "
|
|
228
|
+
"delivery lives on the board.</p>" + "".join(feat_rows) + "</section>")
|
|
229
|
+
scope = ex.get("scope") or {}
|
|
230
|
+
bad = scope.get("unverified", []) + scope.get("orphaned", [])
|
|
231
|
+
if bad:
|
|
232
|
+
items = "".join(
|
|
233
|
+
f'<li><a href="https://github.com/{esc(ex.get("repo", ""))}/issues/{i["number"]}">#{i["number"]}</a> {esc(i["title"])}</li>'
|
|
234
|
+
for i in bad)
|
|
235
|
+
execution_html += (
|
|
236
|
+
f'<section><h2 style="color:var(--bad)">Scope · {len(bad)} unmatched item(s)</h2>'
|
|
237
|
+
f'<ul>{items}</ul></section>')
|
|
238
|
+
else:
|
|
239
|
+
execution_html += ('<section><h2>Scope</h2><p style="color:var(--ok)">'
|
|
240
|
+
"Every open issue matches an approved item in the documents.</p></section>")
|
|
241
|
+
|
|
208
242
|
title = esc(model.get("title", "Project Status"))
|
|
209
243
|
pid = model.get("project")
|
|
210
244
|
back = '<a class="back" href="index.html">← all projects</a>' if pid else ""
|
|
@@ -226,6 +260,7 @@ def render_html(model) -> str:
|
|
|
226
260
|
{c['approved']} approved{scope} · generated {gen}. Do not edit — regenerated from the documents.</p>
|
|
227
261
|
<section><h2>Traceability coverage</h2>{coverage}</section>
|
|
228
262
|
{document_set}
|
|
263
|
+
{execution_html}
|
|
229
264
|
{problems}
|
|
230
265
|
<section><h2>Open risks ({len(model['risks'])})</h2><ul>{risks}</ul></section>
|
|
231
266
|
<section><h2>Latest status report</h2><p>{report}</p></section>
|
|
@@ -24,3 +24,25 @@ def test_pages_emits_badges(tmp_path, monkeypatch):
|
|
|
24
24
|
assert overall["schemaVersion"] == 1 and overall["message"] in {"green", "amber", "red"}
|
|
25
25
|
aur = json.loads((out / "badges" / "PRJ-001-AUR.json").read_text())
|
|
26
26
|
assert aur["label"] == "aur" and aur["message"] in {"green", "amber", "red"}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_pages_execution_panels(tmp_path, monkeypatch):
|
|
30
|
+
import json
|
|
31
|
+
monkeypatch.chdir(ROOT)
|
|
32
|
+
exec_file = tmp_path / "execution.json"
|
|
33
|
+
exec_file.write_text(json.dumps({
|
|
34
|
+
"repo": "o/r",
|
|
35
|
+
"projects": [{"id": "PRJ-001-AUR",
|
|
36
|
+
"features": [{"id": "AUR-PR-014", "issue": 5,
|
|
37
|
+
"stories_total": 2, "stories_closed": 1,
|
|
38
|
+
"closed": False}],
|
|
39
|
+
"stories_closed": 1, "stories_total": 2}],
|
|
40
|
+
"scope": {"unverified": [], "orphaned": []},
|
|
41
|
+
}))
|
|
42
|
+
out = tmp_path / "site"
|
|
43
|
+
assert main(["pages", "--out", str(out), "--execution", str(exec_file)]) == 0
|
|
44
|
+
html = (out / "PRJ-001-AUR.html").read_text()
|
|
45
|
+
assert "Delivery · 1/2 stories closed" in html
|
|
46
|
+
assert "AUR-PR-014" in html and "Every open issue matches" in html
|
|
47
|
+
other = (out / "PRJ-002-ATL.html").read_text()
|
|
48
|
+
assert "Delivery ·" not in other # no execution data, no panel
|
|
@@ -113,6 +113,20 @@ def test_bodies_carry_marker_and_authority_note(tmp_path):
|
|
|
113
113
|
assert f"<!-- docassert-bridge: {s.id} -->" in story_body(s, None)
|
|
114
114
|
|
|
115
115
|
|
|
116
|
+
def test_doc_paths_relative_to_docs_repo_root(tmp_path):
|
|
117
|
+
"""CI checks the docs repo out into a subdirectory; Source links must not
|
|
118
|
+
carry that checkout prefix (they resolve against the docs repo root)."""
|
|
119
|
+
docs = _tree(tmp_path / "pmo")
|
|
120
|
+
plan = build_bridge_plan(docs)
|
|
121
|
+
f = plan.features[0]
|
|
122
|
+
assert f.doc_path == "documents/PRJ-001-TST/prd.md"
|
|
123
|
+
assert f.stories[0].doc_path == "documents/PRJ-001-TST/user-story.md"
|
|
124
|
+
body = feature_body(f, "https://github.com/o/r/blob/main")
|
|
125
|
+
assert ("**Source:** https://github.com/o/r/blob/main/"
|
|
126
|
+
"documents/PRJ-001-TST/prd.md") in body
|
|
127
|
+
assert "/pmo/" not in body
|
|
128
|
+
|
|
129
|
+
|
|
116
130
|
# ── ops against a fake runner ────────────────────────────────────────────────
|
|
117
131
|
class FakeGh:
|
|
118
132
|
"""Records mutations; serves canned issue lists."""
|
|
@@ -320,3 +334,18 @@ def test_scaffold_syncs_board(tmp_path):
|
|
|
320
334
|
assert any("5 item(s) synced" in a for a in actions)
|
|
321
335
|
adds = [q for q in bgh.gql if "addProjectV2ItemById" in q]
|
|
322
336
|
assert len(adds) == 5
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def test_status_includes_scope_classification(tmp_path):
|
|
340
|
+
plan = build_bridge_plan(_tree(tmp_path))
|
|
341
|
+
issues = [
|
|
342
|
+
_mk_issue(1, "TST-US-001"),
|
|
343
|
+
{"number": 2, "node_id": "N2", "state": "open", "title": "rogue",
|
|
344
|
+
"body": "no marker", "labels": []},
|
|
345
|
+
_mk_issue(3, "TST-US-999"),
|
|
346
|
+
_mk_issue(4, "TST-US-002", state="closed"), # closed: not scope-checked
|
|
347
|
+
]
|
|
348
|
+
data = ops.status(plan, FakeGh(issues), "o/r")
|
|
349
|
+
assert data["repo"] == "o/r"
|
|
350
|
+
assert [i["number"] for i in data["scope"]["unverified"]] == [2]
|
|
351
|
+
assert [i["doc"] for i in data["scope"]["orphaned"]] == ["TST-US-999"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/benefits-realization.criteria.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/data-migration-plan.criteria.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/raci-stakeholder.criteria.yaml
RENAMED
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/criteria/release-cutover-plan.criteria.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/benefits-realization.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/post-implementation-review.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/schema/release-cutover-plan.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/benefits-realization.template.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/data-migration-plan.template.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docassert-0.9.0 → docassert-0.10.1}/docassert/_data/templates/release-cutover-plan.template.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|