Skip to content

Use core's slug rule for project status slugs - #36

Merged
ddon merged 3 commits into
BeamLabEU:mainfrom
mdon:main
Aug 10, 2026
Merged

Use core's slug rule for project status slugs#36
ddon merged 3 commits into
BeamLabEU:mainfrom
mdon:main

Conversation

@mdon

@mdon mdon commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This module had its own hand-rolled slugifier:

|> String.downcase()
|> String.replace(~r/[^a-z0-9\s-]/, "")   # or [^\w\s-]
|> String.replace(~r/\s+/, "-")
|> String.trim("-")

That deletes every non-ASCII character, so a Cyrillic or Greek name produced an empty
slug
, and German lost its umlauts and ß:

input before after
Видеопродакшн "" videoprodakshn
Καλημέρα "" kalimera
Größe Fußball gre-fuball grosse-fussball

An empty slug is worse than a wrong one: callers read it as "no slug yet" and regenerate
on every save.

It now delegates to PhoenixKit.Utils.Slug, backed by the
locale_slug package — one implementation for the
whole ecosystem instead of a copy per repo.

Stored slugs are not rewritten; generation only runs when the slug is blank, so
existing URLs are untouched.

Release order

⚠️ Requires the phoenix_kit release carrying the locale-aware Slug, currently in
BeamLabEU/phoenix_kit#692. Does not build against the published core until that ships.
Merge order: core release → this.

Scope

project_status.ex. Worth noting statuses are looked up by slug, and
Statuses.current_status/1 treats "" as nil — so a Cyrillic status name did not
merely get an ugly URL, it silently vanished.

Verification

New slug_generation_test.exs pins the behaviour.

1387 tests, 0 failures against local core.

(An earlier revision of this description claimed the repo does not compile against local
core because of ai_multilang_tabs/1. That was my testing setup, not the repo: it needs
PHOENIX_KIT_AI_PATH alongside PHOENIX_KIT_PATH, since the function lives in
phoenix_kit_ai 0.17. With both exported it compiles and the suite is green.)

mdon added 3 commits August 9, 2026 23:11
project_status.ex stripped every non-ASCII character, so a Cyrillic or Greek
status name produced an EMPTY slug — and statuses are looked up BY slug
(Statuses.current_status/1 treats "" as nil), so the status silently vanished.

Now delegates to PhoenixKit.Utils.Slug (backed by locale_slug).

Requires the phoenix_kit release carrying the locale-aware Slug
(BeamLabEU/phoenix_kit#692).

⚠️ This repo does not currently compile against local core for a reason
unrelated to this change: project_form_live.ex and template_form_live.ex call
`ai_multilang_tabs/1`, which needs a newer phoenix_kit_ai than is resolved. The
same failure occurs with this change stashed.
Verified the new behaviour by hand but never pinned it, which is exactly the
gap a delta audit exists to catch: without these, reverting the slug change
leaves the suite green.

Asserts Cyrillic and Greek now produce a real slug rather than an empty one,
that German keeps its letters, and that plain ASCII is unchanged.
Caught by the upstream review of phoenix_kit_dashboards#5, which found the same
defect there and had to repair it after merge.

PhoenixKit.Utils.Slug.slugify/2 defaults :transliterate to FALSE on every core
released so far. So `Slug.slugify(text)` strips every non-ASCII character —
exactly what the local pipeline this change replaced did. The delegation was
correct and the behaviour was unchanged.

My verification missed it because I tested against local core, where I had made
transliteration the default. Against the core these modules actually resolve, it
was inert. That is the difference between "the code is right" and "the shipped
artifact is right", and only the second one matters.

Forward-compatible: once core ships the locale-aware Slug, :transliterate is
accepted and ignored, so this stays correct.

The pinning tests are rewritten to assert only what holds at EVERY core version.
Asserting output was the second half of the same mistake: each module resolves a
different core (posts' lockfile pins 1.7.189, which predates the :transliterate
option entirely), so a literal expected slug merges red.
ddon added a commit that referenced this pull request Aug 10, 2026
Core pin moves to ~> 2.0. This package does not call migration internals,
so the change is the pin itself.

Merge PR #36 (core slug rule for status slugs) and add @impl true to
source_fields/2, now that ai 0.18.0 declares it on the FormBinding
behaviour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ddon
ddon merged commit bde44be into BeamLabEU:main Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants