Skip to content

Add maintainer activity freshness to the submission gate#335

Merged
ramimbo merged 2 commits into
ramimbo:mainfrom
artylobos:codex/maintainer-activity-gate-319
May 26, 2026
Merged

Add maintainer activity freshness to the submission gate#335
ramimbo merged 2 commits into
ramimbo:mainfrom
artylobos:codex/maintainer-activity-gate-319

Conversation

@artylobos
Copy link
Copy Markdown
Contributor

@artylobos artylobos commented May 25, 2026

Refs #319

What changed

  • Teach the submission quality gate to inspect recent maintainer activity for the referenced bounty.
  • Warn when the bounty thread looks stale or cannot verify maintainer activity.
  • Add a live CLI knob for the maximum acceptable maintainer age.

Why

  • The existing gate already checked bounty refs, summary text, evidence, and duplicate PRs.
  • This closes the gap where a bounty may still be open but effectively dormant, which is exactly the signal we want before spending more time on a claim.

Validation

  • uv run --extra dev python -m pytest tests/test_submission_quality_gate.py tests/test_docs_public_urls.py -q
  • uv run --extra dev ruff check scripts/submission_quality_gate.py tests/test_submission_quality_gate.py
  • uv run --extra dev ruff format --check scripts/submission_quality_gate.py tests/test_submission_quality_gate.py
  • uv run --extra dev python scripts/docs_smoke.py
  • uv run --extra dev python -m mypy scripts/submission_quality_gate.py
  • Live smoke: python scripts/submission_quality_gate.py --text-file <draft> --repo ramimbo/mergework --format text -> PASS, maintainer activity seen 0 days ago

Safety

  • Advisory only; it does not reserve work or block maintainer decisions.
  • No secrets, private payout details, or price claims included.

Summary by CodeRabbit

  • New Features

    • Submission quality gate now verifies recent maintainer activity for referenced bounties.
    • Configurable maximum maintainer age threshold (CLI option, defaults to 14 days).
  • Documentation

    • Clarified Submission Quality Gate wording and WARN remediation to include maintainer-activity checks and guidance.
  • Tests

    • Added unit and live-mode tests covering maintainer-activity pass/warn scenarios.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

📝 Walkthrough

Walkthrough

Adds a maintainer-activity quality gate: UTC datetime helpers and an age-evaluation function, GitHub issue fetching to extract maintainer timestamps, CLI option to configure max age, wiring into evaluation to emit pass/warn checks, updated docs, and comprehensive tests including live-mode mocks.

Changes

Maintainer Activity Quality Gate

Layer / File(s) Summary
Maintainer Activity Check Foundations
scripts/submission_quality_gate.py
Imports UTC/date utilities, defines DEFAULT_MAX_MAINTAINER_AGE_DAYS and maintainer associations, and adds _parse_datetime, _isoformat_utc, _current_time, and _maintainer_activity_check to normalize timestamps and evaluate recent activity.
Loading Maintainer Activity from GitHub
scripts/submission_quality_gate.py
Adds _load_issue_maintainer_activity that calls gh issue view, extracts timestamps from issue author (when repo owner) and maintainer-associated comments, updates _load_live_context signature to accept max_maintainer_age_days, computes referenced_bounties, and enriches only referenced bounty dicts with maintainer activity fields.
Evaluation and CLI Integration
scripts/submission_quality_gate.py
evaluate_submission computes now once, appends the maintainer_activity check result when present, and CLI gains --max-maintainer-age-days (default 14) wired into live-context loading.
Documentation
docs/agent-guide.md
Expands "Submission Quality Gate" description to include recent maintainer activity checks and extends WARN remediation guidance to confirm maintainer activity for potentially stale bounties.
Test Coverage
tests/test_submission_quality_gate.py
Adds unit tests for pass/warn maintainer_activity outcomes, extends fake_run mocks to handle gh issue view JSON (createdAt/comments), and adds live-context tests validating end-to-end loading and evaluation, including MEMBER comment handling and unverified paths.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • weilixiong
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description covers the changes, rationale, and validation steps, but does not follow the required template structure with Summary, Evidence, Test Evidence checklist, and MRWK sections. Restructure the description to follow the repository template: add a Summary section, move validation details to Test Evidence checklist, and format the Evidence section clearly.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding maintainer activity freshness checks to the submission gate.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@TateLyman TateLyman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the maintainer-activity freshness gate for Bounty #319.

