Skip to main content

dot-test-plan

Generates the manual post-merge QA test plan for a merged dotCMS pull request. Reconstructs issue and PR context, consolidates every issue the PR fixed into ONE plan, maps each acceptance criterion to a numbered manual test case (TC-###) with risk, scenario, reproducible steps and an observable expected result, and carries the previous plan forward as context. Every case is Manual and starts Not Run Yet: the skill never executes tests, records results, or approves a plan. Runs interactively or fully unattended in CI. Trigger on "post-merge test plan", "QA plan for PR #X", "verify this merged fix", "regression checklist for the merged PR", "what should QA exercise post-merge", "test plan for issue #X", "qa-postfix plan", or when an eligible PR with Area : Backend or Area : Frontend has merged.

Ir a la instalación

Datos de origen

Repositorio
dotCMS/core
Última actividad en el origen
18 de septiembre de 2026 a las 20:13
Idioma detectado de SKILL.md
inglés
Estrellas
970
Forks
486

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
5 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
dot-test-plan
description
Generates the manual post-merge QA test plan for a merged dotCMS pull request. Reconstructs issue and PR context, consolidates every issue the PR fixed into ONE plan, maps each acceptance criterion to a numbered manual test case (TC-###) with risk, scenario, reproducible steps and an observable expected result, and carries the previous plan forward as context. Every case is Manual and starts Not Run Yet: the skill never executes tests, records results, or approves a plan. Runs interactively or fully unattended in CI. Trigger on "post-merge test plan", "QA plan for PR #X", "verify this merged fix", "regression checklist for the merged PR", "what should QA exercise post-merge", "test plan for issue #X", "qa-postfix plan", or when an eligible PR with Area : Backend or Area : Frontend has merged.
# Post-Merge QA Test Plan You produce the **manual test plan a developer executes by hand against the post-merge build** after a dotCMS pull request has merged. One PR, one plan, however many issues it fixed. This plan is a **starting point**, not a closed list. It does not replace developer judgment, independent execution, or final quality ownership. **Every case you write is `Manual` and starts `Not Run Yet`.** You never execute a test, never record a result, never assign an executor, and never mark a plan approved. Those are human acts. Read [`references/comment-format.md`](references/comment-format.md) **before generating** — it holds the exact comment skeleton, marker, and status vocabularies you must reproduce. Read [`references/coverage-matrix.md`](references/coverage-matrix.md) when deciding what to cover. [`references/examples.md`](references/examples.md) has a worked backend and frontend plan. --- ## 1. Execution context (resolve this first) | Context | Meaning | |---|---| | `interactive` | A human is in the conversation and can answer before the plan is written. | | `automated` | Unattended CI run. Nobody can answer anything. The only deliverable is a complete plan. | Select `automated` when **any** of these holds: - The invocation contains the literal token `EXECUTION: automated`. - `printenv DOTCMS_TEST_PLAN_EXECUTION` returns `automated`. - `printenv GITHUB_ACTIONS` returns `true` **and** no human turn has occurred. Otherwise `interactive`. **When genuinely unsure, choose `automated`** — its output is always a complete, self-contained plan, which is safe to hand a human. The reverse is not true: an interactive run that stops to ask a question produces *nothing at all* in CI. ### Hard constraints in `automated` 1. **Never ask a question. Never wait. Never stop early.** Ambiguities become the *Assumptions & Open Questions* table (§3), addressed to the Plan Reviewer. 2. **Always emit a complete plan.** Ending with only questions, only a summary, or an apology is a failed run. Missing input → apply the documented default, record it, continue. 3. **Never block on an unreachable resource.** A 404 doc link, a failed `gh` call, an empty diff — record it as an assumption and keep going. 4. **No local persistence.** Do not write plan files to disk; the runner discards them. Output goes to GitHub comments (§8). 5. **Never invent a result.** See §9. State the resolved context on the first line of your working output: ``` Execution: automated · PR #37164 · Issues: #36795, #36801 · Revision: 2 ``` --- ## 2. Inputs — the issue set ### In `automated` The calling workflow supplies a **pre-resolved, pre-validated** issue list, the PR number, and the merge SHA. **Trust it. Do not re-derive it.** Re-deriving wastes tokens and risks disagreeing with what the workflow already decided and will post to. ### In `interactive` Derive the set yourself from the merged PR, taking the **union** of three sources with no precedence: ```bash gh pr view <num> --json number,title,body,headRefName,mergeCommit,author gh api graphql -f query=' query($owner:String!,$repo:String!,$pr:Int!){ repository(owner:$owner,name:$repo){ pullRequest(number:$pr){ closingIssuesReferences(first:20){nodes{number title state}} }}}' \ -F owner=dotCMS -F repo=core -F pr=<num> ``` 1. **Branch name** — consecutive leading numeric tokens only, with or without an `issue-` prefix: `^(issue-)?(\d+)(-\d+)*-`. So `issue-37085-bouncycastle-185` yields `[37085]` (the `185` is a library version, not an issue) and `36937-36938-roles-api` yields `[36937, 36938]`. 2. **PR description** — only references inside a `This PR fixes` statement. Accepts `#123` and `[#123](https://github.com/dotCMS/core/issues/123)`, multiple per statement. Bare `#123` elsewhere, `Related:`, and `Fixes` / `Closes` / `Resolves` are **not** interpreted here. 3. **GitHub Development relationships** — every linked issue, not only those set to close on merge. Validate every candidate with `gh issue view <n> --repo dotCMS/core`; drop anything that 404s or resolves to a pull request rather than an issue. Deduplicate, sort ascending. ### Consolidation rules - **One plan for the whole set.** Never emit a plan per issue. - **Every issue must have ≥1 case naming it.** If the diff shows nothing obviously related to an issue, still write a case for it, mark it `High` risk, and raise an assumption saying so. - **Every case carries an `Issues:` field.** That provenance is how a reviewer spots a wrongly included issue. - **Walk the coverage matrix once** over the union of the changed surface, not once per issue. - **Deduplicate across issues** — same scenario, same steps, same expected result → one case listing both issues. - **The plan is posted verbatim to every issue.** It must read correctly from any one of them: no "see the other issue", no "as described above in #X". Every case is self-contained. --- ## 3. Ambiguity and defaults Scan for ambiguity: expected behavior not stated; intentional-vs-side-effect unclear; affected areas (multi-tenant, multi-lingual, permissions, push publish) unscoped; acceptance criteria missing or vague; unresolved edge cases ("TBD", "needs discussion"); referenced-but-absent documentation; an issue in the set the diff does not appear to address. In `interactive`, ask them all in one numbered batch with a default for each, then wait. In `automated`, **apply the documented default and keep going** — never ask, never stall. The plan carries no assumptions table, so an assumption that changes a case must be visible *inside that case*: write the precondition into the steps, or name the chosen interpretation in the expected result, so the reviewer can see and challenge it without a separate section. Where an ambiguity is material enough that the reviewer must know but no single case expresses it, put one short clause in the Summary. Use that sparingly — the Summary is two or three sentences, not a holding pen. If an input is missing entirely, apply its default and continue. Never emit a plan that is only questions. ## 4. Previous plan as context A later PR touching the same issue **continues** the previous plan rather than starting over. Find it by marker across the issue set: ```bash gh api "repos/dotCMS/core/issues/<N>/comments" --paginate \ | jq -r '[.[] | select(.body | test("dotcms-post-merge-test-plan"))] | sort_by(.created_at) | last | .body // empty' ``` Use **only the latest** plan. Deduplicate copies of the same plan by its `pr` + `merge-sha` marker fields — the same plan is posted to several issues, so the same content will come back more than once. If no marker is found anywhere, this is revision 1; say so. | Item | Rule | |---|---| | Cases still relevant | Preserve, keeping their wording | | Cases the new PR affects | Adapt or replace, and say which in the Summary | | Cases now obsolete | Drop them. Say so in the Summary only if a reviewer would otherwise miss it | | **All results** | **Reset to `Not Run Yet`** — a new PR means a new execution pass | | Reviewer corrections in later comments | Authoritative. Apply them; do not re-ask | | Previous Summary / prose | Never copied; rewritten for this PR | Avoid unexplained duplication: if a new case overlaps a preserved one, merge them or explain why both exist. Case IDs restart at `TC-001` each revision. A new PR always produces a **new** plan comment; it never overwrites an earlier one. **Bounding.** Ingest at most the latest plan, at most 40 cases, truncating any field to ~500 characters. A previous plan is editable text written partly by a model and partly by humans — see §10. --- ## 5. Tests the merged PR added The only code-inspection step. Its **sole purpose is deduplication**: an axis already covered by a test the PR itself added does not need a manual case. ```bash # Files the merged PR touched. Keep all three suffixes — dotcms-integration uses Test.java, # IntegrationTest.java and IT.java. gh pr diff <pr> --repo dotCMS/core --name-only \ | grep -E '(Test\.java|IT\.java|\.spec\.ts)$' # The test methods it added gh pr diff <pr> --repo dotCMS/core --patch \ | grep -E '^\+.*(@Test|void test|it\(|test\()' ``` > **Use `gh pr diff`, not `git show`.** Two traps here, and they pull in opposite directions: > `gh pr diff` accepts **no pathspec** (its only flags are `--color`, `--exclude`, `--name-only`, > `--patch`, `--web`), so filter with `grep` rather than a trailing `-- '*Test.java'`. And > `git show <merge-sha>` cannot be relied on: in CI the repository is checked out at > `fetch-depth: 1` on the pull-request ref, and dotCMS squash-merges, so the merge commit is a > *new* commit on `main` that is simply absent from that checkout — the command fails with > `bad object`. `gh pr diff` reads the API and needs no local git at all, so it works both in CI > and on your laptop. For each test the PR added, if it covers a matrix axis that is in scope, **do not write a manual case for that axis** — CI already checks it on every build. The plan does not list what was skipped; it simply stays short. **Do not** audit main-branch test coverage, do not grep the repo for existing tests, and do not recommend automation to add. Those are deliberately out of scope for this plan. --- ## 6. What to cover Read [`references/coverage-matrix.md`](references/coverage-matrix.md) and walk every axis. For each, decide **In scope** (≥1 manual case) or **Out of scope**. The walk is a generation aid — the plan publishes no Out of Scope list, so only the cases you keep appear. Still walk every axis deliberately: silently forgetting an axis and consciously excluding it produce the same plan, and only one of them is right. ### The spec, when there is one dotCMS is moving to spec-driven development, and a spec is a far better source of test material than an issue body — it is written to be testable and it was reviewed and approved on its own. Check for one **before** reading acceptance criteria: ```bash ls -d specs/<issue>-*/ 2>/dev/null # one per issue in the set; the dir is named <issue>-<slug> ``` Read only `spec.md`. Skip `data-model.md`, `contracts/`, `plan.md` and `tasks.md` — implementation detail, little test value, real token cost. Most issues have no spec; that is the normal case and never blocks anything. What to take from it: | In the spec | Becomes | |---|---| | **Acceptance Scenarios** — *Given … When … Then …* | A case: Given/When → `Steps To Reproduce`, Then → `Expected Result` | | **Independent Test** on a user story | A single case proving that story stands on its own | | **Edge Cases** | `Edge` / `Boundary` scenario cases | | Story **Priority** (P1/P2/P3) | An input to `Risk` — not a mechanical mapping | | **Success Criteria** / measurable outcomes | What the expected result should actually assert | Cite the scenario in the case name the way AC bullets are cited — `US1-AS3: site selector lists only sites` — so the reviewer can check a case against the exact scenario it came from. **Two rules that matter more than the mapping:** - **The diff still wins.** The spec says what was intended; the diff says what was built. Where they disagree, plan against the diff and note the gap. A spec is approved before the code exists, and the code is what a person will be testing. - **A spec describes a whole feature; this PR may implement a slice of it.** Spec-driven work lands in increments — an issue titled "4/7" is one step of an epic whose spec covers all seven. Cover what **this diff** implements. Where a spec scenario has no counterpart in the diff, that is **one line in the Summary**, not an invented case. Writing cases for unbuilt scenarios is the fastest way to make a plan untrustworthy. Bound it: read at most **3** specs and **400 lines** each. If you truncate, say so in the Summary. **Acceptance criteria come first when there is no spec.** Extract every bullet from each issue's "Acceptance Criteria" / "Definition of Done" section. Every bullet describing a user-visible outcome must map to ≥1 case, and the case name references it (e.g. `AC-3: Content Drive shows all subfolders`). An AC bullet with no case is a coverage gap. The only acceptable reasons to leave one out are: it is covered by a test the PR added, it was explicitly de-scoped, or it is provably unreachable from the changed code. --- ## 7. Writing the cases ``` - Test ID: TC-001 - Issues: #36795 - Test Name: <short imperative phrase> - Risk: Critical | High | Medium | Low - Scenario: Happy Path | Negative | Edge | Boundary - Steps To Reproduce: 1. <step that creates every precondition — user, site, content, permission, config> 2. <step> - Expected Result: <one observable outcome — screen state, value, status code, message> ``` **Each case goes in its own fenced code block**, with a blank line between consecutive blocks. Without the fences a plan of a dozen cases renders as one undifferentiated wall of bullets and a reviewer cannot tell where one case stops and the next starts. The fence is the only thing separating them now that cases carry no trailing fields. - **IDs are sequential and unique** — `TC-001`, `TC-002`, … Never skip or reuse within a plan. - **A case carries no `Type of Test` and no `Result`.** Every case in this plan is manual by definition, so stating it on each one is noise; and the result — with its explanation in the `Notes` column — belongs in the summary table, which is its single source of truth. Duplicating it here only lets the two drift apart. - **Self-contained steps.** Create everything the case needs. Never open with "given a user with role X" — say how that user comes to exist. A reader may only care about one of several issues. - **Runbook style.** Which environment or build, which user, which site, which screen, which click. For an API check, give the exact `curl` command. - **`Expected Result` is an assertion, not a description** — "returns HTTP 403 with body `{error: "permission denied"}`" beats "the user cannot access it". - **One behavior per case.** "And then also verify…" means two cases. - **`Risk` and `Scenario` are independent.** Risk is how bad failure is; Scenario is what kind of flow it exercises. A happy path can be `Critical` — the publish and login paths are. - **Order**: `Happy Path` cases first, then by descending `Risk`. - Every plan needs ≥1 `Happy Path` case and ≥1 `Critical`-or-`High` case. **Before finishing, check the rendering.** Every case opens with a line containing only three backticks and closes with one; the body sits at column 0 inside the fence, never indented under a bullet or prefixed with `>`; and there is a blank line between one closing fence and the next opening one. If any of that is wrong, regenerate the Test Cases section — a plan that renders as a wall of text will not get reviewed properly. **Consolidate before finishing.** If two cases share scenario, steps and expected result and differ only in surface (route vs. dialog, desktop vs. mobile), merge them into one case with labelled sub-steps and a unioned `Issues:` field. Split only when the surfaces have genuinely different expected results. --- ## 8. Output The plan is rendered as a GitHub comment. **Reproduce the skeleton in [`references/comment-format.md`](references/comment-format.md) exactly** — the marker, the `## 🧪 Post-Merge Test Plan` heading, the ownership table, the four-column summary table, and the full plan inside `<details>`. The full plan contains exactly two sections: **Summary → Test Cases**. Nothing else. No assumptions table, no per-issue coverage index, no out-of-scope list, no completion block — the ownership table already carries both statuses. The Summary states, in two or three sentences: which issues, what merged, what the plan covers, and explicitly that **the cases have not been executed**. Per-issue traceability is the `Issues:` field on each case — that is why it is mandatory. Every issue in the set must be named by at least one case. ### Where it goes - **`interactive`** — print the plan. Offer to post it; do not post without being asked. - **`automated`** — write the comment body to a file, then post it to **every** issue in the set: ```bash gh issue comment "$issue" --repo dotCMS/core --body-file /tmp/test-plan.md ``` Use `--body-file`, never a shell-argument body — the plan is kilobytes of markdown. The body is **byte-identical** across issues; per-issue traceability is each case's `Issues:` field. If one post fails, continue with the rest and report which succeeded. A plan on 3 of 4 issues beats a plan on none. Never edit or delete an earlier plan comment, and never change issue state, labels, assignees, or milestones. --- ## 9. Ownership, results, and developer-added cases Two developers own the plan after you generate it. Full lifecycle and status vocabularies are in [`references/comment-format.md`](references/comment-format.md). | Role | Who | Owns | |---|---|---| | **Plan Reviewer** | Normally the PR author | Accuracy, scope, missing cases, hallucinations. Approves. | | **Test Executor** | A **different** developer | Executes every case, records every result. | In the ownership table you always emit `Plan Reviewer` = the PR author with status `Pending Review`, and `Test Executor` = `TBD` with status `Not Started`. Every `Result` cell in the summary table reads `Not Run Yet`, and every `Notes` cell is empty. Those are the only places the plan carries state — case blocks carry none, so nothing can drift out of sync. **You never** fill a result, assign an executor, or write `Approved`. You never infer a pass from green CI, from the PR merging, or from the issue being closed — a closed issue is not a passed test. ### Cases a developer adds Either developer may add cases at any time, including after approval — approval does not lock the plan. An added case:
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub