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.
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/PKG-INFO +1 -1
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/pyproject.toml +1 -1
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/__init__.py +1 -1
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/PKG-INFO +1 -1
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/SOURCES.txt +1 -0
- prgenius_core-2.2.3/tests/test_harvest.py +88 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/README.md +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/setup.cfg +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/__main__.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/cli.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/contributor_view.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/doctor.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/evaluator.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/issue_evaluator.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/maintainer_view.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/mcp.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/parser.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/pr_metadata.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/py.typed +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/status.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/structural.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/triage.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius/utils.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/dependency_links.txt +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/entry_points.txt +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/requires.txt +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/src/prgenius_core.egg-info/top_level.txt +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_cli.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_contributor_view.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_enrich_cases.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_evaluator.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_evaluator_v163.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_extract_features.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_gh_fallback.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_issue_evaluator.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_maintainer_view.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_mcp.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_mcp_stdio.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_parser_smoke.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_pr_metadata.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_product_quality.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_review_case_evidence.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_score_merge.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_smoke_e2e.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_status.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_structural_checks.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_triage.py +0 -0
- {prgenius_core-2.2.2 → prgenius_core-2.2.3}/tests/test_utils.py +0 -0
- {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.
|
|
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.
|
|
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"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prgenius-core
|
|
3
|
-
Version: 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
|
|
@@ -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
|
|
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
|