workflow-loop 0.1.0__py3-none-any.whl

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 (60) hide show
  1. workflow_loop/__init__.py +6 -0
  2. workflow_loop/acceptance_records.py +338 -0
  3. workflow_loop/artifact_paths.py +278 -0
  4. workflow_loop/artifact_validation.py +1738 -0
  5. workflow_loop/bug_record.py +203 -0
  6. workflow_loop/cli.py +3257 -0
  7. workflow_loop/data/Standardized_Repository/acceptance/acceptance.md +119 -0
  8. workflow_loop/data/Standardized_Repository/acceptance/acceptance_plan.md +105 -0
  9. workflow_loop/data/Standardized_Repository/code_design/code_design.md +204 -0
  10. workflow_loop/data/Standardized_Repository/code_design/project_design_init.md +152 -0
  11. workflow_loop/data/Standardized_Repository/code_design/revise_code_design.md +32 -0
  12. workflow_loop/data/Standardized_Repository/code_design/update_code_design.md +94 -0
  13. workflow_loop/data/Standardized_Repository/global/document_writing.md +77 -0
  14. workflow_loop/data/Standardized_Repository/global/workflow_lifecycle.md +91 -0
  15. workflow_loop/data/Standardized_Repository/impl/code_implementation.md +85 -0
  16. workflow_loop/data/Standardized_Repository/impl/impl.md +164 -0
  17. workflow_loop/data/Standardized_Repository/qa/test.md +167 -0
  18. workflow_loop/data/Standardized_Repository/qa/test_code.md +121 -0
  19. workflow_loop/data/Standardized_Repository/qa/test_code_implementation.md +67 -0
  20. workflow_loop/data/Standardized_Repository/qa/test_plan.md +160 -0
  21. workflow_loop/data/Standardized_Repository/reproduce/reproduce.md +60 -0
  22. workflow_loop/data/Standardized_Repository/spec/spec.md +138 -0
  23. workflow_loop/data/Standardized_Repository/spike/spike.md +236 -0
  24. workflow_loop/data/Template_Repository/acceptance/acceptance_plan.md +142 -0
  25. workflow_loop/data/Template_Repository/acceptance/acceptance_result.md +108 -0
  26. workflow_loop/data/Template_Repository/code_design/code_design.md +260 -0
  27. workflow_loop/data/Template_Repository/code_design/project_design_init_evidence.md +39 -0
  28. workflow_loop/data/Template_Repository/impl/impl.md +112 -0
  29. workflow_loop/data/Template_Repository/qa/test.md +102 -0
  30. workflow_loop/data/Template_Repository/qa/test_plan.md +100 -0
  31. workflow_loop/data/Template_Repository/reproduce/reproduce.md +82 -0
  32. workflow_loop/data/Template_Repository/spec/spec.md +222 -0
  33. workflow_loop/data/Template_Repository/spike/spike.md +135 -0
  34. workflow_loop/installer.py +632 -0
  35. workflow_loop/journal.py +78 -0
  36. workflow_loop/path_composer.py +152 -0
  37. workflow_loop/process_runner.py +176 -0
  38. workflow_loop/project.py +397 -0
  39. workflow_loop/role_doc.py +133 -0
  40. workflow_loop/rollback.py +1738 -0
  41. workflow_loop/spike_validation.py +379 -0
  42. workflow_loop/stage_materials.py +169 -0
  43. workflow_loop/stages/__init__.py +45 -0
  44. workflow_loop/stages/base.py +164 -0
  45. workflow_loop/stages/stages.py +1191 -0
  46. workflow_loop/state.py +582 -0
  47. workflow_loop/test_entry.py +123 -0
  48. workflow_loop/test_execution.py +619 -0
  49. workflow_loop/test_mapping.py +568 -0
  50. workflow_loop/test_runner.py +134 -0
  51. workflow_loop/topic.py +114 -0
  52. workflow_loop/topic_relations.py +202 -0
  53. workflow_loop/traceability.py +533 -0
  54. workflow_loop/verification.py +971 -0
  55. workflow_loop-0.1.0.dist-info/METADATA +187 -0
  56. workflow_loop-0.1.0.dist-info/RECORD +60 -0
  57. workflow_loop-0.1.0.dist-info/WHEEL +5 -0
  58. workflow_loop-0.1.0.dist-info/entry_points.txt +2 -0
  59. workflow_loop-0.1.0.dist-info/licenses/LICENSE +21 -0
  60. workflow_loop-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,203 @@
