From 7bbf28ed7382789a554d5d5b7df7b84871225c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Kr=C3=A1l?= Date: Wed, 15 Jul 2026 13:26:49 +0200 Subject: [PATCH] feat(compound-engineering): assign the most relevant existing label when creating a tracker issue Closes #734 --- CHANGELOG.md | 2 ++ rules/compound-engineering/general.mdc | 14 ++++++++ skills/blueprint/SKILL.md | 1 + skills/create-issue/SKILL.md | 1 + skills/create-issues-from-text/SKILL.md | 1 + skills/product-capability/SKILL.md | 1 + .../CompoundEngineeringContentTest.php | 36 +++++++++++++++++++ tests/Installer/SkillsContentTest.php | 25 +++++++++++++ 8 files changed, 81 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 292b7caf..39c60166 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to `cursor-rules` will be documented in this file. ## [Unreleased] +- ✨ **Added** `feat(compound-engineering)`: every agent/skill that creates a tracker issue now **selects and assigns the most relevant existing label** as part of issue creation (#734). New canonical section `@rules/compound-engineering/general.mdc` *Assign the most relevant existing label when creating a tracker issue* — enumerate the tracker's existing labels (`gh label list --json name,description` on GitHub), match by type + domain, never invent a new label or fall back to a random one, and create the issue unlabelled when nothing genuinely fits; the sanctioned `EPIC` label stays the sole documented exception. Referenced from `skills/create-issue/SKILL.md`, `skills/create-issues-from-text/SKILL.md`, `skills/blueprint/SKILL.md`, and `skills/product-capability/SKILL.md`. Pinned by `tests/Installer/CompoundEngineeringContentTest.php` and `tests/Installer/SkillsContentTest.php`. + - ✨ **Added** `feat(code-review)`: every code review now walks a new **Self-Documenting Code — Comment & Doc Hygiene** lens (#733) — for every comment / PHPDoc / inline doc the diff adds or modifies, flag redundant *what*-narration that restates what the code already says and require its deletion, while never flagging the two preserved exceptions: rationale / considered alternatives / domain language (belongs in ADRs & glossaries) and navigation pointers (`@rules/…`, `@skills/…`, links to collaborating classes/methods). Documented in `@rules/code-review/general.mdc` *Self-Documenting Code — Comment & Doc Hygiene Analysis*, referenced from `skills/code-review/SKILL.md`'s Core Analysis walk (inherited by every CR wrapper), pinned by `tests/Installer/CodeReviewContentTest.php`. - 📝 **Changed**: every code review now runs **`class-refactoring` with `MODE=cr` across its complete guideline set**, and **every item the lens returns reaches the published report**. The read-only lens was already in the always-run set of all four CR skills (`code-review`, `code-review-github`, `code-review-jira`, `code-review-bugsnag`), but each invocation described it narrowly ("DRY duplication, single-responsibility breaches, oversized methods") and the diff-scoped checklist in `@rules/code-review/general.mdc` was a **closed six-bullet list** — so a reviewer reading either literally walked a fraction of what the skill defines and the rest never made it into the report. The closed lists are now explicitly the **high-frequency subset, not the boundary of the walk**: speculative interfaces, pass-through Actions, `>4` parameters → DTO, Simplicity First, business-logic-layer placement, Repository scope, query performance non-regression, intention-revealing extraction, naming, and nesting depth are walked too. A new **Routing & no-drop contract** (`@rules/code-review/general.mdc` *Refactoring & Tech Debt (DRY) Analysis — diff-scoped detail*, step 5) says where each returned item lands: an item whose underlying rule declares a severity goes to its **Critical / Moderate / Minor** bucket with the four reproducer fields (a SQL performance item to `## Database Analysis`), everything else to **Refactoring (DRY / tech debt)** / **Refactoring proposals** — and a violation already raised by the Core Analysis / Strict rule compliance walk is kept there rather than duplicated, so each violation is reported exactly once. `@skills/class-refactoring/SKILL.md` gains the matching **Completeness contract** in *Modes* and now returns the **matched guideline** with every item so the CR can route and de-duplicate it; the four CR output templates ask for that guideline in the `Why:` field on lens-produced items. The walked-guideline list lives **only** in the rule — the four CR skills reference it instead of restating it, so the next guideline added to `class-refactoring` cannot drift across five files. Pinned by `tests/Installer/CodeReviewContentTest.php` (*every CR walks the full class-refactoring guideline set and drops no returned item*). Per user request — *při jakémkoliv CR chci, aby se spustil i skill class-refactoring v readonly mode a do reportu připsal i věci, které jsou definované v tomto skillu*. diff --git a/rules/compound-engineering/general.mdc b/rules/compound-engineering/general.mdc index 6dbfa46b..567172dc 100644 --- a/rules/compound-engineering/general.mdc +++ b/rules/compound-engineering/general.mdc @@ -117,6 +117,20 @@ Any run that begins implementing a tracker issue **must claim it atomically befo The per-tracker mechanics (claim label name, helper script call, selection query negation) live in `@skills/resolve-issue/SKILL.md` and `@skills/autoresolve-oldest-github-issue/SKILL.md`. This section owns the principle; those skills own the execution. +## Assign the most relevant existing label when creating a tracker issue + +Any agent or skill that creates a tracker issue — GitHub or JIRA — must select and assign the most relevant existing label to it as part of issue creation. Creating an issue with no thought given to labelling is an incomplete creation step. + +- **Select from what already exists, never invent.** List the tracker's existing labels, match the issue's type (bug / feat / docs / chore / ...) and domain against that list, and assign the closest match. Never mint a new label ad hoc, and never fall back to a random or first-available label when nothing actually fits — an unrelated label is worse than none. +- **No relevant match → create without a label.** When none of the existing labels are a genuine match, create the issue unlabelled rather than force a bad fit. This is the expected, correct outcome, not an error to work around. +- **GitHub mechanics.** `gh label list --json name,description` to read the repository's current label set, pick the closest match by type + domain, then `gh issue create ... --label ""` — the command itself enforces existing-only, since it fails on a label name that does not already exist, so there is no accidental path to minting one this way. +- **`EPIC` is the sole sanctioned exception.** `@skills/create-issues-from-text/SKILL.md` "EPIC parent & sub-issues" deliberately creates and applies the `EPIC` label for a cross-cutting parent issue; that is a documented, intentional label, not a violation of "never invent" above. +- **Idempotent.** Assigning a label that is already present on the issue is a no-op — never fail or duplicate over it. +- **JIRA is a secondary path.** JIRA labels are free text rather than a fixed, enumerable set, and `acli` carries its own limits (see the project's `acli-jira-quirks` memory entry) — apply the same select-don't-invent principle wherever the tooling allows enumerating existing labels, but do not block issue creation solely because label enumeration is imperfect in JIRA. +- **Bugsnag does not apply.** Bugsnag errors are not tracker issues an agent authors from scratch; this rule scopes to trackers where the agent creates a new issue (GitHub, JIRA). + +This section owns the principle; the issue-creating skills reference it and own the execution — `@skills/create-issue/SKILL.md`, `@skills/create-issues-from-text/SKILL.md`, `@skills/blueprint/SKILL.md`, `@skills/product-capability/SKILL.md`. + ## What Not To Do - Do not let a durable lesson live only in a commit message or a closed PR thread — promote the reusable part into the project's memory file. diff --git a/skills/blueprint/SKILL.md b/skills/blueprint/SKILL.md index ab0f22f2..4b74dc05 100644 --- a/skills/blueprint/SKILL.md +++ b/skills/blueprint/SKILL.md @@ -11,6 +11,7 @@ metadata: ## Constraints - Apply `@rules/git/general.mdc` — branch, commit, and PR conventions below come from it. - Apply `@rules/compound-engineering/general.mdc` — the plan is durable memory the next agent reuses, not throwaway prose. +- Apply `@rules/compound-engineering/general.mdc` *Assign the most relevant existing label when creating a tracker issue* for any tracker issue this plan creates or hands off to `@skills/create-issues-from-text/SKILL.md` to create. - Apply `@rules/laravel/architecture.mdc` when the project uses `pekral/arch-app-services`, so each step lands in the correct layer. - Plan only. Do not implement, commit, or push any step. - Output Markdown only. English only. diff --git a/skills/create-issue/SKILL.md b/skills/create-issue/SKILL.md index cc8924c6..fe9c9c04 100644 --- a/skills/create-issue/SKILL.md +++ b/skills/create-issue/SKILL.md @@ -34,6 +34,7 @@ Create a well-formatted issue while preserving the original content exactly. ### 3. Create Issue - Use available CLI tool - Set title and formatted description +- Assign the most relevant existing label (per `@rules/compound-engineering/general.mdc` *Assign the most relevant existing label when creating a tracker issue*) - Assign to current user ### 4. Output diff --git a/skills/create-issues-from-text/SKILL.md b/skills/create-issues-from-text/SKILL.md index 465af652..0b01fb55 100644 --- a/skills/create-issues-from-text/SKILL.md +++ b/skills/create-issues-from-text/SKILL.md @@ -34,6 +34,7 @@ Split a complex assignment into multiple clear, structured issues. ### 3. Create Issues - One issue per step - Ensure each is independently deliverable +- Assign the most relevant existing label to each issue (per `@rules/compound-engineering/general.mdc` *Assign the most relevant existing label when creating a tracker issue*) ### 4. Output - Return list of created issues with URLs diff --git a/skills/product-capability/SKILL.md b/skills/product-capability/SKILL.md index 827f2832..a5a03c6c 100644 --- a/skills/product-capability/SKILL.md +++ b/skills/product-capability/SKILL.md @@ -9,6 +9,7 @@ metadata: ## Constraints - Apply `@rules/laravel/architecture.mdc` — actors, surfaces, states, and data model must fit the existing layers (Action → ModelService → Repository / ModelManager), not invent new abstractions. - Apply `@rules/compound-engineering/general.mdc` — the plan is a durable artifact the next agent reuses, not throwaway prose. +- Apply `@rules/compound-engineering/general.mdc` *Assign the most relevant existing label when creating a tracker issue* for any tracker issue this plan creates or hands off to `@skills/create-issues-from-text/SKILL.md` to create. - Do not invent product truth. Every unknown is an explicit open question, never a silent assumption. - Separate user-visible promises from implementation detail. Keep them in distinct sections. - Mark each constraint as **fixed policy**, **architectural preference**, or **open question** — never blur the three. diff --git a/tests/Installer/CompoundEngineeringContentTest.php b/tests/Installer/CompoundEngineeringContentTest.php index 2a397eb1..f3534da3 100644 --- a/tests/Installer/CompoundEngineeringContentTest.php +++ b/tests/Installer/CompoundEngineeringContentTest.php @@ -173,6 +173,42 @@ expect($content)->toContain('@skills/autoresolve-oldest-github-issue/SKILL.md'); }); +test('compound-engineering rule mandates assigning the most relevant existing label on issue creation (issue #734)', function (): void { + $packageDir = dirname(__DIR__, 2); + $content = (string) file_get_contents($packageDir . '/rules/compound-engineering/general.mdc'); + + // The section heading must exist. + expect($content)->toContain('## Assign the most relevant existing label when creating a tracker issue'); + expect($content)->toContain('most relevant existing label'); + + // GitHub mechanics: enumerate existing labels before assigning. + expect($content)->toContain('gh label list --json name,description'); + + // No-random-label behaviour: never invent, never fall back to a random label, unlabelled is fine. + expect($content)->toContain('never invent'); + expect($content)->toContain('never fall back to a random or first-available label'); + expect($content)->toContain('create the issue unlabelled rather than force a bad fit'); + + // The EPIC label stays the sole sanctioned exception. + expect($content)->toContain('`EPIC` is the sole sanctioned exception'); + + // Reference the issue-creating skills that inherit this rule. + expect($content)->toContain('@skills/create-issue/SKILL.md'); + expect($content)->toContain('@skills/create-issues-from-text/SKILL.md'); + expect($content)->toContain('@skills/blueprint/SKILL.md'); + expect($content)->toContain('@skills/product-capability/SKILL.md'); + + $createIssue = (string) file_get_contents($packageDir . '/skills/create-issue/SKILL.md'); + $createIssuesFromText = (string) file_get_contents($packageDir . '/skills/create-issues-from-text/SKILL.md'); + $blueprint = (string) file_get_contents($packageDir . '/skills/blueprint/SKILL.md'); + $productCapability = (string) file_get_contents($packageDir . '/skills/product-capability/SKILL.md'); + + foreach ([$createIssue, $createIssuesFromText, $blueprint, $productCapability] as $skillContent) { + expect($skillContent)->toContain('Assign the most relevant existing label'); + expect($skillContent)->toContain('@rules/compound-engineering/general.mdc'); + } +}); + test('compound-engineering rule mandates temporary-file hygiene with a hard memory-files exception (issue #694)', function (): void { $packageDir = dirname(__DIR__, 2); $content = (string) file_get_contents($packageDir . '/rules/compound-engineering/general.mdc'); diff --git a/tests/Installer/SkillsContentTest.php b/tests/Installer/SkillsContentTest.php index 5e37c030..3d496228 100644 --- a/tests/Installer/SkillsContentTest.php +++ b/tests/Installer/SkillsContentTest.php @@ -960,3 +960,28 @@ expect($content)->toContain('skills/_shared/scan-attachments.sh'); expect($content)->toContain('Read only files under `safe/`'); }); + +test('every issue-creating skill carries a byte-identical reference to the most-relevant-existing-label rule (issue #734)', function (): void { + $packageDir = dirname(__DIR__, 2); + + $labelRuleReference = '`@rules/compound-engineering/general.mdc` *Assign the most relevant existing label when creating a tracker issue*'; + + $createIssue = (string) file_get_contents($packageDir . '/skills/create-issue/SKILL.md'); + expect($createIssue)->toContain( + '- Assign the most relevant existing label (per ' . $labelRuleReference . ')', + ); + + $createIssuesFromText = (string) file_get_contents($packageDir . '/skills/create-issues-from-text/SKILL.md'); + expect($createIssuesFromText)->toContain( + '- Assign the most relevant existing label to each issue (per ' . $labelRuleReference . ')', + ); + + $referenceSentence = 'Apply ' . $labelRuleReference + . ' for any tracker issue this plan creates or hands off to `@skills/create-issues-from-text/SKILL.md` to create.'; + + $blueprint = (string) file_get_contents($packageDir . '/skills/blueprint/SKILL.md'); + expect($blueprint)->toContain($referenceSentence); + + $productCapability = (string) file_get_contents($packageDir . '/skills/product-capability/SKILL.md'); + expect($productCapability)->toContain($referenceSentence); +});