Skip to content

Fix 6 bugs in the Amicode settings dialog's Developer Tools / Permissions UI - #318

Merged
jeonghun-jj-lee merged 7 commits into
local/amicodefrom
fix/settings-dialog-bugs
Sep 10, 2026
Merged

Fix 6 bugs in the Amicode settings dialog's Developer Tools / Permissions UI#318
jeonghun-jj-lee merged 7 commits into
local/amicodefrom
fix/settings-dialog-bugs

Conversation

@jeonghun-jj-lee

@jeonghun-jj-lee jeonghun-jj-lee commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Five user-reported bugs plus two found during investigation, all in the settings dialog's Developer Tools section and the Permissions tab.

  • Path validation false negatives — the dev-tools-update handler validated repo paths without expanding a leading ~, so the default settings paths (~/harmoniqs/opencode, ~/harmoniqs/amicode) always failed even when correct. Fixed in the paired amicode repo PR (this fork doesn't own that handler); this PR is the app-side half of the same effort.
  • "Rebuild from Latest" → "Rebuild from Main" — the old label was inaccurate: the remote rebuild pulls different tracked branches for the two repos (local/amicode for opencode, main for amicode), not a single "latest". Renamed across all 18 locales + a stale code comment. (This went through an intermediate label, "Pull & Rebuild", chosen because it read as accurate for both repos — reverted to "Rebuild from Main" per explicit preference, since that's the branch users actually think about when they look at this button.)
  • Premature "Rebuilt!" mid-buildrebuild() set the success flag at the same time as the in-progress flag, so reopening the dialog mid-build showed "Rebuilt!" instead of "Rebuilding...". Fixing this naively (just removing the premature set) would have regressed the success case entirely, since the done handler never set the flag either — extracted the flag lifecycle into a pure, tested function to get both halves right.
  • Validation flickersendUpdate() blanked the status signal on every path edit, so any visible error/success indicator vanished and snapped back on each round-trip. Modeled the request lifecycle as a pure reducer; added a "Validating…" affordance so keeping stale status visible doesn't just trade flicker for silently-stale data.
  • Auto-accept toggle misplaced — moved from the General tab to the Permissions tab, where it belongs as a permissions control. Threaded sessionID through since the Permissions tab never received it before.
  • Header/status misalignment (found during investigation) — the section title's padding threw off flex-centering against the rebuild status text beside it; same fix already applied to the providers/models section titles.
  • Dead building/buildError code (companion to amicode#941) — the paired amicode PR removes the auto-build that used to fire on every amicode-path commit; that build was the only thing that ever set these fields. Removed the now-permanently-unreachable DevToolsStatus fields, the "Building extension…" UI text, and its orphaned i18n key across all 18 locales.

Approach

Each bug is its own TDD slice: a failing test capturing the exact regression first, confirmed RED, then the minimal fix, confirmed GREEN. Two bugs (the flag lifecycle, the flicker) are modeled as pure functions extracted from the SolidJS controller — this codebase has no @solidjs/testing-library render harness, so following the existing pattern (general-controller-behavior.ts / general-controllers.test.ts), the signal-adjacent logic is pulled into plain functions that are directly unit-testable, with the controller as a thin reactive shell around them. The label rename and the structural tab-move used source-scanning regression tests, following the same pattern already established for the "outer relay script forwards preview-file" structural test. The dead-code removal is a removal-driven refactor verified via the full suite before/after rather than a fresh RED/GREEN cycle (there's no new behavior to fail against).

Testing

  • bun test --conditions=solid --preload ./happydom.ts ./src in packages/app: 1103 pass, 0 fail
  • bun test in packages/ui: 491 pass, 0 fail
  • bun turbo typecheck: 30/30 packages pass

New test files

  • packages/app/src/i18n/rebuild-label-rename.test.ts
  • packages/app/src/components/settings-v2/developer-tools-rebuild-flags.ts (+ test)
  • packages/app/src/components/settings-v2/developer-tools-request-state.ts (+ test)
  • packages/app/src/components/settings-v2/permission-scope-relocation.test.ts
  • packages/ui/src/amicode/devtools-header-alignment.test.ts

Companion PR

Paired with harmoniqs/amicode#939 for the tilde-expansion fix and the amicode-path auto-build removal in the extension-side dev-tools-update handler (that repo owns chat_bridge.ts).

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 8b855fee-7e3b-4c76-a805-210b378663d0


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.

❤️ Share

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

The remote rebuild pulls different tracked branches for the two repos
(local/amicode for opencode, main for amicode), so no single label is
fully precise for both sides. This reverses an earlier pass at this same
label ('Pull & Rebuild', chosen because it read as accurate for both
repos) back to 'Rebuild from Main' per explicit preference — it's the
branch users actually think about when they look at this button, even
though it's not the literal branch the opencode side pulls.

TDD: extended the existing rebuild-label-rename regression test (the one
that already scans the app package for the stale 'Rebuild from Latest'
string and asserts en.ts's key value) by updating its NEW_LABEL constant
to 'Rebuild from Main' — same test, same guarantee, new target string.
i18n parity suite and typecheck both still pass.
rebuild() set the amicode:devtools-rebuilt flag at the same time as
amicode:devtools-rebuilding, so reopening the settings dialog while a
rebuild was still running showed 'Rebuilt!' instead of 'Rebuilding...'.
Worse, simply removing that premature set (without a corresponding fix)
would have regressed the success case entirely: the dev-tools-rebuild-status
'done' handler only ever cleared 'rebuilding' — it never set 'rebuilt' — so
after a successful rebuild, onMount would see no flags at all and silently
fall back to 'idle'.

Extracted the flag lifecycle (start / done / failed) into a pure
rebuildFlagMutation() function in developer-tools-rebuild-flags.ts, so the
protocol is unit-testable without a SolidJS reactive root or settings
context. rebuild() now only sets rebuilding+reopen at start; the 'done'
handler now sets 'rebuilt' (and clears 'rebuilding'); the timeout and
'failed' paths share the same clear-only mutation.

TDD: wrote the pure-function tests + a localStorage integration test
reproducing both the original bug (start alone must never look like
'rebuilt') and the naive-fix regression (done must actually set the flag)
before the module existed — confirmed RED (module not found), implemented,
confirmed GREEN (7/7), then wired the controller and reran the full
settings-v2 suite (9/9).
sendUpdate() blanked `status` to undefined the instant a validation
round-trip started, so any visible error/success indicator vanished and
then snapped back once the reply arrived — a visible flash on every path
field blur. The message handler also set multiple signals (status,
pending) outside a batch, risking an intermediate render between them.

Modeled the request lifecycle as a pure reducer (reduceDevToolsRequest)
in developer-tools-request-state.ts: 'request-sent' now keeps the stale
status and only flips pending; 'status-received' replaces status and
clears pending. sendUpdate() and the message handler now go through it,
and the handler's signal writes are wrapped in solid-js's batch().

Since keeping stale status without any visual cue would trade flicker for
silently-stale data, added a 'Validating…' indicator (bound to the
existing but previously-unused `pending` signal) and dim the stale
error text while a new round-trip is in flight. Also added the missing
.settings-v2-field-info CSS rule the 'Building extension…' text already
referenced but never had defined.

TDD: wrote reduceDevToolsRequest's tests first (the key regression case:
a second request-sent must keep the FIRST round-trip's status visible,
not blank it) — confirmed RED (module not found), implemented, confirmed
GREEN (4/4), wired the controller, reran the full settings-v2 suite
(13/13), typechecked clean.
PermissionScopeSetting (the 'Auto-accept permissions' switch) lived in
the General tab, but it is a permissions control — it belongs with the
other permission settings, not general app preferences.

Three coordinated changes make the move real:
- general.tsx: removed the component definition, its controller
  instantiation, and its render call, plus the now-unused
  createPermissionScopeController/PermissionScopeController imports.
- permissions.tsx: added the component (moved verbatim), instantiates
  createPermissionScopeController with the tab's own sessionID prop,
  and renders it at the top of the tab body, above the trust tiers.
- dialog-settings-v2.tsx: threads sessionID into SettingsPermissionsV2,
  which never received it before (the controller needs it to resolve
  the active session's auto-accept state).

TDD: no @solidjs/testing-library render harness exists in this codebase,
so — following the same source-scanning pattern already used elsewhere
(the 'outer relay script forwards preview-file to the iframe' structural
test) — wrote a test asserting the move via source content: general.tsx
no longer references PermissionScopeSetting/createPermissionScopeController,
permissions.tsx does, and the dialog's permissions tab content passes
sessionID. Confirmed RED (4/4 failing — nothing moved yet), performed the
move, confirmed GREEN (4/4). Typechecked clean; full app suite still
passes (1103/1103).
.devtools-section-header lays the 'Developer Tools' title and the
rebuild status indicator side by side with align-items: center. But
.settings-v2-section-title carries a padding-bottom sized for the normal
case (a title sitting above a settings list), which shifts its visual
center upward relative to the status text beside it — the title reads
as sitting slightly higher than 'Rebuilding...' / 'Rebuilt!'.

Added the same padding-bottom: 0 override the .settings-v2-providers and
.settings-v2-models section titles already apply for the identical
reason.

TDD: wrote a test reading amicode.css directly (following the existing
pattern in wave-geometry.test.ts) asserting the override rule exists —
confirmed RED (selector not found), applied the CSS fix, confirmed
GREEN. Full ui package suite still passes (491/491).
The paired amicode PR removes the auto-build that used to fire on every
amicode-path commit — that build was the only thing that ever set
DevToolsStatus.building/buildError, so with it gone these fields, the
'Building extension…' UI text, and its i18n key across all 18 locales
are permanently unreachable.

Removed:
- building?/buildError? from the DevToolsStatus interface and from the
  status-received construction in the message handler
- the building() computed and its <Show> block in developer-tools.tsx
- the now-dead s.buildError check in amicodeError() (falls straight
  through to s.amicodeError, which is what the extension actually sends)
- the orphaned settings.general.row.amicodePath.building i18n key from
  every locale (nothing in code referenced it once the UI text was gone)

Removal-driven refactor, not new behavior — verified via the full suite
before (17/17 settings-v2, 1103/1103 app) and after (same counts, 0
regressions), plus a clean typecheck. i18n parity suite still passes.
@jeonghun-jj-lee
jeonghun-jj-lee force-pushed the fix/settings-dialog-bugs branch from c2101e9 to 6e28200 Compare September 9, 2026 22:42
The earlier padding reset removed one source of misalignment, but the
header still used align-items:center. Centering the 12px status line box
against the 15px title line box left the status baseline visibly lower
than the heading. Aligning the flex row on its baseline is the correct
typographic primitive: the text lines now share a baseline while the
status dot remains centered inside its own inline status row.

TDD: changed the existing CSS regression test to require baseline
alignment, confirmed RED against align-items:center, changed the CSS,
then confirmed GREEN. Full UI suite: 491/491 pass.
@jeonghun-jj-lee
jeonghun-jj-lee merged commit 43f3c36 into local/amicode Sep 10, 2026
3 of 4 checks passed
jeonghun-jj-lee added a commit to harmoniqs/amicode that referenced this pull request Sep 10, 2026
Squash merge of #939 after all required CI checks passed. Companion fork PR harmoniqs/opencode#318 merged first into local/amicode.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant