Revision 4 of 16

Makes the server authoritative on every write, moves the edit and public routes to catch-all alias segments, and folds the reorder and test lists into prose.

10.2 KB

+24 −45 lines of text vs revision 3 (this site's diff)

The text of revision 4 against revision 3: + marks an added line, − a removed one, with three unchanged lines of context around each change; old and new line numbers on the left where there is room.
Old New Change Line
@@ -1,6 +1,6 @@
11# Build The Urlist
22
3Build the complete application in this repository. Work autonomously from start to finish: plan internally, implement all requirements, test them, fix defects, and stop only when the app is complete.
3+Build the complete application in this repository. Work autonomously from start to finish and stop only when the app is complete.
44
55If a minor detail is not specified, choose a reasonable solution and document it. Do not copy an existing implementation of The Urlist.
66
@@ -34,19 +34,19 @@
3434
3535Anyone can view an active list.
3636
37+The server is authoritative: revalidate input, ownership, alias availability, and immutability on every write; ignore client-supplied owner IDs.
38+
3739## Routes
3840
3941| Route | Behavior |
4042|---|---|
4143| `/` | Home page and first-link entry |
4244| `/s/new` | Create or resume the one local draft |
43| `/s/edit/{alias}` | Owner-only, reloadable edit page |
45+| `/s/edit/{...alias}` | Owner-only, reloadable edit page |
4446| `/s/mylists` | Current user's active and deleted lists |
45| `/{alias}` | Active public list, deleted tombstone, or unused-alias state |
46
47Aliases can have multiple segments. Use catch-all App Router routes where needed. Static app routes take priority.
47+| `/{...alias}` | Active public list, deleted tombstone, or unused-alias state |
4848
49Reserve first segments `s`, `api`, `auth`, `__test`, and `.well-known`.
49+Aliases may contain multiple `/`-separated segments. Static routes take priority; reserve first segments `s`, `api`, `auth`, `__test`, and `.well-known`.
5050
5151Global navigation must include Home, New, My Lists when signed in, Login or the current user with Log out, and Light/Dark/System theme controls. Confirm before New clears a non-empty draft.
5252
@@ -58,7 +58,6 @@
5858
5959A list:
6060
61- Requires at least one link to publish or save
6261- Has no product-level link limit
6362- Allows duplicate destination URLs as separate entries
6463- Has an optional description and alias
@@ -71,23 +70,13 @@
7170
7271### Reordering
7372
74Drag-and-drop is the primary reorder interaction.
75
76- Show a visible drag handle on each editable link.
77- Support pointer and touch dragging to any position.
78- Show clear picked-up, dragging, drop-position, and dropped states.
79- Update draft order immediately.
80- Save account-owned order to SQLite.
81- Show saved order on the public page.
82- Support desktop and mobile widths.
83
84Also provide accessible Move up and Move down controls. Drag-and-drop alone is not sufficient.
73+Drag-and-drop is the primary reorder interaction: provide a visible handle per editable link, pointer and touch dragging to any position, and clear picked-up, dragging, drop-target, and dropped states. Also provide accessible Move up and Move down controls. Persist order in the draft and SQLite.
8574
8675## Live metadata
8776
88Fetch and parse the live destination page on the server after a link is added. Show Pending, Succeeded, or Failed per link. Block publish and save while any link is Pending.
77+Fetch and parse the live destination page on the server after a link is added. Show Pending, Succeeded, or Failed per link.
8978
90Stop the network operation after 10 seconds and show a terminal state within one more second. Failure keeps the link and permits manual title/description editing and publication.
79+Abort the fetch after 10 seconds and settle the link into a terminal state. Failure keeps the link and permits manual title/description editing and publication.
9180
9281Metadata precedence:
9382
@@ -97,7 +86,7 @@
9786
9887Resolve relative images against the final URL after redirects. Empty metadata is a successful result. Preserve manual title and description edits. Show a neutral image placeholder when needed.
9988
100The fetcher must allow only HTTP/HTTPS; reject credentials and non-public addresses, including loopback, private, link-local, reserved, and cloud metadata ranges; revalidate DNS and every redirect; prevent DNS rebinding; follow at most five redirects; limit content to 2 MiB; accept only HTML/XHTML; and never forward app credentials.
89+The fetcher must be SSRF-safe: HTTP/HTTPS only, no credentials, reject non-public/internal addresses, revalidate DNS on every redirect to prevent rebinding, follow at most five redirects, limit content to 2 MiB, accept only HTML/XHTML, and never forward app credentials.
10190
10291## Aliases and publication
10392
@@ -107,15 +96,15 @@
10796- Is 1–50 characters
10897- Does not start or end with a hyphen
10998
110The full alias is at most 200 characters. Reject empty, `.`/`..`, reserved, query/fragment, backslash, and encoded-separator forms.
99+The full alias is at most 200 characters.
111100
112Match the exact full path, so `burke` and `burke/links` can both exist. Active, deleted, and anonymous aliases are all reserved. Enforce uniqueness in SQLite.
101+Match the exact full path, so `burke` and `burke/links` can both exist. Once published, aliases are permanent and globally unique in SQLite across active, deleted, and anonymous lists; they never change or become available again.
113102
114If blank, generate an available seven-character lowercase alphanumeric alias. Aliases never change after publication.
103+If blank, generate an available seven-character lowercase alphanumeric alias.
115104
116Block publish/save when the list is empty, metadata is Pending, or the alias is invalid/unavailable. Repeat validation on the server. On failure, keep all user input.
105+Block publish/save when the list is empty, metadata is Pending, or the alias is invalid/unavailable. On failure, keep all user input.
117106
118For anonymous publication, require a blocking confirmation that says the list cannot be edited or deleted and its alias stays reserved permanently. Use **Publish permanently** and **Cancel**. Enforce immutability on the server. Anonymous lists never appear in My Lists.
107+For anonymous publication, require a blocking confirmation that says the list cannot be edited or deleted after publication. Use **Publish permanently** and **Cancel**. Anonymous lists never appear in My Lists.
119108
120109## Mock login and ownership
121110
@@ -123,15 +112,15 @@
123112
124113Use a server-verifiable HTTP-only session cookie with an appropriate SameSite policy. Use Secure in production HTTPS while allowing local HTTP development. Login survives reload; logout ends the session. Use stable user IDs, not display names, for ownership.
125114
126Only an owner can load, save, delete, or restore an account list. Enforce every check on the server and ignore client-supplied owner IDs.
115+Only an owner can load, save, delete, or restore an account list.
127116
128The edit page loads persistent data directly, keeps alias and destinations read-only, and permits description, membership, order, title, and description changes. Show save progress and errors without losing input.
117+The edit page loads persistent data directly and permits list-description, link-membership, order, and per-link title/description changes. Show save progress and errors without losing input.
129118
130My Lists requires login and shows only the current user's lists in separate Active and Deleted sections. Active cards show alias, optional description, and link count. Provide Create new list, Restore, and clear empty/loading/error states.
119+My Lists requires login and shows only the current user's lists in separate Active and Deleted sections. Active cards show alias, optional description, and link count. Provide Create new list and Restore.
131120
132121## Delete and restore
133122
134Delete requires confirmation and performs a soft delete. Keep content, ownership, and alias forever. There is no permanent-delete action.
123+Delete requires confirmation and performs a soft delete that retains content and ownership. There is no permanent-delete action.
135124
136125The public tombstone shows only:
137126
@@ -158,15 +147,9 @@
158147
159148Provide persisted Light, Dark, and System themes. System follows OS preference. Keep accessible contrast and avoid an initial wrong-theme flash when practical.
160149
161Support current Chromium from desktop down to 320 CSS pixels. Keep navigation, editor fields, drag controls, link actions, sharing, QR, lists, and modals usable without page-level two-dimensional scrolling.
162
163Meet WCAG 2.2 AA:
150+Support current Chromium from desktop down to 320 CSS pixels with no page-level horizontal scrolling; modals, drag controls, and the editor must remain usable.
164151
165- Semantic headings, labels, names, errors, and status announcements
166- Full keyboard use, visible focus, logical focus order, and keyboard reordering
167- No color-only meaning; usable at 200% zoom; reduced-motion support
168- Modal focus entry, trap, Escape cancellation, and focus return
169- Logical focus after deletion or reordering
152+Meet WCAG 2.2 AA, including full keyboard operation, reduced-motion support, sensible focus after deletion or reordering, and announced status/error changes.
170153
171154Show truthful loading, empty, success, blocked, and error states. Errors must explain recovery. Never present failure as success.
172155
@@ -176,7 +159,7 @@
176159
177160Persist users, lists, ownership/state/timestamps, links, and positions. Published and deleted data must survive a complete restart. Configure the database path by environment variable with a safe local default; do not commit database files.
178161
179Validate all untrusted input on the server, use parameterized SQL, escape user and remote content, prevent stored/reflected script execution, encode share parameters, apply suitable CSRF protection, and do not expose secrets or internals in errors.
162+Treat user text and fetched metadata as untrusted when rendering, prevent stored/reflected script execution, encode share parameters, apply CSRF protection, and keep internals out of errors.
180163
181164Provide a development/test-only deterministic reset, preferably `POST /__test/reset` returning `204`. It clears lists and sessions and restores the two users. Disable or protect it in production.
182165
@@ -196,13 +179,9 @@
196179db:reset
197180```
198181
199Use Vitest for validation, aliases, metadata parsing/network policy, ownership, SQLite transactions, and share URLs.
200
201Use Playwright for anonymous and account publication, both users, My Lists, owner/non-owner access, drag-and-drop plus persisted order, keyboard reordering, delete/restore/tombstone, public/QR/share views, themes, accessibility basics, and mobile layout.
202
203Tests use a separate temporary database, reset state, avoid order dependence, and clean up processes.
182+Use Vitest for URL/alias validation, metadata parsing and network policy, ownership, SQLite transactions, and share URLs. Use Playwright for anonymous/account publication, owner/non-owner access, drag-and-drop and keyboard reordering with persisted order, delete/restore/tombstone, and public/QR/share views. Tests use a separate temporary database and must not depend on order.
204183
205The README must cover prerequisites, setup, environment, database, development and production commands, mock login, reset, tests, metadata behavior, troubleshooting, and assumptions.
184+Ship a README covering setup, environment variables, database, commands, mock login, reset, tests, and assumptions.
206185
207186## Completion
208187