Skip to content

Add Visual Identity expression packs and VN casting resolver#2584

Merged
rmusser01 merged 61 commits into
devfrom
codex/visual-identity-expression-packs
Jul 3, 2026
Merged

Add Visual Identity expression packs and VN casting resolver#2584
rmusser01 merged 61 commits into
devfrom
codex/visual-identity-expression-packs

Conversation

@rmusser01

@rmusser01 rmusser01 commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Change summary

  • Adds Visual Identity expression pack backend persistence, activation, import provenance, API support, and frontend pack/client/runtime support.
  • Adds the VN generated-file import bridge and VN Play casting adapter so character/persona roles can resolve Visual Identity sprite directives, including override pack/version and fallback controls.
  • Cleans the frontend TypeScript baseline needed to finish the branch after dependency restoration, keeping those fixes tracked under TASK-12099.

Why

  • Character/persona chats need default-associated expression packs that can later be inserted into VN scenes as the character themselves or roleplayed cast roles.
  • VN generated assets need portable provenance so generated files can be replayed/imported into Visual Identity packs without losing source context.
  • Resolver override semantics need to be strict by default so missing expressions or cross-actor/cross-user overrides fail clearly, while explicit fallback remains opt-in.

Test plan

  • Backend focused Visual Identity and VN Play pytest slice: 124 passed.
  • Frontend TypeScript check: bunx tsc --noEmit --pretty false.
  • Focused frontend production and Visual Identity Vitest slice: 103 passed.
  • Stale frontend TypeScript fixture cleanup Vitest slice: 22 passed.
  • Bandit touched backend scope: 0 findings in /tmp/bandit_visual_identity_expression_packs_final.json.
  • git diff --check.

Summary by cubic

Adds shared Visual Identity expression packs for character/persona chat and VN, with strict backend validation, idempotent import/activation, and a lightweight management UI. Integrates a VN role/casting resolver with override/fallback semantics and renders expression sprites in chat without breaking legacy mood images.

  • New Features
    • Backend: New /api/v1/visual-identities for capabilities, slots, packs/drafts/assets, ZIP import jobs, activation, binding resolution, and asset content; persisted via VisualIdentity_DB with size/format/frame checks (animated GIF/WebP; AVIF gated) and bounded source_context provenance; hardened idempotency for imports/activations.
    • VN bridge: Generated-file import with provenance into packs; VN Play casting resolver integrates with the service and supports role overrides, explicit pack/version targeting, and typed fallback reasons (strict by default).
    • Chat/runtime: Assistant messages store resolved visual identity fields; server loader maps them to UI; chat and compare views render expression sprites and prefer them over mood images; manual expression override via the ExpressionPicker and /emote <expression>; reduced-motion preview handling.
    • Frontend: New VisualIdentityPackPanel (with draft review grid/uploader), ExpressionPicker, VisualIdentityStage, VisualIdentityImage, useGeneratedFileImportAction, resolver hook useVisualIdentityResolver, expression utilities, and API domain visual-identities; integrated panels in Character and Persona editors.
    • Stability: Cleaned frontend TypeScript baseline and refreshed tests; small copy/typing fixes across Notes, Skills, Audio Studio, background upload code, and service helpers.

Written for commit 0d4328e. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c02fee33-87cb-40b5-bf94-93cc5157517f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/visual-identity-expression-packs

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements Stages 11A and 11B of the Visual Identity Expression Packs, introducing a VN generated-file import bridge with asset-level provenance (source_context) and a stateless VN role/casting resolver. It also integrates these overrides into the VN Play runtime and resolves various pre-existing frontend TypeScript type errors to ensure a clean baseline check. The reviewer feedback highlights critical improvements: avoiding database schema initialization on every request to prevent SQLite lock contention, securing temporary file creation during ZIP imports against filename collisions, and handling file descriptor cleanup properly to prevent resource leaks if os.fdopen fails.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tldw_Server_API/app/core/Visual_Identities/service.py Outdated
Comment thread tldw_Server_API/app/core/Visual_Identities/archive_import.py
Comment thread tldw_Server_API/app/core/Visual_Identities/storage.py
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add Visual Identity expression packs and VN casting resolver

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add backend persistence, import jobs, activation, and resolve APIs for expression packs.
• Bridge VN generated assets and casting so roles resolve sprite directives with strict overrides.
• Add frontend pack management UI, resolver hook, and tests; track TS baseline cleanup under
 TASK-12099.
Diagram

graph TD
  UI["Frontend UI"] --> Client["TldwApiClient (visual-identities)"] --> API["FastAPI /visual-identities"] --> VISvc["VisualIdentityService"] --> VIRepo[("VisualIdentity DB")]
  VISvc --> Storage["Asset storage"]
  VISvc --> VNBridge["VN bridge / casting"] --> VNRepo[("VN asset packs DB")]
  API --> GFRepo["Generated files repo"] --> Storage
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Unify Visual Identity packs into existing VN asset pack storage
  • ➕ Single asset-pack persistence model across chat and VN
  • ➕ Potentially fewer storage/provenance code paths
  • ➖ Tightly couples chat runtime needs to VN lifecycle and permissions
  • ➖ Harder to enforce strict override semantics for cross-actor resolution
2. Model packs as pure object-store manifests (no DB drafts/versions)
  • ➕ Simpler persistence layer and potentially lower DB complexity
  • ➕ Easier bulk import/export as a single artifact
  • ➖ Harder to support incremental draft review/slot mapping and activation metadata
  • ➖ More complex idempotency, conflict handling, and per-asset validation tracking

Recommendation: Keep the PR’s approach (dedicated Visual Identity domain with drafts/activation/bindings plus explicit VN bridge). It cleanly separates ownership/strict-resolution policy from VN internals while still allowing provenance-aware imports via the generated-file bridge. Consider consolidation with VN asset packs only after semantics stabilize and the permission model is proven.

Files changed (115) +21089 / -153

Enhancement (42) +8475 / -54
Message.tsxRender Visual Identity-related message state in Playground +25/-6

Render Visual Identity-related message state in Playground

• Extends Playground message rendering to handle Visual Identity metadata/state. Aligns UI behavior with resolver-driven expression selection.

apps/packages/ui/src/components/Common/Playground/Message.tsx

message-types.tsAdd Visual Identity message typing support +10/-0

Add Visual Identity message typing support

• Introduces/extends message type definitions required to carry Visual Identity state through the Playground. Enables type-safe downstream handling.

apps/packages/ui/src/components/Common/Playground/message-types.ts

useMessageState.tsUpdate message state hook for Visual Identity +6/-1

Update message state hook for Visual Identity

• Adjusts message state derivation to incorporate Visual Identity fields. Ensures consistent computed state for rendering and actions.

apps/packages/ui/src/components/Common/Playground/useMessageState.ts

ExpressionAssetUploader.tsxAdd expression asset upload component +49/-0

Add expression asset upload component

• Adds a UI component for uploading expression images into a pack draft. Supports validated asset attachment during pack editing.

apps/packages/ui/src/components/Common/VisualIdentity/ExpressionAssetUploader.tsx

ExpressionPicker.tsxAdd expression picker UI +72/-0

Add expression picker UI

• Introduces an expression picker for selecting a slot/expression at runtime or during review. Designed to work with canonical slot keys and aliases.

apps/packages/ui/src/components/Common/VisualIdentity/ExpressionPicker.tsx

ExpressionSlotGrid.tsxAdd slot grid for pack draft review +174/-0

Add slot grid for pack draft review

• Implements a grid UI for mapping assets to expression slots. Enables re-labeling and assignment workflows needed for draft validation.

apps/packages/ui/src/components/Common/VisualIdentity/ExpressionSlotGrid.tsx

VisualIdentityDraftReview.tsxAdd draft review panel for expression packs +235/-0

Add draft review panel for expression packs

• Adds a draft review surface to inspect validation results, slot mapping, and asset lists. Supports activation workflows from a reviewed draft.

apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityDraftReview.tsx

VisualIdentityImage.tsxAdd Visual Identity image renderer +64/-0

Add Visual Identity image renderer

• Provides a reusable image component for rendering resolved expression assets. Centralizes handling for asset URLs and animated/static media.

apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityImage.tsx

VisualIdentityPackPanel.tsxAdd pack management panel UI +411/-0

Add pack management panel UI

• Implements pack listing/editing, draft creation, import entry points, and activation controls. Serves as the primary UI for expression pack lifecycle management.

apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityPackPanel.tsx

VisualIdentityStage.tsxAdd runtime stage for portraits/expressions +50/-0

Add runtime stage for portraits/expressions

• Introduces a stage component for displaying the currently resolved visual identity expression during chat/runtime. Integrates with resolver outputs and selection UI.

apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityStage.tsx

useGeneratedFileImportAction.tsAdd generated-file import action hook +99/-0

Add generated-file import action hook

• Implements the client-side action for attaching a generated asset to a pack draft. Supports provenance-aware import semantics and idempotency keys.

apps/packages/ui/src/components/Common/VisualIdentity/useGeneratedFileImportAction.ts

CharacterEditorForm.tsxIntegrate Visual Identity pack metadata into Character editor +32/-9

Integrate Visual Identity pack metadata into Character editor

• Extends the character editor to support Visual Identity-related fields and/or selection. Enables default-associated expression packs for character/persona chat.

apps/packages/ui/src/components/Option/Characters/CharacterEditorForm.tsx

PlaygroundChat.tsxAdd Visual Identity runtime support to Playground chat +116/-2

Add Visual Identity runtime support to Playground chat

• Integrates Visual Identity resolution and/or stage UI into Playground chat. Ensures chat messages can carry and react to expression directives.

apps/packages/ui/src/components/Option/Playground/PlaygroundChat.tsx

PlaygroundCompareCluster.tsxAdd compare-cluster support for Visual Identity state +18/-0

Add compare-cluster support for Visual Identity state

• Introduces compare-cluster handling needed to display/compare messages with Visual Identity metadata. Supports evaluation workflows in Playground.

apps/packages/ui/src/components/Option/Playground/PlaygroundCompareCluster.tsx

VisualPackEditor.tsxHook Persona Garden into Visual Pack editing +7/-0

Hook Persona Garden into Visual Pack editing

• Adds wiring to expose Visual Identity pack editing within Persona Garden. Enables persona-associated expression pack lifecycle management.

apps/packages/ui/src/components/PersonaGarden/VisualPackEditor.tsx

useChatActions.tsIntegrate Visual Identity metadata into chat actions +119/-5

Integrate Visual Identity metadata into chat actions

• Extends chat actions to send/receive Visual Identity-related fields. Supports expression directives in character/persona chat runtime.

apps/packages/ui/src/hooks/chat/useChatActions.ts

useServerChatLoader.tsLoad server chat with Visual Identity fields +38/-0

Load server chat with Visual Identity fields

• Extends server chat loading to hydrate Visual Identity metadata. Ensures resolver and UI have needed context on page load.

apps/packages/ui/src/hooks/chat/useServerChatLoader.ts

useMessageOption.tsxUpdate message options hook for Visual Identity +10/-1

Update message options hook for Visual Identity

• Adjusts message option derivation to include Visual Identity-related options and state. Supports new UI affordances tied to expression packs.

apps/packages/ui/src/hooks/useMessageOption.tsx

useVisualIdentityResolver.tsAdd Visual Identity resolver hook +361/-0

Add Visual Identity resolver hook

• Implements client-side resolution logic for selecting assets by actor, pack/version overrides, and fallback rules. Designed to fail clearly on invalid overrides unless fallback is explicitly enabled.

apps/packages/ui/src/hooks/useVisualIdentityResolver.ts

TldwApiClient.tsExpose Visual Identity domain via API client +5/-2

Expose Visual Identity domain via API client

• Updates the central API client to include the Visual Identity domain module. Enables UI/hooks to call the new backend endpoints.

apps/packages/ui/src/services/tldw/TldwApiClient.ts

index.tsExport Visual Identity domain +4/-0

Export Visual Identity domain

• Adds the Visual Identity domain export to the domains index. Keeps imports consistent across the UI codebase.

apps/packages/ui/src/services/tldw/domains/index.ts

visual-identities.tsAdd Visual Identity domain client implementation +278/-0

Add Visual Identity domain client implementation

• Implements typed API calls for capabilities, packs/assets, zip import, activation/binding, and resolution. Centralizes endpoint paths and payload shapes.

apps/packages/ui/src/services/tldw/domains/visual-identities.ts

types.tsAdd option-store types for Visual Identity +9/-0

Add option-store types for Visual Identity

