Project

General

Profile

Actions

Feature #1548

open
RA

Durable ticket authorship, so a filer keeps access after submit

Feature #1548: Durable ticket authorship, so a filer keeps access after submit

Added by Redmine Admin 1 day ago.

Status:
Backlog
Priority:
Normal
Assignee:
-
Start date:
08/03/2026
Due date:
% Done:

0%

Estimated time:
spec_ref:
build_status:
build_number:
ci_run_url:
scan_status:
scan_report_url:
deploy_status:
preprod_url:
deployed_at:
branch:
pr_url:
security_key:
severity:
paused:

Description

**Surface:** backend (`api/`) — schema change
**Found during:** #1543 (Kanban visibility matrix), review round 3
**Status:** parked in Backlog, unbilled — nobody has paid for this. Submit it if you want it worked.

### The gap

#1543 widened the developer scope from "assigned to me" to "assigned to me **or filed by me**", because a developer who files a ticket was locked out of it — a filed ticket lands in Backlog unassigned, so the assignee-only rule answered 403 on submitting, editing or deleting their own ticket, and MCP told them it "is not in this project".

Authorship is read from `ticket_charges`, deliberately — Redmine's `author` is the shared "Client Dashboard" user for every dashboard-filed ticket and so identifies nobody.

**But `ticket_charges` rows are cleared at submit** (`application/submit_ticket.py:147` — *"Paid for: drop the pinned charge so it cannot be re-read"*) and at delete. So filer-scope lasts exactly as long as the ticket sits in **Backlog**.

That covers the three operations #1543 was about, since submit, edit and delete are all backlog-window actions. It does not cover what happens next: a developer who files a ticket, **submits it, and is not assigned it** loses sight of it again at Submitted/Spec — where `ticket_is_mutable` would still have let them edit it. They paid for it and can no longer see it.

### Why it needs a schema change

There is no durable record of who filed a ticket. `ticket_charges` is a *billing* row with a deliberately short life, and overloading it as an authorship record is what produces this window. The fix is a small, permanent authorship column or table written at filing and never cleared, with `TicketAuthorship` reading from it instead.

Note the ordering constraint: the debit must still land on the filer, and the refund path (#1169) already credits them, so whatever is added must not disturb how `ticket_charges` drives billing.

### Not urgent, and fails closed

The current behaviour denies access rather than granting it — no leak, no disclosure. Only internal developers are affected; clients see every ticket in their project and CTO/admin see everything.

## Acceptance criteria
- [ ] A durable authorship record is written at filing (both the conversation path and MCP `create_ticket`) and is never cleared by submit or by a refund.
- [ ] `TicketAuthorship.issue_ids_filed_by` reads from it; the batch-intersection shape is kept so the board still resolves a whole column set in **one** query (regression test at 5 and 200 cards).
- [ ] A developer who files a ticket keeps read and edit access after submitting it, at Submitted and Spec, without being assigned it.
- [ ] A ticket with no authorship record is out of every developer's scope — fails closed, never "filed by whoever is asking".
- [ ] A ticket filed by another developer stays out of scope (regression).
- [ ] Billing is untouched: the debit still lands on the filer and the #1169 refund path still credits them.
- [ ] Migration `upgrade head` and `downgrade` both run clean against an ephemeral `pgvector/pgvector:pg16`.
- [ ] Gates green: `python -m pytest -q` (run serially), `ruff check .`, `mypy src`.

No data to display

Actions

Also available in: PDF Atom