feat(app): UI control for max_actions_per_hour (#2486)#2500
feat(app): UI control for max_actions_per_hour (#2486)#2500EvanCarson wants to merge 21 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds a new Agent Autonomy settings leaf: UI panel, route, developer-menu entry, i18n strings, TypeScript RPC wrappers, Rust config ops and JSON‑RPC handlers with 1–10,000 validation, plus unit, integration, and E2E tests covering full stack. ChangesAutonomy Settings Feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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 |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
docs/superpowers/specs/2026-05-22-max-actions-per-hour-ui-design.md (2)
186-199: ⚡ Quick winAdd language specifier to fenced code block.
The file list should have a language specifier. Use
textfor plain lists.📝 Proposed fix
-``` +```text src/openhuman/config/ops.rs (+ ~30 lines, mirror meet pattern)🤖 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 `@docs/superpowers/specs/2026-05-22-max-actions-per-hour-ui-design.md` around lines 186 - 199, The fenced code block that lists changed files is missing a language specifier; update the opening fence from ``` to ```text for the block that begins with the line "src/openhuman/config/ops.rs (+ ~30 lines, mirror meet pattern)" so the file list is rendered as plain text and keep the closing ``` unchanged.
31-54: ⚡ Quick winAdd language specifier to fenced code block.
The architecture diagram should specify a language for proper syntax highlighting and rendering. Use
textorasciifor ASCII art diagrams.📝 Proposed fix
-``` +```text UI (React) DeveloperOptionsPanel — new "Agent autonomy" subsection🤖 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 `@docs/superpowers/specs/2026-05-22-max-actions-per-hour-ui-design.md` around lines 31 - 54, The fenced diagram block in docs/superpowers/specs/2026-05-22-max-actions-per-hour-ui-design.md lacks a language specifier causing incorrect rendering; update the opening triple-backtick for the ASCII diagram (the block starting with "UI (React)" / the architecture diagram) to include a language like text or ascii (e.g., ```text) so the diagram renders with proper syntax highlighting.docs/superpowers/plans/2026-05-22-max-actions-per-hour-ui.md (1)
1029-1050: 💤 Low valueConsider streamlining the i18n discussion.
The exploration of multiple approaches (lines 1029-1042) before settling on the final solution adds cognitive load. Since the decision is already made (register i18n keys in en.json), you could simplify by removing the rejected alternatives and leading directly with the chosen approach.
However, the current version does document the thought process, which may be valuable for future maintainers encountering similar decisions.
🤖 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 `@docs/superpowers/plans/2026-05-22-max-actions-per-hour-ui.md` around lines 1029 - 1050, Remove the exploratory alternatives and keep only the decided i18n approach: register the keys used by the renderer instead of fallbacks. Add "settings.developerMenu.autonomy.title" and "settings.developerMenu.autonomy.desc" to the en.json locale (matching the existing nested structure), so t(item.titleKey) / t(item.descriptionKey) resolve; alternatively, if you prefer not to add i18n keys, set the menu item's titleKey and descriptionKey to undefined and supply literal title and description fields (the code references titleKey, descriptionKey and calls t(item.titleKey) in the render block).
🤖 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.
Nitpick comments:
In `@docs/superpowers/plans/2026-05-22-max-actions-per-hour-ui.md`:
- Around line 1029-1050: Remove the exploratory alternatives and keep only the
decided i18n approach: register the keys used by the renderer instead of
fallbacks. Add "settings.developerMenu.autonomy.title" and
"settings.developerMenu.autonomy.desc" to the en.json locale (matching the
existing nested structure), so t(item.titleKey) / t(item.descriptionKey)
resolve; alternatively, if you prefer not to add i18n keys, set the menu item's
titleKey and descriptionKey to undefined and supply literal title and
description fields (the code references titleKey, descriptionKey and calls
t(item.titleKey) in the render block).
In `@docs/superpowers/specs/2026-05-22-max-actions-per-hour-ui-design.md`:
- Around line 186-199: The fenced code block that lists changed files is missing
a language specifier; update the opening fence from ``` to ```text for the block
that begins with the line "src/openhuman/config/ops.rs (+ ~30
lines, mirror meet pattern)" so the file list is rendered as plain text and keep
the closing ``` unchanged.
- Around line 31-54: The fenced diagram block in
docs/superpowers/specs/2026-05-22-max-actions-per-hour-ui-design.md lacks a
language specifier causing incorrect rendering; update the opening
triple-backtick for the ASCII diagram (the block starting with "UI (React)" /
the architecture diagram) to include a language like text or ascii (e.g.,
```text) so the diagram renders with proper syntax highlighting.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2319c03a-926a-422e-a176-a96eac8becba
📒 Files selected for processing (29)
app/src/components/settings/hooks/useSettingsNavigation.tsapp/src/components/settings/panels/AutonomyPanel.tsxapp/src/components/settings/panels/DeveloperOptionsPanel.tsxapp/src/components/settings/panels/__tests__/AutonomyPanel.test.tsxapp/src/lib/i18n/chunks/ar-5.tsapp/src/lib/i18n/chunks/bn-5.tsapp/src/lib/i18n/chunks/de-5.tsapp/src/lib/i18n/chunks/en-5.tsapp/src/lib/i18n/chunks/es-5.tsapp/src/lib/i18n/chunks/fr-5.tsapp/src/lib/i18n/chunks/hi-5.tsapp/src/lib/i18n/chunks/id-5.tsapp/src/lib/i18n/chunks/it-5.tsapp/src/lib/i18n/chunks/pt-5.tsapp/src/lib/i18n/chunks/ru-5.tsapp/src/lib/i18n/chunks/zh-CN-5.tsapp/src/lib/i18n/en.tsapp/src/pages/Settings.tsxapp/src/services/rpcMethods.tsapp/src/utils/tauriCommands/config.test.tsapp/src/utils/tauriCommands/config.tsapp/test/e2e/specs/settings-advanced-config.spec.tsdocs/superpowers/plans/2026-05-22-max-actions-per-hour-ui.mddocs/superpowers/specs/2026-05-22-max-actions-per-hour-ui-design.mdsrc/openhuman/config/ops.rssrc/openhuman/config/ops_tests.rssrc/openhuman/config/schemas.rssrc/openhuman/config/schemas_tests.rstests/json_rpc_e2e.rs
Scopes issue tinyhumansai#2486 to a single autonomy knob: add a new config_get_autonomy_settings / config_update_autonomy_settings RPC pair, surface it as an "Agent autonomy" subsection in DeveloperOptionsPanel, persist to user config.toml. Mirrors the existing config_*_settings pattern; effect applies to next session. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13-task TDD plan covering Rust ops + RPC schemas + handlers, JSON-RPC roundtrip, TS wrappers + tests, new AutonomyPanel + route, UI tests, and E2E case in settings-advanced-config.spec.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the patch struct and apply function for the autonomy settings domain, following the established MeetSettingsPatch / apply_meet_settings pattern. Validates max_actions_per_hour is between 1 and 10000 before persisting. Includes 4 TDD tests covering persist, no-op, and two validation rejection cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add AutonomySettingsUpdate DTO and ControllerSchema entries for update_autonomy_settings / get_autonomy_settings in the config domain schema registry. Handlers and controller registration follow in Task 4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implement handle_update_autonomy_settings and handle_get_autonomy_settings in config/schemas.rs, register both in all_registered_controllers, and add two handler-level tests covering the happy path and validation rejection. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds json_rpc_config_autonomy_settings_roundtrip E2E test: verifies the default value (20), a successful update to 250 persisted across gets, and that out-of-range values (99999) are rejected with a validation error. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the new settings panel at app/src/components/settings/panels/AutonomyPanel.tsx that lets users view and update the max_actions_per_hour autonomy limit via the openhumanGetAutonomySettings / openhumanUpdateAutonomySettings RPC wrappers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds settings.developerMenu.autonomy.title and settings.developerMenu.autonomy.desc to all 11 non-English locale-5 chunk files with English placeholder values, restoring coverage test parity after the keys were added to en-5.ts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous wording said "running sessions keep their current limit" which was only half correct. The cron scheduler and channel listeners (Telegram/Slack/Discord) also cache the SecurityPolicy at startup and continue using the old limit until core restart. Updated helper text makes this explicit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
These were local planning artifacts that shouldn't have been committed. The files remain on disk via .git/info/exclude. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
97fc9ca to
59c8e5d
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
app/src/components/settings/panels/__tests__/AutonomyPanel.test.tsx (1)
71-78: ⚡ Quick winAdd a regression case for non-integer input shapes
Range validation is covered, but there isn’t a test for decimal/scientific-form input remaining invalid. Adding one would prevent silent parsing regressions.
🤖 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 `@app/src/components/settings/panels/__tests__/AutonomyPanel.test.tsx` around lines 71 - 78, Add a regression test to AutonomyPanel.test.tsx that checks non-integer input shapes remain invalid: after rendering <AutonomyPanel /> (using renderWithProviders) and mocking mockGet to return a valid integer, change the input (found via screen.findByDisplayValue) to decimal and scientific-form strings (e.g., '1.5' and '1e3') using fireEvent.change, assert the same inline validation message (e.g., /Must be an integer between 1 and 10,000/i) appears via screen.findByText, and verify the Save button (screen.getByRole('button', { name: /^Save$/ })) is disabled for those inputs; reuse existing helpers and mocks (mockGet, renderWithProviders, screen, fireEvent) and mirror the existing range test structure for clarity.app/src/lib/i18n/en.ts (1)
1962-1962: ⚡ Quick winRemove trailing period for consistency.
Description strings in this i18n file follow a convention of omitting trailing periods (see lines 64, 66, 68, 82–83, 1924, 1928, 1932, 1936).
✨ Proposed fix
- 'settings.developerMenu.autonomy.desc': 'Tool action rate limits and safety thresholds.', + 'settings.developerMenu.autonomy.desc': 'Tool action rate limits and safety thresholds',🤖 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 `@app/src/lib/i18n/en.ts` at line 1962, The i18n entry 'settings.developerMenu.autonomy.desc' currently ends with a trailing period; update its value to remove the final period so it matches the surrounding description convention (e.g., change "Tool action rate limits and safety thresholds." to "Tool action rate limits and safety thresholds") by editing the string value for the key 'settings.developerMenu.autonomy.desc' in the en.ts file.app/test/e2e/specs/settings-advanced-config.spec.ts (1)
103-121: ⚡ Quick winAvoid no-op passes by deriving the target value from current state.
At Line 103 you read current autonomy settings, but Line 111 always writes
250. If the saved value is already250, this can pass without proving an update path worked.Proposed test hardening
const before = await callOpenhumanRpc('openhuman.config_get_autonomy_settings', {}); expect(before.ok).toBe(true); + const current = before.result?.result?.max_actions_per_hour ?? 20; + const target = current === 250 ? 251 : 250; await navigateViaHash('/settings/autonomy'); await waitForText('Agent autonomy', 15_000); const input = await browser.$('`#autonomy-max-actions`'); await input.waitForExist({ timeout: 10_000 }); - await input.setValue('250'); + await input.setValue(String(target)); await clickText('Save', 10_000); await waitForText('Saved.', 10_000); await browser.waitUntil( async () => { const after = await callOpenhumanRpc('openhuman.config_get_autonomy_settings', {}); - return after.ok && after.result?.result?.max_actions_per_hour === 250; + return after.ok && after.result?.result?.max_actions_per_hour === target; }, { timeout: 15_000, interval: 500, timeoutMsg: 'autonomy setting did not persist' } );As per coding guidelines, “Ensure each spec is runnable in isolation” and “Keep tests deterministic… avoid hidden global state.”
🤖 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 `@app/test/e2e/specs/settings-advanced-config.spec.ts` around lines 103 - 121, The test currently reads the autonomy settings via callOpenhumanRpc('openhuman.config_get_autonomy_settings') (variable before) but always writes 250, which can be a no-op; change the test to derive a new target value from before.result?.result?.max_actions_per_hour (e.g., const current = before.result?.result?.max_actions_per_hour ?? 250; const newValue = current === 250 ? 249 : current + 1), then set that newValue into the '`#autonomy-max-actions`' input, clickText('Save'), and update the browser.waitUntil check to assert max_actions_per_hour === newValue so the test verifies an actual change.
🤖 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 `@app/src/components/settings/panels/AutonomyPanel.tsx`:
- Around line 53-56: The current logic uses Number.parseInt(draft, 10) which
truncates inputs like "1.9" and "1e2"; change parsing/validation so
non-integer-looking strings are rejected: convert with Number(draft) (assign to
parsed) and require Number.isInteger(parsed) AND that draft.trim() matches a
strict integer regex (e.g. /^[+-]?\d+$/) before allowing save; keep the
isChanged (committed) and status.kind !== 'saving' checks as-is.
---
Nitpick comments:
In `@app/src/components/settings/panels/__tests__/AutonomyPanel.test.tsx`:
- Around line 71-78: Add a regression test to AutonomyPanel.test.tsx that checks
non-integer input shapes remain invalid: after rendering <AutonomyPanel />
(using renderWithProviders) and mocking mockGet to return a valid integer,
change the input (found via screen.findByDisplayValue) to decimal and
scientific-form strings (e.g., '1.5' and '1e3') using fireEvent.change, assert
the same inline validation message (e.g., /Must be an integer between 1 and
10,000/i) appears via screen.findByText, and verify the Save button
(screen.getByRole('button', { name: /^Save$/ })) is disabled for those inputs;
reuse existing helpers and mocks (mockGet, renderWithProviders, screen,
fireEvent) and mirror the existing range test structure for clarity.
In `@app/src/lib/i18n/en.ts`:
- Line 1962: The i18n entry 'settings.developerMenu.autonomy.desc' currently
ends with a trailing period; update its value to remove the final period so it
matches the surrounding description convention (e.g., change "Tool action rate
limits and safety thresholds." to "Tool action rate limits and safety
thresholds") by editing the string value for the key
'settings.developerMenu.autonomy.desc' in the en.ts file.
In `@app/test/e2e/specs/settings-advanced-config.spec.ts`:
- Around line 103-121: The test currently reads the autonomy settings via
callOpenhumanRpc('openhuman.config_get_autonomy_settings') (variable before) but
always writes 250, which can be a no-op; change the test to derive a new target
value from before.result?.result?.max_actions_per_hour (e.g., const current =
before.result?.result?.max_actions_per_hour ?? 250; const newValue = current ===
250 ? 249 : current + 1), then set that newValue into the
'`#autonomy-max-actions`' input, clickText('Save'), and update the
browser.waitUntil check to assert max_actions_per_hour === newValue so the test
verifies an actual change.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 726b19fd-7722-47ce-804a-19f57310ebaa
📒 Files selected for processing (27)
app/src/components/settings/hooks/useSettingsNavigation.tsapp/src/components/settings/panels/AutonomyPanel.tsxapp/src/components/settings/panels/DeveloperOptionsPanel.tsxapp/src/components/settings/panels/__tests__/AutonomyPanel.test.tsxapp/src/lib/i18n/chunks/ar-5.tsapp/src/lib/i18n/chunks/bn-5.tsapp/src/lib/i18n/chunks/de-5.tsapp/src/lib/i18n/chunks/en-5.tsapp/src/lib/i18n/chunks/es-5.tsapp/src/lib/i18n/chunks/fr-5.tsapp/src/lib/i18n/chunks/hi-5.tsapp/src/lib/i18n/chunks/id-5.tsapp/src/lib/i18n/chunks/it-5.tsapp/src/lib/i18n/chunks/pt-5.tsapp/src/lib/i18n/chunks/ru-5.tsapp/src/lib/i18n/chunks/zh-CN-5.tsapp/src/lib/i18n/en.tsapp/src/pages/Settings.tsxapp/src/services/rpcMethods.tsapp/src/utils/tauriCommands/config.test.tsapp/src/utils/tauriCommands/config.tsapp/test/e2e/specs/settings-advanced-config.spec.tssrc/openhuman/config/ops.rssrc/openhuman/config/ops_tests.rssrc/openhuman/config/schemas.rssrc/openhuman/config/schemas_tests.rstests/json_rpc_e2e.rs
✅ Files skipped from review due to trivial changes (4)
- app/src/lib/i18n/chunks/pt-5.ts
- app/src/lib/i18n/chunks/es-5.ts
- app/src/lib/i18n/chunks/ar-5.ts
- app/src/lib/i18n/chunks/hi-5.ts
- Strict integer validation: reject '1.5', '1e2', '-5', '0.0' (now uses regex + Number() + Number.isInteger instead of parseInt, which would truncate decimals to a valid-looking int). - Add regression test.each cases for non-integer input shapes. - E2E spec: derive target value from current state so the test exercises a real mutation instead of potentially no-op'ing on 250. - Drop trailing period from autonomy.desc to match en.ts sibling-key convention. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Rust Tauri Coverage job hit a flake in core_process::tests::ensure_running_falls_back_for_unknown_listener_on_port (file unmodified by this branch; same test passes in non-coverage sibling job).
Summary
Scopes #2486 to a single autonomy knob: making
max_actions_per_houreditable from the UI instead of requiring hand-edits to~/.openhuman/users/<id>/config.toml.openhuman.config_get_autonomy_settings/openhuman.config_update_autonomy_settingsJSON-RPC methods. Mirrors the existingconfig_*_meet_settingspattern (patch struct + apply fn + load_and_apply wrapper + handler + controller registration).AutonomyPanelat/settings/autonomy, linked from Settings → Developer Options → Agent autonomy. Number input (1–10000) with presets (20/100/500/1000), client + server validation, inline save confirmation, error recovery to last-committed value.config.toml. Takes effect on the next desktop chat. Cron jobs and channel listeners keep their existing limit until you restart OpenHuman (they cache the policy at startup — surfaced in the panel's helper text so users aren't surprised).The new panel is shaped so follow-up PRs can extend
AutonomySettingsPatchwith other knobs from #2486 (allowed_commands,auto_approve,block_high_risk_commands,max_cost_per_day_cents) without restructuring.Out of scope
PolicyChangedevent + subscribers in every site holding anArc<SecurityPolicy>. Documented in the helper text and PR body.openhuman.security_policy_infobug (returnsSecurityPolicy::default()instead of the loaded config). This PR sidesteps it by reading from the new dedicated RPC; separate follow-up.Test plan
cargo test --lib -- autonomy— 13/13 pass (4 new inapply_*, 1 inload_and_apply_*, 2 in handler-level tests, plus 6 pre-existing autonomy/policy tests).cargo test --test json_rpc_e2e json_rpc_config_autonomy_settings_roundtrip— 1/1 (default-get + update + persist-get + invalid-rejected over real HTTP JSON-RPC).pnpm debug unit src/utils/tauriCommands/config.test.ts— 18/18 (14 pre-existing + 4 new).pnpm debug unit src/components/settings/panels/__tests__/AutonomyPanel.test.tsx— 5/5 (load / save-disabled / save-confirms / out-of-range / error-reverts).pnpm exec vitest run app/src/lib/i18n/__tests__/coverage.test.ts— 46/46 (autonomy keys added to all 12 locale-5 chunks).app/test/e2e/specs/settings-advanced-config.spec.ts— new case'persists autonomy max_actions_per_hour through core RPC') — blocked by pre-existing TypeScript duplicate-key errors inde-3.ts/de-5.tsthat failtsc --noEmitbefore the E2E build runs (also fails onmain). Spec is added and follows the adjacent'persists composio trigger triage settings'pattern; will run once the i18n dedup is fixed in a separate PR.pnpm dev:app) — not runnable in CI (Tauri desktop). Steps for the human reviewer: open Settings → Developer Options → Agent autonomy, change value to 300, save, reopen panel to verify persistence, then trigger >20 tool calls in a new chat to confirm the new ceiling takes effect.CI status
Two checks fail on this PR. Both are pre-existing failures on
main(confirmed against upstreamed3e453b) and not introduced by this branch:Type Check TypeScript— fails on duplicate object literal property keys inapp/src/lib/i18n/chunks/de-3.ts(lines 107/126 and 108/127,subconscious.providerUnavailableTitleandsubconscious.providerSettings) andapp/src/lib/i18n/chunks/de-5.ts(lines 211/526, 217/531, etc.,settings.developerMenu.mcpServer.*andsettings.mcpServer.*). These existed before this branch — verifiable by checking the same check on the currentmainhead.e2e / E2E (Linux / Appium Chromium)— same root cause; the E2E job runstscfirst and exits on the i18n duplicate-key errors before reaching any E2E spec.These are not in scope for this PR. Happy to file a separate cleanup PR to deduplicate the i18n chunks if helpful.
Notes for reviewers
--no-verifywas used on push: the husky pre-push hook runspnpm formatwhich shells out tocargo fmt—cargois not in the hook's PATH on this environment.cargo fmt --checkandpnpm format:checkboth pass when run directly. Not introduced by this branch.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests