Feature #1551
openFeature #1513: Implémenter des agents conversationnels spécialisés pour la qualification des demandes sur claude code
API: mode-aware spec writer and the Sécurité marker at filing
0%
docs/superpowers/specs/2026-08-03-specialized-intake-agents-design.md
Description
**Surface:** backend (`api/`)
**Depends on:** #1549
**Blocks:** nothing
Carry the mode through to the draft and to the filed issue.
`MistralTicketSpecWriter.draft()` (`infrastructure/intake/mistral_spec_writer.py:73`) takes a `request_type` and **forces** the resulting `issue_type` from it rather than letting the model choose: `bug`→`bug`, `feature`→`feature`, `security`→`bug`. Keep the existing faithfulness sentence in `SYSTEM_PROMPT` (*"Base it strictly on what the client said and the context; do not invent scope"*) — it is the guard against the model inventing scope, and it stays.
Then `create_ticket_from_draft.py` stamps a **`Sécurité` marker** into the description metadata block when the mode was `security`, in exactly the shape `BF_DISAGREE_MARKER` uses (`:48`). Redmine custom fields 2–9 are Jenkins-owned, so "tag" in this integration has always meant a greppable token in the body — not a label.
**Two things this ticket must NOT do**, both from #1513's ACs as originally written:
- **Do not set status `Submitted`.** That AC was copied from #47, the SecOps *scanner* path, where we file our own scan output unbilled. On the client path `Submitted` is the paid credit gate. A security conversation files at **Backlog (16)** like every other client ticket. The existing post-create status check (`:192`) stays and must still pass.
- **Do not write `security_key`.** That custom field is the scanner's dedup key; putting a client's free-text concern in it would suppress a real scan finding sharing that key.
The complexity score continues to come from the model in the same JSON call and is pinned verbatim into `ticket_charges` — this ticket does not change pricing.
## Acceptance criteria
- [ ] `draft()` accepts `request_type` and forces `issue_type` from it; a test proves a `security` mode yields `issue_type="bug"` **even when the model returns `"feature"`**.
- [ ] Omitting `request_type` preserves today's behaviour (model-chosen `issue_type`) — existing callers keep working.
- [ ] The faithfulness sentence is still present in `SYSTEM_PROMPT`.
- [ ] A `security` filing renders the `Sécurité` marker in the metadata block; a `bug`/`feature` filing does not.
- [ ] A `security` filing is created with `status_id = RedmineStatus.BACKLOG` — asserted on the outbound Redmine payload, not just the response.
- [ ] A `security` filing sends **no** `security_key` custom field (assert the field is absent from the payload).
- [ ] `bf-disagree` still renders independently and can co-occur with `Sécurité`.
- [ ] The pinned `ticket_charges` cost is unchanged by the mode (same score in → same cost recorded).
- [ ] Gates green: `python -m pytest -q` (**run serially**), `ruff check .`, `mypy src`. TDD: failing test first.
No data to display