Files
t3bootstrap-upgrade-skills/redmine-issue-workflow/SKILL.md
T
melnicenkoandClaude Opus 5 c1e3a059d3 Verallgemeinerte Erkenntnisse aus einem Ticketdurchlauf einarbeiten
Design-Paritaet 1.5 -> 1.6:
- Site-Set-page.tsconfig erreicht keine Datensaetze in Ordnern neben dem
  Site-Root; solche Konfiguration gehoert in die globale page.tsconfig
  der Extension. Symptom ist, dass eine TCEFORM-Aenderung genau fuer
  einen Datensatztyp wirkungslos bleibt.
- Angebotene und gerenderte Crop-Variante koennen auseinanderlaufen,
  dann beschneidet die Redaktion ins Leere.
- Feste Bildhoehe laesst das Seitenverhaeltnis ueber die Breakpoints
  wandern, sodass kein Zuschnitt mehr ueberall passt; aspect-ratio statt
  fester Hoehe.
- Umbrechende Labels brauchen eine reservierte Zeilenhoehe, sonst
  springt der Rhythmus des Rasters; dabei die Untermarge des Absatzes
  neutralisieren und die schmalste Geraetebreite pruefen, nicht nur 360.
- :has() laesst sich nicht in :has() verschachteln, die Regel wird
  komplett verworfen - ohne jede Fehlermeldung.
- Neuer Abschnitt zu Meldungen, die Rasterartefakte statt Fehler sind:
  gegen die Referenzseite pruefen, ueber mehrere Zoomstufen UND
  Subpixel-Versaetze messen, das Ganze per Canvas in einem Durchgang -
  und rechtzeitig sagen, dass Geometrie es nicht loest.

v11->v14 1.1 -> 1.2:
- Ein "verlorenes" Feld ist oft ein umgebautes Feld; zwei Spalten werden
  gern zu einem Feld plus Schalter. Erst das neue Modell pruefen, bevor
  man eine fremde Extension patcht - dann ist es eine Datenmigration.
  Dazu die abgesicherte UPDATE-Bedingung und der Hinweis, dass Fluid
  keinen !-Operator kennt.
- Vor "Regression" gegen die Referenzseite pruefen: zwei Tickets zum
  selben fehlenden Element koennen Verlust und Neuwunsch sein.

Cutover 1.1 -> 1.2:
- Eingebettete Fremddienste sperren sich per frame-ancestors oft gegen
  Staging. Solche Tests sind dort in keinem Browser moeglich; frueh
  klaeren, den eigenen Anteil trotzdem pruefen.

Redmine 1.3 -> 1.4:
- Textile beendet den Durchstrich am ersten Bindestrich im Text, ein
  Pfeil laesst also die Handlungsaufforderung ungestrichen stehen; HTML
  hilft nicht, Redmine escapt es. Und das Ergebnis nachsehen, die API
  bestaetigt nur die Speicherung.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 16:05:42 +02:00

106 lines
7.4 KiB
Markdown

