test_phase3_wp00_baseline_contract.py 24 KB

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