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

def work_proof_submission_requirements(
*,
bounty_id: int | None,
issue_number: int | None,
can_submit: bool | None,
) -> dict[str, Any]:
issue_ref = str(issue_number) if issue_number is not None else "<issue_number>"
bounty_ref = str(bounty_id) if bounty_id is not None else "<bounty_id>"
if can_submit is True:
first_action = {
"id": "confirm_award_slot",
"required": True,
"text": "Confirm this bounty is open and has at least one award slot remaining.",
}
elif can_submit is False:
first_action = {
"id": "choose_open_bounty",
"required": True,
"text": (
"Do not open or claim new work for this bounty unless a maintainer reopens it."
),
}
else:
first_action = {
"id": "select_bounty",
"required": True,
"text": "Select a concrete open bounty before submitting work proof.",
}
return {
"reference_formats": [f"Bounty #{issue_ref}", f"Refs #{issue_ref}"],
"claim_command": "/claim",
"attempt_endpoint": f"/api/v1/bounties/{bounty_ref}/attempts",
"evidence_required": [
"focused PR, issue, report, or evidence URL",
"short verification summary",
"tests, command output, screenshots, or reproduction steps when relevant",
],
"acceptance_trigger": "maintainer_mrwk_accepted_label_or_admin_payout",
"public_metadata_must_avoid": [
"private keys",
"seed material",
"secrets",
"deployment credentials",
"private vulnerability details",
"price claims",
],
"next_actions": [
first_action,
{
"id": "check_duplicate_scope",
"required": True,
"text": (
"Confirm no active claim or duplicate PR already covers the same scope."
),
},
{
"id": "keep_scope_focused",
"required": True,
"text": "Keep changes directly tied to one bounty issue.",
},
{
"id": "include_bounty_reference",
"required": True,
"text": (
f"Include Bounty #{issue_ref} or Refs #{issue_ref} in the submission."
),
},
{
"id": "include_review_evidence",
"required": True,
"text": "Include reviewable validation evidence before claiming.",
},
{
"id": "wait_for_maintainer_acceptance",
"required": True,
"text": (
"Payment requires mrwk:accepted or an admin payout; merge or CI "
"alone is not acceptance."
),
},
],
}

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
Expand Down Expand Up @@ -1608,6 +1691,11 @@ def work_proof_guidance_json(bounty: Bounty) -> dict[str, Any]:
"test or behavior evidence, then comment /claim with the PR or "
"evidence URL and verification summary."
),
"submission_requirements": work_proof_submission_requirements(
bounty_id=bounty_data["id"],
issue_number=bounty_data["issue_number"],
can_submit=can_submit,
),
"safety_rules": [
"Do not include private keys, seed material, secrets, deployment "
"credentials, private vulnerability details, or price claims."
Expand All @@ -1631,6 +1719,11 @@ def generic_work_proof_guidance_json() -> dict[str, Any]:
"Open a focused PR or issue, reference the MRWK bounty, include test "
"evidence, and wait for a maintainer to apply mrwk:accepted."
),
"submission_requirements": work_proof_submission_requirements(
bounty_id=None,
issue_number=None,
can_submit=None,
),
"safety_rules": [
"Do not include private keys, seed material, secrets, deployment "
"credentials, private vulnerability details, or price claims."
Expand Down
75 changes: 75 additions & 0 deletions tests/test_api_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,21 @@ def test_mcp_submit_work_proof_returns_structured_bounty_guidance(sqlite_url: st
assert structured["title"] == "Structured MCP work-proof guidance"
assert structured["acceptance"] == "Return machine-readable work-proof guidance."
assert "/claim" in structured["submission_format"]
requirements = structured["submission_requirements"]
assert requirements["reference_formats"] == ["Bounty #315", "Refs #315"]
assert requirements["claim_command"] == "/claim"
assert requirements["attempt_endpoint"] == f"/api/v1/bounties/{bounty_id}/attempts"
assert requirements["acceptance_trigger"] == ("maintainer_mrwk_accepted_label_or_admin_payout")
assert "focused PR, issue, report, or evidence URL" in requirements["evidence_required"]
assert "price claims" in requirements["public_metadata_must_avoid"]
assert [action["id"] for action in requirements["next_actions"]] == [
"confirm_award_slot",
"check_duplicate_scope",
"keep_scope_focused",
"include_bounty_reference",
"include_review_evidence",
"wait_for_maintainer_acceptance",
]
assert "private keys" in structured["safety_rules"][0]


Expand Down Expand Up @@ -1432,6 +1447,64 @@ def test_mcp_submit_work_proof_returns_structured_generic_guidance(sqlite_url: s
"Open a focused PR or issue, reference the MRWK bounty, include test "
"evidence, and wait for a maintainer to apply mrwk:accepted."
),
"submission_requirements": {
"reference_formats": ["Bounty #<issue_number>", "Refs #<issue_number>"],
"claim_command": "/claim",
"attempt_endpoint": "/api/v1/bounties/<bounty_id>/attempts",
"evidence_required": [
"focused PR, issue, report, or evidence URL",
"short verification summary",
"tests, command output, screenshots, or reproduction steps when relevant",
],
"acceptance_trigger": "maintainer_mrwk_accepted_label_or_admin_payout",
"public_metadata_must_avoid": [
"private keys",
"seed material",
"secrets",
"deployment credentials",
"private vulnerability details",
"price claims",
],
"next_actions": [
{
"id": "select_bounty",
"required": True,
"text": "Select a concrete open bounty before submitting work proof.",
},
{
"id": "check_duplicate_scope",
"required": True,
"text": (
"Confirm no active claim or duplicate PR already covers the same scope."
),
},
{
"id": "keep_scope_focused",
"required": True,
"text": "Keep changes directly tied to one bounty issue.",
},
{
"id": "include_bounty_reference",
"required": True,
"text": (
"Include Bounty #<issue_number> or Refs #<issue_number> in the submission."
),
},
{
"id": "include_review_evidence",
"required": True,
"text": "Include reviewable validation evidence before claiming.",
},
{
"id": "wait_for_maintainer_acceptance",
"required": True,
"text": (
"Payment requires mrwk:accepted or an admin payout; merge or CI "
"alone is not acceptance."
),
},
],
},
"safety_rules": [
"Do not include private keys, seed material, secrets, deployment "
"credentials, private vulnerability details, or price claims."
Expand Down Expand Up @@ -1510,13 +1583,15 @@ def structured_guidance(bounty_id: int, request_id: int) -> dict[str, object]:
"bounty is paid",
"bounty has no award slots remaining",
]
assert paid["submission_requirements"]["next_actions"][0]["id"] == "choose_open_bounty"
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",
]
assert closed["submission_requirements"]["next_actions"][0]["id"] == "choose_open_bounty"


def test_mcp_submit_work_proof_reports_unknown_bounty(sqlite_url: str) -> None:
Expand Down
Loading