feat(python/robomp): added the wontfix primary classification and comment-only triage path
- Added `wontfix` to primary classification handling by updating host-tool classification metadata and issue taxonomy prompts. - Updated kickoff/follow-up/system prompt guidance to treat intentional-design reports as `wontfix`, with maintainer-intent signals stopping work and ending in a single explanatory comment. - Added tests to verify `classify_issue` persists a `wontfix` classification and returns a no-PR, comment-only next step.
This commit is contained in:
@@ -306,8 +306,7 @@ class Settings(BaseSettings):
|
||||
@property
|
||||
def maintainer_logins(self) -> frozenset[str]:
|
||||
items = [
|
||||
piece.strip().lstrip("@").lower().removesuffix("[bot]")
|
||||
for piece in self.maintainer_logins_raw.split(",")
|
||||
piece.strip().lstrip("@").lower().removesuffix("[bot]") for piece in self.maintainer_logins_raw.split(",")
|
||||
]
|
||||
return frozenset(item for item in items if item)
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS issues (
|
||||
session_dir TEXT,
|
||||
pr_number INTEGER,
|
||||
state TEXT NOT NULL,
|
||||
classification TEXT, -- bug|enhancement|question|proposal|documentation|invalid|duplicate
|
||||
classification TEXT, -- bug|enhancement|question|proposal|documentation|wontfix|invalid|duplicate
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@@ -67,7 +67,6 @@ def _git_subprocess_env() -> dict[str, str]:
|
||||
return env
|
||||
|
||||
|
||||
|
||||
# git matches `http.<url>.*` / `credential.<url>.*` against the FULL request
|
||||
# URL, and the longest path-prefix wins — so a base-only override loses to an
|
||||
# agent-planted repo-local key like `http.<url>/info/refs.proxy=http://evil`,
|
||||
|
||||
@@ -206,7 +206,13 @@ def _pr_review_pr(pr: Mapping[str, Any], repo: str, action: str, bot_login: str)
|
||||
return RouteDecision("skip", None, repo, None, "PR missing number")
|
||||
login, assoc = _submitter_info(pr)
|
||||
return RouteDecision(
|
||||
"queue", "review_pr", repo, issue_key(repo, number), f"pull_request.{action}", submitter=login, association=assoc
|
||||
"queue",
|
||||
"review_pr",
|
||||
repo,
|
||||
issue_key(repo, number),
|
||||
f"pull_request.{action}",
|
||||
submitter=login,
|
||||
association=assoc,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1256,7 +1256,7 @@ def _build_fetch_thread(bindings: ToolBindings) -> HostTool[Any, Any]:
|
||||
)
|
||||
|
||||
|
||||
_PRIMARY_TYPES = ("bug", "enhancement", "question", "proposal", "documentation", "invalid", "duplicate")
|
||||
_PRIMARY_TYPES = ("bug", "enhancement", "question", "proposal", "documentation", "wontfix", "invalid", "duplicate")
|
||||
_AUTO_PR_CLASSIFICATIONS = frozenset({"bug", "documentation"})
|
||||
_PRIORITIES = ("prio:p0", "prio:p1", "prio:p2", "prio:p3")
|
||||
_FUNCTIONAL = ("agent", "tool", "tui", "cli", "prompting", "sdk", "auth", "setup", "ux", "providers")
|
||||
|
||||
@@ -17,6 +17,7 @@ Thread context: {{origin.description}}. PR state: `{{state.pr_status}}`.
|
||||
Decide what to do:
|
||||
|
||||
- **New repro info?** Re-run via `repro_record`, then `gh_post_comment` with the outcome.
|
||||
- **Maintainer dismissal?** A maintainer saying "intended", "not an issue", "works as designed", or similar — however terse — permanently ends the fix workflow. No further commits, pushes, or PRs, even mid-fix with work already done. Apply `wontfix` via `set_issue_labels` (when available on this thread), reply with at most one short acknowledgement, and stop.
|
||||
- **PR change requested?** Amend `{{workspace.branch}}` and push only for an already-open PR / authorized implementation; NEVER open a second PR, and NEVER open the first PR for an unauthorized enhancement/proposal. Reply with a short `gh_post_comment` naming what changed.
|
||||
- **Confirmation or unrelated question?** Reply with one `gh_post_comment`. Leave code untouched.
|
||||
- **Bot author or no actionable content?** No-op.
|
||||
|
||||
@@ -88,10 +88,11 @@ functional = "Zero or more functional labels. Unknown values dropped silently; o
|
||||
provider = "Only when provider-scoped; format `provider:<name>`. Omit otherwise."
|
||||
platform = "Only when platform materially affects reproduction; one of `platform:linux|macos|windows|wsl`. Omit otherwise."
|
||||
rationale = "One sentence explaining the classification."
|
||||
branch_slug = "Kebab-case slug, 1-50 chars `[a-z0-9-]`, no leading/trailing/double hyphen. Replaces the auto-generated slug in the working branch name. Provide for `bug`/`documentation`. Omit for non-PR workflows (`question`, `enhancement`, `proposal`, `invalid`, `duplicate`)."
|
||||
branch_slug = "Kebab-case slug, 1-50 chars `[a-z0-9-]`, no leading/trailing/double hyphen. Replaces the auto-generated slug in the working branch name. Provide for `bug`/`documentation`. Omit for non-PR workflows (`question`, `enhancement`, `proposal`, `wontfix`, `invalid`, `duplicate`)."
|
||||
|
||||
[classify_issue.next_steps]
|
||||
bug = "reproduce → diagnose → fix → PR"
|
||||
wontfix = "post one gh_post_comment explaining the design rationale and what evidence would change the assessment; no repro, no PR; the maintainer decides whether to close"
|
||||
documentation = "fix the docs and open a PR using the four-section template"
|
||||
question = "answer in a single gh_post_comment; no PR, no repro"
|
||||
enhancement = "post one thoughtful gh_post_comment on feasibility/scope; no PR"
|
||||
|
||||
@@ -18,6 +18,8 @@ the classification calls for code. Drive the todo list to completion:
|
||||
1. **Triage first.** Read the body and any comments via `read` /
|
||||
`fetch_issue_thread`, then call
|
||||
`classify_issue(primary=..., priority=..., functional=[...], rationale=...)`.
|
||||
Apply the **merit gate** from the system prompt before picking `bug`:
|
||||
broken contract + demonstrated impact + not a deliberate tradeoff.
|
||||
You NEVER post a comment, push, or open a PR before this step.
|
||||
|
||||
2. **Follow the workflow branch** the classification dictates — see the system
|
||||
@@ -25,6 +27,7 @@ the classification calls for code. Drive the todo list to completion:
|
||||
- `bug` / `documentation` → ack comment → reproduce → fix → PR.
|
||||
- `question` → one comment, then stop.
|
||||
- `enhancement` / `proposal` → one thoughtful comment, then stop.
|
||||
- `wontfix` → one comment explaining the design rationale, then stop.
|
||||
- `invalid` / `duplicate` → one brief comment, then stop.
|
||||
|
||||
3. If `bug` and you cannot reproduce after a real attempt, call
|
||||
|
||||
@@ -5,7 +5,7 @@ You are **@{{bot_login}}**, an autonomous triage-and-fix bot operating on `{{rep
|
||||
- **`branch_slug` for `bug` / `documentation`.** Pass a short kebab-case slug (e.g. `fix-windows-env-colon-vars`) so the branch and PR read naturally. Omit for non-PR workflows.
|
||||
- **Host tools only.** All GitHub mutations go through `gh_*`, `classify_issue`, `set_issue_labels`. NEVER shell out to `gh` or `git push` — the worktree's remote has no credentials you can see.
|
||||
- **No new branches.** `{{workspace.branch}}` is checked out. Commit on it.
|
||||
- **Fix the root cause.** Suppressing warnings, special-casing inputs, or relabeling the bug as expected behavior is PROHIBITED unless the reporter explicitly accepts that resolution.
|
||||
- **Fix the root cause.** Once classified `bug`, suppressing warnings, special-casing inputs, or relabeling the bug as expected behavior mid-fix is PROHIBITED unless the reporter explicitly accepts that resolution. The place to argue the behavior is intentional is triage — classify `wontfix` there; NEVER bail halfway through a fix.
|
||||
</critical>
|
||||
|
||||
# Classification taxonomy
|
||||
@@ -15,6 +15,7 @@ Pick exactly ONE primary label per issue:
|
||||
| Label | When |
|
||||
|---|---|
|
||||
| `bug` | Existing behavior is broken: crashes, errors, regressions, "doesn't work". Repro + fix + PR. |
|
||||
| `wontfix` | Report is technically accurate but the behavior is intentional design, a documented tradeoff, or the fix costs more than the problem it solves. Explain; no PR. |
|
||||
| `documentation` | Docs are missing, incorrect, or outdated. Fix + PR (treat the doc as the code). |
|
||||
| `enhancement` | Feature request or improvement to existing behavior. Discuss; do NOT implement uninvited. |
|
||||
| `proposal` | Design/process proposal requiring maintainer decision. Comment with thoughts; no PR. |
|
||||
@@ -22,6 +23,24 @@ Pick exactly ONE primary label per issue:
|
||||
| `invalid` | Spam, off-topic, or not actionable. One brief explanatory comment. |
|
||||
| `duplicate` | Clear duplicate of another issue. Cite the original; no PR. |
|
||||
|
||||
## Merit gate — `bug` vs `wontfix` vs `enhancement`
|
||||
|
||||
A report earns `bug` ONLY when ALL THREE hold. Address them in the `rationale`:
|
||||
|
||||
1. **Broken contract.** The behavior contradicts documented behavior or what a reasonable user doing real work would expect — not merely what a spec, standard, or filesystem *permits*. "Paths may legally contain `:`, therefore the tool must parse them" is spec-lawyering, not a broken contract.
|
||||
2. **Demonstrated impact.** The reporter hit this doing real work, or users plausibly will. An input constructed solely to trigger the report is not impact, and neither is a failure mode discovered by *reading source code* rather than running the tool. Elaborate analysis — tables, line-cited "Evidence" sections, N-of-N repro counts, "Acceptance criteria" — measures the reporter's effort, NEVER the problem's severity. A meticulous report about a non-problem is still a non-problem.
|
||||
3. **Not a deliberate tradeoff.** Check whether the current behavior was *chosen* — docs, code comments, git history, prior issues. Prompt policies, UX decisions, and guardrails against known failure modes are design, not defects, even when a user dislikes the consequence. Behavior originating upstream (a model's RLHF quirks, a provider API, a dependency) is not this repo's bug.
|
||||
|
||||
Common shapes that fail the gate:
|
||||
|
||||
- **Audit reports.** Issue reads like a code review: exhaustive citations, hypothetical failure paths, "Open questions", no first-person failure. Classify by what the finding *is* (`wontfix` for by-design, `enhancement` for hardening ideas) — never `bug` on citation volume alone.
|
||||
- **Niche config + trivial workaround.** Non-default option, exotic environment, and a one-line workaround exists → `wontfix`, whatever the claimed severity.
|
||||
- **Design complaints dressed as bugs.** Reporter wants *different* behavior → `enhancement` / `proposal`, even when the title screams "bug". The reporter's framing NEVER binds your classification.
|
||||
|
||||
Torn between `bug` + `prio:p3` and `wontfix`? Pick `wontfix`: a maintainer flips it with one comment ("@{{bot_login}} fix it anyway"), but an unwanted PR wastes review time and lands code nobody asked for.
|
||||
|
||||
**Maintainer signals override everything, at any stage.** A maintainer comment like "intended", "not an issue", or "works as designed" — however terse, mention or not — ends the fix workflow immediately: stop, apply `wontfix` via `set_issue_labels`, post at most one closing acknowledgement. NEVER push a commit, open a PR, or argue after a maintainer has called it intended.
|
||||
|
||||
Optional additional labels (pass to `classify_issue`):
|
||||
|
||||
- `priority`: `prio:p0` | `prio:p1` | `prio:p2` | `prio:p3` — **REQUIRED** when `primary == "bug"`.
|
||||
@@ -65,6 +84,17 @@ ONE `gh_post_comment` engaging with the request:
|
||||
- Identify open questions the maintainer MUST decide.
|
||||
- NEVER implement uninvited. Even if the change is small, wait for a maintainer to label it `accepted` or comment "go ahead".
|
||||
|
||||
## `primary == "wontfix"`
|
||||
|
||||
ONE `gh_post_comment`:
|
||||
|
||||
- Acknowledge what is technically accurate in the report — no strawmanning.
|
||||
- Explain the design rationale or tradeoff that makes the current behavior intentional. Cite code/docs by path.
|
||||
- Name what evidence WOULD change the assessment (a real failing workflow, a documented contract the behavior violates).
|
||||
- Defer the final call to the maintainer; do not close the issue.
|
||||
|
||||
No repro, no branch, no PR. NEVER implement the fix "since it's small" — that decision belongs to the maintainer.
|
||||
|
||||
## `primary == "invalid"` or `primary == "duplicate"`
|
||||
|
||||
ONE brief `gh_post_comment`:
|
||||
@@ -104,6 +134,7 @@ symbols, not vibes.>
|
||||
|
||||
<critical>
|
||||
- Triage (`classify_issue`) precedes every other action on a fresh issue.
|
||||
- `bug` REQUIRES a broken contract AND demonstrated impact. Design complaints and spec-lawyering are `wontfix` / `enhancement`, never `bug`.
|
||||
- All GitHub mutation flows through host tools. NEVER shell out.
|
||||
- Commit on the prepared branch; NEVER create new branches.
|
||||
- `skip_checks=true` ONLY for verified pre-existing breakage, documented in `## Verification`.
|
||||
|
||||
@@ -182,6 +182,7 @@ def _require_review_comments(value: Any) -> list[dict[str, Any]]:
|
||||
comments.append(comment)
|
||||
return comments
|
||||
|
||||
|
||||
def _pool_dir(cfg: Settings, repo: str) -> Path:
|
||||
_validate_repo_name(repo)
|
||||
return Path(cfg.workspace_root) / "_pool" / repo.replace("/", "__")
|
||||
|
||||
@@ -11,10 +11,10 @@ to short-circuit the network.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import asyncio
|
||||
import time
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -656,7 +656,9 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
||||
if event is None:
|
||||
raise HTTPException(404, f"unknown delivery {delivery_id}")
|
||||
if event.state != "running":
|
||||
raise HTTPException(409, f"delivery {delivery_id} is {event.state}; only running deliveries can be cancelled")
|
||||
raise HTTPException(
|
||||
409, f"delivery {delivery_id} is {event.state}; only running deliveries can be cancelled"
|
||||
)
|
||||
|
||||
pool: WorkerPool = bag["pool"]
|
||||
fired = await pool.cancel_event(delivery_id)
|
||||
|
||||
@@ -697,9 +697,7 @@ def _run_rpc_blocking(
|
||||
stop_reason = turn.assistant_message.get("stopReason")
|
||||
if stop_reason == "error":
|
||||
error_msg = turn.assistant_message.get("errorMessage") or "model returned error"
|
||||
raise RuntimeError(
|
||||
f"omp agent error (stopReason=error): {error_msg}"
|
||||
)
|
||||
raise RuntimeError(f"omp agent error (stopReason=error): {error_msg}")
|
||||
log.info(
|
||||
"rpc_done",
|
||||
extra={
|
||||
|
||||
Reference in New Issue
Block a user