test_phase3_wp00_baseline_contract.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. from __future__ import annotations
  2. import hashlib
  3. import io
  4. import json
  5. import os
  6. import re
  7. import subprocess
  8. import tarfile
  9. from pathlib import Path
  10. ROOT = Path(__file__).resolve().parents[1]
  11. SCOPE = ROOT / "docs/phase3/P3_WP00_SCOPE_BASELINE.md"
  12. REQUIREMENTS = ROOT / "docs/phase3/P3_WP00_REQUIREMENTS.json"
  13. MANIFEST = ROOT / "docs/phase3/P3_WP00_BASELINE_MANIFEST.json"
  14. REGISTERS = ROOT / "docs/phase3/P3_WP00_EXECUTION_REGISTERS.json"
  15. LEDGER = ROOT / "docs/FUNCTION_MODULE_CENSUS_20260726.md"
  16. PHASE3_PLAN = ROOT / "docs/DATAOPS_PHASE3_6_MONTH_DEVELOPMENT_PLAN_20260802.md"
  17. PHASE2_COMMIT = "95023e59bfb9f00be4ed61f7a8307fac56fbcee9"
  18. PHASE3_CAPTURE_COMMIT = "487b57d538f619407f0f2be1db7061b055b13400"
  19. P0_WORK_PACKAGES = {
  20. "P3-WP00",
  21. "P3-WP01",
  22. "P3-WP02",
  23. "P3-WP03",
  24. "P3-WP04",
  25. "P3-WP05",
  26. "P3-WP06",
  27. "P3-WP08",
  28. "P3-WP14",
  29. }
  30. def _load(path: Path) -> dict:
  31. return json.loads(path.read_text(encoding="utf-8"))
  32. def _ledger_rows() -> dict[str, dict[str, str]]:
  33. content = LEDGER.read_text(encoding="utf-8")
  34. section = content[content.index("## 12. 企业级完整产品功能模块台账") :]
  35. rows: dict[str, dict[str, str]] = {}
  36. for line in section.splitlines():
  37. cells = [cell.strip() for cell in line.strip().strip("|").split("|")]
  38. if len(cells) == 4 and re.fullmatch(r"[A-Z]{3}-\d{2}", cells[0]):
  39. module_id, module_name, description, maturity = cells
  40. assert module_id not in rows
  41. rows[module_id] = {
  42. "module_name": module_name,
  43. "description": description,
  44. "maturity_source_text": maturity,
  45. }
  46. return rows
  47. def _git(*args: str) -> str:
  48. return subprocess.check_output(["git", *args], cwd=ROOT, text=True).strip()
  49. def _git_bytes(*args: str) -> bytes:
  50. return subprocess.check_output(["git", *args], cwd=ROOT)
  51. def _normalized_maturity(source: str) -> tuple[str, str]:
  52. mappings = (
  53. ("已建设", "BUILT", "已建设"),
  54. ("工程完成", "ENGINEERING_COMPLETE", "工程完成"),
  55. ("部分建设", "PARTIAL", "部分建设"),
  56. ("规划中", "PLANNED", "规划中"),
  57. ("能力预留", "RESERVED", "能力预留"),
  58. ("明确不扩展", "PRODUCT_BOUNDARY", "明确不扩展"),
  59. ("兼容保留/退役中", "RETIREMENT", "兼容保留/退役中"),
  60. ("影子模式", "SHADOW", "影子模式"),
  61. )
  62. for prefix, normalized, label in mappings:
  63. if source.startswith(prefix):
  64. return normalized, label
  65. raise AssertionError(f"unknown maturity: {source}")
  66. def _operation_count(content: str) -> int:
  67. return len(
  68. re.findall(
  69. r"^ (?:get|post|put|patch|delete|options|head):$",
  70. content,
  71. flags=re.MULTILINE,
  72. )
  73. )
  74. def test_wp00_deliverables_exist_and_scope_is_honest() -> None:
  75. for path in (SCOPE, REQUIREMENTS, MANIFEST, REGISTERS):
  76. assert path.is_file(), path
  77. content = SCOPE.read_text(encoding="utf-8")
  78. for heading in (
  79. "范围冻结",
  80. "264 项模块追踪",
  81. "企业输入",
  82. "责任与退出条件",
  83. "验收指标",
  84. "容量与排期",
  85. "外部门禁",
  86. ):
  87. assert heading in content
  88. assert "工程基线完成待企业输入确认" in content
  89. assert "不代表企业\nUAT" in content or "不代表企业 UAT" in content
  90. assert "不得用 Mock、本地测试或本地数据库计数替代企业验收" in content
  91. plan = PHASE3_PLAN.read_text(encoding="utf-8")
  92. assert "TBD_AFTER_COMMIT" not in plan
  93. assert "git log -1 -- docs/phase3/P3_WP00_SCOPE_BASELINE.md" in plan
  94. def test_all_264_ledger_modules_are_traced_exactly_once() -> None:
  95. ledger = _ledger_rows()
  96. assert len(ledger) == 264
  97. traceability = _load(REQUIREMENTS)["module_traceability"]
  98. assert len(traceability) == 264
  99. traced = {item["module_id"]: item for item in traceability}
  100. assert len(traced) == 264
  101. assert set(traced) == set(ledger)
  102. allowed = {
  103. "P3_WORK_PACKAGE",
  104. "FUTURE_PHASE",
  105. "MAINTAIN",
  106. "PRODUCT_BOUNDARY",
  107. "RETIREMENT",
  108. }
  109. for module_id, ledger_item in ledger.items():
  110. item = traced[module_id]
  111. assert item["module_name"] == ledger_item["module_name"]
  112. assert item["description"] == ledger_item["description"]
  113. assert item["maturity_source_text"] == ledger_item["maturity_source_text"]
  114. normalized, current_label = _normalized_maturity(ledger_item["maturity_source_text"])
  115. assert item["normalized_maturity"] == normalized
  116. assert item["current_maturity"] == current_label
  117. assert item["disposition"] in allowed
  118. assert isinstance(item["target_work_packages"], list)
  119. assert item["rationale"].strip()
  120. if item["disposition"] == "P3_WORK_PACKAGE":
  121. assert item["target_work_packages"]
  122. assert item["implementation_owner_wp"]
  123. assert item["closure_wp"] == "P3-WP14"
  124. assert item["implementation_owner_wp"] in item["target_work_packages"]
  125. assert set(item["dependent_wps"]).issubset(item["target_work_packages"])
  126. else:
  127. assert item["implementation_owner_wp"] is None
  128. assert item["closure_wp"] is None
  129. def test_all_original_backlogs_are_mapped_and_splits_are_recorded() -> None:
  130. payload = _load(REQUIREMENTS)
  131. traceability = {item["module_id"]: item for item in payload["module_traceability"]}
  132. valid_module_ids = set(traceability)
  133. valid_work_packages = {f"P3-WP{number:02d}" for number in range(0, 15)}
  134. mappings = payload["backlog_work_package_mapping"]
  135. assert {item["original_backlog_id"] for item in mappings} == {
  136. f"P3-{number:02d}" for number in range(1, 13)
  137. }
  138. assert all(item["target_work_packages"] for item in mappings)
  139. declared_role_pairs: set[tuple[str, str]] = set()
  140. for mapping in mappings:
  141. assert set(mapping["module_ids"]).issubset(valid_module_ids)
  142. assert set(mapping["target_work_packages"]).issubset(valid_work_packages)
  143. for module_id in mapping["module_ids"]:
  144. traced = traceability[module_id]
  145. roles = {traced["implementation_owner_wp"], *traced["dependent_wps"]}
  146. matching_roles = roles.intersection(mapping["target_work_packages"])
  147. assert matching_roles, (mapping["original_backlog_id"], module_id, roles)
  148. declared_role_pairs.update((module_id, role) for role in matching_roles)
  149. changes = payload["scope_additions"]
  150. assert changes
  151. for item in changes:
  152. assert item["original_backlog_id"] in {f"P3-{number:02d}" for number in range(1, 13)}
  153. assert item["added_module_ids"]
  154. assert set(item["added_module_ids"]).issubset(valid_module_ids)
  155. assert item["split_work_packages"]
  156. assert set(item["split_work_packages"]).issubset(valid_work_packages)
  157. assert item["change_reason"]
  158. addition_map = {
  159. mapping["module_id"]: mapping
  160. for mapping in item["added_module_work_package_map"]
  161. }
  162. assert set(addition_map) == set(item["added_module_ids"])
  163. for module_id, mapping in addition_map.items():
  164. traced = traceability[module_id]
  165. assert mapping["implementation_owner_wp"] == traced["implementation_owner_wp"]
  166. assert mapping["dependent_wps"] == traced["dependent_wps"]
  167. assert mapping["closure_wp"] == traced["closure_wp"] == "P3-WP14"
  168. declared_role_pairs.add((module_id, mapping["implementation_owner_wp"]))
  169. declared_role_pairs.update((module_id, wp) for wp in mapping["dependent_wps"])
  170. traced_role_pairs = {
  171. (module_id, wp)
  172. for module_id, item in traceability.items()
  173. if item["disposition"] == "P3_WORK_PACKAGE"
  174. for wp in [item["implementation_owner_wp"], *item["dependent_wps"]]
  175. }
  176. assert declared_role_pairs == traced_role_pairs
  177. assert all(
  178. item["closure_wp"] == "P3-WP14"
  179. for item in traceability.values()
  180. if item["disposition"] == "P3_WORK_PACKAGE"
  181. )
  182. def test_p0_work_packages_have_complete_accountability_and_exit_contracts() -> None:
  183. packages = _load(REQUIREMENTS)["p0_work_packages"]
  184. assert {item["work_package"] for item in packages} == P0_WORK_PACKAGES
  185. for item in packages:
  186. assert item["owner_role"]
  187. assert item["required_inputs"]
  188. assert item["acceptance_owner_role"]
  189. assert item["exit_criteria"]
  190. assert item["owner_person_name"] is None
  191. assert item["acceptance_owner_person_name"] is None
  192. assert item["enterprise_gate_inputs"] == item["required_inputs"]
  193. assert item["enterprise_confirmation_status"] == "TBD_EXTERNAL"
  194. wp00 = next(item for item in packages if item["work_package"] == "P3-WP00")
  195. assert wp00["engineering_completion_status"] == "COMPLETE"
  196. assert wp00["engineering_baseline_inputs"]
  197. wp14 = next(item for item in packages if item["work_package"] == "P3-WP14")
  198. assert any("P0 不允许" in criterion for criterion in wp14["exit_criteria"])
  199. assert set(wp14["dynamic_required_inputs_by_started_wp"]) == {
  200. "P3-WP09", "P3-WP10", "P3-WP11", "P3-WP12", "P3-WP13"
  201. }
  202. def test_enterprise_inputs_remain_external_and_block_the_right_packages() -> None:
  203. payload = _load(REQUIREMENTS)
  204. inputs = payload["enterprise_inputs"]
  205. required_ids = {
  206. "pilot_goal",
  207. "identity",
  208. "enterprise_source_1",
  209. "enterprise_source_2",
  210. "network",
  211. "monitoring",
  212. "smtp",
  213. "collaboration",
  214. "itsm",
  215. "security_legal",
  216. "infrastructure",
  217. "third_business_domain",
  218. "ai",
  219. "multi_tenancy",
  220. "bi_ai",
  221. "cost",
  222. "plugin",
  223. }
  224. assert {item["id"] for item in inputs} == required_ids
  225. for item in inputs:
  226. assert item["status"] == "TBD_EXTERNAL"
  227. assert item["owner_role"]
  228. assert item["required_by_wp"]
  229. assert item["evidence"] == "TBD_EXTERNAL"
  230. assert item["blocking_effect"]
  231. assert item["enterprise_name"] is None
  232. assert item["due_week"] >= 1
  233. assert item["decision_role"]
  234. assert item["evidence_ref"] == "TBD_EXTERNAL"
  235. assert item["waivable"] is False
  236. by_input = {item["id"]: item for item in inputs}
  237. package_inputs: dict[str, set[str]] = {}
  238. for package in payload["p0_work_packages"]:
  239. package_inputs.setdefault(package["work_package"], set()).update(package["required_inputs"])
  240. for input_id in package["required_inputs"]:
  241. assert package["work_package"] in by_input[input_id]["required_by_wp"]
  242. for started_wp, input_ids in package["dynamic_required_inputs_by_started_wp"].items():
  243. package_inputs.setdefault(started_wp, set()).update(input_ids)
  244. package_inputs.setdefault("P3-WP14", set()).update(input_ids)
  245. for input_id in input_ids:
  246. assert started_wp in by_input[input_id]["required_by_wp"]
  247. assert "P3-WP14" in by_input[input_id]["required_by_wp"]
  248. for package in payload["non_p0_work_packages"]:
  249. package_inputs.setdefault(package["work_package"], set()).update(package["required_inputs"])
  250. for input_id in package["required_inputs"]:
  251. assert package["work_package"] in by_input[input_id]["required_by_wp"]
  252. for input_id, item in by_input.items():
  253. for work_package in item["required_by_wp"]:
  254. assert input_id in package_inputs[work_package]
  255. assert "P3-WP07" in by_input["infrastructure"]["required_by_wp"]
  256. def test_exactly_19_default_acceptance_metrics_are_frozen_without_fake_values() -> None:
  257. metrics = _load(REQUIREMENTS)["acceptance_metrics"]
  258. assert len(metrics) == 19
  259. assert len({metric["id"] for metric in metrics}) == 19
  260. for metric in metrics:
  261. for key in (
  262. "definition",
  263. "target",
  264. "denominator_or_sample",
  265. "window",
  266. "source",
  267. "approver_role",
  268. "status",
  269. ):
  270. assert metric[key] not in (None, "", [])
  271. assert metric["status"] in {"DEFAULT_BASELINED", "TBD_EXTERNAL"}
  272. enterprise_overrides = _load(REQUIREMENTS)["enterprise_metric_overrides"]
  273. assert enterprise_overrides["status"] == "TBD_EXTERNAL"
  274. assert enterprise_overrides["enterprise_name"] is None
  275. assert enterprise_overrides["records"] == []
  276. assert set(enterprise_overrides["record_schema"]) == {
  277. "metric_id",
  278. "old_target",
  279. "new_target",
  280. "direction",
  281. "reason",
  282. "approved_at",
  283. "approver_role",
  284. "approver_person_name",
  285. "evidence_ref",
  286. }
  287. def test_manifest_replays_historical_api_migration_and_release_facts(
  288. tmp_path: Path,
  289. ) -> None:
  290. manifest = _load(MANIFEST)
  291. source = manifest["source_control"]
  292. assert source["captured_local_commit"] == PHASE3_CAPTURE_COMMIT
  293. captured_commit = source["captured_local_commit"]
  294. assert source["phase2_baseline_tag"] == "v0.3.0-phase2"
  295. assert source["phase2_baseline_commit"] == PHASE2_COMMIT
  296. assert source["merge_base_with_phase2"] == PHASE2_COMMIT
  297. assert source["remote_phase2_tracking_commit"] == PHASE2_COMMIT
  298. subprocess.run(
  299. ["git", "cat-file", "-e", f"{captured_commit}^{{commit}}"],
  300. cwd=ROOT,
  301. check=True,
  302. )
  303. assert _git("rev-parse", "v0.3.0-phase2^{commit}") == PHASE2_COMMIT
  304. assert _git("rev-parse", f"{captured_commit}^") == PHASE2_COMMIT
  305. assert _git("merge-base", captured_commit, "v0.3.0-phase2") == PHASE2_COMMIT
  306. assert source["remote_network_refresh_performed"] is False
  307. assert "remote-tracking" in source["notes"]
  308. archive_bytes = _git_bytes(
  309. "archive",
  310. captured_commit,
  311. "app",
  312. "alembic.ini",
  313. "docs/architecture/OPENAPI.yaml",
  314. "migrations",
  315. "scripts/generate_openapi.py",
  316. )
  317. archive_root = tmp_path / "captured"
  318. archive_root.mkdir()
  319. with tarfile.open(fileobj=io.BytesIO(archive_bytes), mode="r:") as archive:
  320. archive.extractall(archive_root)
  321. api = manifest["api_contract"]
  322. captured_openapi = _git_bytes(
  323. "show", f"{captured_commit}:docs/architecture/OPENAPI.yaml"
  324. )
  325. assert captured_openapi == (archive_root / "docs/architecture/OPENAPI.yaml").read_bytes()
  326. generated_openapi = archive_root / "generated-openapi.yaml"
  327. generated = subprocess.run(
  328. [
  329. str(ROOT / ".venv/bin/python"),
  330. str(archive_root / "scripts/generate_openapi.py"),
  331. "--output",
  332. str(generated_openapi),
  333. ],
  334. cwd=archive_root,
  335. env={**os.environ, "PYTHONPATH": str(archive_root)},
  336. text=True,
  337. capture_output=True,
  338. check=True,
  339. )
  340. generated_bytes = generated_openapi.read_bytes()
  341. actual_operation_count = _operation_count(captured_openapi.decode("utf-8"))
  342. assert str(actual_operation_count) in generated.stdout
  343. assert api["operation_count"] == actual_operation_count
  344. assert _operation_count(generated_bytes.decode("utf-8")) == actual_operation_count
  345. assert api["sha256"] == hashlib.sha256(captured_openapi).hexdigest()
  346. assert hashlib.sha256(generated_bytes).hexdigest() == api["sha256"]
  347. assert api["generated_matches_committed"] is True
  348. migration = manifest["migration_contract"]
  349. heads_output = subprocess.check_output(
  350. [
  351. str(ROOT / ".venv/bin/alembic"),
  352. "-c",
  353. str(archive_root / "alembic.ini"),
  354. "heads",
  355. ],
  356. cwd=archive_root,
  357. env={**os.environ, "PYTHONPATH": str(archive_root)},
  358. text=True,
  359. )
  360. actual_heads = re.findall(r"^(\S+) \(head\)$", heads_output, flags=re.MULTILINE)
  361. assert migration["heads"] == actual_heads
  362. assert migration["file_count"] == len(
  363. list((archive_root / "migrations/versions").glob("*.py"))
  364. )
  365. assert migration["git_tree"] == _git(
  366. "rev-parse", f"{captured_commit}:migrations"
  367. )
  368. release = manifest["release_copy_contract"]
  369. assert release["app_git_tree"] == _git(
  370. "rev-parse", f"{captured_commit}:app"
  371. )
  372. assert release["deployment_app_git_tree"] == _git(
  373. "rev-parse", f"{captured_commit}:deployment/app"
  374. )
  375. assert release["migrations_git_tree"] == _git(
  376. "rev-parse", f"{captured_commit}:migrations"
  377. )
  378. assert release["deployment_migrations_git_tree"] == _git(
  379. "rev-parse", f"{captured_commit}:deployment/migrations"
  380. )
  381. assert release["app_git_tree"] == release["deployment_app_git_tree"]
  382. assert release["migrations_git_tree"] == release["deployment_migrations_git_tree"]
  383. def test_current_release_copies_are_recursively_in_sync() -> None:
  384. assert subprocess.run(
  385. ["diff", "-qr", "app", "deployment/app"], cwd=ROOT
  386. ).returncode == 0
  387. assert subprocess.run(
  388. ["diff", "-qr", "migrations", "deployment/migrations"], cwd=ROOT
  389. ).returncode == 0
  390. def test_historical_manifest_does_not_pin_future_checkout_growth() -> None:
  391. manifest = _load(MANIFEST)
  392. captured_commit = manifest["source_control"]["captured_local_commit"]
  393. captured_openapi = _git_bytes(
  394. "show", f"{captured_commit}:docs/architecture/OPENAPI.yaml"
  395. ).decode("utf-8")
  396. historical_count = _operation_count(captured_openapi)
  397. hypothetical_future_openapi = (
  398. captured_openapi
  399. + "\n /future-baseline-proof:\n"
  400. + " get:\n"
  401. + " operationId: futureBaselineProof\n"
  402. )
  403. assert _operation_count(hypothetical_future_openapi) == historical_count + 1
  404. assert manifest["api_contract"]["operation_count"] == historical_count
  405. historical_heads = manifest["migration_contract"]["heads"]
  406. hypothetical_future_heads = [*historical_heads, "20990101_future"]
  407. assert len(hypothetical_future_heads) == len(historical_heads) + 1
  408. assert manifest["migration_contract"]["heads"] == historical_heads
  409. def test_database_unavailability_is_explicit_not_fabricated() -> None:
  410. manifest = _load(MANIFEST)
  411. snapshot = manifest["data_snapshot"]
  412. assert snapshot["read_only_capture"] is True
  413. assert snapshot["enterprise_uat"] is False
  414. assert snapshot["status"] in {"AVAILABLE", "NOT_AVAILABLE"}
  415. if snapshot["status"] == "NOT_AVAILABLE":
  416. assert snapshot["error_category"]
  417. assert snapshot["alembic_current"] == "NOT_AVAILABLE"
  418. assert snapshot["public_table_count"] == "NOT_AVAILABLE"
  419. assert snapshot["record_counts"] == "NOT_AVAILABLE"
  420. results = manifest["verification_results"]
  421. assert {item["id"] for item in results} == {f"P3-EVD-{number:03d}" for number in range(1, 9)}
  422. assert all(isinstance(item["exit_code"], int) for item in results)
  423. assert all(item["enterprise_uat"] is False for item in results)
  424. current = next(item for item in results if item["id"] == "P3-EVD-005")
  425. assert current["result"] == "NOT_AVAILABLE"
  426. assert current["exit_code"] == 1
  427. def test_capacity_schedule_change_rules_and_register_templates_are_complete() -> None:
  428. registers = _load(REGISTERS)
  429. assert registers["capacity_decision"]["status"] == "TBD_EXTERNAL"
  430. assert registers["capacity_decision"]["schedule_is_commitment"] is False
  431. assert registers["capacity_decision"]["profile_selection_mode"] == "EXACTLY_ONE"
  432. assert registers["capacity_decision"]["profiles_mutually_exclusive"] is True
  433. assert set(registers["capacity_decision"]["profiles"]) == {
  434. "PROFILE_1_2", "PROFILE_3", "PROFILE_4_5"
  435. }
  436. defined_work_packages = {f"P3-WP{number:02d}" for number in range(0, 15)}
  437. profiles = registers["capacity_decision"]["profiles"]
  438. for profile in profiles.values():
  439. committed = set(profile["committed_wps"])
  440. eligible = set(profile["eligible_non_p0_wps"])
  441. deferred = set(profile["deferred_wps"])
  442. assert P0_WORK_PACKAGES.issubset(committed)
  443. assert committed | eligible | deferred == defined_work_packages
  444. assert committed.isdisjoint(eligible)
  445. assert committed.isdisjoint(deferred)
  446. assert eligible.isdisjoint(deferred)
  447. assert all(
  448. work_package in defined_work_packages
  449. for work_package in committed | eligible | deferred
  450. )
  451. assert "P3-WP09" in profiles["PROFILE_1_2"]["eligible_non_p0_wps"]
  452. assert (
  453. profiles["PROFILE_1_2"]["delivery_depth"]["P3-WP09"]
  454. == "模型网关/预算/沙箱最小闭环"
  455. )
  456. assert registers["weekly_schedule_contract"]["status"] == "UNAPPROVED_REFERENCE_SEQUENCE"
  457. assert registers["weekly_schedule_contract"]["is_delivery_commitment"] is False
  458. assert len(registers["weekly_schedule"]) == 24
  459. assert all(item["approval_status"] == "TBD_EXTERNAL" for item in registers["weekly_schedule"])
  460. assert registers["scope_change_rules"]
  461. templates = registers["templates"]
  462. assert {"requirement", "risk", "external_gate", "defect", "acceptance_evidence"} == set(templates)
  463. live = registers["registers"]
  464. collection_names = {
  465. "requirement": "requirements",
  466. "risk": "risks",
  467. "external_gate": "external_gates",
  468. "defect": "defects",
  469. "acceptance_evidence": "acceptance_evidence",
  470. }
  471. all_ids: list[str] = []
  472. for template_name, collection_name in collection_names.items():
  473. template = templates[template_name]
  474. records = live[collection_name]
  475. assert template["required_fields"]
  476. assert template["status_values"]
  477. assert records
  478. id_regex = re.compile(
  479. "^" + re.escape(template["id_pattern"]).replace(r"\#", r"\d") + "$"
  480. )
  481. for record in records:
  482. assert set(template["required_fields"]).issubset(record)
  483. assert id_regex.fullmatch(record["id"])
  484. assert record["status"] in template["status_values"]
  485. all_ids.append(record["id"])
  486. assert len(all_ids) == len(set(all_ids))
  487. assert len(live["external_gates"]) == 17
  488. assert len(live["acceptance_evidence"]) == 8
  489. assert all(
  490. evidence["status"] == "ENGINEERING_ONLY"
  491. for evidence in live["acceptance_evidence"]
  492. )
  493. required_gate_fields = {
  494. "due_week", "decision_role", "evidence_ref", "waivable", "waiver_policy"
  495. }
  496. for gate in live["external_gates"]:
  497. assert required_gate_fields.issubset(gate)
  498. assert gate["waivable"] is False
  499. requirements_by_input = {
  500. item["id"]: item["required_by_wp"]
  501. for item in _load(REQUIREMENTS)["enterprise_inputs"]
  502. }
  503. assert {
  504. gate["input_id"]: gate["required_by_wp"] for gate in live["external_gates"]
  505. } == requirements_by_input
  506. assert "P0_SECURITY" in registers["non_waivable_gate_classes"]
  507. def test_untracked_inventory_records_paths_only_and_protects_user_files() -> None:
  508. inventory = _load(MANIFEST)["user_untracked_inventory"]
  509. assert inventory["inspection_mode"] == "PATHS_ONLY_CONTENT_NOT_READ"
  510. assert inventory["modified"] is False
  511. paths = inventory["paths"]
  512. assert paths
  513. assert all(path.startswith("canvas/") or path.startswith("docs/architecture/") for path in paths)
  514. assert any(path.startswith("canvas/") for path in paths)
  515. assert any(path.startswith("docs/architecture/") for path in paths)