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
12 changes: 12 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1601,10 +1601,19 @@ def work_proof_guidance(bounty: Bounty) -> str:

def work_proof_guidance_json(bounty: Bounty) -> dict[str, Any]:
bounty_data = bounty_to_dict(bounty)
can_submit = bounty_data["status"] == "open" and bounty_data["awards_remaining"] > 0
availability_warnings = []
if bounty_data["status"] != "open":
availability_warnings.append(f"bounty is {bounty_data['status']}")
if bounty_data["awards_remaining"] <= 0:
availability_warnings.append("bounty has no award slots remaining")
return {
"bounty_id": bounty_data["id"],
"issue_number": bounty_data["issue_number"],
"status": bounty_data["status"],
"availability": "open_for_submissions" if can_submit else "not_currently_open",
"can_submit": can_submit,
"availability_warnings": availability_warnings,
"awards_remaining": bounty_data["awards_remaining"],
"max_awards": bounty_data["max_awards"],
"awards_paid": bounty_data["awards_paid"],
Expand All @@ -1630,6 +1639,9 @@ def generic_work_proof_guidance_json() -> dict[str, Any]:
"bounty_id": None,
"issue_number": None,
"status": "generic_guidance",
"availability": "unknown_without_bounty",
"can_submit": None,
"availability_warnings": [],
"awards_remaining": None,
"reward_mrwk": None,
"repository": None,
Expand Down
85 changes: 85 additions & 0 deletions tests/test_api_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,9 @@ def test_mcp_submit_work_proof_returns_structured_bounty_guidance(sqlite_url: st
assert structured["bounty_id"] == bounty_id
assert structured["issue_number"] == 315
assert structured["status"] == "open"
assert structured["availability"] == "open_for_submissions"
assert structured["can_submit"] is True
assert structured["availability_warnings"] == []
assert structured["awards_remaining"] == 3
assert structured["max_awards"] == 3
assert structured["awards_paid"] == 0
Expand Down Expand Up @@ -1282,6 +1285,9 @@ def test_mcp_submit_work_proof_returns_structured_generic_guidance(sqlite_url: s
"bounty_id": None,
"issue_number": None,
"status": "generic_guidance",
"availability": "unknown_without_bounty",
"can_submit": None,
"availability_warnings": [],
"awards_remaining": None,
"reward_mrwk": None,
"repository": None,
Expand All @@ -1299,6 +1305,85 @@ def test_mcp_submit_work_proof_returns_structured_generic_guidance(sqlite_url: s
assert json.loads(response_result["content"][0]["text"]) == result


def test_mcp_submit_work_proof_structures_terminal_bounty_availability(
sqlite_url: str,
) -> None:
create_schema(sqlite_url)
with session_scope(sqlite_url) as session:
ensure_genesis(session)
paid_bounty = create_bounty(
session,
repo="ramimbo/mergework",
issue_number=316,
issue_url="https://github.com/ramimbo/mergework/issues/316",
title="Paid structured guidance",
reward_mrwk="100",
acceptance="Expose paid guidance state.",
)
closed_bounty = create_bounty(
session,
repo="ramimbo/mergework",
issue_number=317,
issue_url="https://github.com/ramimbo/mergework/issues/317",
title="Closed structured guidance",
reward_mrwk="100",
max_awards=2,
acceptance="Expose closed guidance state.",
)
paid_bounty_id = paid_bounty.id
closed_bounty_id = closed_bounty.id
pay_bounty(
session,
bounty_id=paid_bounty_id,
to_account="github:alice",
submission_url="https://github.com/ramimbo/mergework/pull/316",
accepted_by="maintainer",
verifier_result={"label": "mrwk:accepted"},
)
close_bounty(
session,
bounty_id=closed_bounty_id,
closed_by="maintainer",
reference="https://github.com/ramimbo/mergework/issues/317#close",
)

client = TestClient(create_app(database_url=sqlite_url, webhook_secret="secret"))

def structured_guidance(bounty_id: int, request_id: int) -> dict[str, object]:
response = client.post(
"/mcp",
json={
"jsonrpc": "2.0",
"id": request_id,
"method": "tools/call",
"params": {
"name": "submit_work_proof",
"arguments": {"bounty_id": bounty_id, "format": "json"},
},
},
).json()["result"]
assert json.loads(response["content"][0]["text"]) == response["structuredContent"]
return response["structuredContent"]

paid = structured_guidance(paid_bounty_id, 1)
closed = structured_guidance(closed_bounty_id, 2)

assert paid["status"] == "paid"
assert paid["availability"] == "not_currently_open"
assert paid["can_submit"] is False
assert paid["availability_warnings"] == [
"bounty is paid",
"bounty has no award slots remaining",
]
assert closed["status"] == "closed"
assert closed["availability"] == "not_currently_open"
assert closed["can_submit"] is False
assert closed["availability_warnings"] == [
"bounty is closed",
"bounty has no award slots remaining",
]


def test_mcp_submit_work_proof_reports_unknown_bounty(sqlite_url: str) -> None:
create_schema(sqlite_url)
with session_scope(sqlite_url) as session:
Expand Down
Loading