---
name: redmine-issue-workflow
description: "Read and update issues on a self-hosted Redmine via its REST API from the CLI — list/read tickets, add notes, change status, edit an existing journal note in place, and download attachments. Use when a project tracks its work/bugs in Redmine and you need to look up 'the next fixes', check a ticket's details, mark work done, or maintain a collection ('Sammelticket') ticket. Covers: API-key auth via header (never inline the secret), the issues/journals/attachments endpoints, Textile (NOT Markdown) formatting, localized statuses, and the posting-style rules (keep customer-visible notes minimal — strike through or 'erledigt', don't write the bug's cause; don't post unless asked)."
metadata:
author: Wappler
version: "1.4"
---
# Redmine issue workflow (REST API, CLI)
Read and update a self-hosted **Redmine** project's issues over its REST API from the shell. Redmine
is where the team tracks bugs / "next fixes"; you look tickets up, and — only when asked — mark work done.
## Placeholders (keep the concrete values in project memory, not here)
- `<REDMINE_URL>` — e.g. `https://redmine.example.com`.
- `<PROJECT_SLUG>` — the project identifier (used as `project_id`), e.g. `ivv`. (Numeric id also works.)
- `<API_KEY_FILE>` — a **chmod 600** file holding the per-user REST API key (from Redmine → *My account
→ API access key*). **Never inline the key** — always read it in the header via `$(cat …)`.
- `<ISSUE_ID>` / `<JOURNAL_ID>` — the ticket / journal-note ids.
## Auth (every call)
```bash
curl -s -H "X-Redmine-API-Key: $(cat <API_KEY_FILE>)" "<REDMINE_URL>/…"
```
Keeps the secret out of the command line (auto-mode/secret guards block a literal key).
## Read
- **Open issues (the "next fixes"):**
`GET /issues.json?project_id=<PROJECT_SLUG>&status_id=open&sort=priority:desc&limit=100`
(`status_id=open` = all non-closed; `sort=updated_on:desc` to see the newest activity.)
- **One issue with its notes:** `GET /issues/<ISSUE_ID>.json?include=journals,attachments`.
- Pipe JSON through `python3 -c "import sys,json;…"` to print `id / status / subject / assignee / notes`.
- **Statuses may be localized.** A German instance uses: **Fehler** = bug (open), **Gewünscht** =
wishlist/requested, **Empfohlen** = recommended, **Abnahme** = ready for customer sign-off. Confirm the
instance's own status names via `GET /issue_statuses.json` before setting one by id.
## Update (only when the user asks — see posting style)
- **Add a note / change status / progress** on an issue:
```bash
curl -s -X PUT -H "X-Redmine-API-Key: $(cat <API_KEY_FILE>)" -H "Content-Type: application/json" \
-d '{"issue":{"notes":"…","status_id":<N>,"done_ratio":<0-100>}}' \
"<REDMINE_URL>/issues/<ISSUE_ID>.json" # → 204 No Content on success
```
- **Edit an EXISTING journal note in place** (e.g. a collection ticket's item list that one person keeps
appending to): `PUT /journals/<JOURNAL_ID>.json` with `{"journal":{"notes":"…"}}` → 204. Get the
journal id from the issue's `include=journals`.
- **Collection / "Sammelticket" pattern:** one ticket stays open and holds a numbered item list (often in
a single journal note). As items are done, **strike them through** and the ticket stays open for more.
## Formatting — Textile, NOT Markdown
Redmine renders **Textile** by default. Consequences:
- Strike-through = `-text-` (Markdown `~~…~~` does nothing).
- A leading `#` is an **ordered-list** item, not a heading (Markdown `#` headings render as `<ol><li>`).
- Bold `*text*`, italic `_text_`, inline code `@code@`.
- **⚠ A strike-through ENDS at the first `-` inside the text.** Wrapping a whole sentence that contains
an arrow (`->`) closes the `<del>` right there, leaving the tail — often the action item, "-> please
clarify" — un-struck, i.e. still looking open. Inserting a single space (`. ->`) makes that dash read
as an opener instead and the strike-through runs to the end. **HTML is no way out: Redmine escapes
`<del>` in journal notes and the raw tag shows.**
- **Verify what you wrote actually RENDERED.** The API only tells you the source was stored. Fetch the
issue page with the same API-key header and grep for `<del>` — it renders the Textile server-side, so
you see exactly what the customer sees. Worth it whenever you strike text through in place.
## Posting style (team rules — important)
- **Don't post unless asked.** The user often does the Redmine updates themselves; offer/confirm first.
- **Keep customer-visible notes minimal. Do NOT write out the bug's cause / technical explanation** in the
ticket. To mark something done, **strike the item through** (`-…-`) or just write **"erledigt"** beside
it, where appropriate. The detailed root-cause/fix belongs in your own notes/memory, not the ticket.
Two or three sentences is the norm: what is done, where to check it. No commit hashes, no file names,
no before/after measurements — the customer did not ask how, only whether.
- **No icons, emoji or decorative symbols in ticket text** (no ✅/✔/→/⚠, no bullets made of glyphs).
Plain sentences, plus Textile markup where it carries meaning (strike-through, bold). Team rule.
- **Never set or change `assigned_to_id`.** Post the note and the status, leave the assignee alone —
reassigning is the user's call and a wrong one pulls a colleague into a ticket that isn't theirs.
Note that `status_id` and `done_ratio` are fine to set; only ownership is off limits.
- Before posting a status change, prefer to draft the (short, German if the project is German) note and
confirm the target status with the user.
## Who does what — the same team across all TYPO3 projects
The assignee tells you what KIND of work a ticket is. These roles hold across projects, so read them
before deciding whether a ticket is yours to act on. Any of them may be the one operating you — don't
assume which, and don't assume a ticket is theirs just because they are asking about it.
- **Ilja** — **senior developer**. Code, templates, SCSS, TypoScript, deployments.
- **Sven** — **senior developer** and maintainer of the shared WapplerSystems extensions
(`address`, `ws_slider`, `hero-item`, …). Coordinate with him before cutting a tag on one of those.
- **Ranju** — **junior developer**. Also on the development side, so a ticket assigned to her is
technical, not editorial.
- **Corinna** — **design and the bulk of editorial work**. Content tickets (texts, images, page
structure) are hers.
- **Eva** — **project management**, plus light editorial changes. Typically the one who maintains a
Sammelticket's item list and re-assigns tickets.
Consequences:
- **A ticket assigned to Corinna or Eva is editorial — don't silently do the work.** Report what you
found if it helps, but leave the change to them.
- **CSS and code are yours even when the ticket is assigned to someone else.** Tickets often mix the
two: split them by the NATURE of each item, not by the assignee, do the technical half, and say
plainly which half you left alone and why.
- This is also why the assignee must never be changed by you (above): it encodes the routing.
## Attachments
`GET /attachments/download/<id>/<name>` needs the **exact** filename (a placeholder → 404 HTML). Reliable:
`GET /attachments/<id>.json` → read `content_url` → `curl -L` THAT with the API-key header. Attached
"screenshots" are often browser print-to-PDF files — readable directly once downloaded.