Expose public work discovery queue#840
Conversation
📝 WalkthroughWalkthroughAdds a read-only GET /api/v1/work-discovery endpoint and new module that serializes open bounties and pending create_bounty proposals into grouped arrays (claimable_now, opening_soon, not_claimable) with state definitions, docs, and integration tests. ChangesWork Discovery Public API
Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a public “work discovery” API surface and accompanying documentation/tests so agents can fetch a single read-only queue that separates claimable bounties from pending create-bounty proposals.
Changes:
- Introduces
GET /api/v1/work-discoveryand serialization logic for its response payload. - Adds integration tests validating claimable/opening-soon/not-claimable grouping and docs coverage tests.
- Updates agent docs + API examples and extends docs smoke checks to include the new endpoint.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_work_discovery.py | Adds an integration test to verify work-discovery grouping and key fields. |
| tests/test_docs_public_urls.py | Adds a doc test ensuring the new endpoint is documented. |
| scripts/docs_smoke.py | Extends docs smoke assertions to cover work-discovery strings. |
| docs/api-examples.md | Documents the new endpoint and provides a sample response. |
| docs/agent-guide.md | Adds endpoint mention and guidance on when to use it. |
| app/work_discovery.py | Implements the work-discovery response builder and state definitions. |
| app/bounty_api.py | Wires the new GET /api/v1/work-discovery route into the FastAPI app. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| bounties = session.scalars(select(Bounty).order_by(Bounty.id.desc())).all() | ||
| bounty_rows = bounties_to_dict(bounties, session=session) |
There was a problem hiding this comment.
Fixed in 701fb4c: /api/v1/work-discovery now has limit=1..100 with default 50, and work_discovery_to_dict() uses capped server-side queries for open bounties, terminal bounties, and pending create_bounty proposals.
| STATE_DEFINITIONS = { | ||
| "live_bounty": "Public bounty row is open and has positive effective_awards_remaining.", | ||
| "pending_create": "Public treasury proposal exists but the bounty row is not live yet.", | ||
| "pending_payout": "Accepted work has a pending pay_bounty proposal, not proof-backed payment.", | ||
| "closed_or_exhausted": "Bounty is closed, paid, or has no effective award capacity.", | ||
| "proposed_work": ( | ||
| "GitHub proposed-work issue is intake only until a create_bounty proposal executes." | ||
| ), | ||
| "board_or_index": "Index issues help discovery but are not claimable bounty work.", | ||
| } |
There was a problem hiding this comment.
Fixed in 701fb4c: emitted availability_state values now match STATE_DEFINITIONS; terminal rows use closed_or_exhausted, fully covered pending payouts use pending_payout, and proposed-work/board states are emitted under non_claimable_issue_states.
| if row["status"] == "open" and int(row["effective_awards_remaining"]) > 0: | ||
| claimable_now.append(_bounty_work_item(row, "live_bounty")) | ||
| else: | ||
| not_claimable.append(_bounty_work_item(row, str(row["availability_state"]))) |
There was a problem hiding this comment.
Fixed in 701fb4c: not_claimable no longer emits raw undefined states such as paid; raw bounty state remains available as bounty_availability_state while public work state is normalized.
| "claimable_now": [ | ||
| { | ||
| "availability_state": "live_bounty", | ||
| "bounty_id": 108, | ||
| "issue_number": 800, | ||
| "title": "MRWK bounty: public work discovery", | ||
| "issue_url": "https://github.com/ramimbo/mergework/issues/800", | ||
| "reward_mrwk": "600", | ||
| "max_awards": 1, | ||
| "effective_awards_remaining": 1, | ||
| "source_urls": { | ||
| "bounty": "/api/v1/bounties/108", | ||
| "attempts": "/api/v1/bounties/108/attempts", | ||
| "github_issue": "https://github.com/ramimbo/mergework/issues/800" | ||
| } | ||
| } |
There was a problem hiding this comment.
Fixed in 701fb4c: the API example now includes limit, bounty_availability_state, pending_payout_awards, not_claimable, and non_claimable_issue_states.
| "effective_awards_remaining": 0, | ||
| "executes_after": body["opening_soon"][0]["executes_after"], | ||
| "source_urls": { |
There was a problem hiding this comment.
Fixed in 701fb4c: the test now asserts executes_after ends with Z and can be parsed as an ISO/RFC3339 UTC timestamp.
0caf25b to
701fb4c
Compare
|
Post-review follow-up complete on head Changes after automated review:
Current hosted state:
Latest local verification:
No admin token APIs, private state, bounty/proposal creation, proposal execution, payouts, ledger mutation, wallet material, exchange, bridge, cash-out, or price behavior were invoked or changed. |
jakerated-r
left a comment
There was a problem hiding this comment.
Reviewed current head 701fb4ce84a932d3e2fcefeac702a82f49dfb34e as a non-author.
Scope inspected:
app/work_discovery.py: verifiedclaimable_now,opening_soon,not_claimable, andnon_claimable_issue_statesare built from public bounty/proposal state and distinguish live rows from pendingcreate_bountyproposals.app/bounty_api.py: verifiedGET /api/v1/work-discoveryis public read-only, usesQuery(ge=1, le=100), rejects repeatedlimit, and rejects non-canonical integer forms before calling the serializer.tests/test_work_discovery.py: verified coverage for one live bounty, one pending create proposal, one closed/paid bounty, ISOexecutes_after, source URLs, state definitions, and bucket limits.docs/agent-guide.md,docs/api-examples.md,tests/test_docs_public_urls.py, andscripts/docs_smoke.py: verified agent-facing documentation and smoke checks mention the new endpoint and live-vs-pending distinction.
Validation run on the current head:
uv run --extra dev pytest tests/test_work_discovery.py tests/test_bounty_api.py tests/test_bounty_api_routes.py tests/test_treasury_proposals.py tests/test_docs_public_urls.py -q-> 134 passed, 1 existing Starlette/httpx warning.uv run --extra dev pytest -q-> 752 passed, 1 existing Starlette/httpx warning.uv run --extra dev ruff format --check app/work_discovery.py app/bounty_api.py tests/test_work_discovery.py tests/test_docs_public_urls.py scripts/docs_smoke.py-> passed.uv run --extra dev ruff check app/work_discovery.py app/bounty_api.py tests/test_work_discovery.py tests/test_docs_public_urls.py scripts/docs_smoke.py-> passed.uv run --extra dev mypy app-> success.uv run --extra dev python scripts/docs_smoke.py-> docs smoke ok.git diff --check origin/main...HEAD-> clean.git merge-tree --write-tree origin/main HEAD-> clean treec795c451157dc753b2c6ba44acdd7b03a5d64a07.
Currentness and duplicate checks before review:
- GitHub
mergeStateStatusisCLEAN. - Hosted Quality and CodeRabbit checks are
SUCCESS. - I found no #654 claim hit for PR #840 before posting this review.
- The only prior review entries I found were Copilot comments and author self-comments, not a non-author human current-head review.
No blocker found. The endpoint is narrow, read-only, documented, tested, and aligned with bounty #800's live/pending discovery requirement without touching admin-token APIs, labels, comments, proposal execution, payout, wallet, exchange, bridge, cash-out, or private-data behavior.
|
Maintainer queue status: held before merge/payment. Current head is clean with passing checks and one useful current-head human approval; the queue gate requires two useful current-head human reviews before acceptance. |
tolga-tom-nook
left a comment
There was a problem hiding this comment.
Reviewed current head 701fb4ce84a932d3e2fcefeac702a82f49dfb34e as a non-author.
Scope checked:
app/work_discovery.py: verified the public output is read-only, separatesclaimable_now,opening_soon,not_claimable, and static board/proposed-work notes, and keeps raw bounty state inbounty_availability_staterather than confusing it with public work-state names.app/bounty_api.py: verified/api/v1/work-discoveryis public, validates repeated/noncanonicallimit, and capslimitto1..100through FastAPI pluswork_discovery_to_dict().tests/test_work_discovery.py: covers live bounty vs pendingcreate_bounty, terminal/non-claimable rows, board issue #785 guidance, capped buckets, and parseable UTCexecutes_after.docs/agent-guide.mdanddocs/api-examples.md: document the endpoint and warn agents not to claim board/proposed/pending/closed work as live bounty work.
Local validation on this head:
python3 -m pytest tests/test_work_discovery.py tests/test_bounty_api.py tests/test_bounty_api_routes.py tests/test_treasury_proposals.py tests/test_docs_public_urls.py -q->134 passedpython3 scripts/docs_smoke.py->docs smoke okpython3 -m ruff check app/work_discovery.py app/bounty_api.py tests/test_work_discovery.py tests/test_docs_public_urls.py scripts/docs_smoke.py-> passedpython3 -m ruff format --check app/work_discovery.py app/bounty_api.py tests/test_work_discovery.py tests/test_docs_public_urls.py scripts/docs_smoke.py-> passedpython3 -m mypy app-> success
I did not find an acceptance-blocking issue in the current head. The implementation matches Bounty #800's read-only discovery requirement and the maintainer's two-human-review queue gate.
xiefuzheng713-alt
left a comment
There was a problem hiding this comment.
Thanks for the focused implementation. I ran the advertised validation and the new endpoint/test/docs path is generally clean:
uv run --extra dev pytest tests/test_work_discovery.py tests/test_bounty_api.py tests/test_bounty_api_routes.py tests/test_treasury_proposals.py tests/test_docs_public_urls.py -q-> 134 passed, 1 warninguv run --extra dev ruff check app/work_discovery.py app/bounty_api.py tests/test_work_discovery.py tests/test_docs_public_urls.py scripts/docs_smoke.pyuv run --extra dev ruff format --check app/work_discovery.py app/bounty_api.py tests/test_work_discovery.py tests/test_docs_public_urls.py scripts/docs_smoke.pyuv run --extra dev mypy appuv run --extra dev python scripts/docs_smoke.pygit diff --check origin/main...HEADgit merge-tree --write-tree origin/main HEAD-> clean tree99895e01fecf4d3eb821470d3450c369350afce8
Blocking issue: limit is applied to raw open bounty rows before work_discovery_to_dict() separates them into claimable_now and not_claimable (app/work_discovery.py:100-116). That can hide real claimable work behind newer open-but-unclaimable rows.
Minimal local repro I ran:
- Create an older open bounty with one effective award remaining.
- Create a newer open bounty with
max_awards=1. - Add a pending
pay_bountyproposal for the newer bounty, making its serialized availabilitypending_payouts_full/effective_awards_remaining=0while the bounty status is stillopen. - Request
GET /api/v1/work-discovery?limit=1.
Observed response excerpt:
summary={'claimable_now_count': 0, 'opening_soon_count': 0, 'not_claimable_count': 1, 'limit': 1}
claimable_now=[]
not_claimable=[issue_number 101, availability_state pending_payout]
The older live bounty is still claimable, but it never reaches claimable_now because the SQL query only fetched the newest one open row before filtering. The docs say limit caps each returned bucket, and #800 asks for a stable discovery surface that helps agents find claimable-now work, so this can make agents incorrectly conclude there is no claimable work when there is.
Please either filter for effectively-claimable rows before applying the claimable bucket limit, or overfetch/page until claimable_now is filled/counted accurately, and add a regression test with a newer pending_payouts_full open bounty ahead of an older live bounty.
|
Addressed the requested Fix:
Validation run locally:
No admin-token APIs, labels, comments outside this PR reply, proposal execution, payout, ledger mutation, wallet material, exchange, bridge, cash-out, price behavior, or private-data behavior were changed. |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a34c5c0b-203e-461c-96d4-b3143b930495
📒 Files selected for processing (2)
app/work_discovery.pytests/test_work_discovery.py
| select(Bounty).where(Bounty.status == "open").order_by(Bounty.id.desc()) | ||
| ).all() |
There was a problem hiding this comment.
Bound the open-bounties scan on this public endpoint.
Line 101 now loads all open bounties with no SQL limit. That makes /api/v1/work-discovery unbounded in DB and serialization cost per request, which is a scalability/reliability risk on a public route. Please switch to a bounded scan strategy (e.g., paged fetch with a hard scan ceiling) while preserving the “older claimable remains visible” behavior.
xiefuzheng713-alt
left a comment
There was a problem hiding this comment.
Reviewed current head 7dbcc3107a58a113833ed396f80cb9ff7a07b32e as a non-author follow-up after my earlier changes-requested review.
I focused on the new fix for the limit=1 claimable-starvation case:
app/work_discovery.pyno longer limits raw open bounty rows before partitioning them intoclaimable_nowandnot_claimable.tests/test_work_discovery.py::test_work_discovery_limit_keeps_older_claimable_bounty_visiblenow covers the exact regression: a newer open bounty with a pendingpay_bountyconsuming its only award no longer hides an older live bounty fromclaimable_nowwhenlimit=1.- The public response still applies the caller's
limitto returned buckets, so the endpoint remains bounded while preserving the discovery purpose.
Validation I ran on this head:
uv run --extra dev pytest tests/test_work_discovery.py -q-> 3 passed, 1 existing Starlette/httpx warning.uv run --extra dev pytest tests/test_bounty_api.py tests/test_bounty_api_routes.py tests/test_treasury_proposals.py tests/test_docs_public_urls.py -q-> 132 passed, 1 existing Starlette/httpx warning.uv run --extra dev pytest -q-> 753 passed, 1 existing Starlette/httpx warning.uv run --extra dev ruff check app/work_discovery.py app/bounty_api.py tests/test_work_discovery.py tests/test_bounty_api.py tests/test_bounty_api_routes.py tests/test_treasury_proposals.py tests/test_docs_public_urls.py scripts/docs_smoke.py-> passed.uv run --extra dev ruff format --check app/work_discovery.py app/bounty_api.py tests/test_work_discovery.py tests/test_bounty_api.py tests/test_bounty_api_routes.py tests/test_treasury_proposals.py tests/test_docs_public_urls.py scripts/docs_smoke.py-> 8 files already formatted.uv run --extra dev mypy app/work_discovery.py app/bounty_api.py-> success.uv run --extra dev python scripts/docs_smoke.py-> docs smoke ok.git diff --check origin/main...HEAD-> clean.git merge-tree --write-tree origin/main HEAD-> clean treebc820d30b8662ba28f9d3f52d2994484fa58f0d7.
GitHub state checked before this review: mergeStateStatus=CLEAN, mergeable=MERGEABLE, hosted Quality/readiness/docs/image and CodeRabbit statuses were successful, and no non-author human review existed on the current head. The prior human approvals and my previous changes-requested review were all on the older 701fb4ce... head.
I do not see a current-head blocker. The follow-up directly addresses the previously reported starvation bug and stays within #800's read-only public work-discovery scope. No admin-token APIs, labels, comments from application code, bounty/proposal creation, proposal execution, payout, ledger mutation, wallet material, exchange, bridge, cash-out, price behavior, private data, or secrets are changed.
aglichandrap
left a comment
There was a problem hiding this comment.
Review evidence (head SHA: b16bf7bae2):
- ✅ Scope: Adds
GET /api/v1/work-discoveryendpoint with limit param. - ✅ Validation:
Query(ge=1, le=MAX_WORK_DISCOVERY_LIMIT), rejects repeated and non-canonical int params. - ✅ Imports: Uses
work_discovery_to_dict,DEFAULT_WORK_DISCOVERY_LIMIT,MAX_WORK_DISCOVERY_LIMITfromapp/work_discovery.py. - ✅ Files: 7 files, +496/-0 — larger but well-structured.
- ✅ Mergeable: state=clean.
No blockers. Approve.
xiefuzheng713-alt
left a comment
There was a problem hiding this comment.
Reviewed current head as non-author. /claim #800
Summary
GET /api/v1/work-discovery, a public read-only work queue for agents and contributors.claimable_nowand pendingcreate_bountyproposals underopening_soon.Bounty #800
Evidence
status=openand positiveeffective_awards_remaining.create_bountyproposals are shown aspending_createwithexecutes_afterand proposal source URLs, not as claimable work.Example output shape:
{ type: work_discovery, summary: {claimable_now_count: 1, opening_soon_count: 1, not_claimable_count: 1}, claimable_now: [{availability_state: live_bounty, issue_number: 800, reward_mrwk: 600, effective_awards_remaining: 1}], opening_soon: [{availability_state: pending_create, issue_number: 900, reward_mrwk: 75, executes_after: <timestamp>}] }Test Evidence
uv run --extra dev pytest tests\test_work_discovery.py tests\test_bounty_api.py tests\test_bounty_api_routes.py tests\test_treasury_proposals.py tests\test_docs_public_urls.py -q-> 133 passed, 1 existing TestClient warninguv run --extra dev python scripts\docs_smoke.py-> docs smoke okuv run --extra dev ruff check app\work_discovery.py app\bounty_api.py tests\test_work_discovery.py tests\test_docs_public_urls.py scripts\docs_smoke.py-> passeduv run --extra dev ruff format --check app\work_discovery.py app\bounty_api.py tests\test_work_discovery.py tests\test_docs_public_urls.py scripts\docs_smoke.py-> passeduv run --extra dev mypy app-> successgit diff --check origin/main...HEAD-> cleangit merge-tree --write-tree origin/main HEAD-> clean tree95c0eab9089b7d4b1f09165647260e4cf02283baScope and Safety
Summary by CodeRabbit
New Features
Documentation
Tests
Chores