• Extends option store typing to include Visual Identity-related state. Enables UI surfaces to store pack/resolution selections consistently.

apps/packages/ui/src/store/option/types.ts

visual-identities.tsAdd shared Visual Identity TypeScript types +204/-0

Add shared Visual Identity TypeScript types

• Introduces frontend types mirroring backend schemas for packs, drafts, assets, bindings, and resolve responses. Ensures end-to-end type safety across client/UI layers.

apps/packages/ui/src/types/visual-identities.ts

visual-identity-emote.tsAdd Visual Identity emote utilities +26/-0

Add Visual Identity emote utilities

• Implements helper utilities for working with Visual Identity emote/sprite directives. Used by chat and VN integration points.

apps/packages/ui/src/utils/visual-identity-emote.ts

visual-identity-expressions.tsAdd expression normalization and slot utilities +107/-0

Add expression normalization and slot utilities

• Implements expression-key normalization, alias mapping, and slot helpers. Enables strict-by-default resolution and predictable UI labels.

apps/packages/ui/src/utils/visual-identity-expressions.ts

character_chat_sessions.pyAdd Visual Identity metadata handling for character chat sessions +158/-0

Add Visual Identity metadata handling for character chat sessions

• Extends character chat session endpoints to support Visual Identity expression pack metadata in chat messages/sessions. Enables chat runtime to reference actor-associated packs and directives.

tldw_Server_API/app/api/v1/endpoints/character_chat_sessions.py

visual_identities.pyAdd Visual Identity API endpoints (packs, assets, imports, resolve) +1128/-0

Add Visual Identity API endpoints (packs, assets, imports, resolve)

• Introduces a full FastAPI endpoint set for Visual Identity capabilities, pack CRUD, asset upload/import, ZIP import job start, draft activation/binding, and expression resolution. Includes strict validation, idempotency handling for generated-file imports, and immutable asset serving.

tldw_Server_API/app/api/v1/endpoints/visual_identities.py

visual_identity_schemas.pyAdd Pydantic schemas for Visual Identity APIs +218/-0

Add Pydantic schemas for Visual Identity APIs

• Adds request/response schemas for packs, assets, drafts, bindings, resolve responses, ZIP import starts, and generated-file asset attachment. Defines actor-kind typing and payload validation constraints.

tldw_Server_API/app/api/v1/schemas/visual_identity_schemas.py

VisualIdentity_DB.pyAdd Visual Identity repository and persistence layer +1862/-0

Add Visual Identity repository and persistence layer

• Implements the DB/repository layer for packs, versions, drafts, assets, bindings, idempotency records, and related integrity constraints. Provides transactional operations used by service and API layers.

tldw_Server_API/app/core/DB_Management/VisualIdentity_DB.py

db_path_utils.pyAdd DB path utility support for Visual Identity +9/-0

Add DB path utility support for Visual Identity

• Extends database path utilities to support Visual Identity-related storage/DB path resolution. Used by storage and repository layers.

tldw_Server_API/app/core/DB_Management/db_path_utils.py

assets.pyWire VN Play assets into Visual Identity bridge +219/-1

Wire VN Play assets into Visual Identity bridge

• Small adjustment to VN Play asset handling to support Visual Identity bridge/provenance needs. Helps generated assets remain portable for expression pack import.

tldw_Server_API/app/core/VN_Play/assets.py

service.pyAdd casting adapter hooks for Visual Identity sprite directives +78/-27

Add casting adapter hooks for Visual Identity sprite directives

• Updates VN Play service logic to integrate a casting adapter/resolver for Visual Identity sprite directives. Enforces strict override semantics with explicit fallback controls.

tldw_Server_API/app/core/VN_Play/service.py

__init__.pyIntroduce Visual Identities core module +13/-0

Introduce Visual Identities core module

• Adds the Visual_Identities package entrypoint to group service, storage, constraints, and VN bridge functionality. Establishes the backend domain boundary.

tldw_Server_API/app/core/Visual_Identities/init.py

archive_import.pyAdd ZIP archive import processing for expression packs +435/-0

Add ZIP archive import processing for expression packs

• Implements ZIP import parsing and validation for expression pack drafts, including size constraints and slot mapping extraction. Produces draft/job artifacts for later review and activation.

tldw_Server_API/app/core/Visual_Identities/archive_import.py

constraints.pyAdd Visual Identity asset constraints and capability reporting +58/-0

Add Visual Identity asset constraints and capability reporting

• Defines supported mime types and limits (bytes/dimensions/frames) and exposes capability-building helpers. Centralizes validation constraints used by API/service layers.

tldw_Server_API/app/core/Visual_Identities/constraints.py

expression_slots.pyDefine canonical expression slots and alias normalization +107/-0

Define canonical expression slots and alias normalization

• Introduces canonical slot keys, alias mappings, and label/normalization utilities. Establishes the shared vocabulary used by backend and frontend for slot resolution.

tldw_Server_API/app/core/Visual_Identities/expression_slots.py

jobs.pyAdd Visual Identity import job creation helpers +124/-0

Add Visual Identity import job creation helpers

• Adds job creation/wiring for ZIP import processing via the job manager. Supports async ingestion into drafts with status tracking.

tldw_Server_API/app/core/Visual_Identities/jobs.py

service.pyAdd Visual Identity service: drafts, activation, binding, resolution +597/-0

Add Visual Identity service: drafts, activation, binding, resolution

• Implements the domain service for pack lifecycle operations, draft activation/versioning, actor bindings, and strict expression resolution with fallback controls. Orchestrates repository operations and applies policy decisions.

tldw_Server_API/app/core/Visual_Identities/service.py

source_context.pyAdd provenance/source context canonicalization +110/-0

Add provenance/source context canonicalization

• Implements canonicalization/validation for source context attached to imported assets. Ensures generated-file provenance remains portable and consistently shaped.

tldw_Server_API/app/core/Visual_Identities/source_context.py

storage.pyAdd Visual Identity asset storage and validation pipeline +592/-0

Add Visual Identity asset storage and validation pipeline

• Implements validated storage for expression assets, including copying from generated-file records, hashing, preview handling, and path resolution. Enforces limits and mime-type constraints prior to persistence.

tldw_Server_API/app/core/Visual_Identities/storage.py

vn_bridge.pyAdd VN bridge for Visual Identity provenance and resolver context +238/-0

Add VN bridge for Visual Identity provenance and resolver context

• Implements VN-specific source context building and integration helpers used when importing VN generated files into Visual Identity packs. Supports replay/import without losing VN provenance context.

tldw_Server_API/app/core/Visual_Identities/vn_bridge.py

Refactor (8) +40 / -21
TimelineEditor.tsxTypeScript baseline tweaks for TimelineEditor +6/-3

TypeScript baseline tweaks for TimelineEditor

• Applies small edits to align with updated TypeScript expectations. Intended to unblock branch completion under TASK-12099.

apps/packages/ui/src/components/Option/AudioStudio/TimelineEditor.tsx

ScheduledTaskAutomationDefinitionEditor.tsxTypeScript baseline tweaks for scheduled task automation editor +3/-3

TypeScript baseline tweaks for scheduled task automation editor

• Minor adjustments to satisfy TypeScript checks. Keeps unrelated TS hygiene changes tracked within the branch cleanup scope.

apps/packages/ui/src/components/Option/ScheduledTasks/ScheduledTaskAutomationDefinitionEditor.tsx

ScheduledTaskCreatePanel.tsxTypeScript baseline tweaks for scheduled task create panel +2/-2

TypeScript baseline tweaks for scheduled task create panel

• Small edits to align with updated typing/props. Supports the branch’s TS baseline cleanup goals.

apps/packages/ui/src/components/Option/ScheduledTasks/ScheduledTaskCreatePanel.tsx

Manager.tsxTypeScript baseline tweaks for Skills Manager +9/-6

TypeScript baseline tweaks for Skills Manager

• Adjusts Skills Manager typing/logic to satisfy TypeScript checks. Maintains compatibility while the branch dependencies are restored.

apps/packages/ui/src/components/Option/Skills/Manager.tsx

background.tsBackground entry TS hygiene adjustments +9/-2

Background entry TS hygiene adjustments

• Small background-entry edits to satisfy TypeScript and keep runtime behavior consistent. Supports overall baseline cleanup.

apps/packages/ui/src/entries/background.ts

scheduled-tasks-control-plane.tsMinor scheduled tasks control plane adjustment +1/-1

Minor scheduled tasks control plane adjustment

• One-line change to maintain compatibility with TS baseline or linting constraints. Not directly related to Visual Identity functionality.

apps/packages/ui/src/services/scheduled-tasks-control-plane.ts

mcp-hub.tsMinor MCP hub TS baseline adjustment +1/-1

Minor MCP hub TS baseline adjustment

• One-line change to maintain TS compatibility. Not directly tied to Visual Identity behavior.

apps/packages/ui/src/services/tldw/mcp-hub.ts

voice-cloning.tsVoice cloning client TS baseline adjustments +9/-3

Voice cloning client TS baseline adjustments

• Applies small edits to satisfy TypeScript checks. Keeps unrelated baseline fixes grouped with branch cleanup work.

apps/packages/ui/src/services/tldw/voice-cloning.ts

Tests (34) +6377 / -22
visual-identity-message-state.test.tsxAdd tests for Playground Visual Identity message state +188/-0

Add tests for Playground Visual Identity message state

• Adds coverage for message-state behavior when Visual Identity directives/metadata are present. Validates expected UI state transitions.

apps/packages/ui/src/components/Common/Playground/tests/visual-identity-message-state.test.tsx

ExpressionPicker.test.tsxAdd tests for ExpressionPicker +49/-0

Add tests for ExpressionPicker

• Validates expression picker rendering and selection behavior. Ensures correct handling of slot keys/labels.

apps/packages/ui/src/components/Common/VisualIdentity/tests/ExpressionPicker.test.tsx

ExpressionSlotGrid.test.tsxAdd tests for ExpressionSlotGrid +23/-0

Add tests for ExpressionSlotGrid

• Covers slot-grid mapping behaviors and expected UI output for assigned/unassigned slots. Protects core draft review interactions.

apps/packages/ui/src/components/Common/VisualIdentity/tests/ExpressionSlotGrid.test.tsx

VisualIdentityDraftReview.test.tsxAdd tests for VisualIdentityDraftReview +156/-0

Add tests for VisualIdentityDraftReview

• Adds test coverage for draft review rendering, including validation summaries and asset lists. Ensures activation-related UI states are stable.

apps/packages/ui/src/components/Common/VisualIdentity/tests/VisualIdentityDraftReview.test.tsx

VisualIdentityImage.test.tsxAdd tests for VisualIdentityImage +60/-0

Add tests for VisualIdentityImage

• Tests basic rendering and props handling for the VisualIdentityImage component. Ensures asset URL and media-type handling is consistent.

apps/packages/ui/src/components/Common/VisualIdentity/tests/VisualIdentityImage.test.tsx

VisualIdentityPackPanel.test.tsxAdd tests for VisualIdentityPackPanel +163/-0

Add tests for VisualIdentityPackPanel

• Covers pack panel flows such as listing packs and driving key actions. Prevents regressions in the main pack management UI surface.

apps/packages/ui/src/components/Common/VisualIdentity/tests/VisualIdentityPackPanel.test.tsx

VisualIdentityStage.test.tsxAdd tests for VisualIdentityStage +42/-0

Add tests for VisualIdentityStage

• Validates stage rendering and resolver-driven display behavior. Ensures stable UI output for runtime portrait/expression presentation.

apps/packages/ui/src/components/Common/VisualIdentity/tests/VisualIdentityStage.test.tsx

useGeneratedFileImportAction.test.tsAdd tests for generated-file import action hook +74/-0

Add tests for generated-file import action hook

• Adds coverage for importing generated files into Visual Identity drafts. Validates idempotency/error states and expected client calls.

apps/packages/ui/src/components/Common/VisualIdentity/tests/useGeneratedFileImportAction.test.ts

NotesListPanel.stage18.accessibility-selected-state.test.tsxFix/extend Notes accessibility selected-state test +2/-0

Fix/extend Notes accessibility selected-state test

• Updates an accessibility-focused Notes test to match current component behavior. Part of TypeScript/test baseline stabilization.

apps/packages/ui/src/components/Notes/tests/NotesListPanel.stage18.accessibility-selected-state.test.tsx

NotesListPanel.stage46.empty-error-states.test.tsxAlign NotesListPanel empty/error state tests +5/-3

Align NotesListPanel empty/error state tests

• Adjusts assertions and fixtures for empty/error states. Keeps the test suite consistent with UI changes and TS baseline fixes.

