v2.0.0 alpha#779
Open
ajslater wants to merge 248 commits into
Open
Conversation
Bump comicbox to 4.0.0a0 and update import paths to match the reorganized layout: - comicbox.schemas.comicbox.*KEY* -> comicbox.formats.comicbox.schema - comicbox.schemas.comicbox.yaml -> comicbox.formats.comicbox.schema.yaml - comicbox.schemas.comicinfo -> comicbox.formats.comic_info.schema - comicbox.fields.fields.IssueField -> comicbox.formats.base.fields.fields - comicbox.fields.number_fields.PAGE_COUNT_KEY -> comicbox.formats.comicbox.schema - ID_KEY_KEY / ID_URL_KEY moved to comicbox.identifiers Adds a local editable source override under [tool.uv.sources] so the branch resolves against ../comicbox while 4.0.0a0 is unpublished. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tag Edit Dialog: - Full metadata editing: groups, credits, technical fields, tags, universes, identifiers, and read-only info (path, dates, size) - Format-aware field disabling based on selected write formats - Per-field clear controls for explicit field clearing - Use comicbox "update" mode (removed write mode selector) - Dismiss dialog on save since metadata is stale until reimport Admin Tagging Tab: - Compound h/m/s timeout input, collapsible credential panels - Removed Comicbox/CoMet/ComicBookInfo format options - Credential status shown green, clear credentials with confirm - Disabled online sources without credentials - Fixed match mode values to match comicbox (strict/normal/fast) - Moved tab after Flags, before Jobs Online Tagging: - Prompt button in browser toolbar with pulse animation - Prompt popup mounted in browser view (was admin-only) - Session state persisted to DB for cross-process/refresh survival - Prompts appear as they're deferred (not just at end of lookup) - Rate limit status shown in librarian status panel - Comic count, rate limit warnings, and time estimates in launcher - Credential-aware source disabling with admin link Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ations Edit Panel: - Change tracking: only modified fields are written, Save button disabled until actual changes are made, changed fields highlighted in orange - Per-field clear controls with tooltips (ClearFieldIcon component) for explicit field deletion vs leaving untouched - Removed write mode selector, always uses comicbox "update" mode - Confirmation dialog before write with conversion count and delete_original checkbox for CBR/CB7→CBZ conversion - Format-filtered select values: age ratings, original formats, reading directions limited to selected format capabilities - Age ratings show ComicInfo equivalents in parentheses - Stories label adapts: "Stories" / "Title" / "Stories / Title" - Added volume_count field, identifier URL parser - Per-role credit change highlighting aware of MetronInfo's per-person credit structure - Moved Tag Online and Edit Tags buttons to metadata controls row - At least one metadata format must be selected Admin Tagging Tab: - delete_original option with help text - Collapsible credential panels - Removed write mode selector Backend: - Preflight endpoint for conversion stats before tag write - Identifier URL parser endpoint using comicbox IDENTIFIER_PARTS_MAP - delete_original wired through task → tag writer → comicbox config - Format field support updated for MetronInfo (imprint, volume, etc.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Critical fixes: - Fix CamelCase parser mangling dict keys in tag write patches (person names, role names, arc names converted to snake_case). Patch now sent as JSON string, decoded server-side. - Fix online tagging deadlock: session blocked thread waiting for prompt resolution tasks that queued behind it. Now polls the thread queue during prompt wait. - Fix double import after tag write: only reimport comics in libraries without filesystem event watching. Online tagging improvements: - Flush auto-matched comic writes during rate limit pauses instead of waiting for entire session to complete. - Conversion warning with delete_original checkbox in launcher - Remove prompt status from librarian status bar (redundant with Match Review button) - Dismiss metadata dialog when online tagging starts - Rename prompt UI to "Match Review" - delete_original passed through online tag session Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… info - Add section headers (Publishing, Description, Credits, Details, Tags, Universes, Identifiers, Notes) for visual hierarchy - Move format selector inline with Save/Cancel toolbar - Replace mixed thirdRow/inlineRow with 2-column CSS grid for Details - Collapse read-only file info (dates, size, type, path) into expansion panel - Reduce section margins from 25px to 4px (headers provide separation) - Unify table footers: credits, universes, identifiers all use consistent flex row with add buttons left, Clear All right - Move universe/identifier footer buttons outside table elements Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a new online tagging task arrives while a session is already running, merge its comics into the active session instead of queueing a separate hidden job. The status total rises dynamically and the OnlineSession's series cache carries over. - _drain_thread_queue handles BulkOnlineTagTask by merging paths - _collect_results loops on pending_paths after each tag_many pass - Cumulative total_comics/completed_comics for accurate progress - RateLimited event detection: restored event-based handling (for comicbox fix) plus time-based fallback (>10s = rate limited) - Removed unused RateLimited import workaround Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Design doc covering an admin upload flow for group/folder custom covers, removal of the Library.covers_only fake-library mechanism, a one-time migration of legacy filesystem-discovered covers into a pk-keyed uploads dir, Volume custom-cover support, and a new admin "Custom Covers" tab. Implementation is deferred. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mp v2.0.0 Match-mode alignment - ComicboxTaggingDefaults.default_match_mode now stores the comicbox MatchMode enum values (careful/auto/eager) instead of the legacy strict/normal/fast strings. Migration 0043 is updated in place since it has not shipped to a stable release yet. - session_manager passes MatchMode(task.mode) directly to OnlineSession per the new comicbox API. - TAGGING_CHOICES + TAGGING_DEFAULTS land in codex.choices.admin and flow through choices_to_json to tagging-choices.json. The Vue components consume the generated JSON instead of hardcoding the arrays in two places. Credential scrubbing - httpx logs every request URL at INFO, leaking ComicVine api_key. - New scrub_secrets() in codex.settings.logging masks api_key/access_token/token/secret/password/auth query params and Authorization: Basic|Bearer headers. - Applied at LoguruHandler.emit (the stdlib bridge) and as a sink filter on main + failed-login sinks. Sink filters live at module level so loguru's enqueue=True pickle path doesn't break. Release prep - Version bump to v2.0.0 in pyproject.toml + frontend/package.json reflects the breaking comicbox 4.x dependency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Split SessionState + helpers into session_state.py and the tag-pass driver into tag_pass_runner.py to lift session_manager MI from B to A - Reduce TagWriter.write_tags complexity from C to B via small helpers - Replace pyright ignores with proper match-exhaustive `case _`, cast() forms, and a protocol-compliant prompt-handler signature - Whitelist string-cast imports (Literal, Mode) in vulture_ignorelist Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renames user-visible "metadata" to "tags" in browser sort/column labels, the comic dialog close button, the reader keyboard shortcut help, and six librarian/scribe + browser-metadata view log lines. Internal identifiers (modules, classes, the metadata_mtime field, URL paths) are left intact; the OPDS PSE debug log keeps "metadata" since it's spec terminology. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plan for adding a "Clear Stale Online Tagging State" janitor task and admin Jobs button, plus an OnlineTagThread startup hook that clears persisted session state on process restart. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit 71889c06142546726f5f88a978cd3efa17d91844
Author: AJ Slater <aj@slater.net>
Date: Mon May 25 13:59:10 2026 -0700
feat: add "Clear Stale Online Tagging State" janitor task
The online tagging pipeline persists session state to
ComicboxTaggingDefaults (active_session_id, active_prompts) so prompt
state survives process restarts. Until now there was no path — manual
or scheduled — to clear those rows when a session dies uncleanly.
- OnlineTagThread.run_start clears persisted state on every thread
start, since in-memory session state cannot survive a process
restart and any persisted row at startup is by definition orphan.
- New janitor task cleanup_tagging_state (status code JTG) runs
nightly and is exposed in the admin Jobs page as "Clear Stale
Online Tagging State". It asks the OnlineTagThread whether the
persisted session id is still in-memory and clears stale or
orphan-prompt-only rows under db_write_lock.
- LibrarianDaemon._create_threads gives ScribeThread (host of the
janitor) a back-reference to OnlineTagThread so the liveness check
can cross the thread boundary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit 2994e3ca8f9579d465d767f415d1e5b49eca2e63 Merge: 48837f60 7ddf056 Author: AJ Slater <aj@slater.net> Date: Mon May 25 14:05:16 2026 -0700 Merge branch 'codex-v2' into skip-all-prompts commit 48837f606a84bede2f78fcd7ae68c6c223c17e30 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:59:55 2026 -0700 feat: add Skip All button to online tag prompt dialog Skip-resolves every queued prompt in one shot so the session can unblock and commit auto-matched tags via the existing deferred-pass write. The session keeps running; new prompts streaming in from the active job will continue to fill the queue. Distinct from the existing controls: - per-prompt Skip resolves a single prompt - Abort Session tears down the session and writes nothing - Skip All resolves every current prompt with action="skip" and leaves the session running Backend: OnlineTagSkipAllPromptsTask routes through the daemon and session-manager's _drain_thread_queue to a new skip_all_prompts() method that snapshots state.deferred_prompts under the lock, preloads skip resolutions, persists once, and signals the wait event when the queue empties. Frontend: new Skip All button in the prompt dialog header, wired to a skipAllPrompts() store action that POSTs to /admin/online-tag/<session_id>/skip-all-prompts and clears the local prompt list. commit 8715b66d69ce7b3fd10aad9e9ea15a25ea5fb3b1 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:52:04 2026 -0700 fix: serialize OnlineTagSessionManager state mutations under one RLock resolve_prompt, _sync_deferred_prompts, and _wait_for_prompts all read-modify-write state.deferred_prompts but previously released the manager lock before mutating. If two of these ran on different threads (e.g. comicbox emits PromptDeferred on a worker thread while the librarian thread is processing a resolution), one update could clobber the other. Switch _lock to RLock so the lock can be held across the full critical section without self-deadlocking on _persist_prompts -> get_pending_prompts. Side effects that don't touch shared state (librarian_queue.put, state.event.set) are released first.
commit 6949f56a76c77371f019304660c33cb4e901b5e0 Merge: c0d91726 c348ebe Author: AJ Slater <aj@slater.net> Date: Mon May 25 14:09:37 2026 -0700 Merge branch 'codex-v2' into worktree-validate-tagging-creds commit c0d917268cedbab78168aa67e69e88550a083175 Author: AJ Slater <aj@slater.net> Date: Mon May 25 14:09:26 2026 -0700 feat: admin button to validate online tagging credentials Adds a "Test" button next to each source's Save under the admin tagging settings. Click sends current form values (falling back to stored credentials field-by-field) to a new /admin/tagging-defaults/validate endpoint, which builds short- lived mokkari / simyan clients with cache disabled and makes one tiny authenticated call per source. Per-source result lands as a green "Connected" chip or a red error chip — so operators learn about bad credentials immediately instead of mid-tagging-run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit be7a607f3855715507b40118d8c822ba4aecf094 Merge: df031486 0a5e0cb Author: AJ Slater <aj@slater.net> Date: Mon May 25 14:21:11 2026 -0700 Merge branch 'codex-v2' into feature/password-reset commit df031486ec30b159d00f4fd0070264f5a8e37ac5 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:42:33 2026 -0700 test(auth): unit-test new auth-store actions Eight cases for updateProfile / sendResetPasswordLink / resetPassword: empty-diff short-circuit; success path that refreshes user state and calls the API with only changed fields; error path that surfaces to commonStore. Pattern matches the existing favorites-store tests (mocked API module, fresh Pinia per test). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit 049af3da31c25375bc51e98ce7c7d55f3bc66ee2 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:41:01 2026 -0700 style: make fix + ty fixes for password-reset feature - ruff/prettier formatting auto-fixes - @OverRide on RegisterView.post (was implicit override warning) - ty: setattr on Field.allow_blank to avoid the static-checker walking the parent Field type's narrower interface - minor doc tweaks introduced by the formatter Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit 0c891d91cb96ff9638e6d56c08d113f163289ddf Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:33:21 2026 -0700 test(auth): password reset + register verification + profile coverage Covers 16 cases across 4 classes: * PasswordResetDisabledTests - default install: flags report emailEnabled=false; send-reset-link and reset-password return 404. * PasswordResetEnabledTests (override_settings + locmem) - flags flip; reset-link by username and by email each produce one mail with a signed link; unknown logins produce success + zero mail (enumeration-safe); happy-path reset actually changes the password; tampered signature 400s without touching it. * ProfileUpdateTests - self-service PATCH for email and username persists; under AUTH_REMOTE_USER the username field becomes read-only and the value sticks; duplicate username collisions surface a 400 via DB uniqueness. * RegisterVerificationTests - RV off creates active users with no outbox; RV on + email creates inactive users with one verification email; REGISTRATION admin flag off returns 404 from the register endpoint. CodexProfileSerializer: rest-registration hard-codes email as read-only on the assumption callers use the register-email/verify-email handshake. Codex's profile dialog edits email directly, so the serializer flips email back to writable alongside the username remote-user guard. USER_EDITABLE_FIELDS narrows the writable set so future fields default to read-only. Tests rely on a small _ensure_admin_flags helper to seed AdminFlag rows that startup normally creates (codex_init isn't invoked by the TestCase harness). cache.clear() in setUp keeps the reset_password ScopedRateThrottle scope from accumulating across sibling tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit c63fd0df12dbfedf9000a88d163ee72692a09828 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:25:19 2026 -0700 docs(auth): document [email] config + password-reset workflow Adds a "Users & Access" feature bullet and a new "Email & Password Reset" subsection under Administration. Calls out the Gmail App Password requirement, the Amazon SES verified-identity requirement, and the from_address fallback for generic SMTP relays. Notes that existing users need email backfill (admin Users tab or self-service Profile) before they can request a reset. Embeds the throttle.reset_password and [email] block in the README's Full codex.toml Reference so the inline docs stay in sync with codex.toml.default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit 9a51d191590afcea9ce9329b7018ec202d538090 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:24:13 2026 -0700 feat(admin): expose email in admin user CRUD Adds email to the admin UserSerializer fields (optional, allow_blank). The admin user-tab gains an Email column and the create/update dialog gains an Email input with format validation. Admins can backfill emails for existing accounts so their users can use the password-reset flow. Removes the unconditional ``validated_data['email'] = ''`` from the create path so submitted emails are honoured; defaults to empty when not provided. Drops the corresponding queryset defer so list/detail GET returns the field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit e916ac2f868b543d1375cf10903656d8a8c5e6c9 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:22:14 2026 -0700 feat(auth): forgot-password dialog + reset-password confirm route Adds two components: * ResetPasswordRequestDialog: link form rendered inside login-dialog, gated by adminFlags.emailEnabled. Submits to /auth/send-reset-password-link/ and always reports the same generic "if that account exists, a link was sent" message so callers can't enumerate users. * ResetPasswordConfirm: standalone page at /auth/reset-password that parses signed user_id / timestamp / signature from the URL the email link landed on, takes a new password + confirm, posts to /auth/reset-password/. Camel-cases the user_id key for the JSON body (the parser snake-cases at the API boundary). Wires the route into vue-router with lazy import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit 6dd25358a39692fdc427eba8810526196adfe50f Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:20:11 2026 -0700 feat(auth): profile dialog with username + email self-service Adds CodexProfileSerializer that marks username read-only when AUTH_REMOTE_USER is on (backend belt-and-braces behind the disabled UI). Registered via REST_REGISTRATION["PROFILE_SERIALIZER_CLASS"]. Adds API helpers updateProfile, sendResetPasswordLink, resetPassword and matching Pinia store actions. New showProfileDialog + showResetPasswordRequestDialog state slots; isAuthDialogOpen widens to include them. New ProfileDialog renames the auth menu item to "Profile" and groups username + email + collapsible password-change in a single dialog. A single Save button computes the diff against the current user and issues PATCH /auth/profile/ for changed fields + POST /auth/change-password/ only when the password panel is filled. The username field is read-only with "Managed by upstream authentication" helper text under remote-user mode; otherwise it carries a warning about OPDS / API client re-configuration. The original change-password-dialog stays in place for the admin user-tab flow (admin changing another user's password). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit ad24f61af354001d4ac40bcc2c4a625a8171a91d Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:16:33 2026 -0700 feat(auth): expose email_enabled + remote_user_enabled + register_verification on /auth/flags/ AdminFlagsView merges the new REGISTER_VERIFICATION AdminFlag plus two settings-derived booleans into its response so the frontend has the full auth-related context in one request. emailEnabled gates the "Forgot password?" link; remoteUserEnabled gates the username field in the (upcoming) profile dialog. Admin flags tab gains a description + group entry for the new RV flag. Frontend auth store declares the new fields as undefined placeholders so reactive bindings don't read missing keys before /auth/flags/ returns. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit 77e8913f49145c357c3a9aa5082d0ba88b272776 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:14:27 2026 -0700 feat(auth): REGISTER_VERIFICATION admin flag + custom register view Adds REGISTER_VERIFICATION = "RV" to AdminFlagChoices (seeded off via FALSE_DEFAULTS) and migration 0044. When the flag is on AND email is configured, new accounts are created inactive and a verification email is sent; otherwise they activate immediately. The custom RegisterView checks both REGISTRATION (existing) and REGISTER_VERIFICATION at request time rather than relying on rest-registration's cached settings, so admin toggles via the UI take effect on the next request. Mounted at /api/v3/auth/register/ before the rest-registration include so it wins URL resolution. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit 400887c81f30afc0583b0a1220e0cbe2b7c34375 Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:09:15 2026 -0700 feat(auth): SMTP config + rest-registration reset-password wiring Adds [email] section to codex.toml with documented Gmail / generic SMTP / SES examples and from_address fallback to user. EMAIL_ENABLED is the boolean gate: when host or sender is missing, EMAIL_BACKEND falls back to dummy and rest-registration's RESET_PASSWORD_VERIFICATION_ENABLED is False, so reset endpoints respond 404. Wraps rest-registration's send-reset-password-link and reset-password views with ScopedRateThrottle (reset_password scope, default 5/hour); overrides are registered before the rest_registration include so they match first. Ships plain-text email templates for the reset and register verification flows. RESET_PASSWORD_VERIFICATION_URL includes GRANIAN_URL_PATH_PREFIX so reverse-proxy installs get correct links. Email is intentionally removed from USER_HIDDEN_FIELDS so users can self-service it through the profile dialog (Phase 5). USER_LOGIN_FIELDS includes both username and email so the reset link can be requested with either. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> commit d756b97e6f9e4c00d4bc2ce7f5f2626ca840a7db Author: AJ Slater <aj@slater.net> Date: Mon May 25 13:00:41 2026 -0700 docs: update password-reset plan with username editing + remote-user guards Folds in the username-editing recommendations: Profile dialog includes username with helper text about OPDS clients, hidden when AUTH_REMOTE_USER is on. Adds remote_user_enabled to the capability flags exposed via /auth/flags/. Tests cover username PATCH, remote-user reject, and uniqueness collision. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…server When the [email] config is unset, password reset can't work, so the email field's primary purpose is gone. Tuck it into an expansion panel (same pattern as Change Password) so it's still settable for when an admin configures SMTP later, without competing for visual weight with the username and password sections. The visible-field variant keeps its current hint. The collapsed variant gets a hint that explains the field is dormant pending mail server setup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit 883b522c0cad38b9bd7c9cd58bd47a4557617fa5
Author: AJ Slater <aj@slater.net>
Date: Mon May 25 16:51:28 2026 -0700
fix(migrations): renumber custom-cover migrations after merge
codex-v2's 0044_register_verification_admin_flag collided with the
custom-cover 0044. Shift the three custom-cover migrations to
0045/0046/0047 and update their dependency chain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit 61d9ff9f7d8b3a077c22b14505ab9c0dc2e8a109
Merge: 6334248a 4f89764
Author: AJ Slater <aj@slater.net>
Date: Mon May 25 16:48:12 2026 -0700
Merge branch 'codex-v2' into worktree-custom-covers
commit 6334248a88e2491eb1740e659e4f5e3b7be44757
Author: AJ Slater <aj@slater.net>
Date: Mon May 25 16:48:06 2026 -0700
feat: custom cover upload UI; remove covers_only library hack
Admins can now upload, replace, and remove custom group covers from
each card's action menu and manage them in a new "Custom Covers"
admin tab — replacing the filesystem-watched ``config/custom-covers/``
and in-library ``.codex-cover.{ext}`` mechanisms with a direct
``POST /api/v3/admin/custom-cover`` flow.
Volumes now support custom covers (previously skipped because their
numeric name couldn't be matched by the legacy sort-name lookup).
The ``Library.covers_only`` flag and its many downstream branches —
filesystem watcher, poller, snapshot, importer, janitor, telemeter,
browser view, library admin view, library serializer, and frontend
admin store — are removed. ``CustomCover.library`` becomes nullable;
the synthetic covers-only ``Library`` row is deleted by a one-time
data migration that also moves legacy on-disk covers into the new
``config/custom-covers/uploads/{pk}-{group_char}-{slug}.{ext}``
layout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User-facing changes since v1.12.7: tag editing & online tagging, custom cover uploads, user-data backup/restore sidecar, email password reset, and the settings move from codex.toml into the Admin UI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- demote the Docker-move announcement to a quiet note - drop the obsolete pymupdf AARCH64 workaround; a prebuilt wheel now ships - fix `docker run` image (ghcr.io), libssl3, and Emergency DB Repair paths (codex.sqlite3 + .before-rebuild.bak) - /api/v3/auth/login -> /api/v4/auth/login - favorites group -> collection name-chain - advertise Edit & Tag / Online Tagging in Features - flag [browser]/[throttle]/[email] config as Admin-UI-managed (deprecated) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Help text under the Comic Vine API Key and Metron password fields in the Online Source Credentials section now links to where users obtain credentials: comicvine.gamespot.com/api and the metron.cloud account signup page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comic.main_team was copy-pasted from main_character and incorrectly targeted the Character model; a "main team" must reference a Team. The importer (PROTAGONIST_FIELD_MODEL_MAP), the comic serializer (TeamSerializer), and the browser ordering/intersection queries already treated main_team as a Team, so only the model field was wrong. Squash the corrective AlterField into migration 0043 (the unreleased 2.0.0 head) instead of shipping a standalone migration; main_team was originally added in released migration 0034, which is left untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 5 JSON files imported by the metadata edit panel (format-field-support, format-field-values, identifier-sources, identifier-types, languages) were gitignored build artifacts that choices_to_json.py never produced, so a clean checkout failed the Vite build with UNLOADABLE_DEPENDENCY. Add codex/choices/tagging.py to derive all five from their upstream sources -- comicbox transforms/enums, pycountry, and codex's own IdentifierType -- and wire them into choices_to_json.py so `make build-choices` emits them. Deriving also corrects stale data the old fixtures carried: ComicInfo now supports country; MetronInfo gains language/original_format and drops country, matching current comicbox. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
USER_LOGIN_FIELDS includes "email" so the password-reset form can resolve a user by username or email, but codex runs on the stock auth.User whose email is non-unique and blank=True. rest-registration's E010 system check requires every login field be DB-unique, which is impossible here: empty emails are normal, so a unique constraint would forbid more than one account without an email. Disable the check via the package's sanctioned USER_LOGIN_FIELDS_UNIQUE_CHECK_ENABLED flag. Email lookup stays best-effort by design -- the login form is username-only and the reset-link endpoint returns generic success (no enumeration), so a non-unique email degrades gracefully rather than being an integrity hazard. Fixes the `make django-check` SystemCheckError. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…BasedCache cachalot's W001 check string-matches CACHES['default']['BACKEND'] against a hardcoded set instead of using issubclass(), so it flags our ResilientFileBasedCache subclass even though its FileBasedCache base is supported. cachalot was never actually disabled; this just quiets the spurious warning. Filebased is also the correct backend for our multi-process layout where LocMemCache would not propagate invalidation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LIBRARIAN_QUEUE and BROADCAST_QUEUE are module-level multiprocessing.Queue singletons. Tests put tasks on them but no librarian process drains the pipe under pytest, so the queue's daemon feeder thread blocks once the pipe buffer fills. At interpreter shutdown multiprocessing's atexit finalizer join()s that feeder thread (Queue._finalize_join) and waits forever: pytest reports "426 passed" then hangs, and CI never advances. Add a pytest_sessionfinish hook that calls cancel_join_thread() on both queues so the unconsumed buffered items are dropped and the process exits cleanly. Production is unaffected: the real librarian daemon drains these queues. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This reverts commit 7ec7729.
commit 1f5015d8ccc10a6b8bf0ec06ded6e5cf17aaf66e
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 16:31:22 2026 -0700
Admin Tagging tab: move Metadata Formats docs into the field hint
The "These metadata formats are written..." line with the ComicInfo /
MetronInfo links was a section-level hint but really describes the
Metadata Formats select. Render it through the select's #message slot so
the links live in the field's own hint (VSelect forwards #message down to
VInput's VMessages), styled identically to every other field hint. The
section hint keeps only the write-permission warning.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit e831f2361af916e5dd3cfd71258051717b904cee
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 16:22:37 2026 -0700
Admin Tagging tab: auto-save defaults, reorder, inline help
Tagging Defaults and Online Tagging Defaults now save on change and the
Save Defaults / Revert buttons are gone. A deep watch on the draft
auto-saves (serialized, with a one-shot re-run if the draft changes
mid-save); the defaults watcher seeds the draft once on load so a save's
echoed response can't clobber an in-flight edit.
Reordered sections to: Tagging Defaults, Online Tagging Defaults, Online
Tagging Sources.
Replaced the Online Tagging Sources hint paragraph with a per-checkbox
title tooltip shown only while the checkbox is disabled (title on a
wrapper element, since Vuetify drops pointer events on disabled
controls).
Added Match Mode and Prompts help text (static strings in data, not
computed).
Updates tagging-tab.test.js (12 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit d25bf69c6b52e1689ebdb64cb89689c65383f77a
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:43:42 2026 -0700
Squash 0044 field alterations back into 0043
Fold the auto-generated 0044 migration into 0043 in place so the
migration history stays a single comicbox-tagging-defaults step:
- adminflag.key: BG label "Browser Default Group" -> "Browser Default
Collection" (group->collection unification drift)
- librarianstatus.status_type: add JDU (Snapshot User Data Sidecar)
and JTG (Cleanup Stale Online Tagging State)
- settingsbrowser.order_by: add the missing AlterField
makemigrations --check --dry-run reports no changes detected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit e1a29cfd5f542a89add7dc05bbefac78dffac40e
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:36:03 2026 -0700
bump alpha version
commit 35859ea7396079298bb8212743f24d77e3bc14ab
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:35:17 2026 -0700
Tag editor: fix Identifiers round-trip (blank Source/Type/Key)
shape_identifiers() now emits the raw `source` (e.g. "comicvine") alongside `displayName` in the {pk, source, type, code, displayName, url} shape, and the edit panel's initFromMetadata reads that structured shape instead of parsing the legacy "source::id_type:key" name string. The legacy parse populated the right number of rows but left every Source/Type/Key field blank.
Tests: shape_identifiers raw-source/null/empty coverage (backend); edit-panel identifiers round-trip regression — N populated rows, none blank (frontend).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit bcbfa9a83ffc034cf9c190cf1a455c2335571153
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:27:49 2026 -0700
Tag editor: tooltips on format-disabled add buttons and inputs
Disabled-by-format controls now carry the "Not supported by selected
metadata formats" tooltip. The title sits on a wrapper element because
Vuetify sets pointer-events:none on disabled controls, so a title on the
control itself never fires. Covers the Add Universe / Add Identifier /
Add URL buttons, the Add-role select, the credits / story-arc / universe
/ identifier table cells, and the Main Character / Main Team selects.
Adds edit-panel-disabled-tooltips.test.js (5 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 9ea8eed6df3ae2db7c2f2c906c9238c4b9deb4cd
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:21:20 2026 -0700
reorg admin tagging tab
commit e5ef1a8a11187b24457ef59f94836479a9fb6cf4
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:15:49 2026 -0700
Online tagging: honor prompts_mode "never" (skip, don't queue)
When a scan's prompts_mode is "never", build the session with
defer_prompts=False so comicbox skips ambiguous matches inline (the
CodexPromptHandler skips by default) and don't persist any prompts —
only confidently-matched comics are written this run. "ask" is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit c0530362ec3e1e832ef6b4a1e3085046aa55d519
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:11:00 2026 -0700
Migrate BROWSER_DEFAULT_COLLECTION admin flag char -> collection
Migration 0039 seeds the BG (Default View) admin flag with the legacy
top-group char "p". The group->collection unification in 0043 rewrote
every char-coded value for SettingsBrowser/Favorite/CustomCover but
missed this AdminFlag, leaving DBs upgraded through 0043 with an invalid
BG value: the read path silently fell back to "publishers" and the admin
"Default View" dropdown rendered blank.
Fold the char->collection flip into 0043 alongside the
SettingsBrowser.top_collection move it mirrors, and add regression tests
covering all seven chars, the reverse move, and idempotency.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit c1d7525d38646aef13feae1efe7a7dc3e4628f40
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:46:44 2026 -0700
Online tagging: non-blocking prompts, drop Prompt Timeout
Tagging scans no longer block waiting for an admin to answer ambiguous-
match prompts. run_session does Pass 1 (auto-match + write the confident
comics), persists any deferred prompts to the cache, and returns — the
librarian thread is never held.
Prompts now persist independently of any scan: they linger in the cache
(no TTL, surviving daemon restarts) until answered or skipped. Answering
a prompt is decoupled — resolve_prompt builds a fresh OnlineSession and
applies that single comic's chosen match (re-mapping a chosen index to
its issue id so a re-search can't mis-pick), then enqueues a one-comic
write. The deferred-prompt fingerprint is deterministic across processes,
so no live session is required.
This makes the Prompt Timeout setting obsolete: removed the
prompt_timeout_seconds field (model, migration 0043, serializer,
user-data dump/restore) and the blocking _wait_for_prompts wait.
Supporting changes:
- session_cache: prompts are a global {fingerprint: prompt} map plus an
active-scan marker; daemon run_start clears only the scan marker.
- prompt endpoints are session-agnostic (/admin/tag-prompts[...]).
- janitor prunes only prompts whose comic is gone, not "orphan" prompts.
- frontend store/socket use the new endpoints and resync on (re)connect;
removed the Prompt Timeout input + timeout-input.vue.
- tests for the non-blocking flow, resolution, skip, endpoints, janitor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 708f7ee822c6b2f64e8da4259a4f3e2ae57eedec
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:37:34 2026 -0700
Tag write errors: admin feedback panel, badge, and docs
Collect comic tag-write failures (read-only mount, permission error)
in the filesystem cache and surface them to admins:
- tagwrite_errors fs-cache accessors + recording in TagWriter
- TAG_WRITE_ERRORS_CHANGED admin-only websocket notification
- read/clear endpoint (AdminTagWriteErrorsView)
- Tagging-tab error panel (#tagging-errors anchor), a red dot on the
hamburger menu, and a sidebar drawer link to the error section
- write-permission help on the Tagging tab; README/DOCKER/NEWS notes
that the comics dir must be mounted writable for tag editing to work
Note: the URL route (urls/api/v4/admin.py) and the socket dispatch
case (stores/socket.js) are intentionally NOT in this commit because
those files are mid-refactor in a concurrent session; both of my hunks
are present in the working tree and should be committed alongside that
work.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit d8605b9002ef61f8e7877b3993c1c2321e816afb
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:31:44 2026 -0700
use key as icon for bearer token
commit f670b9099e26235bd5ead9247f586ac2fc5e644a
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:28:50 2026 -0700
more explicit verbose description of bearer token
commit f53b58ed68dc21a7732c776428e5dca3e96d5507
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:12:04 2026 -0700
Rename Metron online source to "Metron Cloud" in UI
Disambiguates the Metron online tagging source from the MetronInfo
metadata format. Presentation strings only — internal source values,
variables, and method names are unchanged.
- Admin Tagging tab, Online Sources: card title, enable aria-label,
signup link, save/clear credential controls
- Online Tagging launcher dialog: source picker title + rate-limit label
- Match Review dialog: map the source chip to a friendly label
(also Comic Vine), falling back to the raw id for unknown sources
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 15488b07b77bb16f4859eec64262aa8c4a2aa391
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 13:53:17 2026 -0700
change write tags headline
commit 7139e18d4fb16a36747908d190c3ddbd92b81445
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 13:50:37 2026 -0700
Tagging tab: per-source enable checkboxes + Write Defaults help
Replace the "Online Sources" multi-select on the admin Tagging tab. The
Online Source Credentials panels now live inside the Online Tagging
Defaults block, each with an enable checkbox to its left that drives
defaultSources. The checkbox stays disabled until that source's
credentials are saved, and a credential-less source always reads as off.
Add Write Defaults help text noting the formats are written every time
tags are edited, with links to the ComicInfo and MetronInfo docs.
Add tagging-tab unit tests covering the enable-checkbox wiring, the
credential-gated disable, and the help links.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- session_manager: compare prompts_mode against PromptsModeChoices.NEVER.value so the check is str != str, not str != the member's tuple value type (reportUnnecessaryComparison). Runtime-equivalent. - test_onlinetag_session_manager: funnel intentional test doubles through an Any-returning _double() seam so they satisfy the strictly-typed constructor and _pass_runner attribute without weakening production annotations or using casts that basedpyright rejects (reportInvalidCast). - Drop two unnecessary "# pyright: ignore" comments: Django's SimpleTestCase already declares client, and reportPrivateUsage is disabled project-wide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Give failed imports and online-tagging match prompts the same polished treatment as tag-write errors, across the hamburger button and sidebar admin menu. - Hamburger: broaden the red error dot to cover tag-write errors OR failed imports; add a separate amber dot for pending tagging matches. - Sidebar admin menu: add a "Failed Imports" item (deep-links to the Libraries tab) and an "N Matches to Review" item (opens the Match Review dialog). Drop the dead clearFailedImports mapping and the redundant append-icon. - Restyle the Failed Imports panel to the AdminSection look (count + alert icon, hint, striped table) and add a Reason column. Expose the failure reason: FailedImportSerializer now surfaces the model's `name` as `reason`, and set_reason falls back to the exception class name when the message is empty so a reason is never blank. Add a per-session "Clear Warning" control on the Failed Imports panel that dismisses the hamburger dot + sidebar item (the table persists); a failed-imports change event re-activates the warning. Remove the redundant Match-to-Review button from the browser toolbar (the sidebar item replaces it) and mount the prompt dialog in the reader so the item works everywhere the drawer appears. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the in-memory dismissal (which reset on every reload) with a
durable "last seen" marker so a cleared failed-import warning stays
cleared across refreshes, sessions, and browsers.
- Add Timestamp key FI ("Failed Imports Seen"), folded into 0043 — the
row auto-seeds at startup, so no separate migration is needed.
- New /admin/failed-imports/seen endpoint: GET reads the marker, PUT
stamps it to now and notifies other admin sessions.
- admin store gains a hasUnseenFailedImports getter: the hamburger dot,
sidebar item, and the panel's Clear Warning cluster show when any
failed import is newer than the marker (empty marker = never cleared =
show all). Clear Warning PUTs the marker; the socket reloads it so a
clear in one session propagates to others.
Also refine the Failed Imports panel header: cluster the Clear Warning
button + count to the right and hide the whole cluster once cleared.
Tidy two tests touched by this sub-project (unused-arg lambda naming,
extract an OPDS stream-walk helper).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 21218ec8013eef7553ed5337973170449e952c28
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 19:00:13 2026 -0700
update deps
commit 013517ea0b72e34e76280d4f601718af994fda77
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 17:19:07 2026 -0700
feat(browser): refresh filtered views on membership change via head probe
The library.changed gate compared only the page mtime, which catches in-view
edits but can miss a comic *leaving* the active filter — its collection drops
out of the filter-matching set and, if no other matching collection moved, the
mtime is unchanged so the view never refreshes.
Add a membership signal. A lightweight BrowserHeadView returns {mtime, count}
for a route, reusing BrowserView's filtered querysets (so both match the page
exactly) while skipping pagination, card/cover annotation, and serialization.
The browse page now also returns the full filtered membership count. The
frontend gate calls /head on the current route and reloads when *either* mtime
(in-view edit) or count (entered/left the filter) moved.
- BrowserHeadView + BrowserHeadSerializer; /browse/{collection}[/{pks}]/head.
- BrowserPageSerializer exposes the full filtered count; get_object provides it.
- _get_collection_and_books returns the count; OPDS unpack sites widened.
- Frontend getBrowserHead; loadMtimes compares (mtime, count). The MtimeView
probe stays for the reader gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit b9ec23d3911ce654464aede66b9baa6375e68b95
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 16:27:57 2026 -0700
fix(importer): re-stamp the source story arc/folder a comic leaves (m2m)
The committed move re-stamp covered FK collections (publisher/imprint/series/
volume); extend it to the m2m case. When a metadata edit removes a comic from a
story arc, the prune phase now records the unlinked StoryArcNumber's story_arc
(and folder) into the same moved_source_collections accumulator, so the delete
phase's force-update map re-stamps the source arc. Otherwise a viewer of that
arc never refreshes — TimestampUpdater only re-stamps arcs a current comic still
links into. Tag-style m2ms (genres, characters, …) are not collections and are
ignored.
Adds a story-arc-removal regression test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 47f3fc0ec9c09387e087ffd86b14778ce47ffb80
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 15:49:35 2026 -0700
fix(browser): make library.changed refresh gate detect all collection changes
The browser reloads the current view on library.changed only when a scoped
/api/v4/mtime probe differs from the page's stored mtime. Four defects left the
probe blind to real edits (tag changes, publisher renames), so the view never
auto-refreshed:
- TimestampUpdater: `comic__updated_at > start_time` dropped same-second
re-imports — SQLite stamps updated_at at ms precision (3 fractional digits)
while the Python start_time serializes 6, so a same-second row sorts before
it as TEXT. Floor start_time to the second. Force-mapped collections now also
re-stamp when emptied (bypass the child-count filter).
- browser._get_page_mtime read self.model (the child collection) filtered by
pk__in=route_pks (the parent pks) → empty aggregate → EPOCH (0). Read the
route/container model, matching MtimeView.
- A comic that changes publisher/imprint/series/volume moves collections, but
only the destination was re-stamped. Capture the source collections in the
importer and feed them to TimestampUpdater's force-update map.
- The probe annotated per-row then took .first() (implicit ORDER BY pk),
returning the lowest-pk collection's mtime instead of the global max — at
root, edits to any other collection were invisible. Use order_by("-max").
first(). Read the probe uncached (a change-detector must be fresh).
Adds regression tests for the gate scope, precision, move, and global-max paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 6b347661736642cf3820a574bfbad80a534dd445
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 20:23:46 2026 -0700
fix: remove duplicate showTagWriteErrors computed key in admin-menu
The computed block defined showTagWriteErrors() twice with identical bodies, tripping ESLint vue/no-dupe-keys and failing make fix / make lint. Removed the second definition; behavior is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 3503ecc2ae6e5c2cb00b3b126da86bb6d69f9bd7
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 20:19:35 2026 -0700
fix: stop tag-write progress from oscillating during bulk writes
comicbox.bulk_write runs writes on a thread pool and emits per-file
events in completion order, each carrying the file's submission index.
TagWriter set `status.complete = event.index`, so the count jumped up
and down as out-of-order indices streamed in. Creating a fresh
TagWriteStatus per event also reset `since_updated`, defeating the
StatusController 5s update throttle, so every jumbled value was written
and broadcast.
Keep one TagWriteStatus for the whole batch and increment a monotonic
completion counter on each terminal per-file event (parsed, error, or
short-circuit) so the count climbs steadily to total and the throttle
works again.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 8f334c0913ae6f363054d61334e1ffd74701a56c
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 20:15:41 2026 -0700
update version 2.0.0a2
commit 0ac6145cefb5618951b599e10f35763807d8cb4e
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 20:14:35 2026 -0700
feat: combo-box collection inputs to normalize duplicate-case tags
When a browser group spans collections whose names differ only by case (e.g. a 'Marvel' and a 'marvel' publisher), the tag editor pre-filled the field with the first value and gated Save on a diff against that pre-fill -- so the duplicate could not be written across the selection.
Publisher, Imprint, Series, and Volume are now v-comboboxes seeded from the group's own distinct values. An ambiguous field (>1 distinct value) starts blank so picking any option -- including the canonical one -- registers as a change and enables Save. Single-value editing is unchanged and untouched fields are never written. Frontend-only; the existing tag-write path collapses the duplicate on re-import.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 09dc4b535ebde866603f7e5e1db596af1ed0e211
Merge: 1f5015d8c 08f06d3
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 19:43:30 2026 -0700
Merge branch 'pre-release' into codex-v2
commit 1f5015d8ccc10a6b8bf0ec06ded6e5cf17aaf66e
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 16:31:22 2026 -0700
Admin Tagging tab: move Metadata Formats docs into the field hint
The "These metadata formats are written..." line with the ComicInfo /
MetronInfo links was a section-level hint but really describes the
Metadata Formats select. Render it through the select's #message slot so
the links live in the field's own hint (VSelect forwards #message down to
VInput's VMessages), styled identically to every other field hint. The
section hint keeps only the write-permission warning.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit e831f2361af916e5dd3cfd71258051717b904cee
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 16:22:37 2026 -0700
Admin Tagging tab: auto-save defaults, reorder, inline help
Tagging Defaults and Online Tagging Defaults now save on change and the
Save Defaults / Revert buttons are gone. A deep watch on the draft
auto-saves (serialized, with a one-shot re-run if the draft changes
mid-save); the defaults watcher seeds the draft once on load so a save's
echoed response can't clobber an in-flight edit.
Reordered sections to: Tagging Defaults, Online Tagging Defaults, Online
Tagging Sources.
Replaced the Online Tagging Sources hint paragraph with a per-checkbox
title tooltip shown only while the checkbox is disabled (title on a
wrapper element, since Vuetify drops pointer events on disabled
controls).
Added Match Mode and Prompts help text (static strings in data, not
computed).
Updates tagging-tab.test.js (12 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit d25bf69c6b52e1689ebdb64cb89689c65383f77a
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:43:42 2026 -0700
Squash 0044 field alterations back into 0043
Fold the auto-generated 0044 migration into 0043 in place so the
migration history stays a single comicbox-tagging-defaults step:
- adminflag.key: BG label "Browser Default Group" -> "Browser Default
Collection" (group->collection unification drift)
- librarianstatus.status_type: add JDU (Snapshot User Data Sidecar)
and JTG (Cleanup Stale Online Tagging State)
- settingsbrowser.order_by: add the missing AlterField
makemigrations --check --dry-run reports no changes detected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit e1a29cfd5f542a89add7dc05bbefac78dffac40e
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:36:03 2026 -0700
bump alpha version
commit 35859ea7396079298bb8212743f24d77e3bc14ab
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:35:17 2026 -0700
Tag editor: fix Identifiers round-trip (blank Source/Type/Key)
shape_identifiers() now emits the raw `source` (e.g. "comicvine") alongside `displayName` in the {pk, source, type, code, displayName, url} shape, and the edit panel's initFromMetadata reads that structured shape instead of parsing the legacy "source::id_type:key" name string. The legacy parse populated the right number of rows but left every Source/Type/Key field blank.
Tests: shape_identifiers raw-source/null/empty coverage (backend); edit-panel identifiers round-trip regression — N populated rows, none blank (frontend).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit bcbfa9a83ffc034cf9c190cf1a455c2335571153
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:27:49 2026 -0700
Tag editor: tooltips on format-disabled add buttons and inputs
Disabled-by-format controls now carry the "Not supported by selected
metadata formats" tooltip. The title sits on a wrapper element because
Vuetify sets pointer-events:none on disabled controls, so a title on the
control itself never fires. Covers the Add Universe / Add Identifier /
Add URL buttons, the Add-role select, the credits / story-arc / universe
/ identifier table cells, and the Main Character / Main Team selects.
Adds edit-panel-disabled-tooltips.test.js (5 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 9ea8eed6df3ae2db7c2f2c906c9238c4b9deb4cd
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:21:20 2026 -0700
reorg admin tagging tab
commit e5ef1a8a11187b24457ef59f94836479a9fb6cf4
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:15:49 2026 -0700
Online tagging: honor prompts_mode "never" (skip, don't queue)
When a scan's prompts_mode is "never", build the session with
defer_prompts=False so comicbox skips ambiguous matches inline (the
CodexPromptHandler skips by default) and don't persist any prompts —
only confidently-matched comics are written this run. "ask" is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit c0530362ec3e1e832ef6b4a1e3085046aa55d519
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:11:00 2026 -0700
Migrate BROWSER_DEFAULT_COLLECTION admin flag char -> collection
Migration 0039 seeds the BG (Default View) admin flag with the legacy
top-group char "p". The group->collection unification in 0043 rewrote
every char-coded value for SettingsBrowser/Favorite/CustomCover but
missed this AdminFlag, leaving DBs upgraded through 0043 with an invalid
BG value: the read path silently fell back to "publishers" and the admin
"Default View" dropdown rendered blank.
Fold the char->collection flip into 0043 alongside the
SettingsBrowser.top_collection move it mirrors, and add regression tests
covering all seven chars, the reverse move, and idempotency.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit c1d7525d38646aef13feae1efe7a7dc3e4628f40
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:46:44 2026 -0700
Online tagging: non-blocking prompts, drop Prompt Timeout
Tagging scans no longer block waiting for an admin to answer ambiguous-
match prompts. run_session does Pass 1 (auto-match + write the confident
comics), persists any deferred prompts to the cache, and returns — the
librarian thread is never held.
Prompts now persist independently of any scan: they linger in the cache
(no TTL, surviving daemon restarts) until answered or skipped. Answering
a prompt is decoupled — resolve_prompt builds a fresh OnlineSession and
applies that single comic's chosen match (re-mapping a chosen index to
its issue id so a re-search can't mis-pick), then enqueues a one-comic
write. The deferred-prompt fingerprint is deterministic across processes,
so no live session is required.
This makes the Prompt Timeout setting obsolete: removed the
prompt_timeout_seconds field (model, migration 0043, serializer,
user-data dump/restore) and the blocking _wait_for_prompts wait.
Supporting changes:
- session_cache: prompts are a global {fingerprint: prompt} map plus an
active-scan marker; daemon run_start clears only the scan marker.
- prompt endpoints are session-agnostic (/admin/tag-prompts[...]).
- janitor prunes only prompts whose comic is gone, not "orphan" prompts.
- frontend store/socket use the new endpoints and resync on (re)connect;
removed the Prompt Timeout input + timeout-input.vue.
- tests for the non-blocking flow, resolution, skip, endpoints, janitor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 708f7ee822c6b2f64e8da4259a4f3e2ae57eedec
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:37:34 2026 -0700
Tag write errors: admin feedback panel, badge, and docs
Collect comic tag-write failures (read-only mount, permission error)
in the filesystem cache and surface them to admins:
- tagwrite_errors fs-cache accessors + recording in TagWriter
- TAG_WRITE_ERRORS_CHANGED admin-only websocket notification
- read/clear endpoint (AdminTagWriteErrorsView)
- Tagging-tab error panel (#tagging-errors anchor), a red dot on the
hamburger menu, and a sidebar drawer link to the error section
- write-permission help on the Tagging tab; README/DOCKER/NEWS notes
that the comics dir must be mounted writable for tag editing to work
Note: the URL route (urls/api/v4/admin.py) and the socket dispatch
case (stores/socket.js) are intentionally NOT in this commit because
those files are mid-refactor in a concurrent session; both of my hunks
are present in the working tree and should be committed alongside that
work.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit d8605b9002ef61f8e7877b3993c1c2321e816afb
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:31:44 2026 -0700
use key as icon for bearer token
commit f670b9099e26235bd5ead9247f586ac2fc5e644a
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:28:50 2026 -0700
more explicit verbose description of bearer token
commit f53b58ed68dc21a7732c776428e5dca3e96d5507
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:12:04 2026 -0700
Rename Metron online source to "Metron Cloud" in UI
Disambiguates the Metron online tagging source from the MetronInfo
metadata format. Presentation strings only — internal source values,
variables, and method names are unchanged.
- Admin Tagging tab, Online Sources: card title, enable aria-label,
signup link, save/clear credential controls
- Online Tagging launcher dialog: source picker title + rate-limit label
- Match Review dialog: map the source chip to a friendly label
(also Comic Vine), falling back to the raw id for unknown sources
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 15488b07b77bb16f4859eec64262aa8c4a2aa391
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 13:53:17 2026 -0700
change write tags headline
commit 7139e18d4fb16a36747908d190c3ddbd92b81445
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 13:50:37 2026 -0700
Tagging tab: per-source enable checkboxes + Write Defaults help
Replace the "Online Sources" multi-select on the admin Tagging tab. The
Online Source Credentials panels now live inside the Online Tagging
Defaults block, each with an enable checkbox to its left that drives
defaultSources. The checkbox stays disabled until that source's
credentials are saved, and a credential-less source always reads as off.
Add Write Defaults help text noting the formats are written every time
tags are edited, with links to the ComicInfo and MetronInfo docs.
Add tagging-tab unit tests covering the enable-checkbox wiring, the
credential-gated disable, and the help links.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit c2b57fbe0387a103d4ac0554fa7cfdc626429453
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 21:42:31 2026 -0700
update depds and bump version to alpha 3
commit ff20d7e261298e1aed9e2068be362ee96ec98beb
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 21:16:28 2026 -0700
fix(browser): show metadata action toolbar in multi-select
The metadata dialog's action toolbar was gated by
`v-if="!multiSelect && !editing"`, so selecting several comics or
collections and opening Tags showed the intersection tag screen but no
Download / Mark Read / Edit Tags / Tag Online / favorite controls. The
underlying actions already accept multi-id payloads, so the gate was the
only thing missing.
- metadata-header: drop `!multiSelect` from the controls gate.
- favorites store: add `setManyFavorites` (optimistic, skips pks already
in the target state, per-pk rollback over the idempotent endpoints).
- favorite-toggle: accept a `pks` array (cards keep single `pk`); on =
all favorited; click routes single->toggle, bulk->setManyFavorites.
- metadata-controls: favorite the selection target (controlBook) so the
star bulk-favorites the whole selection; hide Read unless the
selection resolves to a single comic.
Adds regression coverage for the toolbar gate, the bulk-favorite store
action, and the one-or-many toggle. vitest 150 passed; lint clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 62b80922e05b502af6dd030dba0661d0a1cc05da
Merge: 21218ec80 69a0280
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 19:52:18 2026 -0700
Merge branch 'pre-release' into codex-v2
commit 21218ec8013eef7553ed5337973170449e952c28
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 19:00:13 2026 -0700
update deps
commit 013517ea0b72e34e76280d4f601718af994fda77
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 17:19:07 2026 -0700
feat(browser): refresh filtered views on membership change via head probe
The library.changed gate compared only the page mtime, which catches in-view
edits but can miss a comic *leaving* the active filter — its collection drops
out of the filter-matching set and, if no other matching collection moved, the
mtime is unchanged so the view never refreshes.
Add a membership signal. A lightweight BrowserHeadView returns {mtime, count}
for a route, reusing BrowserView's filtered querysets (so both match the page
exactly) while skipping pagination, card/cover annotation, and serialization.
The browse page now also returns the full filtered membership count. The
frontend gate calls /head on the current route and reloads when *either* mtime
(in-view edit) or count (entered/left the filter) moved.
- BrowserHeadView + BrowserHeadSerializer; /browse/{collection}[/{pks}]/head.
- BrowserPageSerializer exposes the full filtered count; get_object provides it.
- _get_collection_and_books returns the count; OPDS unpack sites widened.
- Frontend getBrowserHead; loadMtimes compares (mtime, count). The MtimeView
probe stays for the reader gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit b9ec23d3911ce654464aede66b9baa6375e68b95
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 16:27:57 2026 -0700
fix(importer): re-stamp the source story arc/folder a comic leaves (m2m)
The committed move re-stamp covered FK collections (publisher/imprint/series/
volume); extend it to the m2m case. When a metadata edit removes a comic from a
story arc, the prune phase now records the unlinked StoryArcNumber's story_arc
(and folder) into the same moved_source_collections accumulator, so the delete
phase's force-update map re-stamps the source arc. Otherwise a viewer of that
arc never refreshes — TimestampUpdater only re-stamps arcs a current comic still
links into. Tag-style m2ms (genres, characters, …) are not collections and are
ignored.
Adds a story-arc-removal regression test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 47f3fc0ec9c09387e087ffd86b14778ce47ffb80
Author: AJ Slater <aj@slater.net>
Date: Sun Jun 7 15:49:35 2026 -0700
fix(browser): make library.changed refresh gate detect all collection changes
The browser reloads the current view on library.changed only when a scoped
/api/v4/mtime probe differs from the page's stored mtime. Four defects left the
probe blind to real edits (tag changes, publisher renames), so the view never
auto-refreshed:
- TimestampUpdater: `comic__updated_at > start_time` dropped same-second
re-imports — SQLite stamps updated_at at ms precision (3 fractional digits)
while the Python start_time serializes 6, so a same-second row sorts before
it as TEXT. Floor start_time to the second. Force-mapped collections now also
re-stamp when emptied (bypass the child-count filter).
- browser._get_page_mtime read self.model (the child collection) filtered by
pk__in=route_pks (the parent pks) → empty aggregate → EPOCH (0). Read the
route/container model, matching MtimeView.
- A comic that changes publisher/imprint/series/volume moves collections, but
only the destination was re-stamped. Capture the source collections in the
importer and feed them to TimestampUpdater's force-update map.
- The probe annotated per-row then took .first() (implicit ORDER BY pk),
returning the lowest-pk collection's mtime instead of the global max — at
root, edits to any other collection were invisible. Use order_by("-max").
first(). Read the probe uncached (a change-detector must be fresh).
Adds regression tests for the gate scope, precision, move, and global-max paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 6b347661736642cf3820a574bfbad80a534dd445
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 20:23:46 2026 -0700
fix: remove duplicate showTagWriteErrors computed key in admin-menu
The computed block defined showTagWriteErrors() twice with identical bodies, tripping ESLint vue/no-dupe-keys and failing make fix / make lint. Removed the second definition; behavior is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 3503ecc2ae6e5c2cb00b3b126da86bb6d69f9bd7
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 20:19:35 2026 -0700
fix: stop tag-write progress from oscillating during bulk writes
comicbox.bulk_write runs writes on a thread pool and emits per-file
events in completion order, each carrying the file's submission index.
TagWriter set `status.complete = event.index`, so the count jumped up
and down as out-of-order indices streamed in. Creating a fresh
TagWriteStatus per event also reset `since_updated`, defeating the
StatusController 5s update throttle, so every jumbled value was written
and broadcast.
Keep one TagWriteStatus for the whole batch and increment a monotonic
completion counter on each terminal per-file event (parsed, error, or
short-circuit) so the count climbs steadily to total and the throttle
works again.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 8f334c0913ae6f363054d61334e1ffd74701a56c
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 20:15:41 2026 -0700
update version 2.0.0a2
commit 0ac6145cefb5618951b599e10f35763807d8cb4e
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 20:14:35 2026 -0700
feat: combo-box collection inputs to normalize duplicate-case tags
When a browser group spans collections whose names differ only by case (e.g. a 'Marvel' and a 'marvel' publisher), the tag editor pre-filled the field with the first value and gated Save on a diff against that pre-fill -- so the duplicate could not be written across the selection.
Publisher, Imprint, Series, and Volume are now v-comboboxes seeded from the group's own distinct values. An ambiguous field (>1 distinct value) starts blank so picking any option -- including the canonical one -- registers as a change and enables Save. Single-value editing is unchanged and untouched fields are never written. Frontend-only; the existing tag-write path collapses the duplicate on re-import.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 09dc4b535ebde866603f7e5e1db596af1ed0e211
Merge: 1f5015d8c 08f06d3
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 19:43:30 2026 -0700
Merge branch 'pre-release' into codex-v2
commit 1f5015d8ccc10a6b8bf0ec06ded6e5cf17aaf66e
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 16:31:22 2026 -0700
Admin Tagging tab: move Metadata Formats docs into the field hint
The "These metadata formats are written..." line with the ComicInfo /
MetronInfo links was a section-level hint but really describes the
Metadata Formats select. Render it through the select's #message slot so
the links live in the field's own hint (VSelect forwards #message down to
VInput's VMessages), styled identically to every other field hint. The
section hint keeps only the write-permission warning.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit e831f2361af916e5dd3cfd71258051717b904cee
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 16:22:37 2026 -0700
Admin Tagging tab: auto-save defaults, reorder, inline help
Tagging Defaults and Online Tagging Defaults now save on change and the
Save Defaults / Revert buttons are gone. A deep watch on the draft
auto-saves (serialized, with a one-shot re-run if the draft changes
mid-save); the defaults watcher seeds the draft once on load so a save's
echoed response can't clobber an in-flight edit.
Reordered sections to: Tagging Defaults, Online Tagging Defaults, Online
Tagging Sources.
Replaced the Online Tagging Sources hint paragraph with a per-checkbox
title tooltip shown only while the checkbox is disabled (title on a
wrapper element, since Vuetify drops pointer events on disabled
controls).
Added Match Mode and Prompts help text (static strings in data, not
computed).
Updates tagging-tab.test.js (12 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit d25bf69c6b52e1689ebdb64cb89689c65383f77a
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:43:42 2026 -0700
Squash 0044 field alterations back into 0043
Fold the auto-generated 0044 migration into 0043 in place so the
migration history stays a single comicbox-tagging-defaults step:
- adminflag.key: BG label "Browser Default Group" -> "Browser Default
Collection" (group->collection unification drift)
- librarianstatus.status_type: add JDU (Snapshot User Data Sidecar)
and JTG (Cleanup Stale Online Tagging State)
- settingsbrowser.order_by: add the missing AlterField
makemigrations --check --dry-run reports no changes detected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit e1a29cfd5f542a89add7dc05bbefac78dffac40e
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:36:03 2026 -0700
bump alpha version
commit 35859ea7396079298bb8212743f24d77e3bc14ab
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:35:17 2026 -0700
Tag editor: fix Identifiers round-trip (blank Source/Type/Key)
shape_identifiers() now emits the raw `source` (e.g. "comicvine") alongside `displayName` in the {pk, source, type, code, displayName, url} shape, and the edit panel's initFromMetadata reads that structured shape instead of parsing the legacy "source::id_type:key" name string. The legacy parse populated the right number of rows but left every Source/Type/Key field blank.
Tests: shape_identifiers raw-source/null/empty coverage (backend); edit-panel identifiers round-trip regression — N populated rows, none blank (frontend).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit bcbfa9a83ffc034cf9c190cf1a455c2335571153
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:27:49 2026 -0700
Tag editor: tooltips on format-disabled add buttons and inputs
Disabled-by-format controls now carry the "Not supported by selected
metadata formats" tooltip. The title sits on a wrapper element because
Vuetify sets pointer-events:none on disabled controls, so a title on the
control itself never fires. Covers the Add Universe / Add Identifier /
Add URL buttons, the Add-role select, the credits / story-arc / universe
/ identifier table cells, and the Main Character / Main Team selects.
Adds edit-panel-disabled-tooltips.test.js (5 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 9ea8eed6df3ae2db7c2f2c906c9238c4b9deb4cd
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:21:20 2026 -0700
reorg admin tagging tab
commit e5ef1a8a11187b24457ef59f94836479a9fb6cf4
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:15:49 2026 -0700
Online tagging: honor prompts_mode "never" (skip, don't queue)
When a scan's prompts_mode is "never", build the session with
defer_prompts=False so comicbox skips ambiguous matches inline (the
CodexPromptHandler skips by default) and don't persist any prompts —
only confidently-matched comics are written this run. "ask" is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit c0530362ec3e1e832ef6b4a1e3085046aa55d519
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 15:11:00 2026 -0700
Migrate BROWSER_DEFAULT_COLLECTION admin flag char -> collection
Migration 0039 seeds the BG (Default View) admin flag with the legacy
top-group char "p". The group->collection unification in 0043 rewrote
every char-coded value for SettingsBrowser/Favorite/CustomCover but
missed this AdminFlag, leaving DBs upgraded through 0043 with an invalid
BG value: the read path silently fell back to "publishers" and the admin
"Default View" dropdown rendered blank.
Fold the char->collection flip into 0043 alongside the
SettingsBrowser.top_collection move it mirrors, and add regression tests
covering all seven chars, the reverse move, and idempotency.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit c1d7525d38646aef13feae1efe7a7dc3e4628f40
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:46:44 2026 -0700
Online tagging: non-blocking prompts, drop Prompt Timeout
Tagging scans no longer block waiting for an admin to answer ambiguous-
match prompts. run_session does Pass 1 (auto-match + write the confident
comics), persists any deferred prompts to the cache, and returns — the
librarian thread is never held.
Prompts now persist independently of any scan: they linger in the cache
(no TTL, surviving daemon restarts) until answered or skipped. Answering
a prompt is decoupled — resolve_prompt builds a fresh OnlineSession and
applies that single comic's chosen match (re-mapping a chosen index to
its issue id so a re-search can't mis-pick), then enqueues a one-comic
write. The deferred-prompt fingerprint is deterministic across processes,
so no live session is required.
This makes the Prompt Timeout setting obsolete: removed the
prompt_timeout_seconds field (model, migration 0043, serializer,
user-data dump/restore) and the blocking _wait_for_prompts wait.
Supporting changes:
- session_cache: prompts are a global {fingerprint: prompt} map plus an
active-scan marker; daemon run_start clears only the scan marker.
- prompt endpoints are session-agnostic (/admin/tag-prompts[...]).
- janitor prunes only prompts whose comic is gone, not "orphan" prompts.
- frontend store/socket use the new endpoints and resync on (re)connect;
removed the Prompt Timeout input + timeout-input.vue.
- tests for the non-blocking flow, resolution, skip, endpoints, janitor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 708f7ee822c6b2f64e8da4259a4f3e2ae57eedec
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:37:34 2026 -0700
Tag write errors: admin feedback panel, badge, and docs
Collect comic tag-write failures (read-only mount, permission error)
in the filesystem cache and surface them to admins:
- tagwrite_errors fs-cache accessors + recording in TagWriter
- TAG_WRITE_ERRORS_CHANGED admin-only websocket notification
- read/clear endpoint (AdminTagWriteErrorsView)
- Tagging-tab error panel (#tagging-errors anchor), a red dot on the
hamburger menu, and a sidebar drawer link to the error section
- write-permission help on the Tagging tab; README/DOCKER/NEWS notes
that the comics dir must be mounted writable for tag editing to work
Note: the URL route (urls/api/v4/admin.py) and the socket dispatch
case (stores/socket.js) are intentionally NOT in this commit because
those files are mid-refactor in a concurrent session; both of my hunks
are present in the working tree and should be committed alongside that
work.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit d8605b9002ef61f8e7877b3993c1c2321e816afb
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:31:44 2026 -0700
use key as icon for bearer token
commit f670b9099e26235bd5ead9247f586ac2fc5e644a
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:28:50 2026 -0700
more explicit verbose description of bearer token
commit f53b58ed68dc21a7732c776428e5dca3e96d5507
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 14:12:04 2026 -0700
Rename Metron online source to "Metron Cloud" in UI
Disambiguates the Metron online tagging source from the MetronInfo
metadata format. Presentation strings only — internal source values,
variables, and method names are unchanged.
- Admin Tagging tab, Online Sources: card title, enable aria-label,
signup link, save/clear credential controls
- Online Tagging launcher dialog: source picker title + rate-limit label
- Match Review dialog: map the source chip to a friendly label
(also Comic Vine), falling back to the raw id for unknown sources
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit 15488b07b77bb16f4859eec64262aa8c4a2aa391
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 13:53:17 2026 -0700
change write tags headline
commit 7139e18d4fb16a36747908d190c3ddbd92b81445
Author: AJ Slater <aj@slater.net>
Date: Sat Jun 6 13:50:37 2026 -0700
Tagging tab: per-source enable checkboxes + Write Defaults help
Replace the "Online Sources" multi-select on the admin Tagging tab. The
Online Source Credentials panels now live inside the Online Tagging
Defaults block, each with an enable checkbox to its left that drives
defaultSources. The checkbox stays disabled until that source's
credentials are saved, and a credential-less source always reads as off.
Add Write Defaults help text noting the formats are written every time
tags are edited, with links to the ComicInfo and MetronInfo docs.
Add tagging-tab unit tests covering the enable-checkbox wiring, the
credential-gated disable, and the help links.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.