The workspace feature shipped its routes, agent tools and docs, but the
editor was never reachable: the project page had no entry point, the ppy
image had no code-server binary, no certificate was ever requested for a
tunnel, and both nginx and the proxy dropped what the editor needs.
- Add a VS Code button to the project action row and a Workspace item to
the overflow menu, gated by can_open_workspace plus a running instance
(viewer_can_workspace and workspace_editor_url on ProjectDetailOut).
- Install a pinned code-server in ppy.Dockerfile before USER pravda and
assert it in the build smoke test, so an image that cannot run the
editor no longer builds green.
- Run the editor with --auth password and a per workspace 8 character
pronounceable secret, minted once at the ensure_editor_password choke
point and injected as PASSWORD. Keep it off WorkspaceViewOut, which the
admin listing shares.
- Publish the editor tunnel when a workspace is created and issue its
certificate from a new WorkspaceService phase against the molohttp admin
API, then notify the owner with the live URL and the password. Only
pending rows are retried, so a broken host cannot burn the ACME failure
rate limit. Renewal stays molohttp's job.
- Forward the original Host on proxied requests and the client cookie on
proxied websockets, so code-server scopes its session cookie to the
public hostname and authenticates the workbench socket.
- Recreate a container stuck in the created state instead of retrying
docker start forever against an image it can no longer run.
- Return a JSON string from WorkspaceController.dispatch; raw dicts landed
in a tool message and aborted the turn at the model endpoint.
- Let the nginx catch-all carry websocket upgrades, keeping upstream
keepalive, so tunnelled apps and the editor both connect.
Implement a new `avatar_seed` column on the users table that overrides the username-based seed for Multiavatar generation. Introduce a null-safe `avatar_seed(user)` choke point in `avatar.py` that resolves `user.get("avatar_seed") or user.get("username")`, registered as a Jinja global so every render site (`_avatar_link.html`, `avatar_url(...)` calls, SEO `og_image`, issues ad-hoc dicts, devRant payload/PNG) propagates a regenerated seed. Add `POST /profile/{username}/regenerate-avatar` endpoint (owner-or-admin only) that writes a fresh `generate_uid()` to `avatar_seed`, invalidates the target's user cache, and audits `profile.avatar.regenerate`. The previous seed is overwritten and never stored, making regeneration irreversible. Document the feature in `AGENTS.md` and `README.md`, add the API endpoint to `docs_api.py`, and include the `regenerate_avatar` Devii tool in `CONFIRM_REQUIRED`.
- Record audit events in `admin_trash_restore` and `admin_trash_purge` endpoints with target metadata
- Log `notification.read.all` event when user clears devRant notification feed
- Include `devrant` as a valid origin in audit categories
- Add `admin_section` and `pagination_query` fields to audit and backup schemas for UI consistency
Split the monolithic test directory into three tiers (unit, api, e2e) with a path-mirroring directory structure. Added corresponding Makefile targets (test-unit, test-api, test-e2e) and updated all documentation references (CLAUDE.md, README.md, testing-cicd.html, testing-framework.html, testing-make.html) to reflect the new layout and naming conventions.