[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6ACE2M14SX17HE4JKR57DCC":3,"skill-same:01M6ACE2M14SX17HE4JKR57DCC":54},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":21,"linkOnly":22,"markdownContent":23,"markdownBody":23,"frontMatter":24,"refName":25,"commitSha":26,"skillDirectory":27,"forksCount":28,"repositoryHtmlUrl":29,"repositoryHomepageUrl":30,"ownerHtmlUrl":31,"ownerBlogUrl":32,"ownerDescription":6,"updatedAt":33,"install":34,"resources":47},"01M6ACE2M14SX17HE4JKR57DCC","groovy-reassess",null,"apache\u002Fgroovy","apache","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F47359?v=4","01M6AC83E6K4FDKK7HZ8WNMQKE","The Apache Software Foundation","01M6ACDPCXGWGWJN1KV90SHDQB",5482,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovy-reassess\u002FSKILL.md",[],[18,19,20],"groovy","groovy reassess","reassess","2026-11-10 10:12:30",false,"\u003C!--\n  Licensed to the Apache Software Foundation (ASF) under one\n  or more contributor license agreements.  See the NOTICE file\n  distributed with this work for additional information\n  regarding copyright ownership.  The ASF licenses this file\n  to you under the Apache License, Version 2.0 (the\n  \"License\"); you may not use this file except in compliance\n  with the License.  You may obtain a copy of the License at\n\n    http:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing,\n  software distributed under the License is distributed on an\n  \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n  KIND, either express or implied.  See the License for the\n  specific language governing permissions and limitations\n  under the License.\n-->\n---\nname: groovy-reassess\ndescription: Running a bulk reassessment campaign over old GROOVY JIRA issues — narrow JQL selection (Reopened pool surfaces wishlists; Open+EOL surfaces silent fixes and real bugs), per-issue reproducer extraction and execution via `groovy-reproducer`, classification (`fixed-on-master` \u002F `still-fails-same` \u002F `still-fails-different` \u002F `cannot-run-*` \u002F `intended-behaviour` \u002F `duplicate-of-resolved` \u002F `timeout`), orthogonal `nature` analysis (`bug-as-advertised` vs `feature-request-disguised-as-bug` vs `intended-and-documented`), workaround search, related-JIRA scan, split-candidate assessment, structured report and per-issue evidence package, and a strict hand-back contract — no JIRA comments, no transitions, no closures posted on behalf of the project. Use when sweeping old issues to surface candidates for closure (silently-fixed), candidates for a real fix (still-failing), feature-requests-disguised-as-bugs, and documentation gaps.\nlicense: Apache-2.0\ncompatibility: claude, codex, copilot, cursor, gemini, aider\nmetadata:\n  audience: contributors to apache\u002Fgroovy\n  scope: bulk-reassessment-of-old-jira-issues\n---\n\n# Groovy reassess\n\nUse this skill when the task is a **campaign** over old GROOVY JIRA\nissues: pick a bounded candidate set, run each reporter's reproducer\nagainst the current `master`, classify the outcome, and produce a\nreport a committer can scan and act on. The campaign is the safest\nnon-trivial automation against ASF Groovy — read-only against JIRA,\nside-effect-free against the project, advisory output. But \"safe\"\ndoes not mean \"no rules\": the hand-back contract below is firm.\n\nThis skill is the **campaign layer**. Per-issue mechanics live\nelsewhere:\n\n- [`groovy-jira`](..\u002Fgroovy-jira\u002FSKILL.md) — the JQL recipes that\n  select the candidate set; the field-ownership rules; the\n  \"comment, don't transition\" rule that applies at scale here.\n- [`groovy-triage`](..\u002Fgroovy-triage\u002FSKILL.md) — AI guardrails\n  over the single-issue triage methodology in\n  [`CONTRIBUTING.md`](..\u002F..\u002F..\u002FCONTRIBUTING.md#triaging-issues-and-pull-requests);\n  pieces of that methodology apply to each candidate.\n- [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md) — the\n  load-bearing per-issue piece: locate the reproducer, classify the\n  shape, adapt, run, record evidence.\n- [`groovy-fix-workflow`](..\u002Fgroovy-fix-workflow\u002FSKILL.md) — where\n  the `still-fails-*` tail goes once the campaign is done; the\n  campaign produces ready-made reproducers for the fix workflow.\n\n## When to use this skill\n\n**Use it for:**\n\n- A bounded sweep of old GROOVY issues (e.g. 10–50 at a time) to\n  identify silent fixes, still-failing bugs, intended-behaviour\n  misclassifications, or duplicates.\n- Producing a structured report a committer can review in one\n  sitting and decide what to act on.\n- Generating ready-made reproducers and evidence packages that feed\n  [`groovy-fix-workflow`](..\u002Fgroovy-fix-workflow\u002FSKILL.md) for the\n  still-failing tail.\n\n**Don't use it for:**\n\n- Single-issue triage — that's [`groovy-triage`](..\u002Fgroovy-triage\u002FSKILL.md)\n  on its own.\n- Mass JIRA mutation (comments, transitions, closures, label\n  edits). The campaign is **read-only against JIRA**.\n- \"Reassessing all 800 open issues\" in one go — the campaign needs\n  bounding. Many small passes beat one giant one.\n- Anything that needs project-side authorisation (a bot identity,\n  a dev@ mandate). The campaign is contributor work; the report is\n  the output a contributor brings to dev@ or to a committer, not a\n  project-mandated activity.\n\n## Read first\n\n- [`GOVERNANCE.md`](..\u002F..\u002F..\u002FGOVERNANCE.md) — JIRA's role as the\n  canonical record; the campaign produces recommendations, not\n  decisions.\n- [`AGENTS.md`](..\u002F..\u002F..\u002FAGENTS.md) — the no-fabrication,\n  no-comment-on-behalf-of-the-project posture that scales up here.\n- [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md) — fully.\n  The campaign is largely a loop over this skill.\n\n## Top failure modes\n\nThese are the recurring mistakes at the campaign level:\n\n1. **Bulk-posting findings to JIRA.** Even when 30 of 30 findings\n   say `fixed-on-master` with strong evidence, the campaign does\n   *not* post 30 JIRA comments, transition 30 issues, or close\n   anything. The output is a report; a committer decides whether\n   and how to publish it. See *Transitioning workflow state* in\n   [`groovy-jira`](..\u002Fgroovy-jira\u002FSKILL.md) for the underlying rule.\n2. **Optimistic classification on weak evidence.** A reproducer\n   that didn't compile is `cannot-run-extraction`, not\n   `fixed-on-master`. The taxonomy has cells for these for a\n   reason; reach for the precise one. \"Looks fine to me\" is not a\n   classification.\n3. **Confusing \"passes on this JDK\" with \"fixed.\"** The classic\n   over-claim from *Over-claiming \"fixed\" from a single-environment\n   pass* in [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md),\n   multiplied by the campaign size. Where the verdict matters and\n   a JDK retry is feasible, do it before landing `fixed-on-master`.\n4. **Unbounded scope.** Trying to sweep 200 issues in one session\n   blows context, produces low-quality bulk output, and means a\n   crash at issue 150 is a 150-issue loss. Bound the candidate set\n   *before* the loop starts: a JQL with a `LIMIT`-equivalent, an\n   age bucket, a component slice.\n5. **No resumability.** A 50-issue run that crashes at issue 30\n   must be resumable from issue 31. Per-issue evidence files on disk\n   (per [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md)'s\n   evidence package) are the resumption point — in-memory campaign\n   state is not.\n6. **Burying the headlines.** \"30 fixed-on-master, 5 still-fail,\n   15 cannot-run.\" The 5 still-fails are usually the most\n   important rows — they are tickets where work might actually be\n   done. Surface them at the top of the report, not buried by the\n   `fixed-on-master` majority.\n7. **Recommending workflow transitions in the report.** \"Close\n   GROOVY-1234 as Cannot Reproduce\" frames the agent as the\n   decider. Phrase as recommendation: \"`fixed-on-master`; a\n   committer may want to consider closing as Cannot Reproduce\n   after a second pair of eyes.\" See\n   [`groovy-jira`](..\u002Fgroovy-jira\u002FSKILL.md) on\n   comment-not-transition.\n8. **Fabricating evidence for `cannot-run-*`.** \"Probably passes on\n   master.\" That's a guess in a verdict slot. If it can't be run,\n   the verdict is the cannot-run category — no further claim.\n9. **Drifting from the original report.** Heavy adaptation of a\n   reproducer can exercise different code paths from the\n   reporter's original. The verdict has to be against the\n   *original* behaviour; if the adaptation is so heavy that it's\n   really a different test, the issue is `cannot-run-extraction`,\n   not `passes`.\n10. **Skipping the read-first pass on the issue.** A surprising\n    fraction of old issues have a closing comment like \"fixed in\n    4.0.x, issue left open by mistake\" or \"won't fix, see\n    GROOVY-XXXX\". Skim the comments before reproducing — saves the\n    reproduction budget and produces better classifications.\n11. **Hammering JIRA's REST API.** ASF JIRA is anonymous-readable\n    but shared. Cache aggressively (per-issue evidence retains the\n    description and comments), throttle requests, and never run\n    the campaign in a tight loop that re-fetches the same issue.\n12. **Treating duplicates as findings.** If the reassessment\n    discovers issue A is a duplicate of resolved issue B, the\n    classification is `duplicate-of-resolved` with a citation —\n    not \"closing A.\" Same hand-back posture as everything else.\n13. **Posting a wall-of-text to dev@.** A 50-row table without a\n    summary is noise. The report opens with a short stanza — N\n    issues swept, M classified each way, K headline findings — and\n    *then* the table. The committer's first 30 seconds with the\n    report should produce a decision about whether to read on.\n14. **Reading verdict skew as a result instead of a defect.**\n    When a sweep comes back almost all `cannot-run-extraction`\n    (or all one classification), the likely cause is not \"the\n    pool is genuinely all unreproducible\" but that extraction or\n    the run harness regressed mid-campaign. Before reporting,\n    inspect a sample of `original.\u003Cext>` \u002F `run.log` from the\n    skewed issues; a systematic extraction break invalidates the\n    batch.\n15. **Widening the pool when the query returns nothing.** An\n    empty or surprisingly tiny candidate set after applying the\n    JQL is a signal the filter is wrong — surface it and stop,\n    do **not** silently relax the query to \"get some results\".\n    A campaign run against a pool the user didn't choose is\n    worse than no campaign.\n\n## Selecting the candidate set\n\nSelection drives the campaign's quality. Pick a *narrow*, *bounded*\nslice; do not boil the ocean.\n\n**Pool choice matters.** The pool-selection heuristics — which\nstatus \u002F affected-version slices over-represent which natures —\nlive in [`CONTRIBUTING.md`'s \"Pool-selection heuristics for\nre-triage sweeps\"](..\u002F..\u002F..\u002FCONTRIBUTING.md#pool-selection-heuristics-for-re-triage-sweeps).\nChoose the pool deliberately based on what the campaign is hunting\nfor.\n\nJQL building blocks come from\n[`groovy-jira`](..\u002Fgroovy-jira\u002FSKILL.md). Useful slices for\nreassessment:\n\n- **Reopened (wishlist hunting):** `project = GROOVY AND status =\n  Reopened ORDER BY updated ASC`. Small, well-bounded pool.\n- **Open + EOL bugs (silent-fix hunting):** `project = GROOVY AND\n  status = Open AND issuetype = Bug AND affectedVersion in\n  (\"1.5.6\", \"1.6.0\", \"1.6.5\", \"1.7.0\", \"1.8.0\") ORDER BY created\n  ASC`. The version list should target releases before a known\n  major refactor of the relevant subsystem.\n- **Age bucket × open:** `project = GROOVY AND statusCategory != Done\n  AND created \u003C \"2020\u002F01\u002F01\" AND created >= \"2018\u002F01\u002F01\"\n  ORDER BY created ASC`. Buckets of two years are scannable; ten\n  years is not.\n- **Stale within a component:** `project = GROOVY AND statusCategory\n  != Done AND component = \"\u003CX>\" AND updated \u003C -730d`. Pairs well\n  with an area you know — your fix-side strength shapes the\n  candidate set.\n- **No component (triage-then-reassess):** `project = GROOVY AND\n  statusCategory != Done AND component is EMPTY`. The reassessment\n  can also produce a `Component\u002Fs` suggestion (see\n  [`groovy-jira`](..\u002Fgroovy-jira\u002FSKILL.md)).\n\nCap the per-session set. A practical first pilot is 5–10 issues\nspanning *different reproducer shapes* (one runnable script, one\nattachment, one prose-only-but-precise, one `@Grab`,\none comment-with-snippet) so the pipeline meets each shape early.\nPilots beat the first hundred issues you'd naturally pick.\n\n## Procedure\n\nFor each campaign session:\n\n1. **Pick the candidate set, then echo it back for\n   confirmation.** JQL from\n   [`groovy-jira`](..\u002Fgroovy-jira\u002FSKILL.md), capped (see above).\n   Before the loop starts, print the resolved query and the\n   candidate list (key + summary, with the count) and get an\n   explicit go\u002Fcap-down\u002Fcancel from the user — a fuzzy filter\n   that pulled in issues the user didn't mean to sweep is far\n   cheaper to catch here than after burning reproduction budget\n   on them. Starting the campaign is not standing authorisation\n   for whatever set the JQL happened to return (see\n   [`AGENTS.md`](..\u002F..\u002F..\u002FAGENTS.md#untrusted-input-and-confirmation)).\n   Persist the confirmed candidate list to disk before the loop\n   starts so a crash leaves a recoverable plan.\n2. **Set up the scratch corpus.** A directory hierarchy under\n   `~\u002Fwork\u002Fgroovy-reassess\u002F\u003Ccampaign-id>\u002F`, one subdirectory per\n   issue. Per [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md),\n   each subdirectory holds `description.md`, `reproducer.\u003Cext>`,\n   `original.\u003Cext>`, `run.log`, `verdict.json`. This is *not* in\n   the Groovy checkout.\n3. **For each issue, in order:**\n\n   **a. Resumability check (rev-aware).** Read any existing\n   `verdict.json`: if it is well-formed *and* its recorded `rev`\n   matches the current branch revision, skip — done. If it\n   exists but was produced against a *different* `rev`, the\n   verdict is stale; ask whether to refresh it or reuse it for\n   this report, and record the choice. If only partial evidence\n   files exist (no `verdict.json`), resume that issue rather than\n   restarting the campaign. The per-issue evidence files on disk\n   are the resumption point — in-memory campaign state is not.\n\n   **b. Triage the issue per\n   [`CONTRIBUTING.md`'s \"Triaging a JIRA issue\"](..\u002F..\u002F..\u002FCONTRIBUTING.md#triaging-a-jira-issue).**\n   That procedure is the canonical methodology — read the thread\n   (with historical baselines), search for duplicates and\n   same-family related work, attempt reproduction (including\n   per-reproducer execution when the thread contains multiple\n   distinct reproducers), locate the code, check JIRA fields,\n   search for documented workarounds, and assess split\n   candidacy. The bulk of the per-issue work is in that section.\n\n   **AI-specific recording** during this step — these are the\n   campaign's value-adds on top of the manual methodology:\n   - Historical baselines from comments → `verdict.json.cases[].history`\n   - Related-JIRA citations from the JQL scan → `verdict.json.notes`\n   - Per-reproducer outcomes (when multiple reproducers exist) →\n     `verdict.json.cases` (multi-case array)\n   - Workaround-search outcome and the close-path it implies →\n     `verdict.json.notes`\n   - Split-candidacy recommendation → `verdict.json.notes`\n\n   **c. Hand off to\n   [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md)** for the\n   reproducer extraction, adaptation, running, and evidence\n   capture. This includes the optional cross-family probe when\n   the operation under test spans multiple types or operator\n   variants — see\n   [ARCHITECTURE.md \"Operator families\"](..\u002F..\u002F..\u002FARCHITECTURE.md#operator-families)\n   for the family taxonomies. **Its step-6 safety gate runs per\n   reproducer and is not waived by the campaign's set-level\n   authorisation** (echo-and-confirm in step 1 approved *which\n   issues* to sweep, not arbitrary code execution). In an\n   unattended sweep a reproducer the pre-screen flags is **not\n   run**: record `needs-safety-review`, add it to the\n   end-of-run batch for a human to review the code and decide,\n   and continue the sweep — one flagged reproducer does not\n   block the campaign. Comment\u002Fattachment-sourced code on an old\n   issue has typically never been triaged; it gets the same gate\n   as fresh code, not a pass for being \"old\".\n\n   **d. Apply nature analysis.** Per\n   [`CONTRIBUTING.md`'s \"Nature analysis\"](..\u002F..\u002F..\u002FCONTRIBUTING.md#nature-analysis-bug-as-advertised-vs-wouldnt-it-be-nice),\n   determine whether the issue is `bug-as-advertised`,\n   `feature-request-disguised-as-bug`, `intended-and-documented`,\n   etc. Record in `verdict.json.nature`. This is orthogonal to\n   `classification` (a still-reproducing wishlist and a\n   still-reproducing bug recommend different actions).\n\n   **e. Compose the final `verdict.json`.** Classification +\n   nature + cases (if multi-case) + cross-type-probe \u002F\n   operator-variants-probe (if run) + notes with the close-path\n   recommendation. See\n   [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md)'s\n   Evidence package section for the schema.\n\n   **f. Reset the working tree** before the next issue.\n4. **After the loop, build the report** (see below). Do *not*\n   post anything.\n5. **Hand back** to the human — branch (if any local Groovy\n   commits, e.g. adapted `@Test` files kept for follow-up),\n   scratch corpus path, report path.\n\n## Nature analysis (AI-recording layer)\n\nThe nature taxonomy and the \"bug-as-advertised vs wouldn't-it-be-\nnice\" question live in\n[`CONTRIBUTING.md`'s \"Nature analysis\" section](..\u002F..\u002F..\u002FCONTRIBUTING.md#nature-analysis-bug-as-advertised-vs-wouldnt-it-be-nice).\nThat section is the canonical source; apply it per-issue during\nprocedure step 3d above.\n\nThis skill's contribution is to **record** the result in\n`verdict.json.nature` so the campaign-level aggregator can roll\nup nature classifications across the issue set (e.g. \"of N\nissues reassessed, M classified as\n`feature-request-disguised-as-bug` — those rows recommend\nre-typing rather than fixing\"). The allowed values:\n\n- `bug-as-advertised`\n- `bug-as-advertised-partial-fix` (paired with a split\n  recommendation in `notes`)\n- `feature-request` (correctly typed as Improvement; no re-type\n  needed)\n- `feature-request-disguised-as-bug` (Bug → Improvement re-type\n  recommendation)\n- `intended-and-documented`\n\nSee the verdict-template `_field_help` for the schema. Apply\nexactly one value per verdict — `nature` is required.\n\n## Classification taxonomy\n\nThe campaign uses the per-issue classifications produced by\n[`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md), and adds two\nof its own that don't fall out of a single run.\n\nPer-issue (from `groovy-reproducer`):\n\n| Classification | Meaning | Recommendation a committer might consider |\n|---|---|---|\n| `fixed-on-master` | Reproducer passes on current `master` with the originally-affected JDK reachable, and the original failure pattern is gone. | Close as Cannot Reproduce after a second pair of eyes. |\n| `still-fails-same` | Reproducer fails on `master` with the same failure pattern as the report. | Hand off to [`groovy-fix-workflow`](..\u002Fgroovy-fix-workflow\u002FSKILL.md). |\n| `still-fails-different` | Reproducer fails on `master` but with a different signature (different exception, different message). | Committer judgement — could be a regression-of-a-regression, a related bug, or environmental. |\n| `cannot-run-extraction` | No usable reproducer (prose-only, missing attachment, fragment too incomplete to adapt without speculation). | Needs-info: ask the reporter, or close as Incomplete if old enough and reporter is unreachable (committer call). |\n| `cannot-run-environment` | Reproducer needs a JDK\u002FOS\u002Fdep we don't have locally. | Note the gap; may be a candidate for a richer environment matrix. |\n| `cannot-run-dependency` | `@Grab` resolution failed (dep gone from configured repos). | Often correlates with very old issues; needs-info or close as Incomplete. |\n| `timeout` | Hung past the configured bound. | Human review — might be the bug, might be a hung adaptation. |\n| `needs-separate-workspace` | Reproducer is a multi-file project. | Spin up an isolated workspace if it matters; otherwise leave open. |\n| `needs-safety-review` | The step-6 safety gate in [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md) flagged the code (process spawn, network, secret read, `@Grab`, …) and no human was present to authorise the run. | **Not run.** Batch it for a human to review the code and decide run\u002Fsandbox\u002Fskip — never auto-run on the pre-screen alone. Comment\u002Fattachment-sourced code on an old issue is *not* covered by the issue's historical triage. |\n\nCampaign-level adds:\n\n| Classification | Meaning | Recommendation |\n|---|---|---|\n| `intended-behaviour` | Reproduction runs and behaves as the spec\u002Fdocs describe; the \"bug\" is by design. | Doc clarification or close as Not A Bug. |\n| `duplicate-of-resolved` | Reassessment discovered the issue duplicates a resolved one (citation required). | Close as Duplicate, citing the linked issue. |\n\nEach per-issue verdict in the report carries the classification\nplus the evidence-package path. Each campaign-level classification\ncarries the citation that justifies it.\n\n## Report shape\n\nA reassessment report has three layers:\n\n1. **One-paragraph summary at the top.** N issues swept, breakdown\n   per classification, K headline findings worth a committer's\n   attention (typically the `still-fails-*` rows and any\n   `duplicate-of-resolved` with strong citation).\n2. **Per-classification sections, headlines first.** Order:\n   `still-fails-same`, `still-fails-different`, `duplicate-of-resolved`,\n   `fixed-on-master`, `intended-behaviour`, then the cannot-runs and\n   `needs-separate-workspace`. Within each section, a table:\n   JIRA-ID, one-line summary, evidence-package path, recommended\n   next action.\n3. **Per-issue evidence packages** (filesystem; not inlined in the\n   report). The report links into them.\n4. **Methodology \u002F limitations footer.** A short closing block:\n   the pool-selection reasoning (which JQL, why this pool), the\n   bound applied, how many sessions the campaign spanned and\n   whether any verdicts were reused stale across a rev change\n   (see *Resumability check*), and the environment limitations\n   that qualify the findings (JDK, OS, anything `cannot-run-*`\n   skewed). This makes a multi-session campaign auditable and\n   stops a reader over-reading a bounded pilot as exhaustive.\n\nSuggested filename for the report: `report.md` inside the campaign\nscratch directory. A committer reading it should be able to decide\n\"yes I'll act on these N items\" in a single scan.\n\n## Campaign dashboard\n\nFor a sweep of more than a handful of issues, a rendered HTML\ndashboard over the `verdict.json` corpus is the fastest way for a\ncommitter to triage where to spend attention. **Do not emit this\nHTML token-by-token.** A vetted generator ships in this skill's\ndirectory and is the required path, per the\n[Helper mechanisms and token economy](..\u002F..\u002F..\u002FAGENTS.md#helper-mechanisms-and-token-economy)\npolicy — rendering a multi-KB document on every campaign is the\nsingle most token-expensive step here, the inputs and layout\nchange rarely, and maintainer token budgets are a shared resource:\n\n```\ngroovy .agents\u002Fskills\u002Fgroovy-reassess\u002Fdashboard.groovy \\\n       \u003Ccampaign-dir> [out.html]\n```\n\nIt reads `\u003Ccampaign-dir>\u002F*\u002Fverdict.json` (the schema in\n[`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md)) and renders\nthe methodology below. This section *is* the documented manual\nequivalent — render by hand only if the generator is unavailable.\n\n**What the dashboard presents:**\n\n- **Hero cards** — candidates, still-failing, fixed-on-master,\n  partial-fix, intended-behaviour, unrun.\n- **Classification × nature cross-tab** — the load-bearing view.\n  A still-failing *bug-as-advertised* and a still-failing\n  *feature-request-disguised-as-bug* are both \"still-fails-same\"\n  but recommend opposite actions; the flat per-classification\n  tables in *Report shape* hide that, the cross-tab surfaces it.\n- **Health rating** — `\u003C 5%` still-failing → *Healthy*, `\u003C 20%`\n  → *Needs attention*, else *Action needed*. These thresholds\n  are **proposed defaults pending team review**; tune them in\n  the generator's documented block, not ad hoc per run.\n- **Action \u002F hygiene \u002F closure panels** — direct fixes\n  (still-failing × bug-as-advertised), partial fixes, tracker\n  hygiene (re-type candidates), feature requests, Not-A-Bug\n  closure candidates, and new-issue candidates surfaced by\n  cross-family probes.\n- **Staleness** — for still-failing issues, the span derived\n  *only* from recorded `cases[].history` baselines. No\n  fabricated filing dates.\n\n**Aggregation guardrails (the generator enforces these; keep them\nif you ever render by hand):**\n\n- A `verdict.json` that fails to parse, **or whose\n  `schema_version` is not the one the generator understands**, is\n  **surfaced as a visible error in the output, never silently\n  dropped, coerced, or counted as zero** — a skewed total from\n  swallowed failures (or a field-sniffed mis-read of an\n  unrecognised schema generation) is worse than a loud one. The\n  version contract lives in\n  [`groovy-reproducer`](..\u002Fgroovy-reproducer\u002FSKILL.md).\n- **Exactly one campaign directory** per dashboard. Never walk\n  upward or merge sibling campaigns into one view without an\n  explicit instruction — cross-campaign aggregation invites the\n  *Reading verdict skew as a result instead of a defect* trap.\n- Counts are derived from the corpus, never asserted from memory.\n\n## Hand-back to a human\n\nThe campaign produces:\n\n- The scratch corpus path (with per-issue evidence packages).\n- The report path.\n- Any local Groovy-checkout changes worth keeping (e.g. adapted\n  `@Test` files that the committer might want as a starting point\n  for a real regression test).\n- A short verbal summary: \"Swept N issues. M `fixed-on-master`, K\n  `still-fails-same`. Headlines: GROOVY-A, GROOVY-B, ….\"\n- **Split recommendations** for any issue where the reassessment\n  surfaced multiple cases with distinct fates (some silently fixed,\n  some still failing) or distinct user-visible symptoms.\n  Format: \"close GROOVY-X with per-case summary; open new JIRA\n  for GROOVY-X-residual-A (the remaining case).\"\n- **New-JIRA candidates** surfaced by cross-family probes — when\n  probing a sibling type or operator variant uncovered a bug the\n  original report didn't contain, flag it as a candidate for its\n  own JIRA. The verdict notes should sketch the 4-line reproducer.\n- **Documentation candidates** — issues where the close path is\n  \"Not A Bug + add docs\" (workaround exists but undocumented; spec\n  gap; behaviour-by-design but surprising). The report should\n  list each docs deliverable with a one-line scope.\n\nThe campaign does **not**:\n\n- Post comments to JIRA.\n- Transition any issue.\n- Open any PR (draft or otherwise).\n- Email dev@ or anyone else on behalf of the project.\n- Self-assign or otherwise touch JIRA fields.\n\nWith explicit instruction (per [`groovy-jira`](..\u002Fgroovy-jira\u002FSKILL.md)\nand [`groovy-fix-workflow`](..\u002Fgroovy-fix-workflow\u002FSKILL.md)), the\noutput may then be used as:\n\n- The basis for a single dev@ post summarising the campaign\n  (\"Reassessed N issues from 2018–2019; here is the report and\n  evidence — feedback welcome before any committer acts on the\n  recommendations\").\n- Per-issue JIRA comment drafts the committer reviews and posts.\n- A starting point for [`groovy-fix-workflow`](..\u002Fgroovy-fix-workflow\u002FSKILL.md)\n  on each `still-fails-same` row.\n\nThe single dev@-thread shape is the recommended publication path —\nN separate JIRA comments at once is noisy and out of step with how\nthe project communicates.\n\n## Validation checklist\n\nBefore declaring a campaign session complete:\n\n- [ ] **Pool was chosen deliberately** (Reopened vs Open+EOL) and\n      the choice matches the campaign's goal (spec-debate review vs\n      silent-fix hunting).\n- [ ] Candidate set was bounded *before* the loop started; the\n      bound is recorded in the report.\n- [ ] Per-issue evidence package on disk for every candidate, with\n      `verdict.json` and a non-empty `description.md`.\n- [ ] Every `classification` used is one of the taxonomy entries;\n      every `nature` used is one of the nature values; no free-form\n      labels.\n- [ ] **`nature` populated** on every verdict — orthogonal to\n      classification.\n- [ ] **Historical baselines from the comment thread** are recorded\n      in `cases[].history` where applicable, so the headline\n      finding can be \"unchanged since baseline X\" rather than just\n      \"current state is Y.\"\n- [ ] **Workaround search done** for every still-fails-* verdict\n      (`src\u002Fspec\u002Fdoc\u002F` + Groovydoc + JIRA comments). The\n      recommendation distinguishes \"close as Not A Bug\" from\n      \"close + add docs.\"\n- [ ] **Related-JIRA scan** done for every verdict; same-family\n      issues cited in notes.\n- [ ] `fixed-on-master` rows include the rev and JDK in the\n      evidence; the verdict isn't over-claimed.\n- [ ] `still-fails-same` rows are surfaced at the top of the\n      report, not buried.\n- [ ] `cannot-run-*` rows have a concrete reason in the evidence\n      (which dep failed, what was missing) — not \"could not run.\"\n- [ ] **Split candidates flagged** for any issue with multi-case\n      mixed fates or distinct user-visible symptoms.\n- [ ] **Cross-family probe results recorded** in\n      `cross_type_probe` or `operator_variants_probe` when the\n      probe was run; any sibling-type bugs are flagged as new-JIRA\n      candidates.\n- [ ] If a dashboard was produced: it was rendered with\n      `dashboard.groovy` (not emitted token-by-token), over a\n      single campaign directory, and any unparseable `verdict.json`\n      is surfaced in the output rather than silently dropped.\n- [ ] No JIRA mutation occurred. No PR was opened. No dev@ post\n      was sent.\n- [ ] The report opens with a summary stanza a committer can scan\n      in 30 seconds.\n- [ ] Working tree was clean at the end of the session.\n- [ ] Hand-back artefact lists the scratch corpus path, the report\n      path, any local commits worth keeping, any split or\n      documentation candidates surfaced, and the recommended\n      publication path (typically a single dev@ thread).\n\n## References\n\n- [`GOVERNANCE.md`](..\u002F..\u002F..\u002FGOVERNANCE.md) — JIRA as canonical\n  record; dev@ as the project's primary discussion channel.\n- [`AGENTS.md`](..\u002F..\u002F..\u002FAGENTS.md) — the licensing, provenance,\n  and \"what *not* to do\" rules the campaign inherits.\n- `.agents\u002Fskills\u002Fgroovy-jira\u002FSKILL.md` — JQL recipes for selection;\n  the comment-not-transition rule applied campaign-wide.\n- `.agents\u002Fskills\u002Fgroovy-triage\u002FSKILL.md` — AI guardrails over the\n  single-issue triage methodology in\n  [`CONTRIBUTING.md`](..\u002F..\u002F..\u002FCONTRIBUTING.md#triaging-issues-and-pull-requests);\n  pieces of that methodology apply to each candidate.\n- `.agents\u002Fskills\u002Fgroovy-reproducer\u002FSKILL.md` — the per-issue load\n  -bearing skill; the campaign is a loop over it.\n- `.agents\u002Fskills\u002Fgroovy-fix-workflow\u002FSKILL.md` — AI guardrails\n  over the fix workflow in\n  [`CONTRIBUTING.md`](..\u002F..\u002F..\u002FCONTRIBUTING.md#fix-workflow);\n  destination for the `still-fails-same` tail.\n- `.agents\u002Fskills\u002Fgroovy-tests\u002FSKILL.md` — when an adapted `@Test`\n  is kept as the starting point for a real regression test.\n",{},"master","4ab67ec26055b11dc3c98a4a350020b99fecd8bd",".agents\u002Fskills\u002Fgroovy-reassess",1914,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy","https:\u002F\u002Fgroovy-lang.org","https:\u002F\u002Fgithub.com\u002Fapache","https:\u002F\u002Fwww.apache.org\u002F","2026-11-15 01:42:11",{"source":7,"skillName":5,"commands":35,"prompt":45,"zipAvailable":46},[36,39,42],{"tool":37,"command":38},"npx","npx skills add apache\u002Fgroovy --skill groovy-reassess",{"tool":40,"command":41},"bunx","bunx skills add apache\u002Fgroovy --skill groovy-reassess",{"tool":43,"command":44},"pnpm","pnpm dlx skills add apache\u002Fgroovy --skill groovy-reassess","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovy-reassess\u002FSKILL.md",true,[48],{"path":49,"resourceType":50,"resourceTypeDesc":51,"sizeBytes":52,"sourceHtmlUrl":53},"dashboard.groovy","OTHER","其他",13515,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovy-reassess\u002Fdashboard.groovy",[55,61,69,77,84,92],{"skillNo":56,"name":57,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":58,"categories":59,"keywords":60,"publishedAt":21},"01M6ACEDS969D6ZXE8SGZSPR3B","groovysh","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovysh\u002FSKILL.md",[],[57],{"skillNo":62,"name":63,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":66,"publishedAt":21},"01M6ACEBR5V55F0X8YS9EJGRW2","groovy-triage","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovy-triage\u002FSKILL.md",[],[67,18,68],"triage","groovy triage",{"skillNo":70,"name":71,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":74,"publishedAt":21},"01M6ACE9Y7AFJV4ZERD6WPF8QS","groovy-tests","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovy-tests\u002FSKILL.md",[],[18,75,76],"groovy tests","tests",{"skillNo":78,"name":79,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":80,"categories":81,"keywords":82,"publishedAt":21},"01M6ACE7HZNK305VJGV3WN4P5N","groovy-skills","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovy-skills\u002FSKILL.md",[],[18,83],"groovy skills",{"skillNo":85,"name":86,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":89,"publishedAt":21},"01M6ACE5BJCXWJPMZD0M12H3D5","groovy-reproducer","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovy-reproducer\u002FSKILL.md",[],[18,90,91],"groovy reproducer","reproducer",{"skillNo":93,"name":94,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":97,"publishedAt":21},"01M6ACDZWW85YHH8R4CMK1P5G3","groovy-jira","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgroovy\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Fgroovy-jira\u002FSKILL.md",[],[98,18,99],"jira","groovy jira"]