Independently infer and apply group and stage labels

## Proposal

* The bot does doesn't change a stage or group label if it's already set.
* A group label is chosen only if the highest group match from its category labels is > 50%.
* A group label is chosen only if the it matches the already set stage label (if applicable).
* A stage label is set based on the chosen or already set group label.
* The bot leaves a message that explains its inference logic.

### Triage workflow

```mermaid
graph LR
A{Stage label is present?} -- Yes --> B
B{Group label is present?} -- Yes --> X1[Nothing to do.]
B -- No --> E
E{Group is detected based on category labels<br>with a match rate > 50% among all category labels?} -- Yes --> H
E -- No --> K
H{Does detected group label matches stage label?} -- Yes --> X2[Set detected group label.]
H -- No --> K
K{Several potential groups in current<br>stage detected from category labels?} -- Yes --> X3[Manual triage required.]
K -- No --> X4[Set default group label based on stage label.]

A -- No --> C
C{Group label is present?} -- Yes --> X5[Set stage label based on group label, we're done!]
C -- No --> G
G{Group is detected based on feature/category labels?} -- Yes --> X6[Set group and stage labels.]
G -- No --> X7[Manual triage required.]
```

### Use-cases/test-cases

| Stage label | Group label | Category labels | Team label | New labels | Bot message | Note |
| ----------- | ----------- | --------------- | ---------- | ---------- | ----------- | ---- |
| ~"devops::configure" | ~"group::autodevops and kubernetes" | ~"wiki" | ~"Verify" | :no_entry_sign: | :no_entry_sign: | No new labels. |
| ~"devops::configure" | ~"group::autodevops and kubernetes" | ~"wiki" | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | No new labels. |
| ~"devops::configure" | ~"group::autodevops and kubernetes" | :no_entry_sign: | ~"Verify" | :no_entry_sign: | :no_entry_sign: | No new labels. |
| ~"devops::configure" | ~"group::autodevops and kubernetes" | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | No new labels. |
| ~"devops::create" | :no_entry_sign: | ~"wiki" (100% matching stage) | ~"Verify" | ~"group::knowledge" | `Setting ~"group::knowledge" based on ~"wiki".` | Group based on category since category matches stage |
| ~"devops::create" | :no_entry_sign: | ~"wiki" (100% matching stage) | :no_entry_sign: | ~"group::knowledge" | `Setting ~"group::knowledge" based on ~"wiki".` | Group based on category since category matches stage |
| ~"devops::create" | :no_entry_sign: | ~"wiki" ~elasticsearch (100% matching stage, 2 different groups) | ~"Verify" | ~"group::knowledge" | :no_entry_sign: | Manual triage required since the 2 potential groups are conflicting |
| ~"devops::create" | :no_entry_sign: | ~"wiki" ~elasticsearch (100% matching stage, 2 different groups) | :no_entry_sign: | ~"group::knowledge" | :no_entry_sign: | Manual triage required since the 2 potential groups are conflicting |
| ~"devops::create" | :no_entry_sign: | ~"wiki" ~"design management" ~"analytics" (66% matching stage) | ~"Verify" | ~"group::knowledge" | `Setting ~"group::knowledge" based on ~"design management" ~"wiki".` | Group based on category since category matches stage |
| ~"devops::create" | :no_entry_sign: | ~"wiki" ~"design management" ~"analytics" (66% matching stage) | :no_entry_sign: | ~"group::knowledge" | `Setting ~"group::knowledge" based on ~"design management" ~"wiki".` | Group based on category since category matches stage |
| ~"devops::create" | :no_entry_sign: | ~"wiki" ~"analytics" (50% matching stage) | ~"Verify" | ~"group::knowledge" | `Setting ~"group::knowledge" based on ~"wiki".` | Group based on category since category matches stage |
| ~"devops::create" | :no_entry_sign: | ~"wiki" ~"analytics" (50% matching stage) | :no_entry_sign: | ~"group::knowledge" | `Setting ~"group::knowledge" based on ~"wiki".` | Group based on category since category matches stage |
| ~"devops::create" | :no_entry_sign: | ~"wiki" ~"analytics" ~"epics" (33% matching stage) | ~"Verify" | ~"group::knowledge" | `Setting ~"group::knowledge" based on ~"wiki".` | Group based on category since category matches stage |
| ~"devops::create" | :no_entry_sign: | ~"wiki" ~"analytics" ~"epics" (33% matching stage) | :no_entry_sign: | ~"group::knowledge" | `Setting ~"group::knowledge" based on ~"wiki".` | Group based on category since category matches stage |
| ~"devops::package" | :no_entry_sign: | ~"design management" (none matching stage) | ~"Verify" | ~"group::package" | `Setting ~"group::package" based on ~"devops::package".` | Group based on stage since category does not match stage |
| ~"devops::package" | :no_entry_sign: | ~"design management" (none matching stage) | :no_entry_sign: | ~"group::package" | `Setting ~"group::package" based on ~"devops::package".` | Group based on stage since category does not match stage |
| ~"devops::configure" | :no_entry_sign: | :no_entry_sign: | ~"Gitaly" | ~"group::autodevops and kubernetes" | `Setting ~"group::autodevops and kubernetes" based on ~"devops::configure".` | Group based on stage |
| ~"devops::verify" | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | ~"group::ci and runner" | `Setting ~"group::ci and runner" based on ~"devops::verify".` | Default group from stage |
| :no_entry_sign: | ~"group::source code" | ~"design management" ~"markdown" | ~"Verify" | ~"devops::create" | `Setting ~"devops::create" based on ~"group::source code".` | Stage based on group |
| :no_entry_sign: | ~"group::source code" | ~"design management" ~"markdown" | :no_entry_sign: | ~"devops::create" | `Setting ~"devops::create" based on ~"group::source code".` | Stage based on group |
| :no_entry_sign: | ~"group::memory" | :no_entry_sign: | ~"Gitaly" | ~"devops::enablement" | `Setting ~"devops::enablement" based on ~"group::memory".` | Stage label based on group |
| :no_entry_sign: | ~"group::memory" | :no_entry_sign: | :no_entry_sign: | ~"devops::enablement" | `Setting ~"devops::enablement" based on ~"group::memory".` | Stage label based on group |
| :no_entry_sign: | :no_entry_sign: | ~"backend" ~"bug" | ~"Plan" | ~"devops::plan" ~"group::team planning" | `Setting ~"devops::plan" and ~"group::team planning" based on ~"Plan".` | Stage and group based on team |
| :no_entry_sign: | :no_entry_sign: | ~"bug" ~"rake tasks" | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | Manual triage required |
| :no_entry_sign: | :no_entry_sign: | ~"internationalization" (best match: 100%) | ~"Verify" | ~"devops::manage" ~"group::measure" | `Setting ~"devops::manage" and ~"group::measure" based on ~"internationalization".` | Stage and group based on category |
| :no_entry_sign: | :no_entry_sign: | ~"internationalization" (best match: 100%) | :no_entry_sign: | ~"devops::manage" ~"group::measure" | `Setting ~"devops::manage" and ~"group::measure" based on ~"internationalization".` | Stage and group based on category |
| :no_entry_sign: | :no_entry_sign: | ~"snippets" ~"elasticsearch" ~"internationalization" (best match: 66%) | ~"Verify" | ~"devops::create" ~"group::editor" | `Setting ~"devops::create" and ~"group::editor" based on ~"elasticsearch" ~"snippets".` | Stage and group based on category label |
| :no_entry_sign: | :no_entry_sign: | ~"snippets" ~"elasticsearch" ~"internationalization" (best match: 66%) | :no_entry_sign: | ~"devops::create" ~"group::editor" | `Setting ~"devops::create" and ~"group::editor" based on ~"elasticsearch" ~"snippets".` | Stage and group based on category label |
| :no_entry_sign: | :no_entry_sign: | ~"elasticsearch" ~"internationalization" (best match: 50%) | ~"Verify" | ~"devops::verify" ~"group::ci and runner" | `Setting ~"devops::verify" and ~"group::ci and runner" based on ~"Verify".` | Stage and group based on team |
| :no_entry_sign: | :no_entry_sign: | ~"elasticsearch" ~"internationalization" (best match: 50%) | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | Manual triage required |
| :no_entry_sign: | :no_entry_sign: | ~"pipeline" ~"elasticsearch" ~"internationalization" (best match: 33%) | ~"Verify" | ~"devops::verify" ~"group::ci and runner" | `Setting ~"devops::verify" and ~"group::ci and runner" based on ~"Verify"` | Stage and group based on team |
| :no_entry_sign: | :no_entry_sign: | ~"pipeline" ~"elasticsearch" ~"internationalization" (best match: 33%) | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | Manual triage required |
| :no_entry_sign: | :no_entry_sign: | ~"backstage" (no match) | ~"Verify" | ~"devops::verify" ~"group::ci and runner" | `Setting ~"devops::verify" and ~"group::ci and runner" based on ~"Verify"` | Stage and group based on team |
| :no_entry_sign: | :no_entry_sign: | ~"backstage" (no match) | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | Manual triage required |


Story points: 4.0