What I checked:

  • Read the diff in docs/agent-guide.md, scripts/submission_quality_gate.py, and tests/test_submission_quality_gate.py.
  • Verified the new live-context path only enriches the referenced bounty with maintainer activity metadata and keeps the gate advisory: it emits WARN when activity cannot be verified or is stale, not FAIL.
  • Checked the activity source: owner-authored bounty creation and OWNER/MEMBER/COLLABORATOR comments are the only sources counted, so contributor chatter does not make a stale round look active.
  • Verified live smoke against #319 returns PASS with maintainer activity seen 0 days ago.

Validation run locally on head 88c200a:

  • uv run --extra dev python -m pytest tests/test_submission_quality_gate.py tests/test_docs_public_urls.py -q -> 31 passed
  • uv run --extra dev ruff check scripts/submission_quality_gate.py tests/test_submission_quality_gate.py -> passed
  • uv run --extra dev ruff format --check scripts/submission_quality_gate.py tests/test_submission_quality_gate.py -> passed
  • uv run --extra dev python -m mypy scripts/submission_quality_gate.py -> success
  • uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok
  • git diff --check -> clean

No blockers found. One small edge to keep in mind later: the age check uses whole elapsed days, so a threshold boundary can be up to almost one day lenient; that is acceptable for an advisory freshness warning.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/submission_quality_gate.py`:
- Around line 88-90: The current check uses (now - last_activity).days which
floors partial days; change the freshness check to compare the raw timedelta:
compute delta = now - last_activity and use if delta >
timedelta(days=max_age_days) to decide staleness, then derive age_days
separately for display (e.g. age_days = max(0, int(delta.total_seconds() //
86400)) or similar) so human-readable counts are computed independently of the
threshold comparison; update the variables max_age_days, age_days, now,
last_activity, and the staleness if-condition accordingly.
- Around line 291-300: The maintainer-detection logic in
_load_issue_maintainer_activity is only checking
issue.get("author").get("login") against repo_owner but comments use
authorAssociation vs MAINTAINER_ASSOCIATIONS; update the gh CLI fields requested
to include "authorAssociation" (add to the --json list) and change the
issue-author check to mirror comment logic by reading
issue.get("authorAssociation") and treating it as a maintainer when it is in
MAINTAINER_ASSOCIATIONS (instead of comparing logins). Locate and update the
list of fields passed to gh issue view and the conditional that currently uses
issue_author/repo_owner in _load_issue_maintainer_activity to use
issue.get("authorAssociation") with MAINTAINER_ASSOCIATIONS.
- Around line 212-214: The maintainer activity check
(_maintainer_activity_check) is only appended to checks inside the payable-pass
branch, which drops the maintainer freshness signal when payability isn't a
pass; move the call and append logic so activity_check =
_maintainer_activity_check(bounty_ref, bounty, now) is executed regardless of
payability, and if activity_check is not None add it to checks (preserve the
existing None guard) so maintainer_activity is emitted even when payability is
unverified.

In `@tests/test_submission_quality_gate.py`:
- Around line 263-323: Extend the existing
test_submission_quality_gate_live_context_adds_maintainer_activity to include a
regression case where the issue author.login is not equal to the repo owner but
the author's association indicates a maintainer (e.g., "MEMBER" or "OWNER");
update the fake_run for the ["gh", "issue", "view"] branch to return a second
scenario (or add a new test) where "author": {"login": "someone_else"} and one
of the comments has "authorAssociation": "MEMBER"/"OWNER", then call
_load_live_context and evaluate_submission as before and assert that
bounties[0]["maintainer_activity_verified"] is True and
last_maintainer_activity_at matches the maintainer comment timestamp to ensure
maintainer-by-association is accepted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ba8203d3-dd54-4fdf-91db-052a4a56ee1c

📥 Commits

Reviewing files that changed from the base of the PR and between f4fa763 and 88c200a.

📒 Files selected for processing (3)
  • docs/agent-guide.md
  • scripts/submission_quality_gate.py
  • tests/test_submission_quality_gate.py

Comment thread scripts/submission_quality_gate.py Outdated
Comment thread scripts/submission_quality_gate.py
Comment on lines +291 to +300
"--json",
"author,comments,createdAt",
]
)
activity_times = []
repo_owner = repo.split("/", 1)[0].lower()
issue_author = str((issue.get("author") or {}).get("login") or "").lower()
created_at = _parse_datetime(issue.get("createdAt"))
if issue_author == repo_owner and created_at is not None:
activity_times.append(created_at)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does gh issue view --jsonexpose the issue's top-levelauthorAssociation field, and what is the exact field name to request?

💡 Result:

Yes—gh issue view --json exposes the issue’s top-level author association via the authorAssociation field name. [1] To request it, use: gh issue view <number-or-url> --json authorAssociation [1] (According to the gh issue view docs, the available JSON fields for that command include author, assignees, and stateReason etc.; among those supported fields, the author association is requested as authorAssociation.) [1]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import itertools,sys,os,platform,subprocess, textwrap, json, re, pathlib
path="scripts/submission_quality_gate.py"
with open(path,'r',encoding='utf-8') as f:
    lines=f.readlines()
start=260
end=340
for i in range(start-1,end):
    if 0<=i<len(lines):
        ln=i+1
        print(f"{ln}:{lines[i].rstrip()}")
PY

Repository: ramimbo/mergework

Length of output: 3348


Fix maintainer activity detection to use authorAssociation for issue author too.

scripts/submission_quality_gate.py::_load_issue_maintainer_activity() currently counts the issue creator only when issue.get("author").login matches repo_owner, while comments already filter by comment.authorAssociation against MAINTAINER_ASSOCIATIONS. This misses maintainer-opened bounties from MEMBER/COLLABORATOR creators and will mark them unverified until a qualifying comment appears. gh issue view --json supports the top-level authorAssociation field (request via --json authorAssociation). Update the issue view JSON request and apply the same authorAssociation filtering logic to the issue author.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/submission_quality_gate.py` around lines 291 - 300, The
maintainer-detection logic in _load_issue_maintainer_activity is only checking
issue.get("author").get("login") against repo_owner but comments use
authorAssociation vs MAINTAINER_ASSOCIATIONS; update the gh CLI fields requested
to include "authorAssociation" (add to the --json list) and change the
issue-author check to mirror comment logic by reading
issue.get("authorAssociation") and treating it as a maintainer when it is in
MAINTAINER_ASSOCIATIONS (instead of comparing logins). Locate and update the
list of fields passed to gh issue view and the conditional that currently uses
issue_author/repo_owner in _load_issue_maintainer_activity to use
issue.get("authorAssociation") with MAINTAINER_ASSOCIATIONS.

