The hero overlay title, tagline and author row get a dark text shadow
so they stay readable over bright cover art. The owner's More menu
gains Add screenshots: a modal with the shared attachment uploader
whose POST /projects/{slug}/screenshots (ProjectScreenshotsForm,
owner-only, audit project.screenshots.add, catalogued in events.md)
links the uploaded uids through the same attachments.link_attachments
choke point as create/edit and returns to #screenshots. Documented as
projects-screenshots in the API docs, exposed to Devii as
project_add_screenshots (upload via upload_file/attach_url first), and
covered by an api test for the owner flow, the gallery render, and the
non-owner 403.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The hero now matches the showcase reference: the title block, tagline
(first description line), type/platform chips, author row and started
date render OVERLAID on the cover banner behind a bottom scrim, with
the optional project logo as a framed tile beside them and the Visit
Website CTA on the right. The section tab bar switches to the underline
style with Overview active.
Owners control the missing pieces from the create and edit modals,
which are now multipart: cover_image and logo_image file uploads
(stored as bare uploaded filenames via save_inline_image, the
posts.image pattern; a new upload replaces the previous one) plus a
repo_url sibling of website_url (same normalization/validation). The
cover feeds og:image and the schema image, the logo becomes schema
thumbnailUrl, and sameAs now carries website + repository. repo_url
rides ProjectOut, the Devii create/edit actions and the API docs;
tests cover repo normalization, the multipart upload round-trip and
the hero render.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The project detail page becomes a full project showcase on the site's
content measure. The hero card opens with a cover banner from the
project's first image attachment (brand-gradient band as fallback),
then title + status chip, type/platform chips, dates and forked-from
meta, the author row with an owner-set Visit Website CTA, and the
unchanged action row. A sticky anchor tab bar (Overview, Devlog,
Screenshots when images exist, Comments, Files) navigates the page
with plain server-rendered anchors so crawlers index one complete
document. The two-column body keeps About (description + non-image
attachments), the Devlog timeline and the comment thread in the main
column, adds a Screenshots gallery built from image attachments
(lightbox-wired thumbnails), and a sidebar with Links (website, files,
fork source), the Stats card with a last-update line, and the Author
card.
New optional projects.website_url rides the whole stack: normalized
and validated in models (scheme-less input gets https://, non-http(s)
rejected), settable in the create and edit modals, on ProjectOut, in
the Devii create/edit actions and the API docs, rendered as the hero
CTA and Links entry with rel noopener nofollow, and emitted as
schema.org sameAs. The app schema also gains screenshot urls from the
image attachments. The e2e project comment/files tests scope their
locators (.comment-form textarea, .project-detail-actions a) per the
documented dual-control idiom - the composer modal made the bare
selectors ambiguous - and new unit/api tests cover URL normalization,
sameAs/screenshot schema output, the cover, and the gallery.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The project detail page becomes a professional project overview: hero
with status/type/dates/forked-from, a stats strip (stars, updates,
comments, files, forks with #devlog/#comments anchors), an About
section, platforms, and the Devlog timeline under a proper h2 - now
rendered with feed.css loaded so the post cards are actually styled.
The owner posts updates from the page itself: a Post update button
opens the shared create-post composer preset to the devlog topic and
this project. The composer form is extracted into
_post_composer_form.html and reused by feed.html - one form, two
surfaces.
SEO: software_application_schema is type-aware via project_schema_type
(game -> VideoGame with gamePlatform, website -> WebApplication,
software/mobile_app -> SoftwareApplication, game_asset -> CreativeWork)
and now carries keywords, image, an aggregateRating from stars, and a
comment InteractionCounter. project_devlog_schema emits a Blog node
with one BlogPosting per devlog entry. The detail route feeds both,
adds meta keywords, and exposes the devlog cursor as rel=next; the
sitemap's project lastmod follows the newest devlog post via one
grouped query. devlog_count/comment_count ride ProjectDetailOut, the
docs projects-detail endpoint documents the before cursor, and the
routers/projects CLAUDE.md, templates CLAUDE.md and README document the
new surface. Unit, api and e2e tests cover the schema mapping, the
JSON-LD in the rendered page, the stats strip, and the preset composer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements the moderation and consent obligations a social platform carries,
so the web version and any client that speaks to it enforce the same rules.
Moderation core (services/moderation/, database/moderation.py): a reportable
target registry, the content filter and its choke points, the report queue with
atomic resolution, enforcement actions, consent tracking, maturity gating, and
account deletion with a grace window.
Surfaces: POST /reports plus the member report list, /admin/moderation and the
per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and
account deletion under /profile, the report button and dialog partials, the
maturity gate, and the moderation stylesheet and ReportDialog client.
Every user-generated surface stays reportable by construction: new content tables
are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a
reason, and the registry test fails the suite on anything left unclassified.
Docs: community guidelines, content moderation, intellectual property, privacy,
terms, contact, and the admin-only moderation operations page, plus the
moderation API group and the Devii moderation actions.
Compliance record: applecomp.md is the requirement register, applechanges.md the
gap analysis against this codebase, and appleimpl.md the implementation design
they resolve to.
Tests cover the report flow, admin moderation, consent, account deletion, terms
acceptance, workspaces, and the registry invariant across the unit, api, and e2e
tiers.
A new tunnel row was inert at pending until the next WorkspaceService
tick picked it up, so the editor certificate waited up to 30 seconds
before the ACME order even started. Opening a workspace and reaching its
public hostname in that window served the wildcard default certificate
and produced ERR_CERT_COMMON_NAME_INVALID in the browser.
certs.certify now owns the whole transition - provisioning, issue,
active or failed, then the owner notification - and both callers use it.
provision.ensure schedules it for the editor tunnel the moment the
workspace exists, claiming the row synchronously so the tick can never
issue the same host twice and burn a duplicate-certificate slot. The
service loop keeps calling certify per pending row as the safety net for
user-created tunnels and for rows created while molohttp was
unconfigured.
Issuance is a loop task rather than an await: an order takes about ten
seconds and certs.issue allows up to 180, which would hang the workspace
POST. The task is held until it completes so it cannot be collected mid
flight.
resume also restarts the idle clock, and the workspace routes gain
HTTP-level tests covering every refusal path they had none for.
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.
Restores a working import graph and closes two data-correctness bugs, plus
adds a reset for the AI gateway's rolling 24h spend.
Circular import: database/__init__ -> engagement -> content -> utils ->
database made the package unimportable. get_project_devlog moves out of
database/engagement.py into content.py, where enrich_items already lives.
Primary administrator: _can_hold_primary_admin read is_active with
bool(row.get("is_active")), so an admin row whose is_active column is SQL
NULL (any row predating the column) was treated as deactivated and skipped.
Every other site defaults an unknown is_active to active; this one now does
too.
Profile JSON: xp_next_level and xp_progress_pct were computed but only put on
the top-level context, never on profile_user, so they serialised as null even
though UserOut declares them and the API docs document them as embedded there.
Gateway quota reset: a cap previously lifted only with the passage of time.
quota.reset upserts a watermark row into gateway_quota_resets, scoped by the
same three nullable dimensions as a quota rule, and spent_24h sums from
max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on
/admin/ai-usage stay intact. Reaches every surface: POST
/admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool
gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API
docs. Admin's Reset all quotas now stamps a global gateway watermark too,
which is what a caller stuck on "AI gateway daily quota exceeded" needed.
Startup: _backfill_gamification swept every xp=0 user on every boot in every
worker and could never converge, since a user with no content earns no XP.
It now intersects pending users with _milestone_candidates(). db.tables is a
live reflection, so it is hoisted out of the loops that probed it per row.
Docker: the dependency layer now depends on pyproject.toml only, so a source
edit no longer reinstalls every dependency and re-downloads Chromium.
Adds start_interval so the healthcheck probes during the start period, and a
docker-reload target, since docker-up does not restart an unchanged container.
Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz
docs and the tooling all referenced but which never existed: 288 keys across
28 categories, including the families built from a variable at the call site.
Test fixes: both devlog helpers dated post 0 as the newest while the tests
assumed post 2 was; a profile login posted username= to a form that takes
email=; a devlog assertion matched six buttons under strict mode; and the
primary-admin tests seeded founders newer than the back-dated fixture admin,
so they only passed without the api tier.
Full suite: 2989 passed, 1 skipped.
- Policy: every change ends with make test (all tiers, all tests) green; docs and agent guardrails updated accordingly
- schema.py: ensure the full filtered/indexed column set of instances via get_table (ingress_slug, ports_json, container_gateway, slug, status, ...) so a partial first insert can never break the ingress proxy
- docs_api: award body param location body -> json; gateway endpoints documented public -> user to match enforced auth
- tests: missing get_table import (trash restore), audit read as admin (award), deterministic online-roster and leaderboard-cache handling, container visibility updated to the primary-admin-only rule, scoped AI usage heading selector past the hidden Tools nav links
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`.
Add comprehensive test coverage for seven new API areas:
- Backup schedules CRUD operations in admin panel
- Service configuration saving and retrieval for news AI model
- Token authentication with email/username and JSON body support
- Bookmarking for project and news targets with invalid type validation
- Content permissions testing with member and admin session fixtures
- Devii adopt endpoint redirect behavior and session cookie handling
- Devrant authentication flow with user registration and token retrieval
- Add `get_admin_uids()` and `get_primary_admin_uid()` to database.py for resolving the earliest-created admin
- Modify `can_view_project()` in content.py so a project hidden by an admin is invisible to other admins (both web UI and REST API)
- Update `_download_url()` and `_backup_payload()` in admin/backups.py to accept a `can_download` flag, gating the download endpoint with `is_primary_admin()`
- Remove `role` from `_user_facts()` in docs_live.py to avoid leaking admin status in live docs
- Update doc summaries in docs_api.py to reflect the new admin-visibility and backup-download semantics
- 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.