Skip to content

Extract bounty attempt routes from app main#337

Merged
ramimbo merged 4 commits into
ramimbo:mainfrom
AugmentSecurity:bounty-320-admin-routes
May 26, 2026
Merged

Extract bounty attempt routes from app main#337
ramimbo merged 4 commits into
ramimbo:mainfrom
AugmentSecurity:bounty-320-admin-routes

Conversation

@AugmentSecurity
Copy link
Copy Markdown
Contributor

@AugmentSecurity AugmentSecurity commented May 26, 2026

Refs #320

Summary

  • Move bounty-attempt serialization, warning, expiration, registration, listing, and release route handling out of app/main.py into app/bounty_attempts.py.
  • Keep app/main.py responsible for app wiring and shared dependencies while the attempt reservation behavior lives behind a focused route-registration boundary.
  • Add a direct serializer regression for expired active attempts so the moved module is covered outside the route-level tests.

Complexity reduced

The active-attempt reservation feature was adding model-specific route logic, timestamp normalization, stale-attempt cleanup, duplicate handling, and warning construction directly to app/main.py. This PR extracts that coherent subsystem without changing the public API, MCP behavior, ledger, wallet, webhook, admin, or payout paths.

Validation

  • ./.venv/bin/python -m pytest tests/test_bounty_attempts.py tests/test_api_mcp.py -q -> 77 passed
  • ./.venv/bin/python -m pytest -q -> 319 passed
  • ./.venv/bin/python -m ruff check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py -> passed
  • ./.venv/bin/python -m ruff format --check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py -> passed
  • ./.venv/bin/python -m mypy app/bounty_attempts.py app/main.py -> passed
  • ./.venv/bin/python scripts/docs_smoke.py -> docs smoke ok
  • git diff --check -> clean

No secrets, wallet private keys, payout credentials, private vulnerability details, deployment values, or MRWK price claims are included.

Summary by CodeRabbit

  • New Features

    • New API endpoints to list, create, and release bounty attempts. Responses include ISO‑8601 UTC timestamps and a computed effective status (treats past TTLs as expired); attempts are returned newest‑first.
    • Creation validates TTL bounds, prevents duplicate active attempts, accepts empty bodies defaulting submitter to the authenticated login, and allows optional source URLs.
    • Stale active attempts are auto‑expired before relevant operations; release enforces submitter ownership and reports non‑active states as already_{effective_status}.
  • Tests

    • Added tests for expired-status serialization and for creating attempts with empty bodies (defaults to login) and successful release.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 578ad42b-199e-464b-b569-e05106c8c0e2

📥 Commits

Reviewing files that changed from the base of the PR and between 7db500d and c244647.

📒 Files selected for processing (2)
  • app/bounty_attempts.py
  • app/main.py

📝 Walkthrough

Walkthrough

This PR refactors bounty attempt functionality from app/main.py into a new app/bounty_attempts.py module. The new module implements time normalization, serialization, query helpers, stale-expiration persistence, and three HTTP endpoints (GET listing, POST creation with race-condition handling, and POST release). app/main.py is updated to remove the old implementation and delegate route registration via a new function call.

Changes

Bounty Attempt Module Extraction

Layer / File(s) Summary
Attempt serialization and status computation
app/bounty_attempts.py, tests/test_bounty_attempts.py
UTC time helpers normalize datetimes and bounty_attempt_to_dict computes effective status (marking expired active attempts as expired) and renders ISO-8601 timestamps; tests added/imports verify serialization.
Attempt warnings and active-attempt query helpers
app/bounty_attempts.py
bounty_attempt_warnings generates warnings based on bounty state and active-attempt counts; query conditions filter attempts by status and expiration.
Attempt listing helper
app/bounty_attempts.py
list_bounty_attempts fetches attempts (optionally including expired) ordered newest-first and returns warnings and serialized attempts.
Stale attempt expiration
app/bounty_attempts.py
expire_stale_bounty_attempts bulk-updates active attempts past their expires_at to expired status, optionally scoped to a submitter account.
Attempt listing and creation endpoints
app/bounty_attempts.py, tests/test_bounty_attempts.py
GET endpoint returns attempts with computed warnings and effective status; POST endpoint validates TTL and source_url, normalizes submitter account (defaulting to authenticated login), expires stale attempts, checks availability, rejects duplicates, creates active attempts, and handles IntegrityError to return existing duplicate. Includes test that empty POST body uses authenticated login.
Attempt release endpoint
app/bounty_attempts.py
POST endpoint validates id bounds, enforces submitter ownership, returns early if already expired/released, and updates status and timestamp when active.
Main.py refactoring and route delegation
app/main.py
Removes bounty-attempt route implementations, time/status helpers, TTL constants, and related imports; adds call to register_bounty_attempt_routes in create_app to delegate endpoint registration with validation utilities and limits.
Serializer & API tests
tests/test_bounty_attempts.py
Adds test verifying that serializer reports expired effective status and a test that submitting an empty body defaults submitter to authenticated login and allows release.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • ramimbo/mergework#326: Main PR extracting the same bounty-attempt reservation endpoints and helpers (effective/expired status, stale-expiration, serialization, active-attempt query logic) from app/main.py into dedicated module.
  • ramimbo/mergework#334: Related changes that introduce/consume list_bounty_attempts and bounty_attempt_to_dict serialization used by the MCP tool.

