prgenius-core 2.2.2__tar.gz → 2.2.3__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 (49) hide show
  1. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/PKG-INFO +1 -1
  2. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/pyproject.toml +1 -1
  3. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/__init__.py +1 -1
  4. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/PKG-INFO +1 -1
  5. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/SOURCES.txt +1 -0
  6. prgenius_core-2.2.3/tests/test_harvest.py +88 -0
  7. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/README.md +0 -0
  8. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/setup.cfg +0 -0
  9. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/__main__.py +0 -0
  10. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/cli.py +0 -0
  11. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/contributor_view.py +0 -0
  12. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/doctor.py +0 -0
  13. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/evaluator.py +0 -0
  14. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/issue_evaluator.py +0 -0
  15. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/maintainer_view.py +0 -0
  16. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/mcp.py +0 -0
  17. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/parser.py +0 -0
  18. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/pr_metadata.py +0 -0
  19. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/py.typed +0 -0
  20. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/status.py +0 -0
  21. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/structural.py +0 -0
  22. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/triage.py +0 -0
  23. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/utils.py +0 -0
  24. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/dependency_links.txt +0 -0
  25. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/entry_points.txt +0 -0
  26. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/requires.txt +0 -0
  27. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/top_level.txt +0 -0
  28. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_cli.py +0 -0
  29. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_contributor_view.py +0 -0
  30. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_enrich_cases.py +0 -0
  31. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_evaluator.py +0 -0
  32. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_evaluator_v163.py +0 -0
  33. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_extract_features.py +0 -0
  34. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_gh_fallback.py +0 -0
  35. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_issue_evaluator.py +0 -0
  36. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_maintainer_view.py +0 -0
  37. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_mcp.py +0 -0
  38. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_mcp_stdio.py +0 -0
  39. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_parser_smoke.py +0 -0
  40. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_pr_metadata.py +0 -0
  41. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_product_quality.py +0 -0
  42. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_review_case_evidence.py +0 -0
  43. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_score_merge.py +0 -0
  44. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_smoke_e2e.py +0 -0
  45. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_status.py +0 -0
  46. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_structural_checks.py +0 -0
  47. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_triage.py +0 -0
  48. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_utils.py +0 -0
  49. {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_validate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prgenius-core
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Summary: Evidence-backed lookup for big-repo PR contributions. Local-only, zero-runtime, stdlib-first. Renamed from `prgenius` to avoid PyPI name collision with an unrelated 2024 GPT-3 PR-description tool. Distribution renamed again in v0.7.8: `prgenius-kb` → `prgenius-core` (aligned with `misakanet-core` convention).
5
5
  Author: pr-genius contributors
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "prgenius-core"
3
- version = "2.2.2"
3
+ version = "2.2.3"
4
4
  description = "Evidence-backed lookup for big-repo PR contributions. Local-only, zero-runtime, stdlib-first. Renamed from `prgenius` to avoid PyPI name collision with an unrelated 2024 GPT-3 PR-description tool. Distribution renamed again in v0.7.8: `prgenius-kb` → `prgenius-core` (aligned with `misakanet-core` convention)."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -12,7 +12,7 @@ Public surface:
12
12
 
13
13
  from .parser import load, iter_profiles, iter_case_studies, profile_get, schema_info, clear_profile_cache
14
14
 
15
- __version__ = "2.2.2"
15
+ __version__ = "2.2.3"
16
16
  __all__ = [
17
17
  "__version__",
18
18
  "load",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prgenius-core
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Summary: Evidence-backed lookup for big-repo PR contributions. Local-only, zero-runtime, stdlib-first. Renamed from `prgenius` to avoid PyPI name collision with an unrelated 2024 GPT-3 PR-description tool. Distribution renamed again in v0.7.8: `prgenius-kb` → `prgenius-core` (aligned with `misakanet-core` convention).
5
5
  Author: pr-genius contributors
6
6
  License-Expression: MIT
@@ -29,6 +29,7 @@ tests/test_evaluator.py
29
29
  tests/test_evaluator_v163.py
30
30
  tests/test_extract_features.py
31
31
  tests/test_gh_fallback.py
32
+ tests/test_harvest.py
32
33
  tests/test_issue_evaluator.py
33
34
  tests/test_maintainer_view.py
34
35
  tests/test_mcp.py
@@ -0,0 +1,88 @@
1
+ """harvest 结果分类 —— 防止把成功 PR 收割成失败反模式.
2
+
3
+ 背景: 2026-10-04 在 anti-patterns/ 里发现 4 条空壳, 其中 3 条是**已合并**的 PR
4
+ ("已合并 by @维护者"、"Root Cause: 无 maintainer 评论"), 却被盖上
5
+ `category: pr-failure` 且 Lesson/Solution/Verification 全是 TODO。
6
+ 根因是 harvest 的 draft 模板无条件写死 pr-failure。
7
+
8
+ 这里的判据是"有没有真失败信号", 不是"这个 PR 有没有被收割过"。
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ import pytest
17
+
18
+ _SCRIPTS = Path(__file__).resolve().parents[2] / "scripts"
19
+ if str(_SCRIPTS) not in sys.path:
20
+ sys.path.insert(0, str(_SCRIPTS))
21
+
22
+ from harvest import classify_outcome, generate_lesson_draft # noqa: E402
23
+
24
+
25
+ def _pr(merged: bool = True, number: int = 1) -> dict:
26
+ return {
27
+ "number": number,
28
+ "title": "feat: some change",
29
+ "html_url": f"https://example.com/pull/{number}",
30
+ "user": {"login": "someone"},
31
+ "body": "does a thing",
32
+ "merged_at": "2026-08-13" if merged else None,
33
+ }
34
+
35
+
36
+ BOT = [{"author_association": "NONE", "user": {"login": "dco-bot"},
37
+ "body": "## DCO Sign-off Required\nPlease add Signed-off-by to your commits"}]
38
+ MAINTAINER = [{"author_association": "MEMBER", "user": {"login": "maintainer"},
39
+ "body": "Please split this into smaller PRs before we can take it"}]
40
+
41
+
42
+ class TestClassifyOutcome:
43
+ def test_unmerged_pr_is_a_failure(self):
44
+ assert classify_outcome(_pr(merged=False), "Closed without merge", []) == "rejected"
45
+
46
+ def test_merged_with_no_comments_has_no_signal(self):
47
+ assert classify_outcome(_pr(merged=True), "已合并 by @maintainer", []) == "merged-clean"
48
+
49
+ def test_bot_comment_is_not_a_failure_signal(self):
50
+ # DCO / CI bot 评论不是 maintainer 反馈 —— 与 _extract_key_comments 同口径。
51
+ # 若这里判成 merged-with-review, Root Cause 段会显示"无 maintainer 评论",
52
+ # 两段自相矛盾。
53
+ assert classify_outcome(_pr(merged=True), "已合并 by @maintainer", BOT) == "merged-clean"
54
+
55
+ def test_maintainer_comment_on_merged_pr_is_feedback(self):
56
+ assert classify_outcome(_pr(merged=True), "已合并 by @maintainer", MAINTAINER) == "merged-with-review"
57
+
58
+ def test_tiny_comment_is_not_feedback(self):
59
+ # _extract_key_comments 要求 body 长度 > 10; 分类必须同此门槛。
60
+ tiny = [{"author_association": "MEMBER", "user": {"login": "m"}, "body": "ok"}]
61
+ assert classify_outcome(_pr(merged=True), "已合并", tiny) == "merged-clean"
62
+
63
+
64
+ class TestLessonDraftIsHonest:
65
+ def test_merged_clean_pr_is_not_labeled_pr_failure(self):
66
+ out = generate_lesson_draft("example/repo", _pr(merged=True), "已合并 by @m", [])
67
+ assert "category: pr-failure" not in out
68
+ assert "category: merged-reference" in out
69
+ assert "severity: info" in out
70
+
71
+ def test_merged_clean_pr_does_not_prompt_you_to_write_a_lesson(self):
72
+ # 邀请别人给"没有失败的 PR"写教训 = 邀请编造。
73
+ out = generate_lesson_draft("example/repo", _pr(merged=True), "已合并 by @m", [])
74
+ assert "没有失败信号" in out
75
+ assert "不要往这里填 Lesson" in out
76
+ assert "提炼以下可复用教训" not in out
77
+
78
+ def test_rejected_pr_still_gets_lesson_guidance(self):
79
+ out = generate_lesson_draft("example/repo", _pr(merged=False), "Closed without merge", MAINTAINER)
80
+ assert "category: pr-failure" in out
81
+ assert "提炼以下可复用教训" in out
82
+ assert "没有失败信号" not in out
83
+
84
+ def test_root_cause_matches_the_classification(self):
85
+ # 两段不能互相矛盾: 说"无 maintainer 评论"就不能同时判成"有反馈可提炼"。
86
+ out = generate_lesson_draft("example/repo", _pr(merged=True), "已合并 by @m", BOT)
87
+ assert "无 maintainer 评论" in out
88
+ assert "category: merged-reference" in out
File without changes
File without changes