- hypr/apps.lua - hypr/autostart.lua - hypr/envs.lua - hypr/hyprland.lua - hypr/hyprsunset.conf - hypr/input.lua - hypr/looknfeel.lua - hypr/omasettings.lua - hypr/xdph.conf - omarchy/branding/about.txt - omarchy/branding/screensaver.txt - omarchy/extensions/omarchy-menu.jsonc - omarchy/hooks/battery-low.d/play-warning-sound.sample - omarchy/hooks/font-set.d/show-font-notification.sample - omarchy/hooks/post-boot.d/weather.sample - omarchy/hooks/post-update.d/install-voxtype.hook - omarchy/hooks/post-update.d/setup-agent.hook - omarchy/hooks/post-update.d/setup-fingerprint.hook - omarchy/hooks/post-update.d/show-update-notification.sample - omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample - omarchy/hooks/theme-set.d/show-theme-notification.sample - omarchy/shell.json - omarchy/shell.toml - omarchy/theme.name - omarchy/themes/azure-glow/README.md - omarchy/themes/azure-glow/alacritty.toml - omarchy/themes/azure-glow/btop.theme - omarchy/themes/azure-glow/hyprland.conf - omarchy/themes/azure-glow/hyprlock.conf - omarchy/themes/azure-glow/icons.theme - … 269 more
522 lines
23 KiB
Markdown
522 lines
23 KiB
Markdown
# Implementation Plan: Opt-in Bitwarden SSH Agent
|
||
|
||
## Overview
|
||
|
||
Build the SSH-agent design in `docs/ideas/ssh-agent.md` as an optional,
|
||
disabled-by-default feature. The existing Quickshell panel remains the only
|
||
component that invokes `bw` and owns `BW_SESSION`; a supervised Rust companion
|
||
implements the local SSH-agent protocol, holds private SSH keys only while the
|
||
vault is unlocked, and makes signing contingent on a live approval or bounded
|
||
process grant. Before any agent work, the ordinary vault read is sanitized
|
||
outside QML so unsupported cipher types and `sshKey.privateKey` cannot enter
|
||
the long-lived panel process.
|
||
|
||
Detailed tasks and checkpoints are tracked in `tasks/todo.md`. No feature code
|
||
should be written until this plan has human approval.
|
||
|
||
## Development Worktree and Live Checkpoints
|
||
|
||
- Develop on branch `feature/ssh-agent` in `.worktrees/ssh-agent`, based on the
|
||
latest fetched `origin/master`. Keep the primary `master` checkout free of
|
||
feature changes.
|
||
- The enabled Omarchy plugin path
|
||
`~/.config/omarchy/plugins/io.github.elevate08.qs-bitwarden-cli` must resolve
|
||
to this worktree before any live test.
|
||
- At the end of every task slice, run its focused tests plus the applicable
|
||
regression/QML/lint/manifest gates, verify the plugin symlink target, run
|
||
`omarchy restart shell`, confirm `omarchy-shell shell ping`, summon the
|
||
Bitwarden panel, and call its non-secret `status` IPC method.
|
||
- Pause after that live reload so the user can exercise the plugin manually.
|
||
Do not start the next task until the user confirms the checkpoint or reports
|
||
issues to fix.
|
||
|
||
## Scope
|
||
|
||
The first release includes:
|
||
|
||
- a public-only, read-only SSH-key item experience in the panel;
|
||
- Ed25519 and RSA SHA-2 authentication/signing through a stable Unix socket;
|
||
- explicit per-signature approval and short per-process grants;
|
||
- opt-in unlock-on-demand, safe UWSM client routing, and advisory diagnostics;
|
||
- public-key file projection for normal Git SSH-signing configuration;
|
||
- an x86_64 GNU helper bundled with the plugin and verified by reproducible CI.
|
||
|
||
It does not include key creation/import, SSH item editing/cloning, master-
|
||
password re-prompt keys, forwarding, persistent grants, GPG-agent support,
|
||
item types 6–8, aarch64, or a Bitwarden SDK/direct service integration.
|
||
|
||
## Architecture Decisions
|
||
|
||
- Sanitize `bw list items` with static `jq` programs before QML parses it. QML
|
||
receives intact types 1–4 and a public-only type-5 projection; all other
|
||
types fail closed out of the display model.
|
||
- Keep one panel-owned `bw list items` read per unlock/sync. When the agent is
|
||
enabled, a bounded `tee` branch sends only eligible type-5 private material
|
||
to a nonce-framed FIFO. Failure of that optional branch must never prevent
|
||
the panel list from loading.
|
||
- Use one Rust companion and one stable socket under `$XDG_RUNTIME_DIR`. The
|
||
companion spawns no child processes, receives neither `BW_SESSION` nor
|
||
unrelated vault data, and exits when its control channel closes.
|
||
- Make approval, epoch checks, request limits, and final allow/deny decisions
|
||
authoritative in the companion. Process information is prompt context, not
|
||
an authentication boundary.
|
||
- Treat public-key file export as v1 scope because normal Git SSH signing needs
|
||
file paths. The planning assumption is that the companion owns this
|
||
projection from its validated public keystore; Task 4 records or corrects
|
||
that ownership before implementation proceeds.
|
||
- Ship only `x86_64-unknown-linux-gnu` initially. Commit the source, lockfile,
|
||
pinned toolchain, release bytes, and checksum; compare clean CI output byte
|
||
for byte before anything reaches `master` or a release.
|
||
|
||
## Dependency Graph
|
||
|
||
```text
|
||
Task 1 sanitized read contract
|
||
└── Task 2 panel SSH-key slice
|
||
└── Task 3 prerequisites and diagnostics
|
||
|
||
Task 4 Rust dependency/security decision
|
||
└── Task 5 protocol and signing
|
||
└── Task 6 epoch keystore
|
||
├── Task 7 nonce FIFO loading
|
||
└── Task 8 approvals and grants (also depends on Task 5)
|
||
└── Task 9 companion lifecycle (also depends on Task 7)
|
||
|
||
Tasks 3 + 9
|
||
└── Task 10 QML supervision
|
||
├── Task 11 opt-in setup
|
||
└── Task 12 shared vault fan-out (also depends on Tasks 1 + 7)
|
||
└── Task 13 vault lifecycle (also depends on Tasks 8 + 9)
|
||
├── Task 14 approval UI
|
||
└── Task 15 public-key projection
|
||
|
||
Task 9
|
||
└── Task 16 reproducible build
|
||
└── Task 17 pull-request gates
|
||
|
||
Tasks 10 + 16
|
||
└── Task 18 bundled-helper validation
|
||
└── Task 19 protected release (also depends on Task 17)
|
||
|
||
Tasks 3 + 11 + 14 + 15 + 19
|
||
└── Task 20 documentation and release validation
|
||
```
|
||
|
||
## Task List
|
||
|
||
### Phase 1: Remove the Existing Exposure
|
||
|
||
- [x] Task 1: Introduce the sanitized vault-read contract
|
||
- [x] Task 2: Deliver the public SSH-key panel slice
|
||
- [x] Task 3: Enforce SSH support prerequisites
|
||
|
||
### Checkpoint: Safe Panel Baseline
|
||
|
||
- [ ] QML-facing fixture output contains no SSH private-key marker or unknown
|
||
cipher-type marker.
|
||
- [ ] SSH keys are public-only and cannot reach generic detail/edit paths.
|
||
- [ ] All current JavaScript and QML tests pass; the plugin validates and lints
|
||
at its existing baseline.
|
||
- [ ] Human review approves the security boundary before Rust work continues.
|
||
|
||
### Phase 2: Prove the Headless Agent Core
|
||
|
||
- [x] Task 4: Pin the Rust security foundation
|
||
- [x] Task 5: Implement bounded SSH protocol signing
|
||
- [x] Task 6: Implement the vault-epoch keystore
|
||
|
||
### Checkpoint: Rust Primitives
|
||
|
||
- [x] Ed25519 and both RSA SHA-2 modes pass protocol-vector tests.
|
||
- [x] Lock, malformed input, mismatch, and limit paths fail closed.
|
||
- [x] The dependency/zeroization decision is recorded and reviewed.
|
||
|
||
- [x] Task 7: Implement nonce-framed FIFO loading
|
||
- [x] Task 8: Authorize signatures with bounded grants
|
||
- [x] Task 9: Complete the supervised companion lifecycle
|
||
|
||
### Checkpoint: Headless Companion
|
||
|
||
- [ ] Disposable-key tests cover identities, signing, multiple clients,
|
||
approval, grants, lock races, and FIFO rejection.
|
||
- [ ] The helper serves only its private stable socket, has no vault credential,
|
||
spawns no process, and exits on control EOF.
|
||
- [ ] Manual headless authentication and Git SSH-signing smoke tests pass.
|
||
- [ ] Human review approves proceeding to panel integration.
|
||
|
||
### Phase 3: Integrate the Optional Feature
|
||
|
||
- [x] Task 10: Establish companion supervision
|
||
- [x] Task 11: Deliver opt-in session setup
|
||
- [x] Task 12: Feed the companion from the shared vault read
|
||
|
||
### Checkpoint: Optional Data Plane
|
||
|
||
- [x] Disabled mode starts no helper and creates no socket, FIFO, or private-key
|
||
branch.
|
||
- [x] Enabled mode loads keys from the same vault read without delaying or
|
||
breaking the ordinary list on helper failure.
|
||
- [x] QML remains responsive during blocked SSH clients and helper events.
|
||
|
||
- [x] Task 13: Enforce vault lifecycle transitions
|
||
- [x] Task 14: Deliver signing authorization UX
|
||
- [x] Task 15: Project validated public-key files
|
||
|
||
### Checkpoint: End-to-End Feature
|
||
|
||
- [x] Lock, logout, account change, suspend, screen lock, disable, crash, and
|
||
Quickshell reload all satisfy the state machine.
|
||
- [x] Authentication, commit signing, and multi-commit signing work with no
|
||
private key on disk.
|
||
- [x] Public projections survive lock but clear on logout/account change/disable.
|
||
- [ ] Human security and usability review approves packaging.
|
||
|
||
### Phase 4: Establish the Artifact Trust Path
|
||
|
||
- [x] Task 16: Make the release build reproducible
|
||
- [x] Task 17: Add read-only pull-request gates
|
||
|
||
### Checkpoint: Candidate Artifact
|
||
|
||
- [x] Two clean pinned builds produce identical stripped bytes.
|
||
- [x] Existing, Rust, audit, license, and end-to-end gates pass with no PR
|
||
secrets or write token.
|
||
|
||
- [x] Task 18: Validate the bundled helper at launch
|
||
- [~] Task 19: Protect release provenance -- workflow, environment and tests
|
||
are in place; the first protected tag run is still outstanding
|
||
|
||
### Checkpoint: Shippable Artifact
|
||
|
||
- [ ] The tracked helper is executable, native-tested, checksum-consistent,
|
||
protocol-compatible, and byte-identical to the protected build.
|
||
- [ ] A release produces provenance attestation, SBOM, and dependency/license
|
||
report with narrowly scoped permissions.
|
||
|
||
### Phase 5: Document and Release
|
||
|
||
- [~] Task 20: Complete release documentation -- README, CHANGELOG, manifest
|
||
and the design draft are done; the manual release matrix is not
|
||
|
||
### Checkpoint: Complete
|
||
|
||
- [ ] Every task's acceptance criteria and the project Definition of Done pass.
|
||
- [ ] Setup, conflict, logout/login, upgrade, verification, and uninstall paths
|
||
are documented and manually exercised.
|
||
- [ ] No private key, session token, signed payload, or signature appears in
|
||
QML state, argv, logs, files, CI artifacts, or test diagnostics.
|
||
- [ ] Human review approves merge and release.
|
||
|
||
## Verification Strategy
|
||
|
||
Use focused tests after each task and these full gates at checkpoints:
|
||
|
||
```bash
|
||
for test_file in tests/*.test.js; do node "$test_file" || exit 1; done
|
||
QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tests/qml
|
||
mkdir -p /tmp/qs-imports
|
||
ln -sfn /usr/share/omarchy/shell /tmp/qs-imports/qs
|
||
/usr/lib/qt6/bin/qmllint -I /tmp/qs-imports Panel.qml FormPickerRow.qml
|
||
omarchy plugin validate .
|
||
|
||
cargo fmt --manifest-path agent/Cargo.toml --check
|
||
cargo clippy --manifest-path agent/Cargo.toml --locked --all-targets -- -D warnings
|
||
cargo test --manifest-path agent/Cargo.toml --locked --all-targets
|
||
cargo deny --manifest-path agent/Cargo.toml check
|
||
```
|
||
|
||
The final runtime matrix must include a disposable fixture vault, a fake panel
|
||
controller, real OpenSSH clients, Git authentication, Git SSH signing, lock
|
||
during every sensitive transition, helper crash/reload, and both unlock-on-
|
||
demand modes. CI must never use a real vault or credential.
|
||
|
||
## Parallelization Opportunities
|
||
|
||
- After Task 3, the panel baseline can remain stable while Tasks 4–9 build the
|
||
headless Rust core; do not parallelize work that changes the sanitized read
|
||
contract until Task 1 is merged.
|
||
- After Task 9, Task 16's reproducible-build work can run alongside Tasks
|
||
10–11 because it consumes the frozen helper interface rather than QML state.
|
||
- After Task 13, Tasks 14 and 15 can proceed independently if Task 4 has fixed
|
||
the public-export owner and the control protocol is frozen.
|
||
- Tasks 17 and 18 may proceed in parallel after Task 16, but Task 19 waits for
|
||
both so release policy matches launch-time validation.
|
||
|
||
## Risks and Mitigations
|
||
|
||
| Risk | Impact | Mitigation |
|
||
|---|---|---|
|
||
| QML receives private SSH material before sanitization | High | Make Tasks 1–3 a standalone prerequisite gate with marker-based real-pipeline tests. |
|
||
| Selected Rust key types retain secret clones after lock | High | Fail the Task 4/6 spike if parsed private components cannot be bounded and best-effort-zeroized. |
|
||
| A lock races a load, approval, grant, or signature | High | Use vault epochs, an atomic deny linearization point, candidate keystores, bounded acknowledgments, and kill-on-timeout. |
|
||
| Same-UID FIFO injection replaces the key set | High | Require a fresh 128-bit load nonce delivered only over the private control pipe and reject duplicate/stale payloads. |
|
||
| A slow or broken agent branch stalls the core vault list | High | Bound input/output/time, drain eagerly, supervise one process group, and retry the list once without the optional branch. |
|
||
| Process attribution creates false security confidence | Medium | Enforce peer UID; present PID/path/start-time as context only; scope grants narrowly and revalidate at sign time. |
|
||
| UWSM setup overwrites another agent configuration | High | Use one plugin-owned atomic file, refuse symlinks/unexpected contents, show conflicts, and require confirmation plus re-login. |
|
||
| Bundled binary drifts from source | High | Pin every build input and compare clean release bytes byte-for-byte before merge/release. |
|
||
| Fork PR artifact policy becomes impossible to satisfy | Medium | Upload read-only candidates for source-only fork PRs; require the matching bytes before merge to `master`, not inside the untrusted fork job. |
|
||
| Older/self-hosted servers expose partial SSH support | Medium | Enforce the CLI floor, report capability as unconfirmed when appropriate, and document the 2026.8.0 malformed-item fix. |
|
||
| Feature complexity degrades the ordinary vault | High | Keep disabled mode inert, isolate helper failure, checkpoint every 2–3 tasks, and run the full existing regression suite throughout. |
|
||
|
||
## Open Questions for Human Review
|
||
|
||
- **Public export scope:** Revision 2 says both that public-key export is
|
||
required in v1 and that it remains later work. This plan follows the stronger
|
||
end-to-end requirement and includes it in v1. Confirm that choice.
|
||
- **Public export owner:** This plan recommends the companion write the
|
||
projection from its validated public keystore, using an absolute data path
|
||
supplied at launch. Task 4 must record the final owner and its path/cleanup
|
||
contract before implementation.
|
||
- **Dependency outcome:** The Rust crate set, Bitwarden v2 agent status, RFC
|
||
status, zeroization behavior, and license/audit surface must be re-verified
|
||
from current primary sources during Task 4; the design draft's review date is
|
||
not sufficient evidence.
|
||
- **Release governance:** *Resolved during Task 19.* The protected environment
|
||
is `release`; its required reviewer is `@Elevate08`, with self-review
|
||
permitted because this repository has one maintainer -- the gate exists so
|
||
that write, OIDC and attestation credentials never come into existence
|
||
without a deliberate human click, not to simulate a second pair of eyes that
|
||
does not exist. Deployments are restricted to `v*` tags. CODEOWNERS names
|
||
`@Elevate08` throughout, and calls out `/.github/workflows/release.yml`
|
||
separately as the only workflow that can write, mint a token, or sign.
|
||
|
||
## Follow-up Plan: Centered SSH Approval Popup
|
||
|
||
The approved follow-up specification is
|
||
`docs/ideas/ssh-approval-popup.md`. Add a disabled-by-default presentation
|
||
choice without changing the companion protocol or authorization rules.
|
||
|
||
### Dependency order
|
||
|
||
```text
|
||
setting contract
|
||
-> centered approval surface
|
||
-> locked-vault unlock surface
|
||
-> documentation and full verification
|
||
```
|
||
|
||
### Architecture decisions
|
||
|
||
- Keep request and credential state in `Panel.qml`; popup components are views
|
||
that call the same approve, deny, PIN, fingerprint, and password functions.
|
||
- Use a full-output transparent `PanelWindow`, `ExclusionMode.Ignore`, the
|
||
Wayland overlay layer, and a brief Exclusive-to-OnDemand focus prime,
|
||
matching Omarchy's centered transient surfaces.
|
||
- Reuse `SshApprovalScreen.qml` between both presentation modes so the key,
|
||
process, grant, loading, and deadline semantics cannot drift.
|
||
- Keep account login in the full panel. The centered prompt handles only the
|
||
signed-in locked/unlocked states involved in SSH requests.
|
||
|
||
### Risks and mitigations
|
||
|
||
| Risk | Mitigation |
|
||
|---|---|
|
||
| Popup accidentally becomes a second authorization authority | It only calls existing `Panel.qml` request functions; helper checks remain unchanged. |
|
||
| PIN/fingerprint results are discarded because the anchored panel is closed | Define one transient-auth-surface predicate used by all unlock acceptance checks. |
|
||
| The invisible experience leaves a password or pending process behind | Dismissal denies the request and runs popup-specific credential/prewarm cleanup. |
|
||
| A request label injects markup | Every request-derived `Text` remains `Text.PlainText`. |
|
||
| Overlay traps input after the request ends | Visibility and Wayland keyboard focus bind directly to the live pending request. |
|
||
|
||
---
|
||
|
||
# Implementation Plan: Colorized Menu-Bar Icon
|
||
|
||
## Overview
|
||
|
||
Add an opt-in `colorizeIcon` boolean setting to the Bitwarden panel. When
|
||
enabled, the primary menu-bar shield follows Omarchy's live `Color.accent`
|
||
theme color; when disabled, it keeps the current bar foreground color. The
|
||
locked padlock, missing-dependency badge, and urgent/error indicators remain
|
||
unchanged so color personalization does not erase status meaning.
|
||
|
||
The existing settings renderer already turns boolean schema entries into
|
||
keyboard-operable toggle rows. No custom color picker, palette model, new
|
||
dependency, or arbitrary color persistence is required.
|
||
|
||
## Architecture Decisions
|
||
|
||
- Store only `colorizeIcon: boolean`; derive the actual color from
|
||
`Color.accent` at render time so theme changes are inherited automatically.
|
||
- Default the setting to `false` so existing installations retain their current
|
||
appearance.
|
||
- Apply the setting only to the primary shield glyph in `shieldIconComp`.
|
||
Leave the padlock and setup/error badges on their existing bindings.
|
||
- Put the row in the General settings group, using the existing schema-driven
|
||
toggle and `omarchy bar set` persistence path.
|
||
- Treat malformed external values as `false`, using the existing strict
|
||
boolean-setting behavior.
|
||
|
||
## Dependency Graph
|
||
|
||
```text
|
||
manifest default/schema + model schema
|
||
│
|
||
├── settings persistence/value lookup tests
|
||
│
|
||
└── Panel color binding + schema-driven General toggle
|
||
│
|
||
└── QML/static checks + runtime/theme verification
|
||
```
|
||
|
||
## Task List
|
||
|
||
### Phase 1: Settings Contract
|
||
|
||
- [x] Task 1: Add the colorization setting contract
|
||
|
||
### Checkpoint: Settings Contract
|
||
|
||
- [x] `colorizeIcon` exists in both `manifest.json` and `BitwardenModel.js`
|
||
with boolean type and a `false` default.
|
||
- [x] Valid booleans round-trip through the existing writer, while malformed
|
||
values resolve to `false`.
|
||
- [x] Focused settings/model tests pass before UI wiring begins.
|
||
|
||
### Phase 2: Vertical UI Slice
|
||
|
||
- [x] Task 2: Wire the theme-accent shield and General toggle
|
||
|
||
### Checkpoint: Colorized Icon Behavior
|
||
|
||
- [x] With the setting off, the primary shield still uses the bar foreground.
|
||
- [x] With the setting on, the primary shield uses `Color.accent`.
|
||
- [x] Lock/setup/error badges retain their existing foreground or urgent colors.
|
||
- [x] The settings row is keyboard-operable and persists through the existing
|
||
shell reload path.
|
||
|
||
### Phase 3: Verification and Documentation
|
||
|
||
- [~] Task 3: Add regression coverage and document the setting
|
||
|
||
### Checkpoint: Complete
|
||
|
||
- [x] Focused and full JavaScript tests pass.
|
||
- [x] QML tests/lint and plugin validation pass where the Omarchy environment is
|
||
available.
|
||
- [ ] Manual verification covers both toggle states, a theme accent change,
|
||
locked state, setup-required state, and an error/urgent state.
|
||
- [x] README or user-facing feature documentation explains the toggle and its
|
||
theme-derived behavior.
|
||
- [ ] Human review approves the implementation before merge; no commit or push
|
||
is performed automatically.
|
||
|
||
## Task Details
|
||
|
||
### Task 1: Add the colorization setting contract
|
||
|
||
**Description:** Register `colorizeIcon` as a General boolean setting in the
|
||
manifest and model schema, expose its default through the widget defaults, and
|
||
ensure the existing settings value/read/write paths recognize it without
|
||
special-case persistence code.
|
||
|
||
**Acceptance criteria:**
|
||
|
||
- [x] The manifest and model declare the same `colorizeIcon` key, boolean type,
|
||
label, description, and `false` default.
|
||
- [x] `boolSetting("colorizeIcon", true)` returns true; malformed values such
|
||
as strings and numbers fall back to false.
|
||
- [x] The setting is grouped under General and included in visible settings
|
||
without changing existing group ordering or setting semantics.
|
||
|
||
**Verification:**
|
||
|
||
- [x] Tests pass: `node tests/setup-settings.test.js`
|
||
- [x] Tests pass: `node tests/lock-state.test.js`
|
||
- [x] Static check confirms manifest/model schema parity.
|
||
|
||
**Dependencies:** None
|
||
|
||
**Files likely touched:**
|
||
|
||
- `manifest.json`
|
||
- `BitwardenModel.js`
|
||
- `tests/setup-settings.test.js`
|
||
- `tests/lock-state.test.js`
|
||
|
||
**Estimated scope:** Medium: 3–4 files
|
||
|
||
### Task 2: Wire the theme-accent shield and General toggle
|
||
|
||
**Description:** Add the root property that reads `colorizeIcon`, use it only
|
||
for the primary shield glyph in the menu-bar icon component, and rely on the
|
||
existing schema-driven settings delegate to render and persist the toggle.
|
||
Add focused static assertions for the binding and for the unchanged status
|
||
badge color paths.
|
||
|
||
**Acceptance criteria:**
|
||
|
||
- [x] `colorizeIcon` is read from the live setting with a false-safe default.
|
||
- [x] The primary shield resolves to `Color.accent` when enabled and the
|
||
existing bar foreground when disabled.
|
||
- [x] The padlock, missing-tool badge, and urgent/error color bindings are not
|
||
redirected through the new preference.
|
||
|
||
**Verification:**
|
||
|
||
- [x] Tests pass: `node tests/settings-screen.test.js`
|
||
- [x] Focused source assertions pass for shield color precedence and badge
|
||
independence.
|
||
- [x] QML lint passes for `Panel.qml` with the repository's Omarchy import path.
|
||
|
||
**Dependencies:** Task 1
|
||
|
||
**Files likely touched:**
|
||
|
||
- `Panel.qml`
|
||
- `tests/settings-screen.test.js`
|
||
|
||
**Estimated scope:** Small: 1–2 files
|
||
|
||
### Task 3: Add regression coverage and document the setting
|
||
|
||
**Description:** Complete the feature's regression matrix and user-facing
|
||
documentation. Verify that persistence survives the shell reload path and that
|
||
the selected accent is inherited from the active theme while status indicators
|
||
remain recognizable.
|
||
|
||
**Acceptance criteria:**
|
||
|
||
- [x] Tests cover default-off behavior, enabling/disabling through the settings
|
||
path, malformed persisted values, and preservation of badge colors.
|
||
- [x] User-facing documentation says the toggle follows the active Omarchy
|
||
theme accent and does not offer arbitrary color selection.
|
||
- [x] No unrelated panel colors or status semantics change.
|
||
|
||
**Verification:**
|
||
|
||
- [x] Full JavaScript suite passes:
|
||
`for test_file in tests/*.test.js; do node "$test_file" || exit 1; done`
|
||
- [x] QML suite passes:
|
||
`env -u DISPLAY -u WAYLAND_DISPLAY -u QT_QPA_PLATFORMTHEME QML_XHR_ALLOW_FILE_READ=1 QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tests/qml`
|
||
- [x] Plugin validation passes: `omarchy plugin validate .`
|
||
- [ ] Manual runtime check confirms both toggle states and theme-derived color
|
||
behavior when the desktop environment is available.
|
||
|
||
**Dependencies:** Task 2
|
||
|
||
**Files likely touched:**
|
||
|
||
- `README.md` or the relevant feature documentation
|
||
- `tests/setup-settings.test.js`
|
||
- `tests/settings-screen.test.js`
|
||
- `tests/lock-state.test.js`
|
||
|
||
**Estimated scope:** Medium: 3–4 files
|
||
|
||
## Risks and Mitigations
|
||
|
||
| Risk | Impact | Mitigation |
|
||
|---|---|---|
|
||
| Accent color has poor contrast on a supported theme | Medium | Check light/dark themes manually; retain the existing bar foreground as the safe default and do not alter urgent badges. |
|
||
| The setting is added to one schema but not the other | Medium | Keep manifest/model parity assertions in the focused settings tests. |
|
||
| The setting accidentally recolors status badges | High | Limit the binding change to the primary shield `Text` and assert badge bindings remain independent. |
|
||
| Shell reload does not refresh the bar icon immediately | Low | Verify the existing `omarchy bar set` hot-reload behavior; do not add a second persistence mechanism. |
|
||
|
||
## Open Questions
|
||
|
||
- Confirm final user-facing label: **Colorize menu-bar icon** versus **Use
|
||
theme accent for icon**. The plan assumes the former.
|
||
- Confirm which user-facing documentation file should receive the short setting
|
||
note; `README.md` is the default unless project conventions prefer
|
||
`docs/features.md`.
|