1
+ """按阶段追加缺陷修复结果,不改写缺陷复现事实。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from pathlib import Path
7
+
8
+ from . import artifact_paths as artifact_paths_mod
9
+ from .state import load_state
10
+ from .topic import topic_paths
11
+
12
+
13
+ BUG_DIR = "bug"
14
+ BUG_INDEX = artifact_paths_mod.BUG_INDEX_DOC
15
+ WORKFLOW_RE = re.compile(r"^-\s*工作流编号:\s*(.+?)\s*$", re.MULTILINE)
16
+ TOPIC_RE = re.compile(r"^-\s*验收主题:\s*(.+?)\s*$", re.MULTILINE)
17
+ RESULT_SECTION_RE = re.compile(
18
+ r"^##\s+8\.\s*修复与验收结果\s*$\n(.*?)(?=^##\s+|\Z)",
19
+ re.MULTILINE | re.DOTALL,
20
+ )
21
+ # 索引文件不是缺陷记录正文
22
+ INDEX_FILENAMES = {"索引.md"}
23
+
24
+
25
+ def _records_for_workflow(project_root: str, workflow_id: str, topics: list[str]):
26
+ bug_dir = Path(project_root) / BUG_DIR
27
+ if not bug_dir.is_dir():
28
+ raise ValueError("bug/ 目录不存在,无法更新缺陷状态")
29
+
30
+ records: list[tuple[Path, str]] = []
31
+ for path in sorted(bug_dir.glob("*.md")):
32
+ if path.name in INDEX_FILENAMES:
33
+ continue
34
+ content = path.read_text(encoding="utf-8")
35
+ workflow_match = WORKFLOW_RE.search(content)
36
+ topic_match = TOPIC_RE.search(content)
37
+ if (
38
+ workflow_match is not None
39
+ and workflow_match.group(1).strip() == workflow_id
40
+ and topic_match is not None
41
+ and topic_match.group(1).strip() in topics
42
+ ):
43
+ records.append((path, topic_match.group(1).strip()))
44
+
45
+ if not records:
46
+ raise ValueError(f"当前工作流没有找到对应验收主题的缺陷记录: {topics}")
47
+
48
+ found_topics = [topic for _, topic in records]
49
+ missing = sorted(set(topics) - set(found_topics))
50
+ duplicates = sorted(topic for topic in set(found_topics) if found_topics.count(topic) > 1)
51
+ if missing:
52
+ raise ValueError(f"缺陷记录缺少验收主题: {missing}")
53
+ if duplicates:
54
+ raise ValueError(f"同一工作流有多份缺陷记录使用同一验收主题: {duplicates}")
55
+ return records
56
+
57
+
58
+ def _replace_result_update(content: str, stage_label: str, workflow_id: str, body: str) -> str:
59
+ marker = f"### {stage_label}(工作流 {workflow_id})"
60
+ block = f"{marker}\n{body.strip()}\n"
61
+ section_match = RESULT_SECTION_RE.search(content)
62
+ if section_match is None:
63
+ separator = "" if content.endswith("\n") else "\n"
64
+ return f"{content}{separator}\n## 8. 修复与验收结果\n\n{block}"
65
+
66
+ section = section_match.group(1)
67
+ marker_pattern = re.compile(
68
+ rf"^###\s+{re.escape(stage_label)}(工作流 {re.escape(workflow_id)})\s*$\n"
69
+ r".*?(?=^###\s+|\Z)",
70
+ re.MULTILINE | re.DOTALL,
71
+ )
72
+ if marker_pattern.search(section):
73
+ section = marker_pattern.sub(block, section, count=1)
74
+ else:
75
+ section = section.rstrip() + "\n\n" + block
76
+ return content[: section_match.start(1)] + section + content[section_match.end(1) :]
77
+
78
+
79
+ def _update_index_status(project_root: str, filename: str, status: str) -> None:
80
+ index_path = Path(project_root) / BUG_INDEX
81
+ if not index_path.is_file():
82
+ raise ValueError(f"{BUG_INDEX} 不存在,无法更新缺陷索引")
83
+
84
+ content = index_path.read_text(encoding="utf-8")
85
+ lines = content.splitlines()
86
+ markers = {f"({filename})", f"(./{filename})"}
87
+ found = False
88
+ for index, line in enumerate(lines):
89
+ if not any(marker in line for marker in markers) or not line.strip().startswith("|"):
90
+ continue
91
+ cells = [cell.strip() for cell in line.strip().strip("|").split("|")]
92
+ if len(cells) < 2:
93
+ continue
94
+ cells[-1] = status
95
+ lines[index] = "| " + " | ".join(cells) + " |"
96
+ found = True
97
+ break
98
+ if not found:
99
+ raise ValueError(f"{BUG_INDEX} 没有链接缺陷记录: {filename}")
100
+ index_path.write_text("\n".join(lines) + ("\n" if content.endswith("\n") else ""), encoding="utf-8")
101
+
102
+
103
+ def update_status(
104
+ project_root: str,
105
+ workflow_id: str,
106
+ topics: list[str],
107
+ *,
108
+ stage_label: str,
109
+ status: str,
110
+ details: list[str],
111
+ ) -> str:
112
+ """更新当前工作流缺陷记录和缺陷索引,重复执行同一阶段不会重复追加。
113
+
114
+ details 中的 {topic_test_result} 和 {topic_acceptance_result} 会替换为该主题的
115
+ 中文正式结果路径;始终只追加结论,不改写原始复现条件、实际结果、期望和根因。
116
+ """
117
+ records = _records_for_workflow(project_root, workflow_id, topics)
118
+ updated = []
119
+ for path, topic in records:
120
+ paths = topic_paths(project_root, topic)
121
+ topic_details = [
122
+ detail
123
+ .replace("{topic_test_result}", f"../{paths['test_result']}")
124
+ .replace("{topic_acceptance_result}", f"../{paths['acceptance_result']}")
125
+ .replace("{topic_impl_doc}", f"../{paths['impl_doc']}")
126
+ for detail in details
127
+ ]
128
+ body = "\n".join([f"- 最终状态:{status}", *topic_details])
129
+ content = path.read_text(encoding="utf-8")
130
+ updated_content = _replace_result_update(content, stage_label, workflow_id, body)
131
+ if updated_content != content:
132
+ path.write_text(updated_content, encoding="utf-8")
133
+ _update_index_status(project_root, path.name, status)
134
+ updated.append(path.name)
135
+ return f"已更新缺陷状态“{status}”: {updated}"
136
+
137
+
138
+ def record_topic_acceptance_pass(project_root: str, workflow_id: str, topics: list[str]) -> str:
139
+ return update_status(
140
+ project_root,
141
+ workflow_id,
142
+ topics,
143
+ stage_label="主题验收结果",
144
+ status="主题验收通过,待全量回归",
145
+ details=[
146
+ "- 实施记录:[实施记录]({topic_impl_doc})",
147
+ "- 主题测试结果:[测试结果]({topic_test_result})",
148
+ "- 主题验收结果:[验收结果]({topic_acceptance_result})",
149
+ "- 最终全量回归:待执行",
150
+ ],
151
+ )
152
+
153
+
154
+ def record_regression_failure(project_root: str, workflow_id: str, topics: list[str]) -> str:
155
+ return update_status(
156
+ project_root,
157
+ workflow_id,
158
+ topics,
159
+ stage_label="最终全量回归结果",
160
+ status="回归失败,重新处理中",
161
+ details=[
162
+ "- 回归结果:统一测试入口执行失败、超时或无法启动,详情见当前工作流 state.json 和 journal",
163
+ "- 处理要求:修复后重新执行测试代码、主题测试、主题验收、最终全量回归和整体验收",
164
+ ],
165
+ )
166
+
167
+
168
+ def record_regression_pass(project_root: str, workflow_id: str, topics: list[str]) -> str:
169
+ return update_status(
170
+ project_root,
171
+ workflow_id,
172
+ topics,
173
+ stage_label="最终全量回归结果",
174
+ status="全量回归通过,待整体验收",
175
+ details=[
176
+ "- 回归结果:统一测试入口执行通过,详情见当前工作流 state.json 和 journal",
177
+ "- 后续处理:等待用户进行整体验收",
178
+ ],
179
+ )
180
+
181
+
182
+ def has_explicit_regression_failure(project_root: str, workflow_id: str) -> bool:
183
+ """执行失败、超时和无法启动都算未通过,不只有退出码非零一种情况。"""
184
+ state = load_state(project_root)
185
+ return (
186
+ state is not None
187
+ and state.workflow_id == workflow_id
188
+ and state.regression_test.status in ("failed", "timeout", "error", "unavailable")
189
+ )
190
+
191
+
192
+ def record_overall_acceptance_pass(project_root: str, workflow_id: str, topics: list[str]) -> str:
193
+ return update_status(
194
+ project_root,
195
+ workflow_id,
196
+ topics,
197
+ stage_label="整体验收确认",
198
+ status="已修复并验收",
199
+ details=[
200
+ "- 最终全量回归:统一测试入口已通过,详情见当前工作流 state.json 和 journal",
201
+ "- 整体验收:用户已确认",
202
+ ],
203
+ )