Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ taiga api PATCH userstories/123 --field comment="Deployed to staging" --auto-ver
- Swimlanes: `--swimlane` moves the story to a swimlane, `--clear-swimlane` takes it out of any; `story list --swimlane L` and `--no-swimlane` filter by it (always checked locally: Taiga ignores the documented `swimlane` parameter). A story created without `--swimlane` stays without one, even when the project has a default swimlane. Creating, renaming and reordering swimlanes stay in the web UI: the first swimlane of a project takes every story, and the order has no `version`.
- `close` without `--status` uses the project's only closed status; with several (the default template has `Done` and `Archived`) it asks for one.
- Every write accepts `--dry-run` (prints method, path and body) and, except `create`, `--force-version`.
- `comment` sends the text exactly as given (quotes, accents, line breaks) and never touches the description. A blank comment is refused. Taiga accepts any past `version` for a comment, so the version protects nothing there and there is no `--force-version`: the comment is sent once. If the answer is lost (network error or 5xx), the CLI looks for it in the story history: found, the command succeeds; otherwise it exits with `comment_unconfirmed` (exit 1), because a request still running on the server can land later. Check `story comments` before publishing again. Exit 7 (`network_error`) means the connection never opened, so nothing was sent.
- A write whose answer is inconclusive after the request left (network error, 5xx, or a redirect, which the CLI never follows: a proxy may have passed the request on) never exits 7. `update`, `close` and `field set` re-read the story: the change is there, success; otherwise `story_update_unconfirmed` (exit 1). `create` always exits with `story_create_unconfirmed` (exit 1), naming the new stories of this account with the same subject, if any (another process of the same account may have created them); it never links `--epic`. Do **not** re-run blindly: check with `taiga story get REF` or `taiga story list` first. Exit 7 is left for reads and for a connection that never opened.
- `comment` sends the text exactly as given (quotes, accents, line breaks) and never touches the description. A blank comment is refused. Taiga accepts any past `version` for a comment, so the version protects nothing there and there is no `--force-version`: the comment is sent once. If the answer is lost (network error, 5xx or a redirect, which the CLI never follows), the command always exits with `comment_unconfirmed` (exit 1), naming the new comments of this account with the same text found in the history, if any: none of them proves it came from this command (another process of the same account may have published the same text), and a request still running on the server can land later. Check `story comments` before publishing again. Exit 7 (`network_error`) means the connection never opened, so nothing was sent.
- `comments` lists every comment, including edited and deleted ones (`edit_comment_date`, `delete_comment_date`). Comments written by Taiga's own GitLab integration (its inactive `gitlab-<hash>` user, with the push hook templates "This user story has been mentioned by …" and "… changed the status from [GitLab commit]…") are hidden unless `--include-system`; every other author, service accounts included, is always shown. Each entry carries `is_system`, `story_ref` and the story `url`.
- `--epic` and `--replace-epic`: see "Linking stories to epics" below.

Expand Down Expand Up @@ -154,7 +155,7 @@ taiga story update 246 --replace-epic 15 --confirm-delete
| `taiga attachment download REF ATTACHMENT_ID [--task] [--to PATH\|-] [--overwrite] [--timeout 10m]` | Save an attachment, checked against its size and sha1 |

- `upload` is idempotent by content: if the story or task already has an attachment with the same name and sha1, nothing is sent and that attachment comes back with `"created": false`. The same content under another name is a new attachment.
- The upload is sent once and never repeated. Its answer is checked (sha1, size, object); a mismatch is `attachment_postcondition_failed` (exit 4): the file **was stored**. If the answer is lost, the CLI looks for a new attachment with that name and sha1: found, success; otherwise `attachment_unconfirmed` (exit 1). Check `attachment list` before uploading again.
- The upload is sent once and never repeated. Its answer is checked (sha1, size, object); a mismatch is `attachment_postcondition_failed` (exit 4): the file **was stored**. If the answer is lost after the request left (network error, 5xx or a redirect), the command always exits with `attachment_unconfirmed` (exit 1), naming any new attachment with that name and sha1 (another process of the same account may have attached it). A lost body after a 2xx is applied: the new attachment is the answer. Check `attachment list` before uploading again.
- File names Taiga would store differently (control characters, a backslash, an HTML entity such as `&amp;`, with or without `;`, or any `&#`) are refused: rename the file. The idempotence compares the stored name.
- The CLI has no size limit. The proxy in front of Taiga has one (50 MB at Basis): above it the upload fails with `payload_too_large` (exit 2). Empty files are refused, as Taiga refuses them.
- `--dry-run` prints the form fields and the file's name, size and sha1, never its content; every value of the plan (the description, the file name) is printed with URL queries and `token=` values hidden. The real upload sends them as given.
Expand All @@ -179,7 +180,7 @@ taiga attachment download 18 3122 --task --to - | less
| `taiga field create --kind story\|task --name NAME --type text\|date\|checkbox [--description TEXT] [--dry-run]` | Create a definition unless one with that name exists |
| `taiga story field list REF` / `taiga story field set REF ["Name=value"]... [--unset NAME]... [--dry-run] [--force-version]` | Read and merge a story's values |

- `field create` is idempotent: an existing definition with the same name (case-sensitive) and type is returned unchanged; one with another type, or another description when `--description` is given, is `field_definition_conflict` (exit 2). Definitions are never changed or deleted.
- `field create` is idempotent: an existing definition with the same name (case-sensitive) and type is returned unchanged; one with another type, or another description when `--description` is given, is `field_definition_conflict` (exit 2). Definitions are never changed or deleted. An inconclusive answer (network error after the request left, 5xx, redirect) is `field_create_unconfirmed` (exit 1), naming a definition with that name if one is there; Taiga refuses a second definition with the same name, so it is never created twice.
- Only `text`, `date` and `checkbox` are supported so far. Values: text as is (`null` is text), checkbox `true`/`false`, date `YYYY-MM-DD`. The name ends at the first `=`.
- `--unset NAME` (repeatable, name or id) clears a `checkbox` or `date` field: Taiga stores `null` under its key, which stays even when it is the last field (Taiga refuses an empty dictionary). A field without a value, or already `null`, writes nothing. Text fields cannot be unset (usage error); set them to empty with `Name=`. JSON output shows the cleared value as `null`; text output shows it empty, like a field without value. The same merge, check and `--dry-run` apply.
- `story field set` reads the values, merges the named fields and writes the whole dictionary with the `version` of the values resource, not the story's. Unchanged values write nothing. Values of fields without a definition are kept.
Expand Down
24 changes: 13 additions & 11 deletions docs/api-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ Consequência na CLI: `field create` é idempotente pela leitura do catálogo an
mesma definição entre a leitura e o `POST`, o 400 em `name` faz a CLI reler e aceitar a definição só se for
compatível (mesmo tipo e, se `--description` foi dado, mesma descrição). O `POST` nunca é repetido. Limite não
sondado: se os dois `POST` passarem juntos pelo validador, quem recusa é o `unique_together` do banco, o que pode
virar 500 (`server_error`, exit 7) em vez do 400; a unicidade continua garantida e repetir o comando encontra a
definição. A CLI também recusa `=` no nome, porque `story field set` corta a atribuição no primeiro `=`. Definições
virar 500 em vez do 400, que a CLI trata como resultado incerto (`field_create_unconfirmed`, exit 1, US #274); a
unicidade continua garantida e repetir o comando encontra a definição. A CLI também recusa `=` no nome, porque `story field set` corta a atribuição no primeiro `=`. Definições
existentes nunca são alteradas nem apagadas.

### Valores
Expand Down Expand Up @@ -339,13 +339,14 @@ Consequências na CLI (`story comment`):
ocultada);
- resposta 2xx ilegível (`UnreadableBodyError`, corpo truncado) segue o caminho de releitura das outras escritas: o
status já confirma a gravação; se a releitura falhar, `write_applied`;
- erro sem resposta conclusiva (rede depois de aberta a conexão, timeout ou 5xx): antes do `PATCH` a CLI guarda os
ids dos comentários do próprio usuário (`users/me`) com exatamente o mesmo texto; depois da falha relê o histórico.
Um id novo com o texto = publicado (sucesso, story relida). Nenhum, ou histórico ilegível = `comment_unconfirmed`
(exit 1): a ausência não prova que o `PATCH` falhou, porque um gateway pode responder 503 enquanto o servidor ainda
grava, e o comentário aparece depois da conferência (achado da revisão do PR #7: um script que repetia o exit 7
criou duas cópias). O erro de rede original (exit 7, repetível) só sai quando a conexão nem abriu (DNS, conexão
recusada).
- erro sem resposta conclusiva (rede depois de aberta a conexão, timeout, 5xx ou redirect 3xx, que a CLI nunca
segue): antes do `PATCH` a CLI guarda os ids dos comentários do próprio usuário (`users/me`) com exatamente o
mesmo texto; depois da falha relê o histórico e sai **sempre** com `comment_unconfirmed` (exit 1), nomeando os ids
novos com o texto, se houver. Um id novo não prova que veio deste `PATCH` (outro processo da mesma conta pode ter
publicado o mesmo texto; decisão de 2026-10-03, opção B, estendida ao comentário na US #274), e a ausência não
prova que o `PATCH` falhou, porque um gateway pode responder 503 enquanto o servidor ainda grava, e o comentário
aparece depois da conferência (achado da revisão do PR #7: um script que repetia o exit 7 criou duas cópias). O
erro de rede original (exit 7, repetível) só sai quando a conexão nem abriu (DNS, conexão recusada).

### Leitura: `GET history/userstory/<id>`

Expand Down Expand Up @@ -452,8 +453,9 @@ mesmo padrão dos responsáveis (US #247). Constante `statusOrderCheckedWrite =
- **antes:** relê o catálogo logo antes do bulk; se algum status apareceu, sumiu, mudou de nome, de id ou de `order`
desde a leitura do plano (mais os criados pelo próprio apply), recusa com `project_changed` (exit 4) sem enviar;
- **depois:** relê e exige exatamente a ordem pretendida. Se não bate, `status_order_postcondition_failed` (exit 4),
dizendo que a escrita **foi aplicada** (ou teve resultado incerto, em rede/5xx) e para não repetir às cegas. 4xx do
bulk é recusa: o erro dele, sem conferência. Resposta perdida depois do 2xx: vale a releitura;
dizendo que a escrita **foi aplicada** (ou teve resultado incerto, em rede/5xx/3xx) e para não repetir às cegas. 4xx do
bulk é recusa: o erro dele, sem conferência. Resposta perdida depois do 2xx: vale a releitura. Resultado incerto e
releitura que falha: `status_order_unconfirmed` (exit 1);
- nunca há repetição automática do bulk. Como o bulk grava posições absolutas, rodar o apply de novo depois de
conferir é seguro: ele replaneja e confere outra vez;
- limite: uma mudança que cai entre a releitura e o bulk é sobrescrita; só é detectada se deixar outra ordem na
Expand Down
Loading
Loading