apps/packages/ui/src/components/Notes/tests/NotesListPanel.stage46.empty-error-states.test.tsx

NotesSidebar.stage46.list-error-count.test.tsxAlign NotesSidebar error-count tests +4/-3

Align NotesSidebar error-count tests

• Updates error-count expectations and related fixtures. Supports TS cleanup and ensures stable regression coverage.

apps/packages/ui/src/components/Notes/tests/NotesSidebar.stage46.list-error-count.test.tsx

Manager.first-use.test.tsxUpdate Characters manager first-use test +4/-2

Update Characters manager first-use test

• Adjusts the first-use test to match updated editor/manager behavior. Keeps regression coverage aligned with new metadata flows.

apps/packages/ui/src/components/Option/Characters/tests/Manager.first-use.test.tsx

AddSourceModal.stage1.ingestion.test.tsxAdjust AddSourceModal ingestion test +2/-1

Adjust AddSourceModal ingestion test

• Updates ingestion-stage test expectations to match current behavior. Part of baseline cleanup and test stabilization.

apps/packages/ui/src/components/Option/ResearchWorkspace/tests/AddSourceModal.stage1.ingestion.test.tsx

AudioInstallerPanel.test.tsxStabilize AudioInstallerPanel tests +7/-7

Stabilize AudioInstallerPanel tests

• Updates fixtures/assertions to match current UI output. Part of TypeScript/test baseline cleanup.

apps/packages/ui/src/components/Option/Setup/tests/AudioInstallerPanel.test.tsx

SkillsWorkspace.test.tsxUpdate Skills workspace test for baseline changes +2/-1

Update Skills workspace test for baseline changes

• Small test update to match current Skills workspace behavior. Supports suite stability during TS cleanup.

apps/packages/ui/src/components/Option/Skills/tests/SkillsWorkspace.test.tsx

audiobook-migration.test.tsUpdate Dexie audiobook migration test +4/-1

Update Dexie audiobook migration test

• Adjusts a migration test to align with current DB schema/migration expectations. Part of test baseline maintenance.

apps/packages/ui/src/db/dexie/tests/audiobook-migration.test.ts

useVisualIdentityResolver.test.tsxAdd tests for Visual Identity resolver hook +164/-0

Add tests for Visual Identity resolver hook

• Adds comprehensive tests for resolution semantics, including overrides and fallback behavior. Ensures strict-by-default behavior remains stable.

apps/packages/ui/src/hooks/tests/useVisualIdentityResolver.test.tsx

useChatActions.character.integration.test.tsxExtend chat actions integration tests for Visual Identity +77/-3

Extend chat actions integration tests for Visual Identity

• Updates character chat integration tests to include Visual Identity metadata flows. Validates that runtime actions propagate pack/expression state correctly.

apps/packages/ui/src/hooks/chat/tests/useChatActions.character.integration.test.tsx

tldw-api-client.visual-identities.test.tsAdd Visual Identities API client tests +209/-0

Add Visual Identities API client tests

• Adds tests for the new API client/domain methods covering packs, assets, imports, and resolution. Ensures request/response typing and error handling remain correct.

apps/packages/ui/src/services/tests/tldw-api-client.visual-identities.test.ts

visual-identity-emote.test.tsAdd tests for Visual Identity emote utilities +26/-0

Add tests for Visual Identity emote utilities

• Adds unit tests for formatting/handling emote directives tied to Visual Identity. Protects parsing/serialization behavior used by chat/VN flows.

apps/packages/ui/src/utils/tests/visual-identity-emote.test.ts

visual-identity-expressions.test.tsAdd tests for Visual Identity expression utilities +37/-0

Add tests for Visual Identity expression utilities

• Covers normalization/alias handling and other expression-related helpers. Ensures canonical slot behavior remains stable.

apps/packages/ui/src/utils/tests/visual-identity-expressions.test.ts

test_visual_identity_expression_metadata.pyAdd backend tests for chat Visual Identity expression metadata +272/-0

Add backend tests for chat Visual Identity expression metadata

• Adds tests verifying Visual Identity metadata flows through character chat sessions/messages. Covers strict semantics and expected API behaviors.

tldw_Server_API/tests/Character_Chat/test_visual_identity_expression_metadata.py

test_vn_play_api.pyAdd VN Play API tests for Visual Identity integration +107/-0

Add VN Play API tests for Visual Identity integration

• Adds coverage for VN Play API behaviors impacted by the Visual Identity bridge/casting resolver. Ensures VN endpoints handle sprite directives consistently.

tldw_Server_API/tests/VN_Play/test_vn_play_api.py

test_vn_play_assets.pyUpdate VN Play asset tests for generated-file bridge +107/-1

Update VN Play asset tests for generated-file bridge

• Updates/extends VN asset tests to reflect new provenance/import behavior. Ensures generated-file assets remain importable into Visual Identity packs.

tldw_Server_API/tests/VN_Play/test_vn_play_assets.py

test_expression_slots.pyAdd tests for expression slot normalization +52/-0

Add tests for expression slot normalization

• Tests canonical slot definitions and alias normalization behavior. Ensures stable slot vocabulary for resolution and UI.

tldw_Server_API/tests/Visual_Identities/test_expression_slots.py

test_visual_identities_api.pyAdd comprehensive API tests for Visual Identities +1126/-0

Add comprehensive API tests for Visual Identities

• Adds end-to-end tests for Visual Identity endpoints across packs, assets, imports, activation, bindings, and resolution semantics. Validates error handling and idempotency expectations.

tldw_Server_API/tests/Visual_Identities/test_visual_identities_api.py

test_visual_identity_archive_import.pyAdd tests for ZIP archive import pipeline +472/-0

Add tests for ZIP archive import pipeline

• Covers ZIP import parsing, validation, and draft creation behavior. Ensures invalid archives and constraint violations fail predictably.

tldw_Server_API/tests/Visual_Identities/test_visual_identity_archive_import.py

test_visual_identity_capabilities.pyAdd tests for Visual Identity capabilities reporting +51/-0

Add tests for Visual Identity capabilities reporting

• Tests the capabilities payload returned by the API, including supported mime types and size limits. Ensures frontend can rely on reported constraints.

tldw_Server_API/tests/Visual_Identities/test_visual_identity_capabilities.py

test_visual_identity_db.pyAdd repository/DB tests for Visual Identity persistence +910/-0

Add repository/DB tests for Visual Identity persistence

• Adds extensive tests for VisualIdentityRepository persistence, constraints, and integrity behaviors. Validates transactions, versioning, and idempotency record handling.

tldw_Server_API/tests/Visual_Identities/test_visual_identity_db.py

test_visual_identity_jobs.pyAdd tests for Visual Identity job orchestration +90/-0

Add tests for Visual Identity job orchestration

• Tests job creation and state transitions for ZIP import processing. Ensures async paths produce consistent draft/job outputs.

tldw_Server_API/tests/Visual_Identities/test_visual_identity_jobs.py

test_visual_identity_service.pyAdd tests for Visual Identity service behaviors +942/-0

Add tests for Visual Identity service behaviors

• Adds broad coverage for activation, binding, and resolution semantics at the service layer. Validates strict override behavior and explicit fallback rules.

tldw_Server_API/tests/Visual_Identities/test_visual_identity_service.py

test_visual_identity_source_context.pyAdd tests for source context canonicalization +130/-0

Add tests for source context canonicalization

• Tests validation and canonicalization of provenance contexts attached to assets/imports. Ensures portability across import/replay flows.

tldw_Server_API/tests/Visual_Identities/test_visual_identity_source_context.py

test_visual_identity_storage.pyAdd tests for Visual Identity storage validation and copy +540/-0

Add tests for Visual Identity storage validation and copy

• Covers asset validation, hashing, preview behavior, and copying from generated-file records. Ensures storage paths and constraints are enforced.

tldw_Server_API/tests/Visual_Identities/test_visual_identity_storage.py

test_visual_identity_vn_bridge.pyAdd tests for VN bridge provenance building +280/-0

Add tests for VN bridge provenance building

• Tests VN-specific provenance context building and integration used for importing generated VN assets into packs. Ensures correct context canonicalization and guardrails.

tldw_Server_API/tests/Visual_Identities/test_visual_identity_vn_bridge.py

Documentation (29) +6185 / -55
2026-06-24-audio-studio-media-tickets-implementation-plan.mdRefine Audio Studio media tickets implementation plan +76/-7

Refine Audio Studio media tickets implementation plan

• Updates the implementation plan content and sequencing for Audio Studio media ticket work. Clarifies tasks and expected deliverables.

Docs/superpowers/plans/2026-06-24-audio-studio-media-tickets-implementation-plan.md

2026-07-01-openai-realtime-speech-endpoint-implementation-plan.mdAdd realtime speech endpoint implementation plan +724/-0

Add realtime speech endpoint implementation plan

• Introduces a detailed plan for implementing an OpenAI-compatible realtime speech endpoint. Defines milestones, rollout considerations, and testing gates.

Docs/superpowers/plans/2026-07-01-openai-realtime-speech-endpoint-implementation-plan.md

2026-07-01-visual-identity-expression-packs-implementation-plan.mdAdd Visual Identity expression packs implementation plan +1089/-0

Add Visual Identity expression packs implementation plan

• Documents the end-to-end implementation plan for expression pack persistence, import, activation, and client/UI work. Captures staging strategy and review checklists.

Docs/superpowers/plans/2026-07-01-visual-identity-expression-packs-implementation-plan.md

2026-07-02-vn-visual-identity-bridge-implementation-plan.mdAdd VN↔Visual Identity bridge implementation plan +1532/-0

Add VN↔Visual Identity bridge implementation plan

• Adds a comprehensive plan for importing VN generated assets into Visual Identity packs and resolving sprite directives via casting. Defines strict override and fallback behaviors.

Docs/superpowers/plans/2026-07-02-vn-visual-identity-bridge-implementation-plan.md

2026-07-03-frontend-tsc-baseline-cleanup-plan.mdAdd plan for frontend TypeScript baseline cleanup +23/-0

Add plan for frontend TypeScript baseline cleanup

• Documents the baseline cleanup steps required to restore TypeScript checks after dependency restoration. Tracks cleanup under TASK-12099.

Docs/superpowers/plans/2026-07-03-frontend-tsc-baseline-cleanup-plan.md

2026-06-20-research-source-discovery-chokepoint-design.mdRevise research discovery ingest boundary design spec +98/-48

Revise research discovery ingest boundary design spec

• Refines the spec around ingestion boundaries and system responsibilities. Clarifies interfaces and decision points.

Docs/superpowers/specs/2026-06-20-research-source-discovery-chokepoint-design.md

2026-06-30-skills-uat-quality-gates-design.mdAdd Skills UAT quality gates design spec +196/-0

Add Skills UAT quality gates design spec

• Introduces the design for UAT quality gates for Skills workflows. Defines acceptance criteria and validation steps.

Docs/superpowers/specs/2026-06-30-skills-uat-quality-gates-design.md

2026-07-01-openai-realtime-speech-endpoint-design.mdAdd realtime speech endpoint design spec +490/-0

Add realtime speech endpoint design spec

• Adds a design specification for an OpenAI-compatible realtime speech endpoint. Covers API shape, compatibility notes, and operational constraints.

Docs/superpowers/specs/2026-07-01-openai-realtime-speech-endpoint-design.md

2026-07-01-visual-identity-expression-packs-design.mdAdd Visual Identity expression packs design spec +474/-0

Add Visual Identity expression packs design spec

• Defines the data model and behaviors for expression packs, drafts, activation, and resolution semantics. Documents provenance requirements and client expectations.

Docs/superpowers/specs/2026-07-01-visual-identity-expression-packs-design.md

2026-07-02-vn-visual-identity-bridge-design.mdAdd VN↔Visual Identity bridge design spec +346/-0

Add VN↔Visual Identity bridge design spec

• Specifies how VN generated files map into Visual Identity packs and how VN casting resolves sprite directives. Details strict override rules and opt-in fallback behavior.

Docs/superpowers/specs/2026-07-02-vn-visual-identity-bridge-design.md

task-12082 - Revise-research-discovery-ingest-boundary-design-on-dev.mdAdd backlog task for ingest boundary design revision +52/-0

Add backlog task for ingest boundary design revision

• Adds a tracking task describing scope and acceptance criteria for revising ingest boundaries. Keeps planning artifacts aligned with ongoing work.

backlog/tasks/task-12082 - Revise-research-discovery-ingest-boundary-design-on-dev.md

