refac: migration crash, real-database tests, site deleted during create, ruels out of the repo, lists

This commit is contained in:
Florian Sylvain committed 2026-11-03 05:06:04 +01:00
1 parent 680b9a1308
commit 9d284e65a9
62 files changed
+1858 -987

No files matched your search

+39 -4
View File
@@ -41,6 +41,10 @@ Generated from `source/locale-spec.js`. Read `localization.md` first: its explic
| Social locale per language | Per-language owner field on Languages: Derived (from the tag), Set (typed), Not set. Format language_REGION | Never guess a region | Languages page | social_locale per site language | Initial |
| Release split | Source reference (L6) and Freshness and review (L9) are release 2. Frames are drawn in full so they can be checked | Smaller first release | None | None | Release 2 |
| UI wording | The UI says "article", never "story". No revision numbers, IDs or HTTP status names in user-facing copy. Internal notes may say story | Plain language | All localization copy | None | Initial |
| Languages in the sidebar | No sidebar item. Languages is reached only through Settings → Sites → [site] → Languages, which routes.md lists as the only languages route | One route, one entry point, per-site scope | Sidebar unchanged | None | Initial |
| Public URL preview | The preview uses the most recently updated article that has a version in the site default language, in that language. A site without articles previews a sample address | Predictable and never fabricates a language | Public URL page | None | Initial |
| Deleting an article | Dialog names the readable versions and states that the article and all of its translations are deleted. Failure copy: "Couldn't delete the article" with "Nothing was deleted. Every version is still there. Try again in a moment." | Delete is atomic, so there is no partial delete | Delete dialog | None | Initial |
| Destructive cue in the conflict dialog | Keep my changes is the only filled button. Save over theirs is outlined in --ui-error with a triangle-alert icon, the same size as the other secondary buttons | Marks the destructive choice without changing the equal-weight rule | Conflict dialog | None | Initial |
## Capability matrix (per version, enforced server-side)
@@ -88,7 +92,7 @@ Generated from `source/locale-spec.js`. Read `localization.md` first: its explic
| Move story | Proposed | POST /v1/story/{story_id}/move { site }, atomic, revalidates | 409 slug_conflict · 422 language_not_enabled · 403 (neutral) |
| Publish with unsaved edits | Client sequence | PATCH /v1/article/{id} (save) then PATCH /v1/article/{id}/publish. The second call is sent only if the first returns 200. No new endpoint | Save failure: nothing published, edits stay unsaved |
| Public article (two requests) | Proposed | Exact: GET /v1/public/site/{key}/lang/{lang}/article/{slug}. Resolve: GET /v1/public/site/{key}/story/{story_id}?lang=&fallback=default. Existing article-ID links unchanged | See Public lookup |
| Public URL pattern | Proposed | GET/PUT /v1/site/{key} { public_url_pattern }. Placeholders {lang}, {slug}. Owner only | 422 unknown_placeholder · invalid_scheme · not_encoded · missing_slug · 403 |
| Public URL pattern | Proposed | GET/PUT /v1/site/{key} { public_url_pattern }. Placeholders {lang}, {slug}. Owner only | 422 unknown_placeholder · invalid_scheme · invalid_host · not_encoded · missing_slug · 403 |
| Social locale | Proposed | PUT /v1/site/{key}/languages { languages[{ lang, enabled, social_locale\|null }] } | 422 invalid_social_locale |
## Accessibility and interaction
@@ -149,7 +153,7 @@ Generated from `source/locale-spec.js`. Read `localization.md` first: its explic
| Duplicate translation | "French already exists for this article." Action: Open French version | "French can't be created for this article." No Open action, no title, no hint a version exists |
| Create conflict response | 409 language_exists with article_id | 409 language_unavailable; identical body for exists-unreadable, disabled and lost race |
| Article delete confirmation | Names readable versions: "Delete English and French?" | Unreadable versions are not named or counted. If any blocks deletion: "You can't delete this article." plus Delete this version |
| Source unavailable | Deleted: "The source was deleted." | "The source isn't available to you." |
| Source unavailable | Deleted or unreadable at submit: the same neutral 404, shown as "The source isn't available to you." The review banner (L9) says "Source deleted" only for a source the user already knew about | "The source isn't available to you." |
| Missing translation tab | Row shown; Add French translation | Same row; a 409 shows the neutral copy |
| Counts and summaries | Readable versions only | Never included |
@@ -211,7 +215,7 @@ Generated from `source/locale-spec.js`. Read `localization.md` first: its explic
| --- | --- |
| Title editing | Inline underline input in heading style, on the same row as the language, badges and actions on desktop (no box, no visible label). States: pristine, focus, editing, unsaved, invalid ("Enter a title"), too short, too long, saving, failed (value kept). Bounds 3 to 200 characters. Accessible label "Title (French)". Editing the title never changes the slug |
| Slug | See Slug policy. Edit slug lives in Search and sharing and in the overflow menu; its note says "Editing the title never changes the slug." |
| Conflict | "Someone else saved this version at <time>." Choices: Keep my changes (filled; nothing sent), Download my version, Reload theirs (confirm: "This discards your unsaved changes."), Save over theirs (sends the version fetched now; a newer save gives a new conflict) |
| Conflict | "Someone else saved this version at <time>." Choices: Keep my changes (filled; nothing sent), Download my version, Reload theirs (confirm: "This discards your unsaved changes."), Save over theirs (sends the version fetched now; a newer save gives a new conflict) Save over theirs is outlined in the error colour with a warning icon. |
| Overwrite scope | "Replaces the title, body and search fields of this version only. Other languages are untouched." |
| Session expiry | Draft kept locally per article (title, body, search fields) until saved or discarded. After login: "Restored unsaved changes from <time>." |
@@ -323,11 +327,12 @@ Generated from `source/locale-spec.js`. Read `localization.md` first: its explic
| Case | Example | Message |
| --- | --- | --- |
| Valid | https://journal.example.com/{lang}/{slug} | Preview: https://journal.example.com/fr/comment-nous-chiffrons-une-renovation |
| Valid | https://journal.example.com/{lang}/{slug} | Preview: https://journal.example.com/en/how-we-price-a-bathroom-renovation |
| Unknown placeholder | https://journal.example.com/{language}/{slug} | Unknown placeholder {language}. Use {lang} and {slug}. |
| Bad scheme | ftp://journal.example.com/{lang}/{slug} | Start with https:// or http://. |
| Not encoded | https://journal.example.com/my pages/{lang}/{slug} | Remove spaces and characters that need encoding. Use letters, digits, - and _. |
| Missing {slug} | https://journal.example.com/{lang} | Add {slug} so each article has its own address. |
| Invalid host | https://journal..example.com/{lang}/{slug} | Enter a valid host name, such as journal.example.com. |
| Unset | | Public URL not configured, with a link to this setting |
## Social locale per language
@@ -355,3 +360,33 @@ Generated from `source/locale-spec.js`. Read `localization.md` first: its explic
| L6 Source reference | Release 2 | Drawn in full so the checker has frames |
| L9 Freshness and review | Release 2 | Drawn in full so the checker has frames |
## Copy decisions (pass 3)
| Item | Final copy or rule |
| --- | --- |
| Editor banner: save failed | Couldn't save French. Your changes are kept here. Nothing was published. Try saving again. |
| Editor banner: publish failed | Saved as a draft. Publishing failed. Try publishing again. (detail: Your edits are saved. Only the publishing step failed.) |
| Editor banner: online | This version is online. Saving updates the live page right away. |
| Row menu, all languages | Open <language>, Open another language…, Add translation…, Unpublish <language>, Copy API URL, Delete <language> version |
| Row menu, language selected | Open <language>, Open another language…, Publish or Unpublish <language>, Copy API URL, Delete <language> version |
| Disabled-language version in the menu | Row shows the Disabled tag. Footer: "<Language> is disabled on this site. Its version stays readable and editable. It can't be published until an owner re-enables <Language>." |
| Setup done | Languages assigned. N articles now have a language. Nothing was translated, and no articles were grouped. |
| Languages list on 390 px | The version count line ("4 versions · 2 published") is shown, on its own line under the language name |
| Single-language card on 390 px | "Social locale" and "Derived: pt_BR" sit on their own line below the language |
| Partial or failed delete | Couldn't delete the article. Nothing was deleted. Every version is still there. Try again in a moment. |
| Version deleted toast | French version deleted. "<title>" was deleted. English and German weren't changed. |
| Article deleted toast | Article deleted. The article and all of its translations were deleted. |
| Move failed | Couldn't move the article. Nothing was moved. All 4 versions are still on Journal. Try again. |
| Second conflict | Their version changed again. Someone else saved this version again at <time>, before your save went through. Nothing was overwritten. Your changes are still here. |
| New article, language help | Default language for <site>. |
| Invalid host | Enter a valid host name, such as journal.example.com. |
| Toast icon box | 28 × 28 px, 16 px icon, tone colour on its -soft ground (--rc-success-soft, --rc-info-soft, --rc-warning-soft, --rc-error-soft). Body and title as in the Toast table |
| Activity: site.language.add | Added <language> to <site> |
| Activity: site.language.enable | Re-enabled <language> on <site> |
| Activity: site.language.disable | Disabled <language> on <site> |
| Activity: site.language.remove | Removed <language> from <site> |
| Activity: site.language.default | Made <language> the default language of <site> |
| Activity: site.language.social_locale | Set the social locale for <language> on <site> to <value> |
| Activity: site.public_url.update | Changed the public URL pattern of <site> |
| Activity: site.language.setup | Assigned languages to N articles on <sites> |
| Activity: article.move | Moved "<title>" from <site> to <site> |