Browse documentation

Your everyday workflow

Readable URLs

Workspace, list and section names retain their original human-readable title. Migration 022 adds a separate entity_slugs table; it does not rename entities, change task references, replace IDs or change existing ID-based URLs. UI clients can use Store::entity_slugs(actor) to obtain authorized ID-to-slug maps and render @workspace, #list and $section. This bulk read returns only entities the current actor can read. Direct list guests receive no parent workspace slug, ID, scope or name. Permission revocation is effective on the next read.

The canonical base lowercases Unicode text, retains letters and numbers, changes runs of punctuation/whitespace into one ASCII hyphen, and trims leading/trailing hyphens. It uses no transliteration or Unicode normalization dependency. For example My Next_Project becomes my-next-project, while CAFÉ 東京 becomes café-東京. Emoji/punctuation-only names become untitled. The base is limited to 64 Unicode characters. The stored slug is at most 80 characters and contains no whitespace. Raw titles remain available for reading and rename editors.

Workspace collisions are scoped to the initial owner's account. This allocation scope persists when ownership changes rather than silently renaming a shared workspace. Lists are scoped to their workspace; legacy lists without a workspace use their initial member account. Sections are scoped to their list. When the same base is occupied, allocation selects the first available -2, -3, … suffix, bounded to 100,000 candidates. Renames regenerate the slug in the current scope; moving a list recalculates it in its destination scope. IDs and ordinary revision/CAS checks remain authoritative; slugs are display names, not new lookup or authorization keys. Existing URLs keep working after a rename or collision.

Normal workspace/list/section create and rename operations allocate slugs in the same write transaction. A list workspace move changes its slug scope in the same transaction as authorization, audience acknowledgement, task history and tag mapping. Store startup runs incremental backfill within its migration transaction; development seed and trusted JSON/legacy imports backfill within their write transactions too. Backfill processes missing/changed entries by stable ID, retains an unchanged entry's suffix, and never changes raw titles or revisions. It does not run from read operations. Delete triggers remove stale allocations. Direct SQL fixture writes outside these entry points should call backfill before exposing data, or reopen through Store; such writes are not application APIs.

The maps deliberately avoid adding fields to every existing DTO constructor. All REST, CLI and MCP writes already use these shared domain operations, so a UI rename and an agent rename produce the same persisted slug behavior. Derived slugs can be regenerated from trusted exported business rows; stable URLs and references continue to use their original IDs, not the derived labels.

Search tasks, CLI commands, deployment and more.