Comment thread tests/test_submission_quality_gate.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
tests/test_submission_quality_gate.py (1)

352-410: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add a regression for maintainer issue authors without maintainer comments.

This test covers MEMBER via comment association, but not the issue-author association path. Add a case where gh issue view returns top-level "authorAssociation": "MEMBER" (or "COLLABORATOR") with empty comments, and assert maintainer activity is verified from createdAt.

As per coding guidelines, tests/**/*.py: Add or update tests for changed behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_submission_quality_gate.py` around lines 352 - 410, Add a new
regression test similar to
test_submission_quality_gate_live_context_accepts_member_comments that exercises
the "issue-author" maintainer-activity path: mock
submission_quality_gate.subprocess.run so "gh issue view" returns a top-level
"authorAssociation": "MEMBER" (or "COLLABORATOR") and an empty comments list,
ensure submission_quality_gate._load_live_context is invoked with the same
inputs and that the returned data sets last_maintainer_activity_at to the issue
createdAt and maintainer_activity_verified to True, then call
evaluate_submission and assert the maintainer_activity check passes (refer to
test_submission_quality_gate_live_context_accepts_member_comments,
submission_quality_gate._load_live_context, and evaluate_submission to mirror
structure).
scripts/submission_quality_gate.py (1)

293-295: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use authorAssociation for issue-author maintainer detection.

On Line [294], the issue payload does not request top-level authorAssociation, and on Lines [298]-[302] maintainer detection depends on author.login == repo_owner. This misses maintainer-opened issues from MEMBER/COLLABORATOR accounts unless a qualifying comment exists.

Proposed fix
     issue = _run_gh_json(
         [
             "gh",
             "issue",
             "view",
             str(issue_number),
             "--repo",
             repo,
             "--json",
-            "author,comments,createdAt",
+            "author,authorAssociation,comments,createdAt",
         ]
     )
     activity_times = []
-    repo_owner = repo.split("/", 1)[0].lower()
-    issue_author = str((issue.get("author") or {}).get("login") or "").lower()
+    issue_author_association = str(issue.get("authorAssociation") or "").upper()
     created_at = _parse_datetime(issue.get("createdAt"))
-    if issue_author == repo_owner and created_at is not None:
+    if issue_author_association in MAINTAINER_ASSOCIATIONS and created_at is not None:
         activity_times.append(created_at)
Does `gh issue view --json` support top-level `authorAssociation`, and is `--json authorAssociation` the correct field request?

Also applies to: 298-302

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/submission_quality_gate.py` around lines 293 - 295, The gh issue view
JSON request is missing top-level authorAssociation; update the fields list
(currently ["author,comments,createdAt"]) to include "authorAssociation" so the
CLI returns that property, and then change the maintainer-detection logic in
this module to check the issue object's authorAssociation (e.g.,
issue.authorAssociation or issue["authorAssociation"]) for values like "OWNER",
"MEMBER", or "COLLABORATOR" instead of relying solely on author.login ==
repo_owner; ensure the new check is used wherever maintainer detection occurs
(refer to the issue variable and repo_owner symbol in the file).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@scripts/submission_quality_gate.py`:
- Around line 293-295: The gh issue view JSON request is missing top-level
authorAssociation; update the fields list (currently
["author,comments,createdAt"]) to include "authorAssociation" so the CLI returns
that property, and then change the maintainer-detection logic in this module to
check the issue object's authorAssociation (e.g., issue.authorAssociation or
issue["authorAssociation"]) for values like "OWNER", "MEMBER", or "COLLABORATOR"
instead of relying solely on author.login == repo_owner; ensure the new check is
used wherever maintainer detection occurs (refer to the issue variable and
repo_owner symbol in the file).

In `@tests/test_submission_quality_gate.py`:
- Around line 352-410: Add a new regression test similar to
test_submission_quality_gate_live_context_accepts_member_comments that exercises
the "issue-author" maintainer-activity path: mock
submission_quality_gate.subprocess.run so "gh issue view" returns a top-level
"authorAssociation": "MEMBER" (or "COLLABORATOR") and an empty comments list,
ensure submission_quality_gate._load_live_context is invoked with the same
inputs and that the returned data sets last_maintainer_activity_at to the issue
createdAt and maintainer_activity_verified to True, then call
evaluate_submission and assert the maintainer_activity check passes (refer to
test_submission_quality_gate_live_context_accepts_member_comments,
submission_quality_gate._load_live_context, and evaluate_submission to mirror
structure).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4ee51524-71e1-406f-a840-d4be3bf64c37

📥 Commits

Reviewing files that changed from the base of the PR and between 88c200a and 4d3aaa4.

📒 Files selected for processing (2)
  • scripts/submission_quality_gate.py
  • tests/test_submission_quality_gate.py

@artylobos
Copy link
Copy Markdown
Contributor Author

Follow-up after review feedback, pushed in 4d3aaa4:

  • Freshness now compares the raw timedelta against --max-maintainer-age-days, while keeping age_days only for display.
  • maintainer_activity now emits whenever referenced bounty metadata exists, including unverified payability paths, instead of only the payable/pass branch.
  • Added regression coverage for MEMBER maintainer comments and threshold-boundary staleness.
  • Verified that gh issue view --json authorAssociation is not supported by the current GitHub CLI, so the issue-creation fallback remains owner-login based while comment activity continues to use authorAssociation.

Validation after the follow-up:

  • uv run --extra dev python -m pytest tests/test_submission_quality_gate.py -q -> 16 passed.
  • uv run --extra dev python -m pytest tests/test_submission_quality_gate.py tests/test_docs_public_urls.py -q -> 33 passed.
  • uv run --extra dev ruff check scripts/submission_quality_gate.py tests/test_submission_quality_gate.py -> passed.
  • uv run --extra dev ruff format --check scripts/submission_quality_gate.py tests/test_submission_quality_gate.py -> passed.
  • uv run --extra dev python -m mypy scripts/submission_quality_gate.py -> success.
  • uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok.
  • GitHub CI is green on the updated branch.

@ramimbo ramimbo merged commit f9e54ef into ramimbo:main May 26, 2026
2 checks passed
@ramimbo ramimbo added mrwk:accepted Maintainer accepted for payout mrwk:paid Ledger payment recorded labels May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mrwk:accepted Maintainer accepted for payout mrwk:paid Ledger payment recorded

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants