Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/devlens-live-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
assert set(report["scores"]) == dimensions
assert all(0 <= int(v) <= 100 for v in report["scores"].values())
assert os.environ["BADGE"].startswith("https://img.shields.io/badge/")
assert report["action_version"] == "2.0.8"
assert report["action_version"] == "2.0.9"

print(f"Live DevLens integration passed: {score}/100")

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to DevLens, newest first.

## Unreleased — 2026-09-30

- Corrected Issue Response scoring to exclude pull requests from issue counts.
- Added deterministic regression fixtures for issue-response scoring.
- Updated live integration expectations for the `2.0.9` candidate.

### Documentation and distribution status
- Verified by the Human Owner that the live GitHub Marketplace listing displays **DevLens Repo Health** as **Latest v2.0.8** with the current Action-only product content.
- Closed the Marketplace publication/verification blocker and advanced the project to Phase 2 — Adoption and onboarding.
Expand Down
10 changes: 5 additions & 5 deletions docs/AI_PROJECT_CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ As of 2026-09-30:

- `v2.0.0` exists as the original Marketplace release tag.
- `v2.0.1` was previously published successfully.
- `v2.0.8` is the latest verified release and was created and published by the automatic critical-change release workflow.
- `v2.0.8` is the latest currently published release; `v2.0.9` is the pending candidate for the issue-response scoring correction.
- The release preflight executed the real Action successfully and validated the reported Action version against the release version.
- The floating `v2` tag was updated successfully to `v2.0.8`.
- The automatic release workflow detects critical production paths on pushes to `main`, derives the release version from `scripts/devlens.py`, runs static validation and live Action preflight, updates `v2`, and publishes the GitHub Release.
Expand All @@ -331,10 +331,10 @@ As of 2026-09-30:
- Vercel configuration is removed from the repository.
- Legacy scanner code is removed.
- Legacy Groq scoring dependencies/inputs are removed.
- README, contributor, security, documentation, and changelog materials are aligned with v2.0.8.
- README, contributor, security, documentation, and changelog materials are aligned with the current v2.0.9 candidate where applicable.
- Live GitHub integration has executed the actual Action successfully.
- README persistence has been tested on a disposable branch and verified through GitHub API.
- Current release line is v2; `v2.0.8` is the latest verified version.
- Current release line is v2; `v2.0.8` is the latest verified published version, with `v2.0.9` pending validation.
- Floating `v2` points to the latest verified v2 release (`v2.0.8`).
- Repository release automation has been hardened so manual releases complete in one workflow run and do not depend on a token-generated tag push triggering a second workflow.
- Repository metadata may still contain stale external/homepage information and should be verified before treating metadata cleanup as complete.
Expand Down Expand Up @@ -389,7 +389,7 @@ Remaining Phase 1 operational check:

### Phase 3 — Scoring quality and observability — NEXT

- Expand automated edge-case tests.
- **IN PROGRESS:** Expand automated edge-case tests, beginning with Issue Response.
- Improve evidence shown for individual dimension scores.
- Reduce unnecessary API calls and startup cost.
- Improve sparse-history behavior.
Expand Down Expand Up @@ -498,7 +498,7 @@ The repository adopts the AI CEO/CIO → COO execution model and treats document

### D-012 — Action report version must match implementation
**Status:** DECISION / ACTIVE.
The machine-readable report action version must identify the actual DevLens implementation version. This is enforced by release preflight and live integration validation. The current verified implementation reports `2.0.8`.
The machine-readable report action version must identify the actual DevLens implementation version. This is enforced by release preflight and live integration validation. The current verified implementation candidate reports `2.0.9`; the production release remains `v2.0.8` until release preflight completes.

### D-010 — Automated semantic v2 release flow
**Status:** DECISION / ACTIVE.
Expand Down
26 changes: 18 additions & 8 deletions scripts/devlens.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,25 @@ def score_ci():
return 0
except: return 0

def score_issue_response(open_count, closed_count):
# Keep issue response deterministic and independent of pull-request closure activity.
"""Score actual GitHub issues only; pull requests are excluded from both counts."""
if open_count < 0 or closed_count < 0:
raise ValueError("Issue counts cannot be negative")
total = open_count + closed_count
if total == 0:
return 100
return int(closed_count / total * 100)

def score_issues():
try:
open_i = repo.open_issues_count
closed_i = list(repo.get_issues(state="closed"))[:50]
if not closed_i and open_i == 0: return 100
total = open_i + len(closed_i)
if total == 0: return 100
return int(len(closed_i) / total * 100)
except: return 50
# GitHub's /issues collection includes pull requests. Search with is:issue
# so the dimension measures issue response rather than PR closure activity.
open_i = g.search_issues(query=f"repo:{REPO_NAME} is:issue is:open").totalCount
closed_i = g.search_issues(query=f"repo:{REPO_NAME} is:issue is:closed").totalCount
return score_issue_response(open_i, closed_i)
except Exception:
return 50

def score_community():
return min(int(math.log1p(repo.stargazers_count)*15)+int(math.log1p(repo.forks_count)*10), 100)
Expand Down Expand Up @@ -162,7 +172,7 @@ def dim_bar(score):
badge_url = (f"https://img.shields.io/badge/DevLens%20Health-{health}%2F100"
f"-{badge_color(health)}?style={BADGE_STYLE}&logo=github")

report = {"repo":REPO_NAME,"score_model":"action-v2-9d","action_version":"2.0.8","health_score":health,"scores":scores,
report = {"repo":REPO_NAME,"score_model":"action-v2-9d","action_version":"2.0.9","health_score":health,"scores":scores,
"badge_url":badge_url,"generated_at":now.isoformat()}

print(json.dumps(report, indent=2))
Expand Down
31 changes: 31 additions & 0 deletions tests/test_issue_response_scoring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import ast
from pathlib import Path

source = Path("scripts/devlens.py").read_text()
tree = ast.parse(source)
fn = next(node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == "score_issue_response")
module = ast.Module(body=[fn], type_ignores=[])
namespace = {}
exec(compile(module, "scripts/devlens.py", "exec"), namespace)

score = namespace["score_issue_response"]
cases = [
((0, 0), 100),
((0, 10), 100),
((10, 0), 0),
((5, 5), 50),
((1, 9), 90),
]
for args, expected in cases:
actual = score(*args)
assert actual == expected, (args, actual, expected)

for args in [(-1, 0), (0, -1)]:
try:
score(*args)
except ValueError:
pass
else:
raise AssertionError(f"negative counts accepted: {args}")

print("Issue-response scoring fixtures passed.")
Loading