fix: rebuild the task-agent card footer around an always-available trigger#3591
Conversation
…igger The AI summary card's bottom section was rated 4.08/10 by a design-expert panel and 3.8/10 by a user-persona panel, with the same top blocker from all five personas and all six experts: while an automatic update was scheduled, the manual wake button was not disabled — it was gone from the tree entirely (#3508's "one wake affordance per state"), so running the agent by hand meant cancelling the countdown first. The footer is now three bands with no nested surface — what state the summary is in, when it updates, which AI answers: - `TaskAgentAutomationRow` (new) owns the status, the trigger and the switch. The trigger is present in every state; a run in flight swaps its label and glyph in place instead of vacating the slot. - The nested filled/bordered/rounded automation box is gone. The band's wash and top hairline are the only surface; in the light theme that inner fill was the band's own fill, so it cost a nesting level and bought nothing. - `Wrap(spaceBetween)` is replaced by a measured fit: the schedule wording degrades "Next update in 1:30" → "in 1:30" → "1:30" and the row stacks only when nothing else fits. The countdown value, the trigger and the switch never degrade. Measurement uses the painted style — tabular figures change digit advance, and measuring without them clipped the payload. - Freshness carries a word ("Up to date" / "Out of date"), not just a colour. - The bare "✕" becomes a labelled "Skip", grouped with the countdown it cancels rather than sitting beside the switch it does not control. - "Wake agent" becomes "Update now" so the footer reads as one sentence with "Next update in …" and "Automatic updates". Hand-authored across all 11 locales; the agent-internals surfaces keep the wake vocabulary. - Automation on with nothing pending now says "Updates when this task changes" rather than leaving a hole that appears and disappears with the switch. Hover containment, in the identity region and the card header: a stretching Column hands its children a tight width, which silently defeated the `MainAxisSize.min` both rows already declared, so the ink spanned the full reading measure and started flush against the glyph. Both now shrink-wrap and carry their inset inside the ink. The report attribution truncates with a tooltip instead of soft-wrapping a stray fragment onto a second line. The band pays `step4` and each row adds `step2`, so every glyph lands on `cardPadding` — one leading edge shared with the summary prose above. Tests: `task_agent_automation_row_test.dart` mirrors the new widget; the footer test is now a composition test. The countdown keeps its AGENTS.md stable-geometry coverage at both levels, and a width × locale × text-scale matrix pins no-overflow with the trigger and switch surviving every cell. The screenshot matrix gains 320px, German at 1.3×, split attribution and the two hover states, driven with a real mouse pointer.
The agents README still described a `TaskAgentFreshnessStrip` that no longer exists in `lib/` (deleted in 957f1d4) and claimed wide cards place model identity beside the automation cluster, which has not been true since #3527 — and the cluster itself is now gone. Rewrites the footer rationale around what the code actually does: the band as the only surface, the always-present trigger, the measured wording ladder, the reserved-width countdown, and the shared leading edge. Adds a Mermaid flowchart for the fit decision, since the ladder is a real branch in `TaskAgentAutomationRow` rather than a styling note. Also documents why both identity rows shrink-wrap — a stretching Column hands children a tight width, which is what silently defeated their `MainAxisSize.min`. Renames the button in prose to match the UI, and updates the one-line card description in the tasks README.
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
📝 WalkthroughWalkthroughThe AI summary card now uses a dedicated responsive automation row with “Update now,” “Skip,” freshness status, countdown, and automatic-update controls. Footer, identity, and header layouts were adjusted for constrained widths, with updated localization, documentation, lifecycle tests, and screenshot coverage. ChangesTask-agent summary card controls
Sequence Diagram(s)sequenceDiagram
participant User
participant AiSummaryCard
participant TaskAgentAutomationRow
participant TaskAgentService
User->>TaskAgentAutomationRow: tap Update now
TaskAgentAutomationRow->>TaskAgentService: triggerReanalysis
TaskAgentService->>AiSummaryCard: update agent state
User->>TaskAgentAutomationRow: tap Skip
TaskAgentAutomationRow->>TaskAgentService: cancelScheduledWake
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3591 +/- ##
========================================
Coverage 99.15% 99.15%
========================================
Files 1782 1783 +1
Lines 130810 130939 +129
========================================
+ Hits 129704 129833 +129
Misses 1106 1106
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
test/features/agents/ui/ai_summary_card/tldr_section_part_test.dart (1)
144-149: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the rendered outcome, not the
Textproperties.
maxLines/overflowrestate the widget config; they pass even if an ancestor lets the row inflate so nothing actually truncates.task_agent_identity_region_test.dartalready has the behavioural probe — mirror it here.💚 Proposed fix
- expect(tester.widget<Text>(find.text(longName)).maxLines, 1); - expect( - tester.widget<Text>(find.text(longName)).overflow, - TextOverflow.ellipsis, - ); - expect(tester.widget<Text>(find.text('AI summary')).maxLines, 2); + expect( + tester + .renderObject<RenderParagraph>(find.text(longName)) + .didExceedMaxLines, + isTrue, + ); + expect(tester.getSize(find.text(longName)).height, lessThan( + tester.getSize(find.text('AI summary')).height * 2, + ));Requires
import 'package:flutter/rendering.dart';.🤖 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 `@test/features/agents/ui/ai_summary_card/tldr_section_part_test.dart` around lines 144 - 149, Update the assertions in the TLDR section test to verify rendered truncation rather than the Text widget’s maxLines and overflow properties. Mirror the behavioral probe used in task_agent_identity_region_test.dart, adding the Flutter rendering import required to inspect the rendered text outcome.Source: Coding guidelines
lib/features/agents/ui/task_agent_automation_row.dart (1)
708-717: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftTrigger/setting widths re-derive
DesignSystemButtonandDesignSystemToggleinternals.These estimates encode another widget's padding, icon size and gap. A padding change there silently mis-sizes the fit decision here with no compile error and no failing test unless a screenshot catches it. Consider exposing the intrinsic metrics from the components (e.g. a static
DesignSystemButton.intrinsicWidth(...)) so the contract lives in one place.🤖 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 `@lib/features/agents/ui/task_agent_automation_row.dart` around lines 708 - 717, Replace the hardcoded triggerWidth and settingWidth calculations with intrinsic-width APIs owned by DesignSystemButton and DesignSystemToggle, such as static intrinsicWidth methods, passing the relevant labels and text styles. Move the component-specific padding, icon, and gap metrics into those components so this fit decision stays synchronized with their implementations.test/features/agents/ui/task_agent_controls_footer_test.dart (1)
67-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
pumpFooter/tokensOfduplicatepumpRow/tokensOfintask_agent_automation_row_test.dart.Same view sizing, teardown,
MediaQueryDataand token lookup, copied verbatim across the two new test files (and partly intotask_agent_identity_region_test.dart). Extract one parameterised helper (e.g.test/features/agents/ui/agent_ui_test_utils.dart) taking the widget under test.As per coding guidelines: "Extract shared widget pump/setup helpers, repeated mock stubs, and complex state into helpers or test-bench classes; pass varying values as named parameters."
🤖 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 `@test/features/agents/ui/task_agent_controls_footer_test.dart` around lines 67 - 94, Extract the duplicated pump/setup and token lookup logic from pumpFooter and tokensOf into a shared parameterized test helper, such as agent_ui_test_utils.dart, accepting the widget under test and named parameters for varying width, locale, and textScaler. Update task_agent_controls_footer_test.dart, task_agent_automation_row_test.dart, and task_agent_identity_region_test.dart to reuse the helper while preserving their existing sizing, teardown, MediaQueryData, and token lookup behavior.Source: Coding guidelines
🤖 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 `@lib/features/agents/ui/task_agent_automation_row.dart`:
- Around line 499-509: The Semantics wrapper for the task-agent skip action
exposes only the tooltip text, omitting the visible “Skip” label. Update the
`Semantics` label around the `taskAgentSkipScheduledUpdate` `InkWell` so it
includes “Skip” while retaining the existing explanatory tooltip text, ensuring
the accessible name contains the visible label.
In `@lib/l10n/app_es.arb`:
- Line 4135: Update the taskAgentStatusUpToDate localization value from
“Actualizado” to “Al día”, matching the terminology already used by
taskAgentReportUpToDate for the up-to-date status.
In `@lib/l10n/app_fr.arb`:
- Line 4134: Update the taskAgentStatusOutOfDate translation value from
“Obsolète” to “Pas à jour”, preserving the existing taskAgentStatusOutOfDate key
and the intended refreshable stale-status meaning.
In `@lib/l10n/app_localizations_de.dart`:
- Around line 10102-10105: Update the German localization entry for
taskAgentNextUpdateInShort in lib/l10n/app_de.arb to capitalize the standalone
label as “In $countdown”, then regenerate app_localizations_de.dart so the
generated method returns the updated text.
In `@lib/l10n/app_nl.arb`:
- Around line 4728-4729: Update the Dutch localization entries
taskAgentUseCategoryDefault and taskAgentUseCategoryDefaultDescription to use
natural, grammatically correct wording. Clearly state that the agent copies the
category’s current settings and will not follow subsequent changes to that
category.
In `@lib/l10n/app_pt.arb`:
- Line 4720: Update the taskAgentSkipScheduledUpdate translation in app_pt.arb
from “Ignorar” to the established “Pular” translation used for skip actions in
this locale.
In `@lib/l10n/app_sv.arb`:
- Around line 4729-4731: Update the Swedish translations
taskAgentUseCategoryDefault and taskAgentUseProfileDefault to use natural
possessive wording for the default setup options, such as “Kopiera kategorins
standardinställningar” and “Använd profilens standardinställningar,” while
leaving taskAgentUseCategoryDefaultDescription unchanged.
---
Nitpick comments:
In `@lib/features/agents/ui/task_agent_automation_row.dart`:
- Around line 708-717: Replace the hardcoded triggerWidth and settingWidth
calculations with intrinsic-width APIs owned by DesignSystemButton and
DesignSystemToggle, such as static intrinsicWidth methods, passing the relevant
labels and text styles. Move the component-specific padding, icon, and gap
metrics into those components so this fit decision stays synchronized with their
implementations.
In `@test/features/agents/ui/ai_summary_card/tldr_section_part_test.dart`:
- Around line 144-149: Update the assertions in the TLDR section test to verify
rendered truncation rather than the Text widget’s maxLines and overflow
properties. Mirror the behavioral probe used in
task_agent_identity_region_test.dart, adding the Flutter rendering import
required to inspect the rendered text outcome.
In `@test/features/agents/ui/task_agent_controls_footer_test.dart`:
- Around line 67-94: Extract the duplicated pump/setup and token lookup logic
from pumpFooter and tokensOf into a shared parameterized test helper, such as
agent_ui_test_utils.dart, accepting the widget under test and named parameters
for varying width, locale, and textScaler. Update
task_agent_controls_footer_test.dart, task_agent_automation_row_test.dart, and
task_agent_identity_region_test.dart to reuse the helper while preserving their
existing sizing, teardown, MediaQueryData, and token lookup behavior.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: e12f0b9a-6e58-4898-835f-e4db6348cc71
📒 Files selected for processing (42)
CHANGELOG.mdflatpak/com.matthiasn.lotti.metainfo.xmllib/features/agents/README.mdlib/features/agents/ui/ai_summary_card.dartlib/features/agents/ui/ai_summary_card/tldr_section_part.dartlib/features/agents/ui/task_agent_automation_row.dartlib/features/agents/ui/task_agent_controls_footer.dartlib/features/agents/ui/task_agent_identity_region.dartlib/features/tasks/README.mdlib/l10n/app_cs.arblib/l10n/app_da.arblib/l10n/app_de.arblib/l10n/app_en.arblib/l10n/app_es.arblib/l10n/app_fr.arblib/l10n/app_it.arblib/l10n/app_localizations.dartlib/l10n/app_localizations_cs.dartlib/l10n/app_localizations_da.dartlib/l10n/app_localizations_de.dartlib/l10n/app_localizations_en.dartlib/l10n/app_localizations_es.dartlib/l10n/app_localizations_fr.dartlib/l10n/app_localizations_it.dartlib/l10n/app_localizations_nl.dartlib/l10n/app_localizations_pt.dartlib/l10n/app_localizations_ro.dartlib/l10n/app_localizations_sv.dartlib/l10n/app_nl.arblib/l10n/app_pt.arblib/l10n/app_ro.arblib/l10n/app_sv.arbtest/features/agents/ui/ai_summary_card/lifecycle_test.darttest/features/agents/ui/ai_summary_card/screenshots_test.darttest/features/agents/ui/ai_summary_card/test_bench.darttest/features/agents/ui/ai_summary_card/tldr_section_part_test.darttest/features/agents/ui/ai_summary_card/wake_test.darttest/features/agents/ui/task_agent_automation_row_test.darttest/features/agents/ui/task_agent_controls_footer_test.darttest/features/agents/ui/task_agent_identity_region_test.darttest/features/tasks/ui/widgets/task_manual_screenshots_test.darttest/test_helper.dart
| return Semantics( | ||
| button: true, | ||
| label: tooltip, | ||
| excludeSemantics: true, | ||
| child: Tooltip( | ||
| message: tooltip, | ||
| child: Material( | ||
| color: Colors.transparent, | ||
| child: InkWell( | ||
| key: const ValueKey('taskAgentSkipScheduledUpdate'), | ||
| onTap: onSkip, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Accessible name drops the visible word "Skip".
excludeSemantics: true plus label: tooltip means the accessible name is "Cancel pending automatic update" while the visible label reads "Skip". Voice-control users saying "Skip" won't hit this target (WCAG 2.5.3 label-in-name).
♿ Proposed fix
return Semantics(
button: true,
- label: tooltip,
+ label: '$label. $tooltip',
excludeSemantics: true,📝 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.
| return Semantics( | |
| button: true, | |
| label: tooltip, | |
| excludeSemantics: true, | |
| child: Tooltip( | |
| message: tooltip, | |
| child: Material( | |
| color: Colors.transparent, | |
| child: InkWell( | |
| key: const ValueKey('taskAgentSkipScheduledUpdate'), | |
| onTap: onSkip, | |
| return Semantics( | |
| button: true, | |
| label: '$label. $tooltip', | |
| excludeSemantics: true, | |
| child: Tooltip( | |
| message: tooltip, | |
| child: Material( | |
| color: Colors.transparent, | |
| child: InkWell( | |
| key: const ValueKey('taskAgentSkipScheduledUpdate'), | |
| onTap: onSkip, |
🤖 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 `@lib/features/agents/ui/task_agent_automation_row.dart` around lines 499 -
509, The Semantics wrapper for the task-agent skip action exposes only the
tooltip text, omitting the visible “Skip” label. Update the `Semantics` label
around the `taskAgentSkipScheduledUpdate` `InkWell` so it includes “Skip” while
retaining the existing explanatory tooltip text, ensuring the accessible name
contains the visible label.
| "taskAgentSetupTitle": "Configuración del agente", | ||
| "taskAgentSkipScheduledUpdate": "Omitir", | ||
| "taskAgentStatusOutOfDate": "Desactualizado", | ||
| "taskAgentStatusUpToDate": "Actualizado", |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use “Al día” for the up-to-date status.
Actualizado means “updated,” while the existing Spanish UI uses “al día” for “up to date” (taskAgentReportUpToDate at Line 4117). Use Al día here to preserve the intended status meaning and terminology consistency.
Proposed fix
- "taskAgentStatusUpToDate": "Actualizado",
+ "taskAgentStatusUpToDate": "Al día",📝 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.
| "taskAgentStatusUpToDate": "Actualizado", | |
| "taskAgentStatusUpToDate": "Al día", |
🤖 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 `@lib/l10n/app_es.arb` at line 4135, Update the taskAgentStatusUpToDate
localization value from “Actualizado” to “Al día”, matching the terminology
already used by taskAgentReportUpToDate for the up-to-date status.
| "taskAgentSetupSemantics": "Configuration actuelle : {identity}. Active pour la modifier.", | ||
| "taskAgentSetupTitle": "Configuration de l’agent", | ||
| "taskAgentSkipScheduledUpdate": "Ignorer", | ||
| "taskAgentStatusOutOfDate": "Obsolète", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use a stale-status translation instead of “Obsolète”.
“Obsolète” suggests that the report is permanently obsolete, while this state is refreshable and the existing taskAgentReportOutdatedTitle already uses “n’est plus à jour”. Prefer “Pas à jour” to preserve the intended meaning.
🤖 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 `@lib/l10n/app_fr.arb` at line 4134, Update the taskAgentStatusOutOfDate
translation value from “Obsolète” to “Pas à jour”, preserving the existing
taskAgentStatusOutOfDate key and the intended refreshable stale-status meaning.
| @override | ||
| String taskAgentNextUpdateInShort(String countdown) { | ||
| return 'in $countdown'; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Capitalize the standalone German countdown label.
TaskAgentAutomationRow can render this compact value as a complete schedule line, so German should use In $countdown, not in $countdown.
As per coding guidelines, update lib/l10n/app_de.arb and regenerate this generated file.
🤖 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 `@lib/l10n/app_localizations_de.dart` around lines 10102 - 10105, Update the
German localization entry for taskAgentNextUpdateInShort in lib/l10n/app_de.arb
to capitalize the standalone label as “In $countdown”, then regenerate
app_localizations_de.dart so the generated method returns the updated text.
Source: Coding guidelines
| "taskAgentUseCategoryDefault": "Standaard categorie kopiëren", | ||
| "taskAgentUseCategoryDefaultDescription": "Kopieert de categorie . Huidige setup. Latere categorie wijzigingen zal dit agent niet beïnvloeden.", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fix the Dutch category-default wording.
The label is awkward, and the description is grammatically incorrect and unclear. Use wording that explains that the agent copies the category’s current settings and will not follow later category changes.
Proposed wording
- "taskAgentUseCategoryDefault": "Standaard categorie kopiëren",
- "taskAgentUseCategoryDefaultDescription": "Kopieert de categorie . Huidige setup. Latere categorie wijzigingen zal dit agent niet beïnvloeden.",
+ "taskAgentUseCategoryDefault": "Standaard van categorie gebruiken",
+ "taskAgentUseCategoryDefaultDescription": "Kopieert de huidige categorie-instellingen. Latere wijzigingen aan de categorie hebben geen invloed op deze agent.",📝 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.
| "taskAgentUseCategoryDefault": "Standaard categorie kopiëren", | |
| "taskAgentUseCategoryDefaultDescription": "Kopieert de categorie . Huidige setup. Latere categorie wijzigingen zal dit agent niet beïnvloeden.", | |
| "taskAgentUseCategoryDefault": "Standaard van categorie gebruiken", | |
| "taskAgentUseCategoryDefaultDescription": "Kopieert de huidige categorie-instellingen. Latere wijzigingen aan de categorie hebben geen invloed op deze agent.", |
🤖 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 `@lib/l10n/app_nl.arb` around lines 4728 - 4729, Update the Dutch localization
entries taskAgentUseCategoryDefault and taskAgentUseCategoryDefaultDescription
to use natural, grammatically correct wording. Clearly state that the agent
copies the category’s current settings and will not follow subsequent changes to
that category.
| } | ||
| }, | ||
| "taskAgentSetupTitle": "Configuração do agente", | ||
| "taskAgentSkipScheduledUpdate": "Ignorar", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use the established translation for “Skip”.
Ignorar reads as “ignore” rather than skipping/cancelling the scheduled update. Use Pular, which is already used for skip actions elsewhere in this locale.
Proposed translation
- "taskAgentSkipScheduledUpdate": "Ignorar",
+ "taskAgentSkipScheduledUpdate": "Pular",📝 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.
| "taskAgentSkipScheduledUpdate": "Ignorar", | |
| "taskAgentSkipScheduledUpdate": "Pular", |
🤖 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 `@lib/l10n/app_pt.arb` at line 4720, Update the taskAgentSkipScheduledUpdate
translation in app_pt.arb from “Ignorar” to the established “Pular” translation
used for skip actions in this locale.
| "taskAgentUseCategoryDefault": "Kopiera kategori standard", | ||
| "taskAgentUseCategoryDefaultDescription": "Kopierar kategorins nuvarande uppställning. Senare kategoriändringar påverkar inte denna agent.", | ||
| "taskAgentUseProfileDefault": "Använd profilstandard", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files:"
git ls-files 'lib/l10n/*.arb' | sort
echo
echo "Relevant Swedish lines:"
nl -ba lib/l10n/app_sv.arb | sed -n '4715,4740p'
echo
echo "Reference English lines:"
nl -ba lib/l10n/app_en.arb | sed -n '1,80p' | rg -n '"taskAgentUseCategoryDefault"|"taskAgentUseProfileDefault"|"taskAgentUseProfileDescription|taskAgentUseCategory' -C 2 || true
echo
echo "All target keys across locales:"
for f in lib/l10n/app_*.arb; do
printf '\n--- %s ---\n' "$f"
rg -n '"taskAgentUseCategoryDefault|"taskAgentUseCategoryDefaultDescription|"taskAgentUseProfileDefault|"taskAgentUseProfileDescription' "$f"
done
echo
echo "Make lint/check for sort?"
if [ -f Makefile ]; then rg -n 'sort_arb|ARB|l10n' Makefile || true; fi
fd -a '.*arb$' lib/l10n | wc -lRepository: matthiasn/lotti
Length of output: 468
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Relevant Swedish lines:"
awk 'NR>=4715 && NR<=4740 { printf "%7d %s\n", NR, $0 }' lib/l10n/app_sv.arb
echo
echo "Relevant English lines:"
awk 'NR>=1 && NR<=80 { if (/taskAgentUseCategoryDefault|taskAgentUseProfileDefault|taskAgentUseProfileDescription|taskAgentUseCategory/) printf "%7d %s\n", NR, $0 }' lib/l10n/app_en.arb
echo
echo "All target keys across locales:"
for f in lib/l10n/app_*.arb; do
printf '\n--- %s ---\n' "$f"
awk '{ if (/taskAgentUseCategoryDefault|taskAgentUseCategoryDefaultDescription|taskAgentUseProfileDefault|taskAgentUseProfileDescription/) printf "%7d %s\n", NR, $0 }' "$f"
done
echo
echo "Locales count and nearby Swedish context:"
python3 - <<'PY'
from pathlib import Path
for path in sorted(Path('lib/l10n').glob('app_*.arb')):
lines = path.read_text(encoding='utf-8').splitlines()
for i, line in enumerate(lines, 1):
if i == 4730:
print(f'{path}: {line}')
start=max(1,i-15); end=min(len(lines), i+15)
print(f'{path}: lines {start}-{end}')
for n in range(start,end+1):
print(f'{n:4}: {lines[n-1]}')
break
else:
print(f'{path}: no line 4730')
PYRepository: matthiasn/lotti
Length of output: 14851
Use Swedish possessive wording for these default setup options.
Kopiera kategori standard is grammatically unnatural in Swedish, and Använd profilstandard reads as clumsy compound-phrase shorthand. Use clearer phrasing that matches the source semantics, such as Kopiera kategorins standardinställningar / Kopiera kategoriens standard and Använd profilens standardinställningar / Använd profilen som standard.
🤖 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 `@lib/l10n/app_sv.arb` around lines 4729 - 4731, Update the Swedish
translations taskAgentUseCategoryDefault and taskAgentUseProfileDefault to use
natural possessive wording for the default setup options, such as “Kopiera
kategorins standardinställningar” and “Använd profilens standardinställningar,”
while leaving taskAgentUseCategoryDefaultDescription unchanged.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb92130bfd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| final trigger = _UpdateNowButton( | ||
| isRunning: widget.isRunning, | ||
| onRunNow: widget.inferenceAvailable ? widget.onRunNow : null, |
There was a problem hiding this comment.
Preserve the pending wake when running an immediate update
When a countdown is visible, this enables Update now without changing its callback, but that callback reaches TaskAgentService.triggerReanalysis, whose WakeOrchestrator.enqueueManualWake unconditionally calls clearThrottle(agentId) and removes the queued subscription job. Tapping the newly available button therefore clears nextWakeAt and drops the scheduled update, contrary to the new UI contract and changelog; the manual-update path must preserve or restore the pending wake rather than merely avoiding cancelScheduledWake.
Useful? React with 👍 / 👎.
| triggerLabel: widget.isRunning | ||
| ? messages.aiSummaryThinkingLabel | ||
| : messages.taskAgentUpdateNow, |
There was a problem hiding this comment.
Reserve one width for both trigger states
When the agent transitions between idle and running, this recomputes the trigger width from only the currently visible localized label. Those labels differ substantially in locales such as German (Jetzt aktualisieren versus Denkt nach …), so the button shrinks while running and, near a responsive threshold, _AutomationMetrics.resolve can even switch the footer between stacked and wide layouts. This contradicts the new fixed-slot behavior and visibly moves the status and automatic-updates control; measure the maximum of both labels and reserve that width in both states.
Useful? React with 👍 / 👎.
| // hole that appears and disappears as the user flips the switch. | ||
| if (widget.automaticUpdatesEnabled && widget.inferenceAvailable) { | ||
| return [messages.taskAgentUpdatesOnChange]; |
There was a problem hiding this comment.
Add a narrow fallback for the no-pending schedule label
When automatic updates are enabled but no wake is pending—for example immediately after the user taps Skip—this returns only the full localized sentence. Unlike the countdown path there is no shorter tier, and _ScheduleLabel renders the measured width with wrapping and ellipsis disabled, so German Aktualisiert sich, wenn sich die Aufgabe ändert exceeds the footer width on the explicitly supported 320px/1.3× layout and overflows or clips. Add shorter tiers or allow this non-payload prose to wrap/truncate.
Useful? React with 👍 / 👎.
Why
The AI summary card's bottom section was scored by a design-expert panel at
4.08/10 and a user-persona panel at 3.8/10, both against reproducible
screenshots. All six experts and all five personas led with the same blocker:
while an automatic update was scheduled, the manual wake button was not
disabled — it was gone from the tree entirely (#3508's "one wake affordance
per state"), so running the agent by hand meant cancelling the countdown first.
Alongside it: a nested card inside the footer band, hover state layers that
spanned the whole card, an attribution line that wrapped mid-value, and a
bottom section that became unusable at phone width in longer languages.
What changed
The footer is now three bands with no nested surface — what state the summary
is in, when it updates, which AI answers.
Nächste Aktualisier…— no time, no action✕beside the switchLayout is measured, not guessed.
TaskAgentAutomationRow(new) measures itslocalized labels with a
TextPainterat the livetextScalerinstead ofbranching on a pixel breakpoint — no constant can know whether "Automatische
Aktualisierungen" fits beside a trigger at 1.3× text scale. The schedule steps
down
Next update in 1:30→in 1:30→1:30, and only when no rung fits doesthe row stack. The countdown value, the trigger and the switch never degrade.
Hover containment. A stretching
Columnhands its children a tight width,which silently defeated the
MainAxisSize.minthe identity row alreadydeclared — so the ink spanned the full reading measure and started flush against
the brain glyph. Same root cause in
TldrHeader, viaExpanded. Both nowshrink-wrap and carry their inset inside the ink.
ai_summary_card.dartneeded no behavioural change:runNowwas already livethroughout a countdown. The regression was entirely in the view.
Localization
Update now,Up to date,Out of date,Skip,in {countdown}andUpdates when this task changesare hand-authored across all 11 locales.The agent-internals surfaces (Wake Cycles, Last wake, Wake count) keep the wake
vocabulary — this is the everyday surface, those are the expert ones.
Testing
task_agent_automation_row_test.dart(new) mirrors the new widget: triggerliveness during a countdown, in-place running swap, the wording ladder, the
stacking decision, and a width × locale × text-scale matrix that pins
no-overflow with the trigger and switch surviving every cell.
task_agent_controls_footer_test.dartis now a composition test — bandchrome, the shared leading edge, and "no surface between the band and the
controls".
1:00:00→59:59crossing movesneither the label, the trigger, the switch, nor the identity region.
against the pre-fix code.
attribution, and the two hover states driven with a real mouse pointer.
make analyzeclean (zero warnings, zero infos), formatter clean, 2199 testsgreen across
test/features/agents/ui/andtest/features/tasks/ui/.Note
A second panel round on the redesigned screenshots is running; I'll post the
scores here when it lands.
pubspec.yamlis deliberately untouched.Summary by CodeRabbit