Skip to content

Commit 1dafea4

Browse files
committed
Merge remote-tracking branch 'origin/main' into jamadeo/goose2-bundle-config
2 parents f70136b + 000d7c5 commit 1dafea4

66 files changed

Lines changed: 2970 additions & 1270 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
---
2+
name: ui-refactor-review
3+
description: >-
4+
Review ui/goose2 frontend changes specifically for refactoring opportunities
5+
and long-term maintainability. Use when the user wants cleanup feedback or
6+
wants to improve structure, decomposition, layering, type hygiene,
7+
duplication, dead code, readability, and extensibility in goose2 React +
8+
TypeScript + Tauri UI code.
9+
---
10+
11+
# UI Refactor Quality
12+
13+
Use this skill for `ui/goose2` pull requests.
14+
15+
Keep the focus on behavior-preserving frontend improvement. Favor the repo's
16+
existing architecture and patterns over generic frontend advice.
17+
18+
## Goals
19+
20+
- Review changed code for refactor quality, not just correctness.
21+
- Bias toward detecting maintainability smells even when the code is still functionally correct.
22+
- Review the final shape of the changed code, not whether it is better than what came before.
23+
- Judge changes by whether they leave the code easier to maintain and extend in future work, not just whether they are correct today.
24+
- Produce an actionable checklist instead of vague feedback.
25+
- Ask for approval before making code changes unless the user explicitly asks for fixes.
26+
- Preserve `ui/goose2` boundaries: `ui/`, `hooks/`, `api/`, `lib/`, `stores/`, and `shared/`.
27+
28+
## Workflow
29+
30+
1. Determine the review scope.
31+
- Review only the changed lines in the branch or working tree.
32+
- If both committed and uncommitted changes exist, clarify which scope to review when needed.
33+
- If the change mixes feature work with refactoring, call that out explicitly and review the feature changes separately from the cleanup quality.
34+
2. Inspect only the changed lines in that scope, but follow each changed code path into surrounding modules when needed to judge whether the shape is clean.
35+
3. Run the `Smell Checklist` below before looking for bugs.
36+
- Do not require a user-visible bug before calling out a maintainability problem.
37+
- Prefer concrete findings like "this responsibility is misplaced" or "this logic should be extracted" over generic style commentary.
38+
- If a Smell Checklist item is true, turn it into an `Issue`.
39+
- Do not leave a confirmed smell unaccounted for in the final review.
40+
- Do not suppress an `Issue` because the PR already improved the previous version.
41+
- Do not treat partial cleanup as resolution. A smell that remains in the post-PR code is still an `Issue`.
42+
4. Evaluate the changed code against the `Rules` below and identify what the PR already improved and what should still be refactored or cleaned up.
43+
5. Review the changed code separately for these buckets:
44+
- decomposition
45+
- layering
46+
- hooks/effects
47+
- pure helpers
48+
- type shapes
49+
- duplication
50+
- tests
51+
- feature wiring
52+
- For each bucket, explicitly determine whether zero, one, or multiple `Issues` remain.
53+
- If any remain, report all distinct `Issues` you can verify in that bucket.
54+
6. Verify each non-trivial issue against the actual code before turning it into a task.
55+
- Trace the relevant code path end to end.
56+
- Check whether the issue is already handled elsewhere.
57+
- Confirm the suggested cleanup would actually simplify the code.
58+
- Keep the finding if the maintainability problem is real, local, and behavior-preserving to fix, even when the code still works.
59+
- Drop speculative or preference-only findings.
60+
7. Before finalizing the review, run a second pass focused only on finding issues not already listed.
61+
- Discover issues before prioritizing them.
62+
- Do not stop after the first few findings.
63+
- Do not omit a verified issue because it is lower priority than other findings.
64+
8. Produce review output in this order:
65+
- `Applied Well`
66+
- `Issues`
67+
- one ordered `Checklist` for the whole reviewed scope
68+
- Do not stop after the findings until the ordered checklist is complete.
69+
9. Stop after the checklist and ask for approval before making code changes, unless the user explicitly asked to implement fixes.
70+
10. Fix approved checklist items in order, using the `Rules` below as the quality bar for the implementation.
71+
- State the main maintainability problem in one sentence.
72+
- Fix the highest-value items first.
73+
- Make the smallest behavior-preserving change that clearly improves the code.
74+
11. Summarize what changed, what remains, and what verification ran.
75+
76+
## Strict Mode
77+
78+
- Any confirmed smell in the changed code must be reported as an `Issue`.
79+
- Review the post-PR shape. Do not grade on a curve.
80+
- Partial extraction, partial deduplication, or partial cleanup does not clear a remaining smell.
81+
- If multiple distinct smells remain in one file, report each distinct responsibility problem separately.
82+
83+
## Smell Checklist
84+
85+
Before finalizing the review, explicitly ask:
86+
87+
- Is any view or page component still doing too many jobs?
88+
- Is any pure derivation logic still trapped in a component instead of `lib/`?
89+
- Is any repeated async UI workflow ready for a focused hook?
90+
- Are helpers duplicated or living in the wrong layer?
91+
- Are any inline object shapes large enough to deserve a named type?
92+
- Did logic move without moving or adding the right tests?
93+
- Did the refactor preserve feature wiring while improving structure?
94+
95+
## Rules
96+
97+
### Size And Decomposition
98+
99+
- Treat these as smell thresholds, not hard limits:
100+
- components around 200 lines
101+
- functions around 40 lines
102+
- files around 300 lines
103+
- JSX nesting around 4 levels
104+
- Treat "many unrelated state variables + many handlers + many effects in one view" as a smell even when the line count is still tolerated.
105+
- Treat a file that owns multiple unrelated responsibilities across data loading, derivation, mutation, and rendering orchestration as a smell unless there is a strong reason to keep it together.
106+
- If a component does more than its name claims, rename it or split it.
107+
- Split by responsibility, not by arbitrary line count.
108+
- When a view contains substantial pure derivation logic, prefer extracting it into `lib/` helpers with direct tests.
109+
- When a view contains substantial effectful workflow logic, prefer extracting it into a focused hook.
110+
- Do not suppress a decomposition `Issue` just because the PR already extracted some responsibilities.
111+
- If the remaining file still does too many jobs, report that as an `Issue`.
112+
- File size alone is not the finding. The finding is the number of unrelated responsibilities still owned by the final file.
113+
114+
### Naming Reveals Intent
115+
116+
- Use names that describe intent, not implementation trivia.
117+
- Prefer domain terms over generic placeholders like `data`, `value`, or `handler`.
118+
- A helper name should describe what it returns or decides, not how it computes it.
119+
- Rename misleading functions before adding comments to explain them.
120+
121+
### Layer Discipline
122+
123+
- `ui/`: rendering and light view logic only.
124+
- `hooks/`: glue between React state/effects and lower layers.
125+
- `api/`: backend transport wrappers and DTO adaptation only.
126+
- `lib/`: pure functions and domain helpers only.
127+
- `stores/`: shared feature state only.
128+
- Keep business logic out of render-heavy components when a hook or utility would make it clearer.
129+
- If a component mixes pure transforms and UI event orchestration, split the pure transforms out first.
130+
- Do not move simple local state into a store unless multiple consumers truly need it.
131+
- Keep `api/` free of UI imports, path logic, and unrelated domain policy.
132+
- Keep `lib/` free of React, DOM, `window`, and I/O.
133+
- Prefer shared domain helpers in `lib/` when the same normalization, formatting, or parsing logic appears in multiple modules.
134+
- If logic lives in the wrong layer after the PR, report that as an `Issue` even if the PR reduced the amount of misplaced logic.
135+
136+
### Module Encapsulation
137+
138+
- Export the minimum surface a module needs to share.
139+
- Keep helpers, constants, and intermediate transforms private unless another module genuinely needs them.
140+
- Treat removing stale exports as a quality improvement.
141+
- If a helper is used in only one module, default to keeping it local.
142+
- If similar helpers appear across two modules, default to extracting them.
143+
144+
### DRY And Hooks
145+
146+
- Extract shared behavior once the duplication is clear and the shared abstraction is stable.
147+
- Two call sites can be enough when the shared shape is obvious and both call sites become simpler.
148+
- Prefer a hook when the shared logic is stateful or effectful.
149+
- Keep each hook focused on one job.
150+
- Keep hook return shapes stable so callers are not forced to handle shifting contracts.
151+
- Do not use a hook as the default extraction target for oversized components.
152+
- If the logic is pure and React-independent, report extraction to `lib/`.
153+
- If the logic coordinates React state, effects, async actions, or UI event orchestration, report extraction to `hooks/`.
154+
- Treat repeated pure UI derivation logic as helper extraction candidates.
155+
- If repeated effectful orchestration remains in the changed code, report that as an `Issue`.
156+
- If repeated pure transforms remain in the changed code, report that as an `Issue`.
157+
158+
### Type Hygiene
159+
160+
- Keep canonical cross-feature types in `src/shared/types/`.
161+
- Do not duplicate types across features when one shared type should exist.
162+
- Give inline object types with 3 or more fields a name when they start obscuring the code.
163+
- Prefer `Pick`, `Omit`, and `Partial` over restating shapes by hand.
164+
- Avoid `any`, unchecked `as`, non-null assertions, and string-encoded pseudo-unions when a discriminated union would be clearer.
165+
- Treat repeated or verbose inline object shapes as extraction candidates for named types.
166+
- If verbose or repeated inline shapes remain after the PR, report that as an `Issue`.
167+
168+
### React And UI
169+
170+
- Prefer straight-line render logic, guard clauses, and early returns over deep nesting.
171+
- Prefer controlled components where practical.
172+
- Use semantic HTML like `<main>`, `<nav>`, `<header>`, and `<aside>`.
173+
- Prefer existing shared UI button primitives over plain `<button>` elements.
174+
- Treat new plain `<button>` usage as a refactor smell unless there is a specific semantic or integration reason.
175+
- If a plain `<button>` is genuinely necessary, it must use `type="button"` in goose2.
176+
- Use `cn()` from `@/shared/lib/cn` for Tailwind class merging.
177+
- Prefer existing shared UI primitives before creating new one-off markup patterns.
178+
- Avoid inline styles except for truly dynamic values.
179+
- Respect reduced-motion behavior when touching animation.
180+
181+
### Notifications, Localization, And Accessibility
182+
183+
- Route success and error feedback through the app's shared notification primitive.
184+
- Route user-facing Goose UI copy through `react-i18next` in already-migrated surfaces.
185+
- Prefer stable translation keys over inline English strings.
186+
- Avoid raw user-facing strings inside `catch` blocks.
187+
- Add text alternatives for icon-only or color-only affordances.
188+
- Keep interactive semantics explicit with labels, roles, and selected state where applicable.
189+
190+
### Tauri And Backend Boundaries
191+
192+
- Frontend-to-core communication goes through `SDK -> ACP -> goose`.
193+
- Do not add ad hoc `fetch()` calls for goose core behavior.
194+
- Do not add `invoke()` calls as proxies to goose core behavior; reserve them for desktop-shell concerns.
195+
- Do not call ACP clients directly from UI components; keep backend access in `shared/api/` or `features/*/api/`.
196+
197+
### Errors, State Drift, And Dead Code
198+
199+
- Handle errors explicitly and close to the source.
200+
- Keep the happy path easy to see.
201+
- In async UI flows, keep local state, persisted state, and backend-confirmed state from drifting apart.
202+
- Delete unused exports, imports, parameters, fields, and commented-out code.
203+
- Remove tests that only protect deleted internals rather than user-visible behavior.
204+
- When logic moves across modules, expect coverage to move with it rather than disappear.
205+
- Treat coverage loss in refactors as suspicious unless the behavior was intentionally removed.
206+
- If behavior-preserving logic moved but coverage did not move with it, report that as an `Issue`.
207+
- Report redundant props, fields, parameters, and intermediate values as `Issues`.
208+
209+
## Review Output
210+
211+
### Applied Well
212+
213+
- List what the PR already improved.
214+
- Use concrete examples with file references.
215+
- Skip generic praise.
216+
217+
### Issues
218+
219+
- List only issues that are actually in scope for the changed code.
220+
- For each issue, explain:
221+
- what is wrong
222+
- why it matters
223+
- the smallest change that would improve it
224+
- Only include issues that survived a verification pass against the actual code.
225+
226+
### Checklist
227+
228+
- End with one ordered actionable checklist for the whole reviewed scope.
229+
- Do not create a separate checklist per issue.
230+
- Each item should be specific enough to implement directly.
231+
- Each item should be small enough to fix as one unit.
232+
- If an item would require sub-steps, split it into multiple checklist items instead of nesting.
233+
- Treat `Checklist` as the complete fix inventory for the reviewed scope.
234+
- Do not defer concrete fix items from `Checklist` into later sections.
235+
- Each checklist item must describe a concrete code change, not a high-level goal.
236+
- A user should be able to implement the fix directly from the `Checklist`.
237+
- Order the checklist by implementation sequence:
238+
- boundary and layering issues first
239+
- naming and decomposition next
240+
- type and hook cleanup after that
241+
- dead code and polish last
242+
243+
## Done Criteria
244+
245+
- No unresolved in-scope boundary violations remain.
246+
- The code is clearer without changing intended behavior.
247+
- No new dead code or needless exports were introduced.
248+
- Naming and decomposition are improved where the review identified them.
249+
- Review findings were verified before being turned into fix tasks.
250+
- Verification was run when appropriate, or explicitly called out if not run.

