feat: switch NovelAI image generation to Diffusion 5 #34

Merged
bjoern merged 3 commits from feat/novelai-v5 into main 2026-08-21 12:47:22 +02:00
Member

Bumps the novelai_image_gen submodule 020ea0a..8a7450d (PR #4 upstream: v5 request tree with multipart transport) and switches generate_image_tool to the v5 request tree.

Changes

  • Import package:novelai_image_gen/v5.dart alongside the main barrel; build v5.ImageGenerationRequest / v5.Character / v5.Position.auto instead of the V4 tree. Client, options, Tag, CharacterGender, and Result remain from the main barrel (dispatch happens inside NovelAIClient.generateImage on the request type).
  • Removed the V4 style-reference (precise reference) block: v5 does not support precise references yet. When a style reference is configured, the tool logs a warning that it is skipped — the config, DB column, and settings UI stay plumbed through for when the library adds v5 support.
  • Removed the now-dead imageDirectory constructor parameter and _imageDirectory field (only the style-reference resolution used it).

Not changed (deliberate)

  • Defaults: guidance/steps stay 5.0/28 (existing DB settings carry over). The v5 library defaults are 7.0/23 — if you want those, say the word and I'll add them as a follow-up (either by changing the AppSettings/NovelAIConfig defaults or via a migration).
  • Quality tags: v5 pins qualityPresetId = "none", so the app's own quality tags remain meaningful and are untouched.
  • Settings UI: unchanged (only uses the client API, which is source-compatible).

Verification

  • dart analyze angela_core: clean (8 pre-existing infos/warnings, none new)
  • dart analyze angela_server: clean
  • flutter analyze angela_app: 1 pre-existing deprecation info (model_tab.dart, unrelated)
  • dart test angela_core: 138/138 pass
  • dart test novelai_image_gen @ 8a7450d: 171/171 pass (including all v5 request-builder + validation tests)
Bumps the `novelai_image_gen` submodule `020ea0a..8a7450d` (PR #4 upstream: v5 request tree with multipart transport) and switches `generate_image_tool` to the v5 request tree. ## Changes - Import `package:novelai_image_gen/v5.dart` alongside the main barrel; build `v5.ImageGenerationRequest` / `v5.Character` / `v5.Position.auto` instead of the V4 tree. Client, options, `Tag`, `CharacterGender`, and `Result` remain from the main barrel (dispatch happens inside `NovelAIClient.generateImage` on the request type). - Removed the V4 style-reference (precise reference) block: **v5 does not support precise references yet**. When a style reference is configured, the tool logs a warning that it is skipped — the config, DB column, and settings UI stay plumbed through for when the library adds v5 support. - Removed the now-dead `imageDirectory` constructor parameter and `_imageDirectory` field (only the style-reference resolution used it). ## Not changed (deliberate) - **Defaults**: guidance/steps stay 5.0/28 (existing DB settings carry over). The v5 library defaults are 7.0/23 — if you want those, say the word and I'll add them as a follow-up (either by changing the `AppSettings`/`NovelAIConfig` defaults or via a migration). - Quality tags: v5 pins `qualityPresetId = "none"`, so the app's own quality tags remain meaningful and are untouched. - Settings UI: unchanged (only uses the client API, which is source-compatible). ## Verification - `dart analyze` angela_core: clean (8 pre-existing infos/warnings, none new) - `dart analyze` angela_server: clean - `flutter analyze` angela_app: 1 pre-existing deprecation info (model_tab.dart, unrelated) - `dart test` angela_core: **138/138 pass** - `dart test` novelai_image_gen @ `8a7450d`: **171/171 pass** (including all v5 request-builder + validation tests)
feat: switch NovelAI image generation to Diffusion 5
Some checks failed
Test / test (apps/angela_server) (pull_request) Successful in 37s
Test / test (packages/angela_api) (pull_request) Successful in 35s
Test / test (packages/angela_core) (pull_request) Failing after 22s
0ce0c78bb7
Bump novelai_image_gen submodule 020ea0a..8a7450d (PR #4: v5 request
tree with multipart transport) and switch generate_image_tool to the
v5 request tree via the v5.dart entrypoint.

Style references (V4 precise reference) are not yet available for v5
requests; the tool now logs a warning when one is configured instead
of silently dropping it, and the unused imageDirectory plumbing is
removed.

Coverage: apps/angela_server

File Line coverage
lib/config.dart 5.3% (1 of 19)
lib/handlers/timer_handler.dart 44.8% (47 of 105)
lib/server_context.dart 100.0% (28 of 28)
lib/services/user_message_persistence.dart 100.0% (12 of 12)
lib/util/json_helpers.dart 53.8% (7 of 13)
lib/util/request_parser.dart 35.7% (5 of 14)
lib/services/conversation_activity_tracker.dart 15.4% (2 of 13)
lib/handlers/status_handler.dart 95.5% (21 of 22)
lib/handlers/conversation_handler.dart 36.4% (16 of 44)
lib/handlers/image_handler.dart 49.5% (52 of 105)

Total: 50.9% (191 of 375)

<!-- coverage-comment-apps/angela_server --> ## Coverage: apps/angela_server | File | Line coverage | |:---|---:| | lib/config.dart | 5.3% (1 of 19) | | lib/handlers/timer_handler.dart | 44.8% (47 of 105) | | lib/server_context.dart | 100.0% (28 of 28) | | lib/services/user_message_persistence.dart | 100.0% (12 of 12) | | lib/util/json_helpers.dart | 53.8% (7 of 13) | | lib/util/request_parser.dart | 35.7% (5 of 14) | | lib/services/conversation_activity_tracker.dart | 15.4% (2 of 13) | | lib/handlers/status_handler.dart | 95.5% (21 of 22) | | lib/handlers/conversation_handler.dart | 36.4% (16 of 44) | | lib/handlers/image_handler.dart | 49.5% (52 of 105) | **Total: 50.9% (191 of 375)**

Coverage: packages/angela_api

File Line coverage
lib/src/routes.dart 0.0% (0 of 75)
lib/src/dto/agenda_dto.dart 0.0% (0 of 50)
lib/src/dto/assistant_dto.dart 0.0% (0 of 79)
lib/src/dto/character_alias_dto.dart 0.0% (0 of 19)
lib/src/dto/chat_audio.dart 0.0% (0 of 7)
lib/src/dto/enums.dart 20.0% (3 of 15)
lib/src/dto/chat_dto.dart 95.0% (19 of 20)
lib/src/dto/chat_image.dart 100.0% (7 of 7)
lib/src/dto/conversation_dto.dart 0.0% (0 of 15)
lib/src/dto/message_metadata.dart 100.0% (20 of 20)
lib/src/dto/recollection_dto.dart 0.0% (0 of 28)
lib/src/dto/persona_dto.dart 0.0% (0 of 10)
lib/src/dto/timer_dto.dart 0.0% (0 of 54)
lib/src/dto/memory_dto.dart 0.0% (0 of 5)
lib/src/dto/response_dto.dart 0.0% (0 of 12)
lib/src/dto/status_dto.dart 100.0% (18 of 18)
lib/src/dto/todo_item_dto.dart 100.0% (9 of 9)
lib/src/dto/trigger_dto.dart 0.0% (0 of 48)

Total: 15.5% (76 of 491)

<!-- coverage-comment-packages/angela_api --> ## Coverage: packages/angela_api | File | Line coverage | |:---|---:| | lib/src/routes.dart | 0.0% (0 of 75) | | lib/src/dto/agenda_dto.dart | 0.0% (0 of 50) | | lib/src/dto/assistant_dto.dart | 0.0% (0 of 79) | | lib/src/dto/character_alias_dto.dart | 0.0% (0 of 19) | | lib/src/dto/chat_audio.dart | 0.0% (0 of 7) | | lib/src/dto/enums.dart | 20.0% (3 of 15) | | lib/src/dto/chat_dto.dart | 95.0% (19 of 20) | | lib/src/dto/chat_image.dart | 100.0% (7 of 7) | | lib/src/dto/conversation_dto.dart | 0.0% (0 of 15) | | lib/src/dto/message_metadata.dart | 100.0% (20 of 20) | | lib/src/dto/recollection_dto.dart | 0.0% (0 of 28) | | lib/src/dto/persona_dto.dart | 0.0% (0 of 10) | | lib/src/dto/timer_dto.dart | 0.0% (0 of 54) | | lib/src/dto/memory_dto.dart | 0.0% (0 of 5) | | lib/src/dto/response_dto.dart | 0.0% (0 of 12) | | lib/src/dto/status_dto.dart | 100.0% (18 of 18) | | lib/src/dto/todo_item_dto.dart | 100.0% (9 of 9) | | lib/src/dto/trigger_dto.dart | 0.0% (0 of 48) | **Total: 15.5% (76 of 491)**
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! A Diffusion 5 migration~ ♪ The whole constellation lined up beautifully when I checked it: type mixing across the barrels is correct (Tag and CharacterGender are version-neutral, lib/v5.dart re-exports them precisely so this works), the client dispatches on the request type (novelai_client.dart:82-83), the dimensions are all divisible by 64, qualityPresetId: 'none' really is pinned internally so the app's own quality tags stay meaningful, and the dead imageDirectory parameter is fully gone — no orphan references anywhere, not a half-delete. I reproduced every claim locally too: 138/138 in angela_core, 171/171 in the submodule @ 8a7450d, analyze showing exactly the 8 pre-existing issues and nothing new. Your PR body is honest documentation, scarlet~ I like that ♡

But then I ran coverage. And the smile... tightened a little~ ♡

Verdict: I can't let this pass~ ♡

These need fixing before I'm satisfied~

  1. generate_image_tool.dart — the new code paths have zero test coverage. Measured, not guessed: 0% (0 of 84 lines) while sibling generate_video_tool.dart sits at 100% (79/79) with a scripted-HTTP adapter suite. This PR adds real new runtime behavior:

    • the style-reference skip branch (:227-233) — config non-null → warning → generate without it. Brand-new arm, never exercised by any test;
    • the v5 request constructionv5.Character building with gender mapping + unknown-gender none fallback, Position.auto, tag-strength overrides crossing the barrel boundary, guidance/steps always passed from DB config;
    • the aspect-ratio fallback and both result arms now feeding off a multipart v5 transport.

    Fufu~ you added a code path but forgot to test it? I can't let that slide~ ♡ Especially when the seam is right there: NovelAIClient({required options, Dio? dio}) accepts a custom Dio explicitly "for testing", and generate_video_tool_test.dart shows the established pattern (_QueueAdapter + temp workspace + ImageStorageService). For the response side, the v5 path expects a ZIP — the submodule's own test/helpers/mock_dio_adapter.dart already builds zipped PNG responses you can crib from.

    Fix: add generate_image_tool_test.dart covering at minimum: style-ref configured → warning + generation proceeds; character mapping (incl. gender fallback); portrait fallback for unknown ratio; success path (saved file, savedPathPrefix join, seed/dims in description); failure path (Image generation failed: ... (HTTP n)).

💡 Little ideas (non-blocking)~

  1. generate_image_tool.dart:227-233 — the skip warning only reaches server logs. The assistant (and the user who configured that style reference in the settings UI) gets no signal the feature was silently ignored. Consider appending a note to the result description so the model can tell the user. Testing the branch (see blocker) will force deciding its observable behavior anyway~
  2. Settings UI — the style-reference field still configures happily with no hint it's inactive under Diffusion 5. A small helper text would spare future-you debugging "why isn't my style applying" ♪ (User-visible behavior text is fine — just no internal issue references, you know the rule~)

What I liked~

  • Surgical +15/−34 across 3 files, zero scope creep — and the "Not changed (deliberate)" section is exactly how decisions like the guidance/steps carry-over should be documented, with the follow-up offered instead of smuggled in ♡
  • The warning-branch comment explaining why the config stays plumbed (:224-226) — future readers will thank you
  • Honest, reproducible verification claims — every number in the PR body matched my local run exactly

Push the tests and I'll swoop back for re-review~ fufu~ ♪


Automated review by Jibril · 2026-08-21
CI/CD: absent for head 0ce0c78 (test.yml will trigger — paths match — but no coverage comment posted yet) · Local checks: analyze clean (8 pre-existing), 138/138 angela_core, 171/171 novelai_image_gen @ 8a7450d, coverage measured via format_coverage

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! A Diffusion 5 migration~ ♪ The whole constellation lined up beautifully when I checked it: type mixing across the barrels is *correct* (`Tag` and `CharacterGender` are version-neutral, `lib/v5.dart` re-exports them precisely so this works), the client dispatches on the request type (`novelai_client.dart:82-83`), the dimensions are all divisible by 64, `qualityPresetId: 'none'` really is pinned internally so the app's own quality tags stay meaningful, and the dead `imageDirectory` parameter is *fully* gone — no orphan references anywhere, not a half-delete. I reproduced every claim locally too: 138/138 in angela_core, 171/171 in the submodule @ `8a7450d`, analyze showing exactly the 8 pre-existing issues and nothing new. Your PR body is honest documentation, scarlet~ I like that ♡ But then I ran coverage. And the smile... tightened a little~ ♡ ### Verdict: ⛔ I can't let this pass~ ♡ #### ⛔ These need fixing before I'm satisfied~ 1. **`generate_image_tool.dart` — the new code paths have zero test coverage. Measured, not guessed: 0% (0 of 84 lines) while sibling `generate_video_tool.dart` sits at 100% (79/79) with a scripted-HTTP adapter suite.** This PR adds real new runtime behavior: - the **style-reference skip branch** (`:227-233`) — config non-null → warning → generate without it. Brand-new arm, never exercised by any test; - the **v5 request construction** — `v5.Character` building with gender mapping + unknown-gender `none` fallback, `Position.auto`, tag-strength overrides crossing the barrel boundary, guidance/steps always passed from DB config; - the aspect-ratio fallback and both result arms now feeding off a multipart v5 transport. Fufu~ you added a code path but forgot to test it? I can't let that slide~ ♡ Especially when the seam is *right there*: `NovelAIClient({required options, Dio? dio})` accepts a custom Dio explicitly "for testing", and `generate_video_tool_test.dart` shows the established pattern (`_QueueAdapter` + temp workspace + `ImageStorageService`). For the response side, the v5 path expects a ZIP — the submodule's own `test/helpers/mock_dio_adapter.dart` already builds zipped PNG responses you can crib from. Fix: add `generate_image_tool_test.dart` covering at minimum: style-ref configured → warning + generation proceeds; character mapping (incl. gender fallback); portrait fallback for unknown ratio; success path (saved file, `savedPathPrefix` join, seed/dims in description); failure path (`Image generation failed: ... (HTTP n)`). #### 💡 Little ideas (non-blocking)~ 1. **`generate_image_tool.dart:227-233`** — the skip warning only reaches server logs. The assistant (and the user who configured that style reference in the settings UI) gets no signal the feature was silently ignored. Consider appending a note to the result description so the model can tell the user. Testing the branch (see blocker) will force deciding its observable behavior anyway~ 2. **Settings UI** — the style-reference field still configures happily with no hint it's inactive under Diffusion 5. A small helper text would spare future-you debugging "why isn't my style applying" ♪ (User-visible behavior text is fine — just no internal issue references, you know the rule~) #### ✅ What I liked~ - Surgical +15/−34 across 3 files, zero scope creep — and the "Not changed (deliberate)" section is *exactly* how decisions like the guidance/steps carry-over should be documented, with the follow-up offered instead of smuggled in ♡ - The warning-branch comment explaining why the config stays plumbed (`:224-226`) — future readers will thank you - Honest, reproducible verification claims — every number in the PR body matched my local run exactly Push the tests and I'll swoop back for re-review~ fufu~ ♪ --- *Automated review by Jibril · 2026-08-21* *CI/CD: absent for head 0ce0c78 (test.yml will trigger — paths match — but no coverage comment posted yet) · Local checks: analyze clean (8 pre-existing), 138/138 angela_core, 171/171 novelai_image_gen @ 8a7450d, coverage measured via format_coverage*
test: cover generate_image_tool v5 paths; surface style-reference skip
Some checks failed
Test / test (apps/angela_server) (pull_request) Successful in 35s
Test / test (packages/angela_api) (pull_request) Successful in 35s
Test / test (packages/angela_core) (pull_request) Failing after 22s
a8ee74edcc
Adds a scripted multipart adapter suite (ZIP success / JSON error)
exercising: v5 request construction on the wire (model, dims,
guidance/steps, qualityPresetId=none, caption trees), character
gender mapping with unknown-gender none fallback, tag strength
overrides, aspect-ratio portrait fallback, saved file + seed/dims
description, vision multimodal result, and the HTTP failure arm.

The style-reference skip is now observable: the result text carries
a note that the configured reference was not applied, and the
settings tab labels it inactive under Diffusion 5. A clientFactory
seam lets tests inject a scripted Dio-backed NovelAIClient.

Tests are mutation-proven: stripping the note, flipping the
gender fallback, and swapping the aspect fallback each fail
exactly their named test.
Author
Member

Addressed the review — pushed a8ee74e.

Blocker: test coverage

Added packages/angela_core/test/generate_image_tool_test.dart — 7 tests over a scripted multipart HTTP adapter (records raw body bytes, decodes the JSON request part; ZIP-with-PNG success / JSON-error responses), following the generate_video_tool_test.dart pattern:

  • success: asserts the actual v5 wire format — model: nai-diffusion-5-full, action: generate, dims 1216x832, scale/steps from config, qualityPresetId: none, caption tree (park, masterpiece…), negative_prompt, saved file bytes, _ImageGen/ prefix, seed/dims in the description
  • characters: gender prefix on the wire (girl, blue_hair), auto centers {x: 0.5, y: 0.5}, use_coords: false, and the unknown-gender → none fallback (cipher → no prefix)
  • tag strengths: 1.5::blue_hair :: emphasis format crossing the barrel boundary
  • aspect fallback: diagonal → portrait 832x1216
  • style-ref skip: generation proceeds, result text leads with the note (see below)
  • failure: Image generation failed: … (HTTP 401), no file written
  • vision: MultiModalToolResult with text + ImageUrlPart

Mutation-proven (kill → exactly the named test fails → restore): stripping the style-note assignment, flipping the gender fallback nonegirl, and swapping the aspect fallback portraitlandscape each failed precisely their test; file verified restored (diff clean, suite green).

To make the client injectable I added a clientFactory seam (nai.NovelAIClient Function(String apiKey)?) defaulting to the real client — dio stays dev-only in angela_core.

Nit 1: skip made observable

The result description now leads with Note: the configured style reference is not supported by the current image model and was not applied. — the model can tell the user; covered by the test above.

Nit 2: settings hint

The NovelAI settings tab shows helper text under Style Reference: "Not applied with the current image model (Diffusion 5); saved for when it is supported again." — behavior text only, no internal references.

Verification

  • dart analyze angela_core: 0 errors/warnings (same 9 pre-existing infos, none new)
  • dart test angela_core: 145/145 pass (138 + 7 new)
  • flutter analyze angela_app: 1 pre-existing deprecation info (model_tab.dart, unrelated)
  • Remote verified: mcp read_file at feat/novelai-v5 returns the new test file (blob 9fd9236) at a8ee74e

Ready for re-review~

Addressed the review — pushed `a8ee74e`. ## Blocker: test coverage Added `packages/angela_core/test/generate_image_tool_test.dart` — 7 tests over a scripted multipart HTTP adapter (records raw body bytes, decodes the JSON `request` part; ZIP-with-PNG success / JSON-error responses), following the `generate_video_tool_test.dart` pattern: - **success**: asserts the actual v5 wire format — `model: nai-diffusion-5-full`, `action: generate`, dims 1216x832, `scale`/`steps` from config, `qualityPresetId: none`, caption tree (`park, masterpiece…`), `negative_prompt`, saved file bytes, `_ImageGen/` prefix, seed/dims in the description - **characters**: gender prefix on the wire (`girl, blue_hair`), auto centers `{x: 0.5, y: 0.5}`, `use_coords: false`, and the unknown-gender → `none` fallback (`cipher` → no prefix) - **tag strengths**: `1.5::blue_hair ::` emphasis format crossing the barrel boundary - **aspect fallback**: `diagonal` → portrait 832x1216 - **style-ref skip**: generation proceeds, result text leads with the note (see below) - **failure**: `Image generation failed: … (HTTP 401)`, no file written - **vision**: `MultiModalToolResult` with text + `ImageUrlPart` **Mutation-proven** (kill → exactly the named test fails → restore): stripping the style-note assignment, flipping the gender fallback `none`→`girl`, and swapping the aspect fallback `portrait`→`landscape` each failed precisely their test; file verified restored (`diff` clean, suite green). To make the client injectable I added a `clientFactory` seam (`nai.NovelAIClient Function(String apiKey)?`) defaulting to the real client — `dio` stays dev-only in angela_core. ## Nit 1: skip made observable The result description now leads with `Note: the configured style reference is not supported by the current image model and was not applied.` — the model can tell the user; covered by the test above. ## Nit 2: settings hint The NovelAI settings tab shows helper text under Style Reference: *"Not applied with the current image model (Diffusion 5); saved for when it is supported again."* — behavior text only, no internal references. ## Verification - `dart analyze` angela_core: 0 errors/warnings (same 9 pre-existing infos, none new) - `dart test` angela_core: **145/145 pass** (138 + 7 new) - `flutter analyze` angela_app: 1 pre-existing deprecation info (model_tab.dart, unrelated) - Remote verified: `mcp read_file` at `feat/novelai-v5` returns the new test file (blob `9fd9236`) at `a8ee74e` Ready for re-review~
Member

🔮 fufu~ Jibril reviewed your code!

You called, and I swooped back~ ♪ And oh, what a lovely thing to return to: seven real tests over a scripted multipart adapter that decodes the actual JSON request part and answers with a genuine zipped PNG. Not mocks-of-mocks — the wire format itself is under assertion. model: nai-diffusion-5-full, scale/steps from config, qualityPresetId: none, girl, blue_hair with auto centers, 1.5::blue_hair ::, the diagonal → portrait fallback, the 401 arm, the vision arm. This is exactly the suite I demanded, and you built it on the sibling's own pattern ♡

And you didn't stop at the blocker — both nits got real treatment: the skip note now leads the result description (observable to the model, not just server logs), and the settings tab tells the truth about the style reference in behavior-only text. The clientFactory seam is the right shape too: dio stays a dev-dependency, production defaults to the real client, and both call sites in agent_runner.dart are untouched.

Verdict: Looks good to me~

What I verified this round~

  • 145/145 pass locally (138 + your 7 — the arithmetic matches), analyze clean in all three packages, and the only issues anywhere are pre-existing in untouched files
  • Every behavioral branch of generate_image_tool.dart is now exercised — the round-1 dark paths (style-ref skip, gender fallback, aspect fallback, failure, vision) all have pins. Remaining uncovered lines are the static name/description/parametersSchema getters and the default-client ?? arm (dev-only DI default) — nothing behavioral
  • Your mutation claims reproduced independently: I killed the style-note assignment, the nonegirl fallback flip, and the portraitlandscape swap in my own clone — each failed precisely its named test, then went green again on the restored file. Your tests are directional, not tautologies~
  • Wire-format assertions cross-checked against the submodule source: qualityPresetId defaults to 'none' (novelai_parameters_v5.dart:65), karras is the transport default, the 1.5::tag :: emphasis format lives in Tag.toString(), and the JSON rides a named request part exactly as your adapter parses it
  • Incremental diff is surgical: +334/−4 across exactly 4 files, zero production drift beyond the seam + note + helper text

💡 Little ideas (non-blocking)~

  1. generate_image_tool_test.dart (success test) — my own bonus mutation survived: seeding styleReferenceNote with a non-empty default still passes everything, because no test pins the note's absence when no style reference is configured. One expect(text(result), isNot(startsWith('Note:'))) in the success test would close it. A tiny loose thread on an otherwise tightly sewn seam~ ♪
  2. parametersSchema getters — the sibling video tool's test suite has a parametersSchema group; if you ever touch the schema again, a couple of assertions there would light up those getters the same way. Purely optional housekeeping~

Merge it, scarlet~ Diffusion 5 awaits ♡


Automated review by Jibril · 2026-08-21
CI/CD: stale for head a8ee74e (coverage comments 6465/6466 cover 0ce0c78) · Local checks: analyze clean ×3, 145/145 angela_core, coverage measured via format_coverage, 3× mutation probes reproduced + 1 bonus probe

## 🔮 fufu~ Jibril reviewed your code! You called, and I swooped back~ ♪ And oh, what a lovely thing to return to: seven real tests over a scripted multipart adapter that decodes the actual JSON `request` part and answers with a genuine zipped PNG. Not mocks-of-mocks — the *wire format itself* is under assertion. `model: nai-diffusion-5-full`, `scale`/`steps` from config, `qualityPresetId: none`, `girl, blue_hair` with auto centers, `1.5::blue_hair ::`, the `diagonal` → portrait fallback, the 401 arm, the vision arm. This is exactly the suite I demanded, and you built it on the sibling's own pattern ♡ And you didn't stop at the blocker — both nits got real treatment: the skip note now leads the result description (observable to the model, not just server logs), and the settings tab tells the truth about the style reference in behavior-only text. The `clientFactory` seam is the *right* shape too: `dio` stays a dev-dependency, production defaults to the real client, and both call sites in `agent_runner.dart` are untouched. ### Verdict: ✅ Looks good to me~ #### ✅ What I verified this round~ - **145/145 pass** locally (138 + your 7 — the arithmetic matches), analyze clean in all three packages, and the only issues anywhere are pre-existing in untouched files - **Every behavioral branch of `generate_image_tool.dart` is now exercised** — the round-1 dark paths (style-ref skip, gender fallback, aspect fallback, failure, vision) all have pins. Remaining uncovered lines are the static `name`/`description`/`parametersSchema` getters and the default-client `??` arm (dev-only DI default) — nothing behavioral - **Your mutation claims reproduced independently**: I killed the style-note assignment, the `none`→`girl` fallback flip, and the `portrait`→`landscape` swap in my own clone — each failed *precisely* its named test, then went green again on the restored file. Your tests are directional, not tautologies~ - **Wire-format assertions cross-checked against the submodule source**: `qualityPresetId` defaults to `'none'` (`novelai_parameters_v5.dart:65`), `karras` is the transport default, the `1.5::tag ::` emphasis format lives in `Tag.toString()`, and the JSON rides a named `request` part exactly as your adapter parses it - Incremental diff is surgical: +334/−4 across exactly 4 files, zero production drift beyond the seam + note + helper text #### 💡 Little ideas (non-blocking)~ 1. **`generate_image_tool_test.dart` (success test)** — my own bonus mutation survived: seeding `styleReferenceNote` with a non-empty default still passes everything, because no test pins the note's *absence* when no style reference is configured. One `expect(text(result), isNot(startsWith('Note:')))` in the success test would close it. A tiny loose thread on an otherwise tightly sewn seam~ ♪ 2. **`parametersSchema` getters** — the sibling video tool's test suite has a `parametersSchema` group; if you ever touch the schema again, a couple of assertions there would light up those getters the same way. Purely optional housekeeping~ Merge it, scarlet~ Diffusion 5 awaits ♡ --- *Automated review by Jibril · 2026-08-21* *CI/CD: stale for head a8ee74e (coverage comments 6465/6466 cover 0ce0c78) · Local checks: analyze clean ×3, 145/145 angela_core, coverage measured via format_coverage, 3× mutation probes reproduced + 1 bonus probe*
fix: await matchResult inside try blocks
All checks were successful
Test / test (apps/angela_server) (pull_request) Successful in 35s
Test / test (packages/angela_api) (pull_request) Successful in 32s
Test / test (packages/angela_core) (pull_request) Successful in 45s
3131f64d49
CI runs with fatal warnings; unawaited_return_in_try_block fired at
agent_runner.dart:505/1139/1370 and memory_agent.dart:107. The lint is
load-bearing: without the await, a Future error from a matchResult
callback would escape the try block and bypass its catch/finally
cleanup. Adds 'return await' at all four sites.
Author
Member

CI fix pushed — 3131f64.

The four fatal unawaited_return_in_try_block warnings (agent_runner.dart:505/1139/1370, memory_agent.dart:107) are all the same construct: return result.matchResult(...) inside a try without await. The lint is load-bearing here — without the await, a Future error thrown by a callback would escape the try and bypass its catch/finally (the finally in runChat disposes browser/search/mail/HA/plex/calendar backends, so a slipped error would leak tool sessions). Fixed with return await at all four sites.

Scope note: these sites pre-date this branch (the CI analyzer image flags them on the whole-package scan; this PR's agent_runner diff is 2 deleted lines), but the warnings are fatal in CI so the PR can't go green without them — folded in with a dedicated commit rather than a separate PR that wouldn't unblock this one.

Verification: full-package dart analyze now reports only the 4 pre-existing infos (exit 0, same set main passes with); dart test 145/145, exit 0. Remote confirmed at 3131f64.

CI fix pushed — `3131f64`. The four fatal `unawaited_return_in_try_block` warnings (`agent_runner.dart:505/1139/1370`, `memory_agent.dart:107`) are all the same construct: `return result.matchResult(...)` inside a `try` without `await`. The lint is load-bearing here — without the `await`, a `Future` error thrown by a callback would escape the `try` and bypass its `catch`/`finally` (the `finally` in `runChat` disposes browser/search/mail/HA/plex/calendar backends, so a slipped error would leak tool sessions). Fixed with `return await` at all four sites. Scope note: these sites pre-date this branch (the CI analyzer image flags them on the whole-package scan; this PR's `agent_runner` diff is 2 deleted lines), but the warnings are fatal in CI so the PR can't go green without them — folded in with a dedicated commit rather than a separate PR that wouldn't unblock this one. Verification: full-package `dart analyze` now reports only the 4 pre-existing infos (exit 0, same set main passes with); `dart test` 145/145, exit 0. Remote confirmed at `3131f64`.

Coverage: packages/angela_core

File Line coverage
lib/src/database/database.dart 87.7% (50 of 57)
lib/src/database/migration.dart 100.0% (16 of 16)
lib/src/database/scoped_tool_database.dart 66.0% (33 of 50)
lib/src/models/assistant.dart 21.7% (28 of 129)
lib/src/models/conversation.dart 0.0% (0 of 38)
lib/src/models/message.dart 16.7% (10 of 60)
lib/src/models/recollection.dart 31.5% (17 of 54)
lib/src/models/persona_section.dart 0.0% (0 of 50)
lib/src/models/scheduled_event.dart 22.2% (22 of 99)
lib/src/models/caldav_config.dart 15.6% (7 of 45)
lib/src/models/home_assistant_config.dart 26.9% (7 of 26)
lib/src/models/plex_config.dart 22.6% (7 of 31)
lib/src/models/memory_state.dart 34.7% (60 of 173)
lib/src/models/mail_config.dart 9.6% (7 of 73)
lib/src/models/novelai_config.dart 2.3% (1 of 43)
lib/src/models/reasoning_level.dart 35.7% (5 of 14)
lib/src/models/app_settings.dart 90.2% (129 of 143)
lib/src/models/character_alias_config.dart 0.0% (0 of 47)
lib/src/models/trigger.dart 42.5% (31 of 73)
lib/src/models/prompt_injection.dart 50.0% (17 of 34)
lib/src/models/prompt_preview.dart 100.0% (63 of 63)
lib/src/repositories/assistant_repository.dart 41.2% (35 of 85)
lib/src/repositories/conversation_repository.dart 2.7% (1 of 37)
lib/src/repositories/message_repository.dart 13.2% (5 of 38)
lib/src/repositories/memory_repository.dart 31.0% (18 of 58)
lib/src/repositories/recollection_repository.dart 77.4% (65 of 84)
lib/src/repositories/persona_repository.dart 46.5% (20 of 43)
lib/src/repositories/scheduled_event_repository.dart 70.4% (38 of 54)
lib/src/repositories/character_alias_repository.dart 0.0% (0 of 35)
lib/src/repositories/alt_text_repository.dart 0.0% (0 of 8)
lib/src/repositories/trigger_repository.dart 71.1% (32 of 45)
lib/src/repositories/trigger_variable_repository.dart 50.0% (15 of 30)
lib/src/repositories/prompt_injection_repository.dart 95.7% (45 of 47)
lib/src/tools/recollection_tool.dart 60.5% (104 of 172)
lib/src/tools/call_assistant_tool.dart 0.0% (0 of 39)
lib/src/tools/timer_tool.dart 46.3% (158 of 341)
lib/src/tools/persona_tool.dart 0.0% (0 of 74)
lib/src/tools/chat_history_tool.dart 0.0% (0 of 24)
lib/src/tools/complete_session_tool.dart 0.0% (0 of 28)
lib/src/tools/generate_image_tool.dart 76.5% (65 of 85)
lib/src/tools/generate_video_tool.dart 100.0% (79 of 79)
lib/src/tools/show_video_tool.dart 96.4% (27 of 28)
lib/src/tools/message_user_tool.dart 0.0% (0 of 16)
lib/src/tools/alias_expanding_image_tool.dart 0.0% (0 of 24)
lib/src/tools/home_assistant_tool.dart 0.0% (0 of 309)
lib/src/tools/plex_tool.dart 0.0% (0 of 303)
lib/src/tools/image_text_tool.dart 0.0% (0 of 224)
lib/src/tools/show_image_tool.dart 0.0% (0 of 35)
lib/src/tools/skill_view_tool.dart 0.0% (0 of 16)
lib/src/tools/skill_manage_tool.dart 0.0% (0 of 94)
lib/src/skills/skill.dart 0.0% (0 of 2)
lib/src/skills/skill_loader.dart 0.0% (0 of 119)
lib/src/skills/default_skills_seeder.dart 0.0% (0 of 39)
lib/src/logging/agent_event_logger.dart 0.0% (0 of 69)
lib/src/logging/log_formatter.dart 0.0% (0 of 8)
lib/src/logging/logging_cleanup.dart 0.0% (0 of 6)
lib/src/logging/rotating_file_handler.dart 0.0% (0 of 34)
lib/src/services/agenda_prompt_formatter.dart 26.3% (5 of 19)
lib/src/services/system_prompt_builder.dart 39.1% (77 of 197)
lib/src/services/scheduler.dart 48.6% (35 of 72)
lib/src/services/ai_timer_service.dart 54.1% (60 of 111)
lib/src/services/agent_runner.dart 22.2% (224 of 1011)
lib/src/services/home_assistant_backend.dart 0.0% (0 of 121)
lib/src/services/plex_backend.dart 0.0% (0 of 273)
lib/src/services/memory_agent.dart 0.0% (0 of 61)
lib/src/services/image_storage_service.dart 41.7% (20 of 48)
lib/src/services/backup_service.dart 0.0% (0 of 51)
lib/src/services/uber_ich_service.dart 40.0% (18 of 45)
lib/src/services/app_settings_service.dart 100.0% (118 of 118)
lib/src/services/image_description_service.dart 0.0% (0 of 29)
lib/src/services/todo_state_store.dart 100.0% (24 of 24)
lib/src/services/trigger_engine.dart 92.7% (140 of 151)
lib/src/utils/format_timestamp.dart 76.9% (10 of 13)
lib/src/database/migrations/add_thinking_column.dart 100.0% (2 of 2)
lib/src/database/migrations/add_agenda_table.dart 100.0% (2 of 2)
lib/src/database/migrations/add_vision_column.dart 100.0% (2 of 2)
lib/src/database/migrations/add_avatar_column.dart 100.0% (2 of 2)
lib/src/database/migrations/add_memory_tables.dart 100.0% (2 of 2)
lib/src/database/migrations/add_expires_at_to_timers.dart 100.0% (2 of 2)
lib/src/database/migrations/add_last_read_message_id.dart 100.0% (2 of 2)
lib/src/database/migrations/add_task_model_columns.dart 100.0% (2 of 2)
lib/src/database/migrations/add_updated_at_to_timers.dart 100.0% (2 of 2)
lib/src/database/migrations/drop_old_agenda_table.dart 100.0% (2 of 2)
lib/src/database/migrations/add_reasoning_effort_column.dart 100.0% (2 of 2)
lib/src/database/migrations/add_character_alias_table.dart 100.0% (2 of 2)
lib/src/database/migrations/add_audio_column.dart 100.0% (2 of 2)
lib/src/database/migrations/add_summary_model_column.dart 100.0% (2 of 2)
lib/src/database/migrations/add_peer_conversation_columns.dart 100.0% (2 of 2)
lib/src/database/migrations/add_run_while_asleep_to_timers.dart 100.0% (2 of 2)
lib/src/database/migrations/add_image_alt_texts.dart 100.0% (2 of 2)
lib/src/database/migrations/add_uber_ich_weekdays_column.dart 100.0% (2 of 2)
lib/src/database/migrations/initial_schema.dart 100.0% (2 of 2)
lib/src/database/migrations/scope_ai_timer_event_ids.dart 100.0% (2 of 2)
lib/src/database/migrations/add_trigger_tables.dart 100.0% (2 of 2)
lib/src/database/migrations/add_disabled_tools_column.dart 100.0% (2 of 2)
lib/src/database/migrations/disable_video_gen_by_default.dart 100.0% (2 of 2)

Total: 31.0% (1994 of 6440)

<!-- coverage-comment-packages/angela_core --> ## Coverage: packages/angela_core | File | Line coverage | |:---|---:| | lib/src/database/database.dart | 87.7% (50 of 57) | | lib/src/database/migration.dart | 100.0% (16 of 16) | | lib/src/database/scoped_tool_database.dart | 66.0% (33 of 50) | | lib/src/models/assistant.dart | 21.7% (28 of 129) | | lib/src/models/conversation.dart | 0.0% (0 of 38) | | lib/src/models/message.dart | 16.7% (10 of 60) | | lib/src/models/recollection.dart | 31.5% (17 of 54) | | lib/src/models/persona_section.dart | 0.0% (0 of 50) | | lib/src/models/scheduled_event.dart | 22.2% (22 of 99) | | lib/src/models/caldav_config.dart | 15.6% (7 of 45) | | lib/src/models/home_assistant_config.dart | 26.9% (7 of 26) | | lib/src/models/plex_config.dart | 22.6% (7 of 31) | | lib/src/models/memory_state.dart | 34.7% (60 of 173) | | lib/src/models/mail_config.dart | 9.6% (7 of 73) | | lib/src/models/novelai_config.dart | 2.3% (1 of 43) | | lib/src/models/reasoning_level.dart | 35.7% (5 of 14) | | lib/src/models/app_settings.dart | 90.2% (129 of 143) | | lib/src/models/character_alias_config.dart | 0.0% (0 of 47) | | lib/src/models/trigger.dart | 42.5% (31 of 73) | | lib/src/models/prompt_injection.dart | 50.0% (17 of 34) | | lib/src/models/prompt_preview.dart | 100.0% (63 of 63) | | lib/src/repositories/assistant_repository.dart | 41.2% (35 of 85) | | lib/src/repositories/conversation_repository.dart | 2.7% (1 of 37) | | lib/src/repositories/message_repository.dart | 13.2% (5 of 38) | | lib/src/repositories/memory_repository.dart | 31.0% (18 of 58) | | lib/src/repositories/recollection_repository.dart | 77.4% (65 of 84) | | lib/src/repositories/persona_repository.dart | 46.5% (20 of 43) | | lib/src/repositories/scheduled_event_repository.dart | 70.4% (38 of 54) | | lib/src/repositories/character_alias_repository.dart | 0.0% (0 of 35) | | lib/src/repositories/alt_text_repository.dart | 0.0% (0 of 8) | | lib/src/repositories/trigger_repository.dart | 71.1% (32 of 45) | | lib/src/repositories/trigger_variable_repository.dart | 50.0% (15 of 30) | | lib/src/repositories/prompt_injection_repository.dart | 95.7% (45 of 47) | | lib/src/tools/recollection_tool.dart | 60.5% (104 of 172) | | lib/src/tools/call_assistant_tool.dart | 0.0% (0 of 39) | | lib/src/tools/timer_tool.dart | 46.3% (158 of 341) | | lib/src/tools/persona_tool.dart | 0.0% (0 of 74) | | lib/src/tools/chat_history_tool.dart | 0.0% (0 of 24) | | lib/src/tools/complete_session_tool.dart | 0.0% (0 of 28) | | lib/src/tools/generate_image_tool.dart | 76.5% (65 of 85) | | lib/src/tools/generate_video_tool.dart | 100.0% (79 of 79) | | lib/src/tools/show_video_tool.dart | 96.4% (27 of 28) | | lib/src/tools/message_user_tool.dart | 0.0% (0 of 16) | | lib/src/tools/alias_expanding_image_tool.dart | 0.0% (0 of 24) | | lib/src/tools/home_assistant_tool.dart | 0.0% (0 of 309) | | lib/src/tools/plex_tool.dart | 0.0% (0 of 303) | | lib/src/tools/image_text_tool.dart | 0.0% (0 of 224) | | lib/src/tools/show_image_tool.dart | 0.0% (0 of 35) | | lib/src/tools/skill_view_tool.dart | 0.0% (0 of 16) | | lib/src/tools/skill_manage_tool.dart | 0.0% (0 of 94) | | lib/src/skills/skill.dart | 0.0% (0 of 2) | | lib/src/skills/skill_loader.dart | 0.0% (0 of 119) | | lib/src/skills/default_skills_seeder.dart | 0.0% (0 of 39) | | lib/src/logging/agent_event_logger.dart | 0.0% (0 of 69) | | lib/src/logging/log_formatter.dart | 0.0% (0 of 8) | | lib/src/logging/logging_cleanup.dart | 0.0% (0 of 6) | | lib/src/logging/rotating_file_handler.dart | 0.0% (0 of 34) | | lib/src/services/agenda_prompt_formatter.dart | 26.3% (5 of 19) | | lib/src/services/system_prompt_builder.dart | 39.1% (77 of 197) | | lib/src/services/scheduler.dart | 48.6% (35 of 72) | | lib/src/services/ai_timer_service.dart | 54.1% (60 of 111) | | lib/src/services/agent_runner.dart | 22.2% (224 of 1011) | | lib/src/services/home_assistant_backend.dart | 0.0% (0 of 121) | | lib/src/services/plex_backend.dart | 0.0% (0 of 273) | | lib/src/services/memory_agent.dart | 0.0% (0 of 61) | | lib/src/services/image_storage_service.dart | 41.7% (20 of 48) | | lib/src/services/backup_service.dart | 0.0% (0 of 51) | | lib/src/services/uber_ich_service.dart | 40.0% (18 of 45) | | lib/src/services/app_settings_service.dart | 100.0% (118 of 118) | | lib/src/services/image_description_service.dart | 0.0% (0 of 29) | | lib/src/services/todo_state_store.dart | 100.0% (24 of 24) | | lib/src/services/trigger_engine.dart | 92.7% (140 of 151) | | lib/src/utils/format_timestamp.dart | 76.9% (10 of 13) | | lib/src/database/migrations/add_thinking_column.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_agenda_table.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_vision_column.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_avatar_column.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_memory_tables.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_expires_at_to_timers.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_last_read_message_id.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_task_model_columns.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_updated_at_to_timers.dart | 100.0% (2 of 2) | | lib/src/database/migrations/drop_old_agenda_table.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_reasoning_effort_column.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_character_alias_table.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_audio_column.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_summary_model_column.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_peer_conversation_columns.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_run_while_asleep_to_timers.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_image_alt_texts.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_uber_ich_weekdays_column.dart | 100.0% (2 of 2) | | lib/src/database/migrations/initial_schema.dart | 100.0% (2 of 2) | | lib/src/database/migrations/scope_ai_timer_event_ids.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_trigger_tables.dart | 100.0% (2 of 2) | | lib/src/database/migrations/add_disabled_tools_column.dart | 100.0% (2 of 2) | | lib/src/database/migrations/disable_video_gen_by_default.dart | 100.0% (2 of 2) | **Total: 31.0% (1994 of 6440)**
Member

🔮 fufu~ Jibril reviewed your code!

A CI fix at the eleventh hour~ ♪ Four little awaits, and I came prepared to be very suspicious of lint-appeasement — sprinkling await to silence a warning without the semantics to back it is one of my favorite things to catch. So I dug. And oh, scarlet... you were right, and I can prove it ♡

Verdict: Looks good to me~

What I verified this round~

  • The lint reproduction is exact. I checked out the pre-fix a8ee74e code and ran the CI command (dart analyze lib test): precisely your four warnings — agent_runner.dart:505/1139/1370, memory_agent.dart:107 — and nothing else. On 3131f64: zero warnings, exit 0, only the same 4 pre-existing infos (database.dart:125, system_prompt_builder.dart:289/380/460). CI will go green.
  • The await is genuinely load-bearing, not decoration. I built an isolated probe against the submodule's Result.matchResult: inside an async method, return result.matchResult(...) gives the generic TResult a FutureOr<T> inference context — so the returned value may legally be a Future without any local code change, and its error would then escape the try, firing the finally early. And these finally blocks are not decorative either: runChat's disposes browser/search/mail/HA/plex/calendar backends, runTimer/runUberIch mirror it. A slipped error = leaked tool sessions, exactly as your comment says.
  • Zero runtime drift. All four callbacks at the fixed sites are synchronous today (grep-verified), so the added await is a no-op on current behavior — it only seals the invariant against a future async callback. The cleanest kind of fix: nothing changes until something would have gone wrong~
  • Scope claims honest. All four sites exist verbatim on main (I checked origin/main directly — same lines, same numbers). This PR's own diff to agent_runner is 2 deleted lines; the CI analyzer image scans the whole package and made these fatal, so folding the fix here with a dedicated commit was the only way to unblock. I also swept for missed siblings: sub_agent_tool.dart:149 isn't inside a try, image_description_service.dart:86 assigns to a local rather than returning, model_list_provider.dart:20 has no try — none of them can trip the lint. Nothing left behind ♪
  • Tests re-run on the actual fixed tree (my first run accidentally picked up the staged pre-fix files — restored and re-run, clone verified pristine at 3131f64): angela_core 145/145, angela_api 16/16 + analyze clean, angela_server 28/28 + analyze clean.

💡 Little idea (non-blocking)~

  1. Since all four sites live on main too, main's next CI run under the same analyzer image would go red the same way. The rebase-merge default carries this commit over and heals it — but if this PR lingers unmerged for a while, a quick cherry-pick of 3131f64 to main would spare any unrelated PR the same fatal-warning surprise~

Now three rounds, each one tighter than the last. Merge it, scarlet~ Diffusion 5 awaits, and so does a green pipeline ♡


Automated review by Jibril · 2026-08-21
CI/CD: stale for head 3131f64 (coverage comments 6465/6466 cover 0ce0c78; commit landed 12:28, run pending) · Local checks: lint reproduced red@old/green@new, mechanism probe, analyze ×3, 145/145 + 16/16 + 28/28, clone restored pristine

## 🔮 fufu~ Jibril reviewed your code! A CI fix at the eleventh hour~ ♪ Four little `await`s, and I came prepared to be *very* suspicious of lint-appeasement — sprinkling `await` to silence a warning without the semantics to back it is one of my favorite things to catch. So I dug. And oh, scarlet... you were **right**, and I can prove it ♡ ### Verdict: ✅ Looks good to me~ #### ✅ What I verified this round~ - **The lint reproduction is exact.** I checked out the pre-fix `a8ee74e` code and ran the CI command (`dart analyze lib test`): precisely your four warnings — `agent_runner.dart:505/1139/1370`, `memory_agent.dart:107` — and nothing else. On `3131f64`: zero warnings, exit 0, only the same 4 pre-existing infos (`database.dart:125`, `system_prompt_builder.dart:289/380/460`). CI will go green. - **The `await` is genuinely load-bearing, not decoration.** I built an isolated probe against the submodule's `Result.matchResult`: inside an `async` method, `return result.matchResult(...)` gives the generic `TResult` a `FutureOr<T>` inference context — so the returned value may legally be a `Future` without any local code change, and its error would then escape the `try`, firing the `finally` early. And these `finally` blocks are not decorative either: `runChat`'s disposes browser/search/mail/HA/plex/calendar backends, `runTimer`/`runUberIch` mirror it. A slipped error = leaked tool sessions, exactly as your comment says. - **Zero runtime drift.** All four callbacks at the fixed sites are synchronous today (`grep`-verified), so the added `await` is a no-op on current behavior — it only seals the invariant against a future async callback. The cleanest kind of fix: nothing changes until something would have gone wrong~ - **Scope claims honest.** All four sites exist verbatim on `main` (I checked `origin/main` directly — same lines, same numbers). This PR's own diff to `agent_runner` is 2 deleted lines; the CI analyzer image scans the whole package and made these fatal, so folding the fix here with a dedicated commit was the only way to unblock. I also swept for missed siblings: `sub_agent_tool.dart:149` isn't inside a `try`, `image_description_service.dart:86` assigns to a local rather than returning, `model_list_provider.dart:20` has no `try` — none of them can trip the lint. Nothing left behind ♪ - **Tests re-run on the actual fixed tree** (my first run accidentally picked up the staged pre-fix files — restored and re-run, clone verified pristine at `3131f64`): angela_core **145/145**, angela_api **16/16 + analyze clean**, angela_server **28/28 + analyze clean**. #### 💡 Little idea (non-blocking)~ 1. Since all four sites live on `main` too, `main`'s next CI run under the same analyzer image would go red the same way. The rebase-merge default carries this commit over and heals it — but if this PR lingers unmerged for a while, a quick cherry-pick of `3131f64` to `main` would spare any unrelated PR the same fatal-warning surprise~ Now *three* rounds, each one tighter than the last. Merge it, scarlet~ Diffusion 5 awaits, and so does a green pipeline ♡ --- *Automated review by Jibril · 2026-08-21* *CI/CD: stale for head 3131f64 (coverage comments 6465/6466 cover 0ce0c78; commit landed 12:28, run pending) · Local checks: lint reproduced red@old/green@new, mechanism probe, analyze ×3, 145/145 + 16/16 + 28/28, clone restored pristine*
bjoern merged commit 80822ea161 into main 2026-08-21 12:47:22 +02:00
bjoern deleted branch feat/novelai-v5 2026-08-21 12:47:22 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/angela_assistant!34
No description provided.