Files
Brandon ThomasandClaude Opus 5.5 f37f82b400 Serve Craft App release info for ArtCraft Launcher (#71)
GET /v1/craft_apps/release_info reports the latest release of every
Craft App (and ArtCraft Launcher itself), so launchers stop polling
GitHub directly. A background thread polls GitHub and keeps a shared
cache.

Polling (threads/poll_craft_app_releases_thread):
- One round at startup (after 0–30 s of jitter, so a fleet deploy is
spread out), then every CRAFT_APPS_RELEASE_POLL_INTERVAL_SECS (30 min
default, 5 min minimum) with ±10% jitter
- One request per app (GET /repos/{repo}/releases/latest) with ETag
revalidation and 1 s between requests
- Rate limits stop the round and wait for GitHub's reset, backing off
2×, 4×, … the interval up to CRAFT_APPS_RELEASE_MAX_BACKOFF_SECS (4 h);
rounds where every request failed retry after 1, 2, 4… min, never more
often, up to the interval
- Per app all-or-nothing: either its release and app_last_fetched are
stored, or nothing about it changes
- CRAFT_APPS_RELEASE_POLLING_ENABLED, CRAFT_APPS_RELEASE_GITHUB_TOKEN
and CRAFT_APPS_RELEASE_APPS (key=Name=org/repo,…) configure it

Cache (state/craft_apps_release_cache): Arc<RwLock<BTreeMap<..>>> of
typed entries. release_info.last_fetched is the oldest app_last_fetched,
absent until every app has been fetched. Apps are listed from the start,
without a release or app_last_fetched until fetched.

The endpoint never fails, sets Cache-Control: public, max-age=60, and
returns empty app_renames, repo_renames and important_notices for now.
API types live in artcraft_api_defs::craft_apps::release_info, with each
asset's OS / arch / package type parsed from its file name. reqwest
moves from a dev-dependency to a dependency of storyteller-web.

Tests: cache freshness, asset-name parsing for every published file
type, untrusted-answer filtering, backoff, and an ignored live round
against GitHub that also writes a sample the launcher's parser is tested
against.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 10:35:45 -04:00

6.7 KiB

AGENTS.md

This file provides guidance for coding agents when working with the Artcraft monorepo.

Project Overview

ArtCraft Services contains the Rust backend, HTTP API, workers, and TypeScript web frontends for ArtCraft. The Tauri desktop application is maintained separately at https://github.com/storytold/artcraft.

Project Structure

artcraft-services/
├── _database/                    # SQL migrations and Elasticsearch schemas
│   └── sql/
│       ├── migrations/           # Server MySQL migrations
│       └── migrations_squashed/  # Materialized MySQL table definitions
├── _tools/postman/               # HTTP request collections
├── build/                        # Service Dockerfiles
├── crates/
│   ├── api_clients/              # ArtCraft API types, clients, router, provider clients
│   ├── cli/                      # Development and operations tools
│   ├── lib/                      # Shared Rust utilities
│   ├── schema/                   # Database queries, public tokens/enums, bucket paths
│   │   └── database/
│   │       ├── mysql_queries/    # SQLx queries shared by services, jobs, and CLI tools
│   │       ├── redis_common/     # Redis support
│   │       └── redis_schema/     # Redis key and HKEY topology
│   └── service/
│       ├── job/                  # Provider workers, media processing, analytics
│       ├── plugins/              # Shared billing and service components
│       └── web/storyteller_web/  # Main Actix Web HTTP API
├── frontend/
│   ├── apps/artcraft-webapp/       # Browser application at app.getartcraft.com
│   ├── apps/artcraft-website-next/ # Current public website at getartcraft.com (Next.js)
│   ├── apps/artcraft-desktop-website/ # Static landing page at desktop.getartcraft.com (Vite)
│   └── libs/                      # Shared React components and TypeScript libraries
└── Cargo.toml                    # Rust workspace

Desktop release links are configured independently in two places. When updating the advertised ArtCraft release, update both WINDOWS_VERSION and MAC_VERSION in both unless the request explicitly targets one platform or app:

  1. Current public website (Next.js): frontend/apps/artcraft-website-next/src/lib/download-links.ts. This controls the installer links and visible version at https://getartcraft.com/download. Add the release to DOWNLOAD_HISTORY (newest first), then update both version selectors.
  2. Browser webapp: frontend/apps/artcraft-webapp/src/config/github_download_links.ts. Update both version constants; these links are used by the welcome and checkout-success pages at app.getartcraft.com.

Keep previous releases for rollback. The Next.js download page reads its visible version from DOWNLOAD_VERSIONS, which uses the same selectors as the URLs; do not hardcode a separate version in the page component.

Check that both configs produce the requested release tag and installer filenames for Windows (ArtCraft_<version>_x64-setup.exe) and macOS (ArtCraft_<version>_universal.dmg). Run npm run typecheck from frontend/apps/artcraft-website-next after changing its config. Repository edits require deployment of the affected apps before the public pages change; do not report a local update as a deployed update.

Code Style

  • Rust with no minimum supported version
  • Actix-web for HTTP services
  • SQLx for MySQL and SQLite; prefer sqlx::query! / sqlx::query_as! compile-time checked macros over runtime sqlx::query() whenever possible
  • MySQL queries belong in the mysql_queries crate, not in application/service crates like storyteller-web — call those query functions instead of embedding sqlx::query! / sqlx::query() in handlers. (Query-writing conventions live in crates/schema/database/mysql_queries/AGENTS.md.)
  • A mix of wreq and reqwest for Rust HTTP clients
  • Never use println! or eprintln! outside of tests; use log crate macros instead
  • When two crates export the same type name, alias imports with a suffix: use foo::Bar as BarFoo;
  • Prefer use imports over inline fully-qualified paths; only qualify inline for true one-offs or std collisions
  • TypeScript with Nx, React, Vite, Zustand, and Three.js
  • Use two spaces for indentation

File Layout

Organize for top-to-bottom reading. Important things first, details later.

  • mod.rs holds module declarations only (pub mod foo;, plus re-exports if needed). Never put functions, structs, enums, constants, impls or tests in a mod.rs: give each its own file in that directory (for example threads/poll_foo_thread/poll_foo.rs, declared from threads/poll_foo_thread/mod.rs).

  • Constants at the top (after imports)

  • Structs/enums next; outer structs above inner sub-structs

  • API types in order: Request, Response, Error

  • In impl blocks: constructors first, then public methods, then private helpers

  • Private helpers go below the methods that call them

  • Among helpers: meatier logic above leaf-level formatters

  • In test modules: constants first, then test cases (grouped into sub-modules when 2+), then helper functions last

Public API Compatibility

  • Public HTTP endpoints have deployed legacy clients. Preserve their request and response schemas, defaults, limits, and pagination behavior when optimizing them.
  • Put a changed contract behind a new endpoint/version and update new clients to call it explicitly. Do not add a new mode or change response fields on an existing public endpoint as part of a performance fix.
  • For an endpoint-local revision, version the operation path (for example, /v1/media_files/list_v2/user/{username}). Reserve a top-level /v2 for broader API changes.
  • Add regression tests for the original contract alongside tests for the new route.

Markdown

  • Tables must be space-padded so columns align in plain text. Markdown tables are read raw (terminals, diffs, editors) at least as often as rendered, and condensed tables are unreadable there. Pad every cell to its column width:

    | Model        | Configuration | Credits    | Speed | Score   |
    |--------------|---------------|--------------------|---------|
    | Meshy 6      | text or image | 104        | 80.0  | +24     |
    | Rodin 2.5    | text or image | 13         | 10.0  | +3      |
    

    Not: | Model | Configuration | Credits | Speed | Score | packed tight with varying widths per row.