crates/goose/src/acp/server.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,13 @@ impl GooseAcpAgent {
957957
goose_platform: GoosePlatform,
958958
) -> Result<Self> {
959959
let session_manager = Arc::new(SessionManager::new(data_dir));
960+
961+
// Eagerly initialize the SQLite pool so it's ready when providers/sessions need it.
962+
let storage_clone = session_manager.storage().clone();
963+
tokio::spawn(async move {
964+
let _ = storage_clone.pool().await;
965+
});
966+
960967
let thread_manager = Arc::new(crate::session::ThreadManager::new(
961968
session_manager.storage().clone(),
962969
));

crates/goose/src/providers/google.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use super::api_client::{ApiClient, AuthMethod};
22
use super::base::MessageStream;
33
use super::errors::ProviderError;
4-
use super::openai_compatible::handle_status;
4+
use super::openai_compatible::{handle_status, map_http_error_to_provider_error};
55
use super::retry::ProviderRetry;
66
use super::utils::RequestLog;
77
use crate::conversation::message::Message;
88

99
use crate::model::ModelConfig;
1010
use crate::providers::base::{ConfigKey, Provider, ProviderDef, ProviderMetadata};
1111
use crate::providers::formats::google::{create_request, response_to_streaming_message};
12+
use crate::providers::inventory::{config_secret_value, InventoryIdentityInput};
1213
use anyhow::Result;
1314
use async_stream::try_stream;
1415
use async_trait::async_trait;
@@ -135,6 +136,27 @@ impl ProviderDef for GoogleProvider {
135136
) -> BoxFuture<'static, Result<Self::Provider>> {
136137
Box::pin(Self::from_env(model))
137138
}
139+
140+
fn supports_inventory_refresh() -> bool {
141+
true
142+
}
143+
144+
fn inventory_identity() -> Result<InventoryIdentityInput> {
145+
let config = crate::config::Config::global();
146+
let mut identity = InventoryIdentityInput::new(GOOGLE_PROVIDER_NAME, GOOGLE_PROVIDER_NAME)
147+
.with_public(
148+
"host",
149+
config
150+
.get_param::<String>("GOOGLE_HOST")
151+
.unwrap_or_else(|_| GOOGLE_API_HOST.to_string()),
152+
);
153+
154+
if let Some(api_key) = config_secret_value(config, "GOOGLE_API_KEY") {
155+
identity = identity.with_secret("api_key", api_key);
156+
}
157+
158+
Ok(identity)
159+
}
138160
}
139161

140162
#[async_trait]
@@ -153,6 +175,13 @@ impl Provider for GoogleProvider {
153175
.request(None, "v1beta/models")
154176
.response_get()
155177
.await?;
178+
let status = response.status();
179+
if !status.is_success() {
180+
let body = response.text().await.unwrap_or_default();
181+
let payload = serde_json::from_str::<serde_json::Value>(&body).ok();
182+
return Err(map_http_error_to_provider_error(status, payload));
183+
}
184+
156185
let json: serde_json::Value = response.json().await?;
157186
let arr = json
158187
.get("models")

crates/goose/src/providers/inventory/mod.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use super::canonical::{map_provider_name, map_to_canonical_model, CanonicalModel
33
use crate::config::declarative_providers::{DeclarativeProviderConfig, ProviderEngine};
44
use crate::config::Config;
55
use crate::session::session_manager::SessionStorage;
6-
use anyhow::Result;
6+
use anyhow::{Context, Result};
77
use chrono::{DateTime, Duration, Utc};
88
use serde::{Deserialize, Serialize};
99
use sha2::{Digest, Sha256};
@@ -307,9 +307,18 @@ impl ProviderInventoryService {
307307

308308
pub async fn entries(&self, provider_ids: &[String]) -> Result<Vec<ProviderInventoryEntry>> {
309309
let ids = self.resolve_provider_ids(provider_ids).await;
310-
let mut entries = Vec::with_capacity(ids.len());
311-
for provider_id in ids {
312-
if let Some(entry) = self.entry_for_provider(&provider_id).await? {
310+
let handles: Vec<_> = ids
311+
.into_iter()
312+
.map(|id| {
313+
let this = self.clone();
314+
tokio::spawn(async move { this.entry_for_provider(&id).await })
315+
})
316+
.collect();
317+
let results = futures::future::join_all(handles).await;
318+
let mut entries = Vec::with_capacity(results.len());
319+
for result in results {
320+
let inner = result.context("provider inventory task panicked")?;
321+
if let Some(entry) = inner? {
313322
entries.push(entry);
314323
}
315324
}

0 commit comments

Comments
 (0)