task-12088 - Design-OpenAI-compatible-realtime-speech-endpoint.mdAdd backlog task for realtime speech endpoint design +58/-0

Add backlog task for realtime speech endpoint design

• Introduces a task capturing design scope, deliverables, and review requirements for the realtime speech endpoint.

backlog/tasks/task-12088 - Design-OpenAI-compatible-realtime-speech-endpoint.md

task-12089 - Design-shared-visual-identity-expression-packs-for-character-persona-chat.mdAdd backlog task for expression pack design +39/-0

Add backlog task for expression pack design

• Adds a tracking task for shared Visual Identity expression packs across character/persona chat. Captures problem statement and success criteria.

backlog/tasks/task-12089 - Design-shared-visual-identity-expression-packs-for-character-persona-chat.md

task-12090 - Plan-shared-visual-identity-expression-packs-implementation.mdAdd backlog task for expression packs implementation planning +39/-0

Add backlog task for expression packs implementation planning

• Adds planning task metadata for implementing shared expression packs. Links stages and acceptance criteria.

backlog/tasks/task-12090 - Plan-shared-visual-identity-expression-packs-implementation.md

task-12090.1 - Implement-visual-identity-chat-runtime-portraits-picker-and-stage.mdAdd backlog task for chat runtime portraits/picker/stage +70/-0

Add backlog task for chat runtime portraits/picker/stage

• Tracks implementation of chat runtime Visual Identity picker and stage. Captures test expectations and rollout steps.

backlog/tasks/task-12090.1 - Implement-visual-identity-chat-runtime-portraits-picker-and-stage.md

task-12090.2 - Design-VN-visual-identity-import-bridge-and-casting-resolver.mdAdd backlog task for VN bridge and casting resolver design +47/-0

Add backlog task for VN bridge and casting resolver design

• Adds task documentation for designing the VN import bridge and role/casting resolver semantics for Visual Identity directives.

backlog/tasks/task-12090.2 - Design-VN-visual-identity-import-bridge-and-casting-resolver.md

task-12090.3 - Plan-VN-visual-identity-bridge-and-resolver-implementation.mdAdd backlog task for VN bridge implementation plan +58/-0

Add backlog task for VN bridge implementation plan

• Tracks the implementation plan for VN-to-Visual Identity bridging and resolver integration. Documents scope and validation steps.

backlog/tasks/task-12090.3 - Plan-VN-visual-identity-bridge-and-resolver-implementation.md

task-12090.4 - Implement-Stage-11A-VN-visual-identity-generated-file-import-bridge.mdAdd backlog task for VN generated-file import bridge +70/-0

Add backlog task for VN generated-file import bridge

• Tracks the implementation of importing VN generated assets into Visual Identity packs with provenance preservation.

backlog/tasks/task-12090.4 - Implement-Stage-11A-VN-visual-identity-generated-file-import-bridge.md

task-12090.5 - Implement-Stage-11B-VN-visual-identity-role-and-casting-resolver.mdAdd backlog task for VN role/casting resolver +71/-0

Add backlog task for VN role/casting resolver

• Tracks implementation of the VN casting adapter and strict resolution semantics for Visual Identity sprite directives.

backlog/tasks/task-12090.5 - Implement-Stage-11B-VN-visual-identity-role-and-casting-resolver.md

task-12091 - Implement-visual-identity-expression-pack-backend-foundation.mdAdd backlog task for backend foundation +59/-0

Add backlog task for backend foundation

• Adds the task description for implementing Visual Identity backend persistence foundations. Captures acceptance criteria and test expectations.

backlog/tasks/task-12091 - Implement-visual-identity-expression-pack-backend-foundation.md

task...

@qodo-code-review

qodo-code-review Bot commented Jul 3, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 74 rules

Grey Divider


Action required

1. Public cached private assets ✓ Resolved 🐞 Bug ⛨ Security
Description
get_visual_identity_asset_content serves authenticated, user-owned asset bytes with
Cache-Control: public, max-age=31536000, immutable, which allows shared intermediaries
(CDNs/proxies) to cache and potentially replay responses for the same URL across users in
deployments that have shared caching. This is a privacy/security footgun because the URL does not
encode user identity and access control happens at the app layer.
Code

tldw_Server_API/app/api/v1/endpoints/visual_identities.py[R806-811]

+    return FileResponse(
+        asset_path,
+        media_type=content_type,
+        filename=Path(str(asset.get("source_filename") or asset_path.name)).name,
+        headers={"Cache-Control": _IMMUTABLE_ASSET_CACHE_CONTROL},
+    )
Evidence
The Visual Identity service dependency requires an authenticated request user, yet the asset content
endpoint returns the file with a public cache directive derived from
_IMMUTABLE_ASSET_CACHE_CONTROL, meaning shared caches are explicitly permitted to store the
response.

tldw_Server_API/app/api/v1/endpoints/visual_identities.py[69-88]
tldw_Server_API/app/api/v1/endpoints/visual_identities.py[777-811]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An authenticated endpoint that returns user-owned media sets `Cache-Control: public, max-age=31536000, immutable`, which permits shared caches to store responses for reuse across users.

## Issue Context
The endpoint is authenticated via `Depends(get_request_user)` in the service dependency and returns bytes from user-scoped storage.

## Fix Focus Areas
- tldw_Server_API/app/api/v1/endpoints/visual_identities.py[69-71]
- tldw_Server_API/app/api/v1/endpoints/visual_identities.py[777-811]