Suggested reviewers

  • g8rr5dg2p7-svg
  • weilixiong
  • TateLyman
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely and clearly summarizes the main change: extracting bounty attempt route logic out of app/main.py.
Description check ✅ Passed The description covers the main changes, lists complexity reduction, provides comprehensive validation results, and includes the required MRWK reference, but the Test Evidence checklist items are not marked with checkmarks (though validation was performed separately).
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

@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: 1

🤖 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 `@app/bounty_attempts.py`:
- Around line 45-46: bounty_attempt_to_dict currently assigns now = now or
_utc_now() but does not ensure injected now is timezone-aware UTC, causing
TypeError when _attempt_effective_status uses _as_utc(attempt.expires_at) <=
now; update bounty_attempt_to_dict to normalize any provided now via _as_utc (or
convert naive datetimes to UTC) so the value passed into
_attempt_effective_status is always an aware UTC datetime; reference the
functions bounty_attempt_to_dict, _attempt_effective_status, _as_utc, and
_utc_now when making the change.
🪄 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: c9496cee-19be-4de9-945f-802e0a890e50

📥 Commits

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

📒 Files selected for processing (3)
  • app/bounty_attempts.py
  • app/main.py
  • tests/test_bounty_attempts.py

Comment thread app/bounty_attempts.py Outdated
Copy link
Copy Markdown
Contributor

@MolhamHamwi MolhamHamwi 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 extraction on head 079c38b. Evidence: checked app/bounty_attempts.py against the removed app/main.py routes, verified the same validation paths are still wired through register_bounty_attempt_routes, inspected tests/test_bounty_attempts.py for serializer coverage, and confirmed GitHub's Quality/readiness check is green. I did not find a blocking issue in the moved code; local pytest could not run here because this runner only has Python 3.9 while the project requires >=3.12.

@ramimbo
Copy link
Copy Markdown
Owner

ramimbo commented May 26, 2026

Maintainer update: holding this with mrwk:needs-info. Please address the valid timestamp-normalization issue in app/bounty_attempts.py so injected now values are normalized before comparison with expires_at. No PR bounty payout is recorded while it is held.

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 bounty-attempt route extraction for #320.

What I checked:

  • app/bounty_attempts.py: moved attempt serialization, active/expired status calculation, warning construction, stale-attempt expiry, attempt registration, duplicate detection, list filtering, and release route behavior out of app/main.py.
  • app/main.py: now wires the extracted route module with the same dependencies for login, JSON parsing, string/int parsing, account normalization, bounty id validation, and SQLite max id checks.
  • tests/test_bounty_attempts.py: added direct serializer coverage for expired active attempts while preserving the existing route-level register/list/duplicate/release checks.

Validation run locally:

  • uv run --extra dev python -m pytest tests/test_bounty_attempts.py::test_bounty_attempt_serializer_reports_expired_effective_status tests/test_bounty_attempts.py::test_bounty_attempts_register_list_duplicate_and_release -q -> 2 passed.
  • uv run --extra dev python -m pytest tests/test_bounty_attempts.py tests/test_api_mcp.py -q -> 77 passed.
  • uv run --extra dev ruff check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py -> passed.
  • uv run --extra dev ruff format --check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py -> passed.
  • uv run --extra dev mypy app/bounty_attempts.py app/main.py -> passed.
  • uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok.
  • git diff --check origin/main...HEAD -> clean.

Behaviorally this looks like a clean extraction. Repository-level blocker: GitHub currently reports mergeState DIRTY, likely because main has moved after nearby bounty-attempt/MCP work. The branch needs a rebase/refresh before it can merge.

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: 1

🤖 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 `@app/bounty_attempts.py`:
- Around line 146-147: The handlers call data = await json_object(request) which
raises a 400 on empty POST bodies; change both occurrences (the block that calls
json_object(request) before attempt_submitter_account(...) and the similar one
later) to accept empty bodies by treating missing/invalid JSON as an empty dict:
either check request.content_length/request.body and use {} when empty, or wrap
the await json_object(request) in a try/except (catching the invalid-json/error
thrown) and set data = {} on failure, then pass data into
attempt_submitter_account as before so bodyless create/release requests succeed.
🪄 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: 07a0b802-d380-4a33-8a43-fbe24e7e092c

📥 Commits

Reviewing files that changed from the base of the PR and between 1a68850 and 55181b6.

📒 Files selected for processing (3)
  • app/bounty_attempts.py
  • app/main.py
  • tests/test_bounty_attempts.py

Comment thread app/bounty_attempts.py Outdated
@AugmentSecurity AugmentSecurity force-pushed the bounty-320-admin-routes branch from 55181b6 to 7db500d Compare May 26, 2026 01:09
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: 1

🤖 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 `@app/main.py`:
- Around line 27-33: Replace the private helper usage in main.py by adding a
public listing helper in app.bounty_attempts (e.g., def
list_bounty_attempts(...):) that encapsulates the filtering/formatting logic
currently using _active_attempt_conditions and _utc_now and reuses
bounty_attempt_to_dict / bounty_attempt_warnings and the same query logic used
by register_bounty_attempt_routes; then change main.py to call that new public
list_bounty_attempts from the place that invokes _call_mcp_tool(...,
name="list_bounty_attempts") instead of importing _active_attempt_conditions or
_utc_now directly.
🪄 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: 9284909f-ad86-42b9-9bdc-5cada0e84871

📥 Commits

Reviewing files that changed from the base of the PR and between 55181b6 and 7db500d.

📒 Files selected for processing (3)
  • app/bounty_attempts.py
  • app/main.py
  • tests/test_bounty_attempts.py

Comment thread app/main.py
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.

Rechecked latest head after the follow-up Encapsulate bounty attempt listing commit. The previous CodeRabbit concern about importing private attempt-listing helpers into app/main.py is addressed: main.py now calls the public list_bounty_attempts() helper from app.bounty_attempts, and that helper owns the same active/expired filtering, serialization, and warning construction used by the route module.\n\nValidation on current head c244647:\n- uv run --extra dev python -m pytest tests/test_bounty_attempts.py tests/test_api_mcp.py -q -> 81 passed.\n- uv run --extra dev python -m pytest -q -> 337 passed.\n- uv run --extra dev ruff check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py -> passed.\n- uv run --extra dev ruff format --check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py -> passed.\n- uv run --extra dev python -m mypy app/bounty_attempts.py app/main.py -> passed.\n- uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok.\n- git diff --check origin/main...HEAD -> clean.\n\nNo new blocker found in the refreshed extraction. GitHub currently shows mergeState UNSTABLE only because CodeRabbit is still pending on the latest commit; hosted CI is green.

@AugmentSecurity
Copy link
Copy Markdown
Contributor Author

Maintainer update: the mrwk:needs-info items have been addressed on current head c244647.

Resolved:

  • Normalized injected now values in bounty_attempt_to_dict() before comparing with stored timestamps.
  • Added empty-body create/release handling for attempt endpoints with regression coverage.
  • Added public list_bounty_attempts() helper so app.main no longer imports private bounty-attempt helpers.
  • Rebased/refreshed against current main; GitHub now reports the branch mergeable and clean.

Validation:

  • ./.venv/bin/python -m pytest tests/test_bounty_attempts.py tests/test_api_mcp.py -q -> 81 passed.
  • ./.venv/bin/python -m pytest -q -> 337 passed.
  • ./.venv/bin/python -m ruff check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py tests/test_api_mcp.py -> passed.
  • ./.venv/bin/python -m ruff format --check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py tests/test_api_mcp.py -> passed.
  • ./.venv/bin/python -m mypy app/bounty_attempts.py app/main.py -> passed.
  • ./.venv/bin/python scripts/docs_smoke.py -> docs smoke ok.
  • git diff --check -> clean.

Hosted CI is green and CodeRabbit is green on the latest push.

Copy link
Copy Markdown
Contributor

@ayskobtw-lil ayskobtw-lil left a comment

Choose a reason for hiding this comment

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

No blockers found on current head c244647 after the needs-info follow-up.

Evidence checked:

  • Inspected app/bounty_attempts.py against the removed app.main attempt routes and confirmed route registration still wires list/create/release behavior through the same login, account normalization, TTL, public URL, bounty id, and SQLite integer validators.
  • Verified the previous timestamp-normalization concern is addressed: bounty_attempt_to_dict() normalizes injected now, and _attempt_effective_status() compares UTC-normalized expires_at values.
  • Checked list_bounty_attempts() now provides a public helper boundary for both route and MCP usage, so app.main no longer imports the private active-attempt helpers.
  • Confirmed empty POST bodies now default to the authenticated GitHub login for both create and release paths, preserving the existing ownership checks.

Validation on this PR head:

  • python -m pytest tests/test_bounty_attempts.py tests/test_api_mcp.py -q -> 81 passed
  • python -m ruff check app/bounty_attempts.py app/main.py tests/test_bounty_attempts.py tests/test_api_mcp.py -> passed
  • git diff --check origin/main...HEAD -> clean

@ramimbo ramimbo merged commit 13b3487 into ramimbo:main May 26, 2026
2 checks passed
@ramimbo ramimbo added mrwk:paid Ledger payment recorded and removed mrwk:needs-info More information needed labels May 26, 2026
@ramimbo ramimbo added the mrwk:accepted Maintainer accepted for payout label 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.

5 participants