## Suggested fix
- Change `_IMMUTABLE_ASSET_CACHE_CONTROL` to `private, max-age=31536000, immutable` (or remove long-lived caching entirely with `private, no-store` if that better matches the privacy model).
- Update/adjust tests asserting the cache-control header accordingly (e.g., `tldw_Server_API/tests/Visual_Identities/test_visual_identities_api.py`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Disallowed @pytest.mark.asyncio marker ✓ Resolved 📘 Rule violation ▣ Testability
Description
A new test adds @pytest.mark.asyncio, but the allowed marker set is restricted to unit,
integration, external_api, or local_llm_service. This violates the required pytest marker
policy and can break test selection in CI.
Code

tldw_Server_API/tests/VN_Play/test_vn_play_api.py[R1121-1123]

+@pytest.mark.asyncio
+async def test_session_response_includes_visual_identity_cast_sprite(
+    client: TestClient,
Evidence
PR Compliance ID 380651 restricts tests to exactly one approved marker. The new test is explicitly
annotated with @pytest.mark.asyncio, which is outside the allowed set.

Rule 380651: Apply appropriate pytest markers to all tests
tldw_Server_API/tests/VN_Play/test_vn_play_api.py[1121-1126]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new test uses `@pytest.mark.asyncio`, which is not in the approved marker allow-list.

## Issue Context
Tests must be decorated with exactly one accepted marker: `unit`, `integration`, `external_api`, or `local_llm_service`.

## Fix Focus Areas
- tldw_Server_API/tests/VN_Play/test_vn_play_api.py[1121-1126]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Blocking temp-file write in _upload_to_temp_file() ✓ Resolved 📘 Rule violation ➹ Performance
Description
The async helper _upload_to_temp_file() performs synchronous filesystem writes via
tempfile.NamedTemporaryFile and handle.write(...), which can block the event loop under load.
This violates the requirement to avoid blocking I/O in async-capable request paths.
Code

tldw_Server_API/app/api/v1/endpoints/visual_identities.py[R412-433]

+async def _upload_to_temp_file(
+    upload: UploadFile,
+    *,
+    max_bytes: int,
+) -> tuple[Path, int]:
+    suffix = Path(str(upload.filename or "")).suffix
+    handle = tempfile.NamedTemporaryFile(prefix="visual_identity_upload_", suffix=suffix, delete=False)
+    temp_path = Path(handle.name)
+    total = 0
+    try:
+        with handle:
+            while True:
+                chunk = await upload.read(_UPLOAD_CHUNK_SIZE_BYTES)
+                if not chunk:
+                    break
+                total += len(chunk)
+                if total > max_bytes:
+                    raise HTTPException(
+                        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
+                        detail="file_too_large",
+                    )
+                handle.write(chunk)
Evidence
PR Compliance ID 224212 requires avoiding synchronous I/O in async-capable code paths. The added
async function writes upload chunks to disk with handle.write(chunk) using a standard (blocking)
file handle.

Rule 224212: Prefer asynchronous I/O over blocking synchronous calls
tldw_Server_API/app/api/v1/endpoints/visual_identities.py[412-433]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`_upload_to_temp_file()` is `async` but writes to disk using blocking calls (`NamedTemporaryFile` + `handle.write`). This can block the event loop in FastAPI request handling.

## Issue Context
This code runs in the visual identity upload endpoint flow and may execute frequently.

## Fix Focus Areas
- tldw_Server_API/app/api/v1/endpoints/visual_identities.py[412-433]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. VNPlayService constructs VisualIdentityService internally ✓ Resolved 📘 Rule violation ⌂ Architecture
Description
VNPlayService._get_visual_identity_service() creates VisualIdentityService using self.repo.db
instead of receiving it via dependency injection. This hard-codes infrastructure wiring into core
logic and makes testing/composition harder.
Code

tldw_Server_API/app/core/VN_Play/service.py[R4353-4359]

+    def _get_visual_identity_service(self) -> Any:
+        if self._visual_identity_service is None:
+            self._visual_identity_service = VisualIdentityService(
+                self.repo.db,
+                owner_user_id=self.owner_user_id,
+            )
+        return self._visual_identity_service
Evidence
PR Compliance ID 380635 requires providing DB connections and service instances via dependency
injection. The method _get_visual_identity_service() directly constructs
VisualIdentityService(...) inside the VN play service implementation.

Rule 380635: Provide database connections and service instances via dependency injection
tldw_Server_API/app/core/VN_Play/service.py[4353-4359]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`VNPlayService` lazily instantiates `VisualIdentityService` internally, rather than requiring it to be provided by the caller/composition root.

## Issue Context
Compliance requires DB/service dependencies be passed in via constructor/parameters except in explicit bootstrap wiring modules.

## Fix Focus Areas
- tldw_Server_API/app/core/VN_Play/service.py[4353-4359]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. VisualIdentityService raises generic ValueError ✓ Resolved 📘 Rule violation ✧ Quality
Description
The new core service raises built-in ValueError for domain/application errors (e.g.,
visual_identity_binding_actor_required, pack_not_found). This violates the requirement to use
project-specific exceptions for domain failures, which improves consistency and handling across
layers.
Code

tldw_Server_API/app/core/Visual_Identities/service.py[R92-133]

+        if (actor_kind is None) != (actor_id is None):
+            raise ValueError("visual_identity_binding_actor_required")
+        normalized_actor_id = (
+            self._validate_actor_for_binding(actor_kind, actor_id)
+            if actor_kind is not None and actor_id is not None
+            else None
+        )
+        activation = self.repository.activate_draft_as_version(
+            owner_user_id=self.owner_user_id,
+            draft_id=draft_id,
+            actor_kind=actor_kind,
+            actor_id=normalized_actor_id,
+        )
+        binding = activation.get("binding")
+        return VisualIdentityActivationResult(
+            draft_id=int(draft_id),
+            pack_id=int(activation["pack"]["id"]),
+            pack_version_id=int(activation["pack_version"]["id"]),
+            asset_ids=tuple(int(asset["id"]) for asset in activation["assets"]),
+            binding_id=int(binding["id"]) if binding is not None else None,
+        )
+
+    def resolve_expression_asset(
+        self,
+        actor_kind: str,
+        actor_id: ActorId,
+        requested_expression_key: str,
+        manual_override_expression_key: str | None = None,
+        mood_expression_key: str | None = None,
+        *,
+        role_id: str | None = None,
+        role_label: str | None = None,
+        override_pack_id: int | None = None,
+        override_pack_version_id: int | None = None,
+        allow_override_fallback: bool = False,
+    ) -> VisualIdentityResolvedAsset:
+        """Resolve the best asset for a requested actor expression."""
+        normalized_actor_id = self._validate_actor_for_binding(actor_kind, actor_id)
+        normalized_requested = normalize_expression_key(requested_expression_key)
+        if override_pack_version_id is not None and override_pack_id is None:
+            raise ValueError("pack_not_found")
+        if override_pack_id is not None:
Evidence
PR Compliance ID 380621 requires using project-specific custom exceptions instead of generic
built-ins when representing domain/application error cases. The new service raises ValueError(...)
for multiple domain error conditions that are part of the feature’s contract.

Rule 380621: Use project-specific custom exceptions instead of generic built-ins
tldw_Server_API/app/core/Visual_Identities/service.py[92-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Domain-level failures in `VisualIdentityService` are signaled with generic `ValueError`.

## Issue Context
The codebase provides project-specific exception types (e.g., `BadRequestError` in `app/core/exceptions.py`) intended for invalid caller arguments and domain contract failures.

## Fix Focus Areas
- tldw_Server_API/app/core/Visual_Identities/service.py[92-133]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Unhandled VN resolver exceptions ✓ Resolved 🐞 Bug ☼ Reliability
Description
VNPlayService._apply_visual_directives calls resolve_visual_identity_directive without a broad
exception guard, while resolve_visual_identity_directive only converts ValueError into a
rejected resolution. Non-ValueError failures (e.g., DB/runtime errors) can bubble up and abort the
VN turn instead of producing a warning/rejection like the manifest directive branch does.
Code

tldw_Server_API/app/core/VN_Play/service.py[R4270-4280]

+            resolution = None
+            directive_rejection_reason = default_rejection_reason
+            if (
+                visual_identity_resolver is not None
+                and is_visual_identity_directive(directive_payload)
+            ):
+                resolution = resolve_visual_identity_directive(
+                    visual_identity_resolver,
+                    directive_payload,
+                )
+            elif manifest is not None:
Evidence
The VN Play service resolves Visual Identity directives without any exception handling, and the
Visual Identity resolver helper only catches ValueError, leaving other exception types uncaught in
this code path.

tldw_Server_API/app/core/VN_Play/service.py[4249-4295]
tldw_Server_API/app/core/VN_Play/assets.py[102-136]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
VN Play visual directive application wraps manifest-based directive resolution in a try/except, but Visual Identity directive resolution is not similarly guarded. Since the helper only catches `ValueError`, other exceptions can propagate and fail the overall VN turn.

## Issue Context
`VNPlayService._apply_visual_directives` resolves directives per-item; the manifest path catches `Exception` and records `resolver_error`, while the Visual Identity path does not.

## Fix Focus Areas
- tldw_Server_API/app/core/VN_Play/service.py[4249-4295]
- tldw_Server_API/app/core/VN_Play/assets.py[102-136]

## Suggested fix
Implement one of:
1) Add a try/except `Exception` around `resolve_visual_identity_directive(...)` in `_apply_visual_directives`, mirroring the manifest branch behavior (log exception, set `manifest_error`, set `directive_rejection_reason="resolver_error"`, and continue).
2) Alternatively (or additionally) broaden `resolve_visual_identity_directive` to catch `Exception` (not just `ValueError`) and return a rejected `VisualDirectiveResolution(applied=False, reason="resolver_error", ...)` while logging.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Missing docstrings in metadata helpers ✓ Resolved 📘 Rule violation ✧ Quality
Description
New helper functions like _optional_int_metadata and _optional_text_metadata were added without
docstrings. This violates the requirement that all functions have docstrings, reducing
maintainability and reviewer clarity.
Code

tldw_Server_API/app/api/v1/endpoints/character_chat_sessions.py[R779-792]

+def _optional_int_metadata(value: Any) -> int | None:
+    if isinstance(value, bool) or value is None:
+        return None
+    try:
+        return int(value)
+    except (TypeError, ValueError):
+        return None
+
+
+def _optional_text_metadata(value: Any) -> str | None:
+    if not isinstance(value, str):
+        return None
+    normalized = value.strip()
+    return normalized or None
Evidence
PR Compliance ID 224214 requires docstrings for all functions. The new helper functions begin
immediately with logic (e.g., if isinstance(...)) and contain no initial string literal docstring.

Rule 224214: Require docstrings for all modules, classes, and functions
tldw_Server_API/app/api/v1/endpoints/character_chat_sessions.py[779-792]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New helper functions were introduced without docstrings.

## Issue Context
This repository compliance policy requires docstrings for all functions/methods.

## Fix Focus Areas
- tldw_Server_API/app/api/v1/endpoints/character_chat_sessions.py[779-792]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread tldw_Server_API/app/api/v1/endpoints/visual_identities.py Outdated
Comment thread tldw_Server_API/app/api/v1/endpoints/character_chat_sessions.py
Comment thread tldw_Server_API/tests/VN_Play/test_vn_play_api.py Outdated
Comment thread tldw_Server_API/app/core/VN_Play/service.py
Comment thread tldw_Server_API/app/core/Visual_Identities/service.py
Comment thread tldw_Server_API/app/api/v1/endpoints/visual_identities.py
Comment thread tldw_Server_API/app/core/VN_Play/service.py
rmusser01 added 25 commits July 3, 2026 10:22
rmusser01 added 26 commits July 3, 2026 10:28
@rmusser01 rmusser01 force-pushed the codex/visual-identity-expression-packs branch from ffad4cd to 0d4328e Compare July 3, 2026 17:47
@rmusser01 rmusser01 merged commit f961b34 into dev Jul 3, 2026
2 of 36 checks passed
@rmusser01 rmusser01 deleted the codex/visual-identity-expression-packs branch July 3, 2026 18:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tldw_Server_API/app/core/VN_Play/service.py (2)

906-924: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Inconsistent active_sprites contract between success and fallback paths.

On the happy path (line 943) active_sprites is always set, even as an empty list. On manifest-build failure (lines 917-923) the key is only added if active_sprites:, so it can be entirely absent from the response when there are no embedded Visual Identity sprites. Clients relying on scene_state.active_sprites always being an array could break.

🐛 Proposed fix
             active_sprites = [
                 sprite
                 for sprite in _list_of_dicts(enriched.get("active_sprite_items"))
                 if _is_embedded_scene_asset(sprite)
             ]
-            if active_sprites:
-                enriched["active_sprites"] = active_sprites
+            enriched["active_sprites"] = active_sprites
             return enriched
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tldw_Server_API/app/core/VN_Play/service.py` around lines 906 - 924, The
fallback branch in VN_Play service currently returns an inconsistent scene state
because `active_sprites` is only added when non-empty. Update the exception path
in `service.py` around `self._build_pack_manifest(...)` so
`enriched["active_sprites"]` is always set to a list, matching the happy-path
contract used elsewhere in the scene state response. Keep the existing filtering
logic with `_list_of_dicts(...)` and `_is_embedded_scene_asset(...)`, but assign
the result unconditionally before returning `enriched`.

4253-4304: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

directive_error_type mislabeled for Visual Identity directives when the resolver is unavailable.

directive_error_type is initialized to the (unrelated) VN-asset-manifest manifest_error for every directive, then only overwritten inside the except blocks. When is_visual_identity is true and visual_identity_resolver is None, only directive_rejection_reason is set to "visual_identity_resolver_unavailable"directive_error_type is left as whatever manifest_error happened to be (e.g. a stale manifest exception class name from an unrelated non-identity directive in the same turn, or None). The resulting rejected-directive event/warning then carries a misleading error_type unrelated to the actual failure cause.

🐛 Proposed fix
             resolution = None
-            directive_error_type = manifest_error
-            directive_rejection_reason = default_rejection_reason
             if is_visual_identity:
+                directive_error_type = None
                 if visual_identity_resolver is None:
                     directive_rejection_reason = "visual_identity_resolver_unavailable"
                 else:
                     try:
                         resolution = resolve_visual_identity_directive(
                             visual_identity_resolver,
                             directive_payload,
                         )
                     except Exception as exc:
                         logger.exception(
                             "Failed to resolve VN Play Visual Identity directive: session_id={}",
                             session_id,
                         )
                         directive_error_type = exc.__class__.__name__
                         directive_rejection_reason = "resolver_error"
             elif manifest is not None:
+                directive_error_type = manifest_error
+                directive_rejection_reason = default_rejection_reason
                 try:
                     resolution = resolve_visual_directive(
                         manifest,
                         directive_payload,
                         seed=f"{seed}:{index}",
                     )
                 except Exception as exc:
                     logger.exception(
                         "Failed to resolve VN Play visual directive: session_id={}, pack_id={}",
                         session_id,
                         session.vn_asset_pack_id,
                     )
                     directive_error_type = exc.__class__.__name__
                     directive_rejection_reason = "resolver_error"
+            else:
+                directive_error_type = manifest_error
+                directive_rejection_reason = default_rejection_reason
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tldw_Server_API/app/core/VN_Play/service.py` around lines 4253 - 4304, The
Visual Identity directive rejection path in this loop is leaving
directive_error_type tied to manifest_error even when visual_identity_resolver
is missing, so the recorded error can be stale or unrelated. Update the handling
in the directive processing block around resolve_visual_identity_directive so
that when is_visual_identity is true and visual_identity_resolver is None,
directive_error_type is explicitly set to a Visual Identity–specific value (and
not inherited from manifest_error), alongside directive_rejection_reason =
"visual_identity_resolver_unavailable". Ensure the event/warning emitted from
this branch reflects the actual Visual Identity resolver failure rather than the
VN manifest state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/packages/ui/src/components/Common/Playground/Message.tsx`:
- Around line 850-858: `VisualIdentityImage` is receiving `previewUrl` and
`isAnimated` from `props.visualPreviewUrl`/`props.visualIsAnimated` even when
`portraitImage` has fallen back to `moodCharacterAvatar`, `baseCharacterAvatar`,
or `props.modelImage` instead of `visualCharacterAvatar`. Update the `Message`
component’s `portraitImage`/`resolvedModelImage` flow so those props are only
passed when the resolved image is actually the visual identity asset, and
otherwise clear them so the fallback avatar renders correctly.

In
`@apps/packages/ui/src/components/Common/VisualIdentity/ExpressionSlotGrid.tsx`:
- Around line 8-17: `CANONICAL_SLOT_ORDER` in `ExpressionSlotGrid` duplicates
the canonical expression keys already defined in
`VISUAL_IDENTITY_EXPRESSION_OPTIONS`, so update the grid to derive its order
from that shared source instead of maintaining a second hardcoded list. Use the
shared expression options export from `utils/visual-identity-expressions.ts` and
map/extract the canonical keys in `ExpressionSlotGrid.tsx`, keeping the same
order but ensuring any future additions or renames stay in sync automatically.

In
`@apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityDraftReview.tsx`:
- Around line 80-102: slotFromEntry is doing redundant work by calling
assetToSlot(asset, buildAssetUrl) only to keep the nested asset field and
discard the rest. Refactor the VisualIdentityDraftReview.tsx logic to use a
helper that returns just the asset shape (or otherwise reuse the already
available asset data) so the code is clearer and avoids building an unused full
slot object; keep the change localized around slotFromEntry, assetToSlot, and
buildAssetUrl.

In
`@apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityImage.tsx`:
- Around line 3-28: The reduced-motion state in usePrefersReducedMotion is
initialized to false and only corrected in useEffect, which causes
VisualIdentityImage to request the animated asset on the first render. Update
usePrefersReducedMotion to derive its initial value synchronously from
window.matchMedia when available, while keeping the effect/listener logic for
updates so the first render can choose previewUrl immediately for reduced-motion
users.

In
`@apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityPackPanel.tsx`:
- Around line 196-237: Pre-validate the selected ZIP in handleArchiveSelected
before calling fileToUploadData/startVisualIdentityZipImport by checking the
fetched capabilities first. Use the existing capabilities fields
(upload_max_bytes, archive_max_bytes, supported_mime_types) to reject
unsupported MIME types and oversized files up front, set an informative error,
and avoid making the network call when the file is obviously invalid. Keep the
validation close to handleArchiveSelected so the same logic can be reused for
the other upload flow mentioned in the diff.
- Around line 213-228: The polling timeout feedback in VisualIdentityPackPanel
misses non-terminal statuses other than importing, so update the post-loop
status handling after the draft polling in the VisualIdentityPackPanel logic.
Replace the exact latestDraft.status === "importing" check with a condition that
covers any non-terminal status when the loop ends without reaching a terminal
draft state, and keep using setStatusMessage to show the refresh guidance.
Reference the polling block that uses isTerminalDraftStatus,
DRAFT_POLL_ATTEMPTS, and setDraft so the fix applies regardless of whether the
final status is queued, validating, or importing.

In `@apps/packages/ui/src/components/Option/AudioStudio/TimelineEditor.tsx`:
- Around line 209-212: The ref callback in TimelineEditor’s setAudioElementRef
is setting referrerpolicy on an <audio> element, but that attribute is not
applied there. Remove the no-op element?.setAttribute("referrerpolicy",
"no-referrer") from setAudioElementRef and apply the referrer policy at a
document-level <meta name="referrer"> or via the server response header instead,
keeping the audio ref setup unchanged.

In `@apps/packages/ui/src/hooks/__tests__/useVisualIdentityResolver.test.tsx`:
- Around line 10-14: Reset the module-level resolver caches between tests so
each case starts clean. In useVisualIdentityResolver.test.tsx, extend the
existing beforeEach alongside vi.clearAllMocks() to clear the cache Maps used by
useVisualIdentityResolver.ts, so tests don’t accidentally reuse stale results
from prior cases. Locate the cache-handling logic in useVisualIdentityResolver
and make the test setup explicitly reset those singletons before every test.

In `@apps/packages/ui/src/hooks/useVisualIdentityResolver.ts`:
- Around line 273-361: The availability cache bypass in
useVisualIdentityResolver is tied to revision === 0, so after refresh()
increments revision once, the effect will never reuse cached availability for
that component instance again. Update the cache check in the React.useEffect
block to distinguish an initial load from later refreshes without permanently
disabling cache hits, and keep refresh() focused on invalidating the current
cacheKey and triggering a re-fetch rather than changing the cache eligibility
logic.
- Around line 118-225: The cache hit logic in useVisualIdentityResolver is
incorrectly gated by revision === 0, which prevents any later cacheKey from
reusing an already cached resolution after refresh() has been called once.
Update the effect’s cache check so it only depends on
resolutionCache.has(cacheKey) (and the existing null/guard conditions), and keep
refresh() responsible for invalidating the current cache entry and bumping
revision to force a re-run for the same cacheKey.

In `@apps/packages/ui/src/services/scheduled-tasks-control-plane.ts`:
- Line 288: The buildQuery helper is widened too far by using object, which
drops key/value typing for Object.entries. Update buildQuery to use a generic
constraint such as <T extends Record<string, unknown>>(params?: T) so callers
can pass typed request interfaces while preserving type safety inside the
function. Keep the change focused on the buildQuery symbol and any related call
sites that rely on its parameter typing.

In `@backlog/tasks/task-12090` -
Plan-shared-visual-identity-expression-packs-implementation.md:
- Line 4: The task metadata is inconsistent because the status is marked Done
while the Definition of Done checklist remains unchecked. Update the task entry
so the status in the frontmatter and the DOD items in the task body match by
either completing the checklist in the task document or changing the status back
out of Done; use the existing task frontmatter status field and the checklist
section in the task content to locate the mismatch.

In `@backlog/tasks/task-12090.3` -
Plan-VN-visual-identity-bridge-and-resolver-implementation.md:
- Around line 46-48: The FINAL_SUMMARY section has duplicate closing markers,
making the document structure ambiguous. Update the markdown around the
FINAL_SUMMARY boundary in the task document so there is only one
`SECTION:FINAL_SUMMARY:END` marker and the section is closed exactly once.

In `@backlog/tasks/task-12090.4` -
Implement-Stage-11A-VN-visual-identity-generated-file-import-bridge.md:
- Around line 37-54: The task notes are currently outside the
IMPLEMENTATION_NOTES section because the SECTION:IMPLEMENTATION_NOTES block
closes before the verification text. Move the existing task/status content back
inside the IMPLEMENTATION_NOTES markers in this markdown file, keeping the notes
under that section and leaving the rest of the document unchanged.

In `@backlog/tasks/task-12090.5` -
Implement-Stage-11B-VN-visual-identity-role-and-casting-resolver.md:
- Around line 66-72: The implementation notes are duplicated, which will trigger
markdownlint MD024; fold the new Task 10 text into the existing Implementation
Notes block instead of creating a second same-level heading. Update the content
around the existing Implementation Notes section in this task document, keeping
one `## Implementation Notes` heading and appending the follow-up notes there,
or rename the later heading if a separate section is truly needed.
- Around line 52-54: The document contains a duplicated FINAL_SUMMARY closer, so
remove the extra SECTION:FINAL_SUMMARY:END marker and keep only a single closing
marker for the final summary section; locate the repeated markers near the end
of the markdown task doc and clean up the redundant one so the section structure
is unambiguous.

In `@backlog/tasks/task-12098` -
Implement-visual-identity-pack-management-and-draft-review-UI.md:
- Around line 23-27: The document has duplicated section fences in the markdown
sections, so the parser will see nested BEGIN/END markers. Clean up the affected
sections by keeping only one matching begin/end pair per section, especially the
SECTION:DESCRIPTION block and the duplicated SECTION:FINAL_SUMMARY:END markers,
and verify the same fix in the other referenced section. Use the section marker
names to locate and remove the extra fences without changing the section
content.

---

Outside diff comments:
In `@tldw_Server_API/app/core/VN_Play/service.py`:
- Around line 906-924: The fallback branch in VN_Play service currently returns
an inconsistent scene state because `active_sprites` is only added when
non-empty. Update the exception path in `service.py` around
`self._build_pack_manifest(...)` so `enriched["active_sprites"]` is always set
to a list, matching the happy-path contract used elsewhere in the scene state
response. Keep the existing filtering logic with `_list_of_dicts(...)` and
`_is_embedded_scene_asset(...)`, but assign the result unconditionally before
returning `enriched`.
- Around line 4253-4304: The Visual Identity directive rejection path in this
loop is leaving directive_error_type tied to manifest_error even when
visual_identity_resolver is missing, so the recorded error can be stale or
unrelated. Update the handling in the directive processing block around
resolve_visual_identity_directive so that when is_visual_identity is true and
visual_identity_resolver is None, directive_error_type is explicitly set to a
Visual Identity–specific value (and not inherited from manifest_error),
alongside directive_rejection_reason = "visual_identity_resolver_unavailable".
Ensure the event/warning emitted from this branch reflects the actual Visual
Identity resolver failure rather than the VN manifest state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9e738505-37b0-4706-9f78-60b263f9a8e6

📥 Commits

Reviewing files that changed from the base of the PR and between 4ca5c7b and 0d4328e.

⛔ Files ignored due to path filters (11)
  • Docs/superpowers/plans/2026-06-24-audio-studio-media-tickets-implementation-plan.md is excluded by !docs/**
  • Docs/superpowers/plans/2026-07-01-openai-realtime-speech-endpoint-implementation-plan.md is excluded by !docs/**
  • Docs/superpowers/plans/2026-07-01-visual-identity-expression-packs-implementation-plan.md is excluded by !docs/**
  • Docs/superpowers/plans/2026-07-02-vn-visual-identity-bridge-implementation-plan.md is excluded by !docs/**
  • Docs/superpowers/plans/2026-07-03-frontend-tsc-baseline-cleanup-plan.md is excluded by !docs/**
  • Docs/superpowers/specs/2026-06-20-research-source-discovery-chokepoint-design.md is excluded by !docs/**
  • Docs/superpowers/specs/2026-06-30-skills-uat-quality-gates-design.md is excluded by !docs/**
  • Docs/superpowers/specs/2026-07-01-openai-realtime-speech-endpoint-design.md is excluded by !docs/**
  • Docs/superpowers/specs/2026-07-01-visual-identity-expression-packs-design.md is excluded by !docs/**
  • Docs/superpowers/specs/2026-07-02-vn-visual-identity-bridge-design.md is excluded by !docs/**
  • apps/packages/ui/node_modules/antd is excluded by !**/node_modules/**
📒 Files selected for processing (113)
  • apps/packages/ui/src/components/Common/Playground/Message.tsx
  • apps/packages/ui/src/components/Common/Playground/__tests__/visual-identity-message-state.test.tsx
  • apps/packages/ui/src/components/Common/Playground/message-types.ts
  • apps/packages/ui/src/components/Common/Playground/useMessageState.ts
  • apps/packages/ui/src/components/Common/VisualIdentity/ExpressionAssetUploader.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/ExpressionPicker.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/ExpressionSlotGrid.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityDraftReview.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityImage.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityPackPanel.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityStage.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/__tests__/ExpressionPicker.test.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/__tests__/ExpressionSlotGrid.test.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/__tests__/VisualIdentityDraftReview.test.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/__tests__/VisualIdentityImage.test.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/__tests__/VisualIdentityPackPanel.test.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/__tests__/VisualIdentityStage.test.tsx
  • apps/packages/ui/src/components/Common/VisualIdentity/__tests__/useGeneratedFileImportAction.test.ts
  • apps/packages/ui/src/components/Common/VisualIdentity/useGeneratedFileImportAction.ts
  • apps/packages/ui/src/components/Notes/__tests__/NotesListPanel.stage18.accessibility-selected-state.test.tsx
  • apps/packages/ui/src/components/Notes/__tests__/NotesListPanel.stage46.empty-error-states.test.tsx
  • apps/packages/ui/src/components/Notes/__tests__/NotesSidebar.stage46.list-error-count.test.tsx
  • apps/packages/ui/src/components/Option/AudioStudio/TimelineEditor.tsx
  • apps/packages/ui/src/components/Option/Characters/CharacterEditorForm.tsx
  • apps/packages/ui/src/components/Option/Characters/__tests__/Manager.first-use.test.tsx
  • apps/packages/ui/src/components/Option/Playground/PlaygroundChat.tsx
  • apps/packages/ui/src/components/Option/Playground/PlaygroundCompareCluster.tsx
  • apps/packages/ui/src/components/Option/ResearchWorkspace/__tests__/AddSourceModal.stage1.ingestion.test.tsx
  • apps/packages/ui/src/components/Option/ScheduledTasks/ScheduledTaskAutomationDefinitionEditor.tsx
  • apps/packages/ui/src/components/Option/ScheduledTasks/ScheduledTaskCreatePanel.tsx
  • apps/packages/ui/src/components/Option/Setup/__tests__/AudioInstallerPanel.test.tsx
  • apps/packages/ui/src/components/Option/Skills/Manager.tsx
  • apps/packages/ui/src/components/Option/Skills/__tests__/SkillsWorkspace.test.tsx
  • apps/packages/ui/src/components/PersonaGarden/VisualPackEditor.tsx
  • apps/packages/ui/src/db/dexie/__tests__/audiobook-migration.test.ts
  • apps/packages/ui/src/entries/background.ts
  • apps/packages/ui/src/hooks/__tests__/useVisualIdentityResolver.test.tsx
  • apps/packages/ui/src/hooks/chat/__tests__/useChatActions.character.integration.test.tsx
  • apps/packages/ui/src/hooks/chat/useChatActions.ts
  • apps/packages/ui/src/hooks/chat/useServerChatLoader.ts
  • apps/packages/ui/src/hooks/useMessageOption.tsx
  • apps/packages/ui/src/hooks/useVisualIdentityResolver.ts
  • apps/packages/ui/src/services/__tests__/tldw-api-client.visual-identities.test.ts
  • apps/packages/ui/src/services/scheduled-tasks-control-plane.ts
  • apps/packages/ui/src/services/tldw/TldwApiClient.ts
  • apps/packages/ui/src/services/tldw/domains/index.ts
  • apps/packages/ui/src/services/tldw/domains/visual-identities.ts
  • apps/packages/ui/src/services/tldw/mcp-hub.ts
  • apps/packages/ui/src/services/tldw/voice-cloning.ts
  • apps/packages/ui/src/store/option/types.ts
  • apps/packages/ui/src/types/visual-identities.ts
  • apps/packages/ui/src/utils/__tests__/visual-identity-emote.test.ts
  • apps/packages/ui/src/utils/__tests__/visual-identity-expressions.test.ts
  • apps/packages/ui/src/utils/visual-identity-emote.ts
  • apps/packages/ui/src/utils/visual-identity-expressions.ts
  • backlog/tasks/task-12082 - Revise-research-discovery-ingest-boundary-design-on-dev.md
  • backlog/tasks/task-12088 - Design-OpenAI-compatible-realtime-speech-endpoint.md
  • backlog/tasks/task-12089 - Design-shared-visual-identity-expression-packs-for-character-persona-chat.md
  • backlog/tasks/task-12090 - Plan-shared-visual-identity-expression-packs-implementation.md
  • backlog/tasks/task-12090.1 - Implement-visual-identity-chat-runtime-portraits-picker-and-stage.md
  • backlog/tasks/task-12090.2 - Design-VN-visual-identity-import-bridge-and-casting-resolver.md
  • backlog/tasks/task-12090.3 - Plan-VN-visual-identity-bridge-and-resolver-implementation.md
  • backlog/tasks/task-12090.4 - Implement-Stage-11A-VN-visual-identity-generated-file-import-bridge.md
  • backlog/tasks/task-12090.5 - Implement-Stage-11B-VN-visual-identity-role-and-casting-resolver.md
  • backlog/tasks/task-12091 - Implement-visual-identity-expression-pack-backend-foundation.md
  • backlog/tasks/task-12092 - Implement-visual-identity-asset-storage-validation.md
  • backlog/tasks/task-12093 - Implement-visual-identity-ZIP-import-draft-jobs.md
  • backlog/tasks/task-12094 - Implement-visual-identity-service-activation-and-resolution.md
  • backlog/tasks/task-12095 - Implement-visual-identity-API-schemas-and-endpoints.md
  • backlog/tasks/task-12096 - Implement-visual-identity-character-chat-message-metadata-integration.md
  • backlog/tasks/task-12097 - Implement-visual-identity-frontend-API-client-and-expression-utilities.md
  • backlog/tasks/task-12098 - Implement-visual-identity-pack-management-and-draft-review-UI.md
  • backlog/tasks/task-12099 - Clean-up-frontend-TypeScript-baseline-blockers-for-Visual-Identity-branch-finish.md
  • backlog/tasks/task-530.14 - Implement-Skills-UAT-and-quality-gates.md
  • tldw_Server_API/app/api/v1/endpoints/character_chat_sessions.py
  • tldw_Server_API/app/api/v1/endpoints/visual_identities.py
  • tldw_Server_API/app/api/v1/endpoints/vn_play.py
  • tldw_Server_API/app/api/v1/router_groups/core.py
  • tldw_Server_API/app/api/v1/schemas/visual_identity_schemas.py
  • tldw_Server_API/app/core/DB_Management/VisualIdentity_DB.py
  • tldw_Server_API/app/core/DB_Management/db_path_utils.py
  • tldw_Server_API/app/core/VN_Play/assets.py
  • tldw_Server_API/app/core/VN_Play/service.py
  • tldw_Server_API/app/core/Visual_Identities/__init__.py
  • tldw_Server_API/app/core/Visual_Identities/archive_import.py
  • tldw_Server_API/app/core/Visual_Identities/constraints.py
  • tldw_Server_API/app/core/Visual_Identities/expression_slots.py
  • tldw_Server_API/app/core/Visual_Identities/jobs.py
  • tldw_Server_API/app/core/Visual_Identities/service.py
  • tldw_Server_API/app/core/Visual_Identities/source_context.py
  • tldw_Server_API/app/core/Visual_Identities/storage.py
  • tldw_Server_API/app/core/Visual_Identities/vn_bridge.py
  • tldw_Server_API/app/services/startup_content_jobs_pollers.py
  • tldw_Server_API/app/services/startup_tail_finalization.py
  • tldw_Server_API/app/services/visual_identity_jobs_worker.py
  • tldw_Server_API/tests/Character_Chat/test_visual_identity_expression_metadata.py
  • tldw_Server_API/tests/Services/test_lifecycle_worker_catalog.py
  • tldw_Server_API/tests/Services/test_startup_content_jobs_pollers.py
  • tldw_Server_API/tests/Services/test_startup_tail_finalization.py
  • tldw_Server_API/tests/VN_Play/test_vn_play_api.py
  • tldw_Server_API/tests/VN_Play/test_vn_play_assets.py
  • tldw_Server_API/tests/VN_Play/test_vn_play_turns.py
  • tldw_Server_API/tests/Visual_Identities/test_expression_slots.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identities_api.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_archive_import.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_capabilities.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_db.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_jobs.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_jobs_worker.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_service.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_source_context.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_storage.py
  • tldw_Server_API/tests/Visual_Identities/test_visual_identity_vn_bridge.py

Comment on lines +850 to +858
const visualCharacterAvatar =
shouldUseCharacterIdentity && typeof props.visualAssetUrl === "string"
? props.visualAssetUrl.trim()
: ""
const moodCharacterAvatar = shouldUseCharacterIdentity
? resolveCharacterMoodImageUrl(props.characterIdentity, resolvedMoodLabel)
: ""
const characterAvatar = moodCharacterAvatar || baseCharacterAvatar
const characterAvatar =
visualCharacterAvatar || moodCharacterAvatar || baseCharacterAvatar

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

previewUrl/isAnimated mismatched with portraitImage when visual identity asset isn't used.

portraitImage (via resolvedModelImage) can resolve to moodCharacterAvatar, baseCharacterAvatar, or props.modelImage when visualCharacterAvatar is empty — i.e. portraitImage is not necessarily props.visualAssetUrl. But previewUrl/isAnimated passed to VisualIdentityImage are unconditionally sourced from props.visualPreviewUrl/props.visualIsAnimated, which only make sense paired with props.visualAssetUrl.

VisualIdentityImage chooses assetUrl vs previewUrl based on isAnimated and prefers-reduced-motion, then renders an with the provided alt. So if a user prefers reduced motion and visualIsAnimated happens to be true (stale from the message's original resolution) while portraitImage actually fell back to a mood/base/model avatar, the component will incorrectly swap in the unrelated visualPreviewUrl image instead of the intended static avatar.

Gate previewUrl/isAnimated on whether portraitImage actually is the visual-identity asset.

🐛 Proposed fix
+  const isVisualIdentityPortrait =
+    shouldUseCharacterIdentity &&
+    Boolean(visualCharacterAvatar) &&
+    portraitImage === visualCharacterAvatar
   const portraitPanel = shouldShowPortrait ? (
     <button
       type="button"
       onClick={() => setIsAvatarPreviewOpen(true)}
       className="relative hidden h-40 w-28 shrink-0 overflow-hidden rounded-2xl border border-border/60 bg-surface/30 shadow-sm transition hover:brightness-110 focus:outline-none focus:ring-2 focus:ring-focus sm:block md:h-52 md:w-36"
       aria-label={t("playground:previewCharacterAvatar", {
         defaultValue: "Preview character avatar"
       }) as string}
     >
       <VisualIdentityImage
         assetUrl={portraitImage}
-        previewUrl={props.visualPreviewUrl}
-        isAnimated={Boolean(props.visualIsAnimated)}
+        previewUrl={isVisualIdentityPortrait ? props.visualPreviewUrl : undefined}
+        isAnimated={isVisualIdentityPortrait ? Boolean(props.visualIsAnimated) : false}

Also applies to: 1928-1939

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/packages/ui/src/components/Common/Playground/Message.tsx` around lines
850 - 858, `VisualIdentityImage` is receiving `previewUrl` and `isAnimated` from
`props.visualPreviewUrl`/`props.visualIsAnimated` even when `portraitImage` has
fallen back to `moodCharacterAvatar`, `baseCharacterAvatar`, or
`props.modelImage` instead of `visualCharacterAvatar`. Update the `Message`
component’s `portraitImage`/`resolvedModelImage` flow so those props are only
passed when the resolved image is actually the visual identity asset, and
otherwise clear them so the fallback avatar renders correctly.

Comment on lines +8 to +17
const CANONICAL_SLOT_ORDER = [
"neutral",
"happy",
"excited",
"sad",
"angry",
"thinking",
"confused",
"surprised"
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive canonical order from the shared expression options instead of duplicating it.

CANONICAL_SLOT_ORDER re-lists the same keys already defined in VISUAL_IDENTITY_EXPRESSION_OPTIONS (utils/visual-identity-expressions.ts). Keeping two independent lists risks them drifting apart if a canonical expression is added/renamed in one place but not the other.

♻️ Proposed fix
-const CANONICAL_SLOT_ORDER = [
-  "neutral",
-  "happy",
-  "excited",
-  "sad",
-  "angry",
-  "thinking",
-  "confused",
-  "surprised"
-]
+import { VISUAL_IDENTITY_EXPRESSION_OPTIONS } from "`@/utils/visual-identity-expressions`"
+
+const CANONICAL_SLOT_ORDER = VISUAL_IDENTITY_EXPRESSION_OPTIONS.map((option) => option.key)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const CANONICAL_SLOT_ORDER = [
"neutral",
"happy",
"excited",
"sad",
"angry",
"thinking",
"confused",
"surprised"
]
import { VISUAL_IDENTITY_EXPRESSION_OPTIONS } from "`@/utils/visual-identity-expressions`"
const CANONICAL_SLOT_ORDER = VISUAL_IDENTITY_EXPRESSION_OPTIONS.map((option) => option.key)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/packages/ui/src/components/Common/VisualIdentity/ExpressionSlotGrid.tsx`
around lines 8 - 17, `CANONICAL_SLOT_ORDER` in `ExpressionSlotGrid` duplicates
the canonical expression keys already defined in
`VISUAL_IDENTITY_EXPRESSION_OPTIONS`, so update the grid to derive its order
from that shared source instead of maintaining a second hardcoded list. Use the
shared expression options export from `utils/visual-identity-expressions.ts` and
map/extract the canonical keys in `ExpressionSlotGrid.tsx`, keeping the same
order but ensuring any future additions or renames stay in sync automatically.

Comment on lines +80 to +102
const slotFromEntry = (
slotKey: string,
fallbackLabel: string,
canonical: boolean,
aliases: string[] = []
): ExpressionSlotGridSlot => {
const entry = isRecord(slotMap[slotKey]) ? slotMap[slotKey] : null
const expressionKey = stringFromEntry(entry, "expression_key") || slotKey
const label =
stringFromEntry(entry, "display_label") ||
fallbackLabel ||
getVisualIdentityExpressionDisplayLabel(expressionKey) ||
expressionKey
const assetId = assetIdFromEntry(entry)
const asset = assetId != null ? assetsById.get(assetId) : null
return {
key: slotKey,
label,
canonical,
aliases,
asset: asset ? assetToSlot(asset, buildAssetUrl).asset : null
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Building a full slot object just to discard most of it.

slotFromEntry calls assetToSlot(asset, buildAssetUrl).asset purely to extract the nested asset sub-object, discarding the key/label/canonical fields that assetToSlot also computes. A small helper that returns just the asset shape would avoid the redundant work and clarify intent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityDraftReview.tsx`
around lines 80 - 102, slotFromEntry is doing redundant work by calling
assetToSlot(asset, buildAssetUrl) only to keep the nested asset field and
discard the rest. Refactor the VisualIdentityDraftReview.tsx logic to use a
helper that returns just the asset shape (or otherwise reuse the already
available asset data) so the code is clearer and avoids building an unused full
slot object; keep the change localized around slotFromEntry, assetToSlot, and
buildAssetUrl.

Comment on lines +3 to +28
const usePrefersReducedMotion = (): boolean => {
const [prefersReducedMotion, setPrefersReducedMotion] = React.useState(false)

React.useEffect(() => {
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
return
}

const query = window.matchMedia("(prefers-reduced-motion: reduce)")
setPrefersReducedMotion(query.matches)

const handleChange = (event: MediaQueryListEvent) => {
setPrefersReducedMotion(event.matches)
}

if (typeof query.addEventListener === "function") {
query.addEventListener("change", handleChange)
return () => query.removeEventListener("change", handleChange)
}

query.addListener(handleChange)
return () => query.removeListener(handleChange)
}, [])

return prefersReducedMotion
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Reduced-motion preview is applied only after mount, causing a wasted fetch of the animated asset.

prefersReducedMotion starts false and flips via useEffect, so on first render an animated assetUrl is requested even for users who prefer reduced motion, then swapped to previewUrl once the effect runs — defeating the purpose of the preference for the initial load.

⚡ Proposed fix: lazy-initialize from matchMedia
-  const [prefersReducedMotion, setPrefersReducedMotion] = React.useState(false)
+  const [prefersReducedMotion, setPrefersReducedMotion] = React.useState(() => {
+    if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
+      return false
+    }
+    return window.matchMedia("(prefers-reduced-motion: reduce)").matches
+  })

Also applies to: 49-51

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityImage.tsx`
around lines 3 - 28, The reduced-motion state in usePrefersReducedMotion is
initialized to false and only corrected in useEffect, which causes
VisualIdentityImage to request the animated asset on the first render. Update
usePrefersReducedMotion to derive its initial value synchronously from
window.matchMedia when available, while keeping the effect/listener logic for
updates so the first render can choose previewUrl immediately for reduced-motion
users.

Comment on lines +196 to +237
const handleArchiveSelected = async (file: File) => {
if (typeof client.startVisualIdentityZipImport !== "function") return
setImporting(true)
setError(null)
try {
const archive = await fileToUploadData(file)
const started = await client.startVisualIdentityZipImport({
archive,
title: `${actorLabel} expression pack`,
pack_id: activePack?.id ?? null,
idempotency_key: makeIdempotencyKey()
})
setStatusMessage(`Import ${started.status}.`)
if (started.draft_id && typeof client.getVisualIdentityDraft === "function") {
let latestDraft = await client.getVisualIdentityDraft(started.draft_id)
if (!isMountedRef.current) return
setDraft(latestDraft)
for (
let attempt = 0;
!isTerminalDraftStatus(latestDraft.status) &&
attempt < DRAFT_POLL_ATTEMPTS &&
isMountedRef.current;
attempt += 1
) {
await wait(DRAFT_POLL_INTERVAL_MS)
latestDraft = await client.getVisualIdentityDraft(started.draft_id)
if (!isMountedRef.current) return
setDraft(latestDraft)
if (isTerminalDraftStatus(latestDraft.status)) break
}
if (latestDraft.status === "importing" && isMountedRef.current) {
setStatusMessage("Import is still processing. Refresh the draft to check again.")
}
}
} catch (importError) {
if (isMountedRef.current) {
setError(importError instanceof Error ? importError.message : "Failed to import expression ZIP.")
}
} finally {
if (isMountedRef.current) setImporting(false)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Pre-validate file size/type against fetched capabilities before uploading.

capabilities.upload_max_bytes, archive_max_bytes, and supported_mime_types are already loaded but not used to reject an obviously oversized/unsupported file before making the network call, so users only find out after a full upload round-trip fails server-side.

Also applies to: 263-293

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/packages/ui/src/components/Common/VisualIdentity/VisualIdentityPackPanel.tsx`
around lines 196 - 237, Pre-validate the selected ZIP in handleArchiveSelected
before calling fileToUploadData/startVisualIdentityZipImport by checking the
fetched capabilities first. Use the existing capabilities fields
(upload_max_bytes, archive_max_bytes, supported_mime_types) to reject
unsupported MIME types and oversized files up front, set an informative error,
and avoid making the network call when the file is obviously invalid. Keep the
validation close to handleArchiveSelected so the same logic can be reused for
the other upload flow mentioned in the diff.

Comment on lines +46 to +48
<!-- SECTION:FINAL_SUMMARY:END -->

<!-- SECTION:FINAL_SUMMARY:END -->

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the duplicate FINAL_SUMMARY:END markers.

The repeated closing tags make the section structure ambiguous and can trip markdown linting/parsing.

♻️ Proposed fix
-<!-- SECTION:FINAL_SUMMARY:END -->
-<!-- SECTION:FINAL_SUMMARY:END -->
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backlog/tasks/task-12090.3` -
Plan-VN-visual-identity-bridge-and-resolver-implementation.md around lines 46 -
48, The FINAL_SUMMARY section has duplicate closing markers, making the document
structure ambiguous. Update the markdown around the FINAL_SUMMARY boundary in
the task document so there is only one `SECTION:FINAL_SUMMARY:END` marker and
the section is closed exactly once.

Source: Linters/SAST tools

Comment on lines +37 to +54
<!-- SECTION:IMPLEMENTATION_NOTES:BEGIN -->

<!-- SECTION:IMPLEMENTATION_NOTES:END -->

Task 1 complete: source_context helper added in 4f0da62fae, review feedback fixed in 00b4c40717. Focused pytest passed with 21 tests; source_context.py Bandit scan clean. Spec compliance and code-quality re-review approved.

Task 2 complete: asset source_context_json DB/schema/create_asset/activation/manifest support added in 7d9a4decc4, review feedback fixed in 6b9b31c57b. Focused pytest passed with 43 tests; VisualIdentity_DB.py Bandit scan clean; raw test-scope Bandit only reports expected pytest B101 asserts. Spec compliance and code-quality re-review approved.

Task 3 complete: API/schema/idempotency source_context support added in 3d692271f5. Focused API pytest passed with 12 tests; quality review approved with only an advisory invalid-context API hardening test suggestion. Bandit production scope clean; raw test-scope Bandit only reports expected pytest B101 asserts.

Task 4 complete: VN generated-file provenance bridge and endpoint integration added in c5991c3293, review fixes applied in 3e0fc91643, and final source_feature trust-boundary hardening added in 2b6d62c59c. Focused backend pytest passed with 33 tests; git diff --check clean; Bandit on vn_bridge.py reported zero findings. Spec compliance and code-quality re-review approved.

Task 5 complete: frontend generated-file import action/types/client contract coverage added in 94f319fec9, with source_context fixture follow-up in cce9082805. Focused frontend Vitest passed with 11 tests for the visual identity client contract and generated-file import helper. Expanded component test run was blocked by the existing local antd dependency resolution issue, but the two reviewed source_context fixture gaps are fixed. Spec compliance and code-quality re-review approved.

Stage 11A verification: worktree-local `.venv` was absent, so backend verification used the shared project venv at `/Users/macbook-dev/Documents/GitHub/tldw_server2/.venv`. Focused backend pytest passed with 97 tests across source_context, VN bridge, DB, service, and API suites. Focused frontend Vitest passed with 11 tests across the visual identity client contract and generated-file import helper. Bandit Stage 11A backend scope wrote `/tmp/bandit_vn_visual_identity_stage11a.json` with 0 errors and 0 findings. `git diff --check` was clean. Expanded VisualIdentityDraftReview/VisualIdentityPackPanel Vitest remains blocked by the existing local antd dependency resolution issue, not by the source_context fixture changes.

Final review follow-up: 98ef07b746 fixed three whole-slice review findings. VN imports now always persist derived structural provenance, verify `vn_asset_items.generated_file_id` matches the imported generated-file record, and replay completed VN idempotency responses before live generated-file/VN validation. Red tests failed for the three expected gaps before implementation; focused fix tests passed with 36 tests. Final whole-slice review approved. Fresh final verification passed with 100 backend tests, 11 frontend tests, `git diff --check` clean, and `/tmp/bandit_vn_visual_identity_stage11a_final.json` reporting 0 errors and 0 findings.
<!-- SECTION:NOTES:END -->

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Move the notes back inside the IMPLEMENTATION_NOTES block.

The current markers close that section before the actual task notes, so the recorded verification details are not captured by the structured field.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backlog/tasks/task-12090.4` -
Implement-Stage-11A-VN-visual-identity-generated-file-import-bridge.md around
lines 37 - 54, The task notes are currently outside the IMPLEMENTATION_NOTES
section because the SECTION:IMPLEMENTATION_NOTES block closes before the
verification text. Move the existing task/status content back inside the
IMPLEMENTATION_NOTES markers in this markdown file, keeping the notes under that
section and leaving the rest of the document unchanged.

Comment on lines +52 to +54
<!-- SECTION:FINAL_SUMMARY:END -->

<!-- SECTION:FINAL_SUMMARY:END -->

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the extra FINAL_SUMMARY:END markers.

These repeated closers are redundant and make the document structure harder to parse.

♻️ Proposed fix
-<!-- SECTION:FINAL_SUMMARY:END -->
-<!-- SECTION:FINAL_SUMMARY:END -->
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backlog/tasks/task-12090.5` -
Implement-Stage-11B-VN-visual-identity-role-and-casting-resolver.md around lines
52 - 54, The document contains a duplicated FINAL_SUMMARY closer, so remove the
extra SECTION:FINAL_SUMMARY:END marker and keep only a single closing marker for
the final summary section; locate the repeated markers near the end of the
markdown task doc and clean up the redundant one so the section structure is
unambiguous.

Source: Linters/SAST tools

Comment on lines +66 to +72
## Implementation Notes

<!-- SECTION:IMPLEMENTATION_NOTES:BEGIN -->
Task 9 complete. Added frontend resolver request/response fields for VN role/casting override parameters, serialized them through the Visual Identity API client, and included hook role/override options in the resolver cache key and client request. Added focused tests for role override query serialization and override-pack cache separation. Verification: red run failed on the missing query/cache behavior; green run `bunx vitest run src/services/__tests__/tldw-api-client.visual-identities.test.ts src/hooks/__tests__/useVisualIdentityResolver.test.tsx` passed 13 tests. TypeScript diagnostics were attempted with the standard command and hit Node heap OOM, then rerun with `NODE_OPTIONS=--max-old-space-size=8192`; full package diagnostics still fail on existing baseline issues, mainly missing `antd` types, and a filtered rerun showed no diagnostics for the touched Task 9 files. `git diff --check` passed.
Task 9A complete. Added a non-persistent VN Play Visual Identity casting adapter for sprite directives carrying actor_kind/actor_id, role metadata, expression, and optional override pack/version controls. VN Play now routes those directives through VisualIdentityService.resolve_expression_asset, converts resolved assets into renderable sprite items with Visual Identity content URLs and metadata, and preserves embedded Visual Identity sprites during scene-state enrichment without mutating actor bindings. Added focused asset resolver tests plus a VN Play turn/API regression for active_sprites. Verification: red run failed on missing resolve_visual_identity_directive; green run `python -m pytest tldw_Server_API/tests/VN_Play/test_vn_play_assets.py tldw_Server_API/tests/VN_Play/test_vn_play_api.py::test_session_response_includes_visual_identity_cast_sprite -q` passed 8 tests; compileall on VN_Play/assets.py and VN_Play/service.py passed; Bandit report /tmp/bandit_task12090_5_vn_play_casting_adapter.json has errors [] and results []; git diff --check passed.
Task 10 final verification complete. Backend focused verification passed: `python -m pytest tldw_Server_API/tests/Visual_Identities/test_visual_identity_source_context.py tldw_Server_API/tests/Visual_Identities/test_visual_identity_vn_bridge.py tldw_Server_API/tests/Visual_Identities/test_visual_identity_db.py tldw_Server_API/tests/Visual_Identities/test_visual_identity_service.py tldw_Server_API/tests/Visual_Identities/test_visual_identities_api.py tldw_Server_API/tests/VN_Play/test_vn_play_assets.py tldw_Server_API/tests/VN_Play/test_vn_play_api.py::test_session_response_includes_visual_identity_cast_sprite -q` passed 124 tests with 15 warnings. Frontend focused verification passed: `bunx vitest run src/services/__tests__/tldw-api-client.visual-identities.test.ts src/components/Common/VisualIdentity/__tests__/useGeneratedFileImportAction.test.ts src/hooks/__tests__/useVisualIdentityResolver.test.tsx` passed 16 tests. Full frontend TypeScript diagnostics still fail on existing package baseline issues, primarily missing `antd` types and unrelated scheduled-task/background/voice-cloning diagnostics; the filtered rerun for touched Stage 11 frontend files produced no diagnostics. Bandit touched backend scope report `/tmp/bandit_vn_visual_identity_stage11.json` has errors [] and results []. Final status review showed only plan/Backlog metadata plus unrelated untracked watchlist template files not touched by this task.
<!-- SECTION:IMPLEMENTATION_NOTES:END -->

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fold this into the earlier Implementation Notes section.

The second ## Implementation Notes heading duplicates the first one and will trip markdownlint MD024; if these are follow-up notes, append them to the existing block or rename the heading.

🧰 Tools
🪛 LanguageTool

[grammar] ~71-~71: Use a hyphen to join words.
Context: ...sed 124 tests with 15 warnings. Frontend focused verification passed: `bunx vites...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.22.1)

[warning] 66-66: Multiple headings with the same content

(MD024, no-duplicate-heading)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backlog/tasks/task-12090.5` -
Implement-Stage-11B-VN-visual-identity-role-and-casting-resolver.md around lines
66 - 72, The implementation notes are duplicated, which will trigger
markdownlint MD024; fold the new Task 10 text into the existing Implementation
Notes block instead of creating a second same-level heading. Update the content
around the existing Implementation Notes section in this task document, keeping
one `## Implementation Notes` heading and appending the follow-up notes there,
or rename the later heading if a separate section is truly needed.

Source: Linters/SAST tools

Comment on lines +23 to +27
<!-- SECTION:DESCRIPTION:BEGIN -->
<!-- SECTION:DESCRIPTION:BEGIN -->
<!-- SECTION:DESCRIPTION:END -->

<!-- SECTION:DESCRIPTION:END -->

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the duplicated section fences.

SECTION:DESCRIPTION is nested twice, and SECTION:FINAL_SUMMARY:END appears twice. That will break any parser that relies on these markers; please keep one begin/end pair per section.

Also applies to: 59-71

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backlog/tasks/task-12098` -
Implement-visual-identity-pack-management-and-draft-review-UI.md around lines 23
- 27, The document has duplicated section fences in the markdown sections, so
the parser will see nested BEGIN/END markers. Clean up the affected sections by
keeping only one matching begin/end pair per section, especially the
SECTION:DESCRIPTION block and the duplicated SECTION:FINAL_SUMMARY:END markers,
and verify the same fix in the other referenced section. Use the section marker
names to locate and remove the extra fences without changing the section
content.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant