docs: Add specs and tasks for previously scoped work in todo.md
This commit is contained in:
76
.config/dot/.claude/spec/dot-kde.md
Normal file
76
.config/dot/.claude/spec/dot-kde.md
Normal file
@@ -0,0 +1,76 @@
|
||||
## Problem Statement
|
||||
|
||||
Several KDE settings on this machine have already been changed by hand away from their KDE/CachyOS defaults — the caps-lock/Escape swap is live right now, and screenshot-related keybind changes (Spectacle bindings, moving Lock Session off `Meta+L`) are planned next — but none of this is tracked anywhere in the dotfiles repo. If the machine were rebuilt today, these settings would silently revert to defaults with no record of what needs to be reapplied. There's also no way to notice *unexpected* drift (a setting that changed without the owner deliberately choosing to change it), and no tooling to bring a manually-tweaked setting under tracking without hand-writing one-off `kwriteconfig6`/D-Bus calls — exactly the accumulation of ad hoc scripts the dotfiles project has otherwise avoided.
|
||||
|
||||
## Solution
|
||||
|
||||
Add a `dot kde` subcommand family with three verbs:
|
||||
|
||||
- **`dot kde apply`** — pushes every setting declared in a tracked manifest onto the live KDE session (repo → system).
|
||||
- **`dot kde diff`** — a broad, read-only scan reporting every live KDE setting that differs from its default, tagging each mismatch as either already-declared (in the manifest) or undeclared (system → discovery, no write).
|
||||
- **`dot kde save`** — the write path into the manifest (system → repo). Run with no arguments, it refreshes every already-declared entry's stored value from the live system. Run with explicit coordinates, it begins tracking one new setting, seeded from its current live value.
|
||||
|
||||
The manifest is a single flat, mechanism-agnostic file: opaque `identifier=value` lines. `dot kde` internally figures out *how* to read/write a given identifier (three different underlying mechanisms exist across KDE's config surface), so the manifest itself never needs to know or care how KDE happens to store that particular setting.
|
||||
|
||||
## User Stories
|
||||
|
||||
1. As the machine owner, I want to declare that a KDE setting should have a specific value, so that a freshly-built machine ends up with the same intentional deviations from KDE's defaults without me re-discovering and re-typing the underlying `kwriteconfig6`/D-Bus incantations.
|
||||
2. As the machine owner, I want `dot kde apply` to push all declared settings onto a live session in one idempotent command, so that re-running it after a KDE update or on a new machine is safe and has no unintended side effects.
|
||||
3. As the machine owner, I want `dot kde diff` to show me every KDE setting currently different from default, so that I can catch drift I didn't intend, not just check the handful of settings I already know about.
|
||||
4. As the machine owner, I want `dot kde diff`'s output to distinguish "this is already declared and intentional" from "this is undeclared and I've never seen it before," so that the noise of broad scanning doesn't bury genuinely unexpected changes.
|
||||
5. As the machine owner, I want to run `dot kde save` with no arguments and have every already-tracked setting's manifest value refreshed from whatever is currently live, so that if I tweak a tracked setting by hand (e.g. change a keybind in System Settings) the manifest catches up without me re-typing its identifier.
|
||||
6. As the machine owner, I want to run `dot kde save` with an explicit identifier to begin tracking one specific setting I just noticed via `diff`, so that I control exactly what enters the manifest instead of everything non-default being swept in at once.
|
||||
7. As the machine owner, I want global keyboard shortcuts to be read and written through KDE's own shortcut-management service rather than by hand-editing `kglobalshortcutsrc`, so that changes take effect immediately in the running session and I never have to reconstruct KDE's internal triplet bookkeeping (current/default/friendly-name) myself.
|
||||
8. As the machine owner, I want KConfigXT-schema-backed settings to have their "default" value discovered automatically wherever KDE's schema declares it, so that broad drift-scanning covers as much of the KDE config surface as possible without me manually cataloguing every setting I might ever care about.
|
||||
9. As a future contributor to this dotfiles repo, I want `dot kde`'s subcommand files to live alongside its Python helper in one place, discoverable the same way every other `dot` subcommand is, so that adding this feature doesn't require bespoke wiring outside the established convention.
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
- **Subcommand family**: `dot kde apply` / `dot kde diff` / `dot kde save`, following the project's existing nested-subcommand dispatch convention (each level checks for `help` as its first positional argument before `argparse`, calling its own usage function).
|
||||
- **`dot kde save` has two modes**:
|
||||
- No arguments: iterate every identifier already in the manifest, read its current live value via the appropriate mechanism, and rewrite the manifest with the refreshed value.
|
||||
- Explicit coordinates given: read the current live value for that one setting and add it to the manifest as a new declared entry. This is the only way new entries enter the manifest — there is no bulk/"track everything currently non-default" mode, by design, so that curation stays deliberate.
|
||||
- **`dot kde diff`**: enumerates every setting it knows how to check (see mechanisms below), compares live vs. default, and reports every mismatch. Each reported mismatch is tagged as declared (present in the manifest, i.e. an intentional, already-tracked deviation) or undeclared (never explicitly declared). Diff never writes anything.
|
||||
- **Manifest**:
|
||||
- Location: a flat file directly under `~/.config/dot/` (not nested in a subdirectory — no near-term plan for multiple KDE-like targets that would justify one), named to convey "the set of KDE settings intentionally different from default."
|
||||
- Format: plain text, one entry per line, `identifier=value`, split on the *first* `=` only (so values may themselves contain `=`).
|
||||
- Identifier scheme: `file.group.key`, split on the first two `.`s only (so the key portion may contain further dots, spaces, or other characters freely — relevant for `kglobalshortcutsrc` action names, which can contain spaces).
|
||||
- The manifest carries no mechanism/type discriminator field. It is a pure `identifier → value` map; `dot kde` decides internally how to resolve a given identifier.
|
||||
- **Three underlying mechanisms**, dispatched purely by inspecting the identifier (no stored metadata):
|
||||
1. **Shortcuts** (`kglobalshortcutsrc.<componentUnique>.<actionUnique>`) — resolved not by editing the rc file directly, but through KDE's `kglobalaccel` D-Bus service:
|
||||
- Read current value: `shortcut(actionId)`.
|
||||
- Read default value: `defaultShortcut(actionId)`.
|
||||
- Write: `setShortcut(actionId, keys, flags)` with `flags = NoAutoloading` (so the declared value always wins over any previously-saved shortcut; using the `Autoloading` flag would make `apply` a no-op after the first run).
|
||||
- `actionId` is a 4-element list: `[componentUniqueName, actionUniqueName, componentFriendlyName, actionFriendlyName]` (confirmed against KDE's own `actionIdFields` enum and verified live via `gdbus`). Only `componentUnique`/`actionUnique` are stored in the manifest; the two friendly-name fields (needed to actually place the D-Bus call) are resolved dynamically at call time by looking up the component's shortcut list, not stored.
|
||||
- No read-modify-write is needed for this mechanism — `setShortcut` only ever touches the live/current value, never the default, so there's no risk of clobbering KDE's own bookkeeping.
|
||||
2. **KConfigXT schema-backed settings** (most `kwinrc`, `kdeglobals`, etc. entries) — read/write via `kreadconfig6`/`kwriteconfig6`; the "default" value comes from the setting's `.kcfg` schema.
|
||||
- The `(rcfile → [kcfg files])` mapping table is auto-derived at runtime by scanning the system's `.kcfg` schema directory for files that statically declare their target rc file (`<kcfgfile name="...">`), plus a small hand-maintained list for the exceptions that declare `<kcfgfile arg="true">` (i.e. the target file is only known at runtime by the owning app, not in the schema — `kwin.kcfg` is a known example).
|
||||
- This mechanism is what enables `diff`'s broad-scan coverage: every entry reachable through the mapping table can be checked automatically, not just entries someone has already thought to add to the manifest.
|
||||
3. **Freeform/schema-less settings** (e.g. `kxkbrc`'s `Options=` line) — read/write via `kreadconfig6`/`kwriteconfig6`; there is no schema, so "default" is defined as "the key is absent." Because there's no schema to enumerate from, this mechanism cannot participate in broad undeclared-drift discovery the way schema-backed settings can — it can only be checked for settings that are already declared in the manifest.
|
||||
- Mechanism selection for a given identifier: if the rc file is `kglobalshortcutsrc`, use the shortcuts mechanism; otherwise, if the mapping table resolves the `(rcfile, group, key)` to a schema, use the schema-backed mechanism; otherwise, treat it as freeform.
|
||||
- **File/module layout**: the fish dispatcher and its Python helper live together in one subdirectory under the project's existing commands location, rather than the Python helper sitting as a same-directory sibling of a same-named fish file at the top level.
|
||||
- **Cross-cutting change to `dot` itself**: the subcommand-discovery mechanism (used both for help-listing and for dispatch) is extended to glob one additional directory level deep, not just the flat top level — required to support the subcommand-plus-helper layout above. This must be updated in both places the discovery logic currently exists (they are intentionally duplicated today rather than shared, for fish-autoload reasons), and applies to any future subcommand that wants a companion file, not just this one.
|
||||
|
||||
## Testing Decisions
|
||||
|
||||
- **Guiding principle**: tests should exercise this feature's own logic (manifest parsing, identifier dispatch, mapping-table auto-derivation, mechanism selection), not re-verify that external dependencies (`kreadconfig6`, `kwriteconfig6`, the KDE session itself) work correctly.
|
||||
- **Primary seam**: full CLI invocation of `dot kde apply` / `dot kde diff` / `dot kde save`, run against a scratch `$HOME`, mirroring the existing project convention for testing `dot` subcommands (override `$HOME` per test case, no mocking of the real `kreadconfig6`/`kwriteconfig6` binaries — they run for real against fixture rc files under the scratch home). This covers the schema-backed and freeform mechanisms end-to-end: manifest read/write, identifier parsing, mechanism dispatch, and mapping-table-driven default lookup.
|
||||
- **New seam introduced for this feature**: the KConfigXT schema directory is normally a fixed system path outside `$HOME`. To make the auto-derivation logic testable without depending on (or mutating) the real system's schema files, the schema directory location must be overridable (e.g. via an environment variable), defaulting to the real system path in normal use and pointing at a small fixture directory of synthetic `.kcfg` files in tests.
|
||||
- **Deliberately not covered by automated tests**: the shortcuts mechanism (`kglobalaccel` D-Bus calls). It depends on a live, already-running session service that isn't practically substitutable without building dedicated mock infrastructure, which is disproportionate to what it would protect (three D-Bus calls). This path is verified manually against the real session instead.
|
||||
- **Prior art**: the existing test suite for `dot`'s other subcommands already establishes the scratch-`$HOME`-plus-`fishtape` pattern this feature reuses.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- A `dot setup`-style subcommand for machine bootstrap tasks (extra groups, etc.) — considered during planning and set aside as not currently relevant.
|
||||
- Folder naming / XDG user-dirs conventions — a real, separate piece of planned work, but standalone from `dot kde` and not part of this spec.
|
||||
- Tracking Plasma's panel layout (`plasma-org.kde.plasma.desktop-appletsrc`) — previously decided this doesn't need tracking, since the current panel is CachyOS's own shipped default and reproduces automatically on a fresh install.
|
||||
- An "empirical fallback" mechanism (spinning up a scratch config environment to let an app generate its own default config for diffing) — not needed given the three mechanisms above cover everything currently in scope; noted only as a possible future extension if some setting fits none of them.
|
||||
- A bulk/`--all` mode for `dot kde save` — deliberately excluded so that every new manifest entry is a deliberate choice.
|
||||
- Interactive picker UX for `diff`/`save` (e.g. selecting an undeclared entry from a list rather than typing its identifier) — not part of this spec.
|
||||
- `dot voice` (hands-free dictation) — an unrelated, separately shelved piece of work, not touched by this feature.
|
||||
|
||||
## Further Notes
|
||||
|
||||
- The caps-lock/Escape swap (`kxkbrc`'s `Options=caps:escape_shifted_capslock`) is already live on this machine by hand, unrecorded anywhere — it's a ready-made first real candidate for the explicit-coordinates form of `dot kde save` once built, and a natural first end-to-end smoke test beyond the automated suite.
|
||||
- The screenshot-related keybind work (Spectacle bindings, moving Lock Session off `Meta+L` to `Meta+X`, renaming Spectacle's save folder) was the original motivating case for this feature but is applied *through* `dot kde apply`/`save` rather than being separate work — once `dot kde` exists, those keybind changes are just manifest entries.
|
||||
- Per the project's own cross-cutting convention, once any keybind changes are actually applied via this feature, the corresponding rows in the project's keybindings reference document need to be added/updated in the same change.
|
||||
59
.config/dot/.claude/spec/dot-setup-folders.md
Normal file
59
.config/dot/.claude/spec/dot-setup-folders.md
Normal file
@@ -0,0 +1,59 @@
|
||||
## Problem Statement
|
||||
|
||||
The old `~/wrk/dotfiles` repo's `setup_folders` (part of its bash `bin/dot init`) renamed the standard XDG user folders to short names (`Documents→doc`, `Downloads→dwn`, etc.) for better fish shell-completion ergonomics — shorter shared prefixes are easier to disambiguate by typing fewer characters. That behavior has no equivalent in the new bare-repo `dot` CLI. Right now this machine's `user-dirs.dirs` is untracked and has drifted from even the old convention: it uses the full XDG default names, plus an ad hoc `XDG_PROJECTS_DIR=$HOME/Projects` line that never existed in the old repo at all. If this machine were rebuilt today, none of the short-name convention would be restored, and the current drifted state isn't recorded anywhere.
|
||||
|
||||
## Solution
|
||||
|
||||
Add a `folders` task to a new `dot setup` subcommand family (the general home for idempotent, re-runnable machine-setup tasks, as opposed to `dot init`'s one-shot bootstrap). `dot setup folders` brings the 8 standard XDG user directories under the project's short-name convention, tracks the resulting `user-dirs.dirs` directly as a plain dotfile, and safely migrates any content sitting in the old, full-named folders into their short-named replacements.
|
||||
|
||||
`~/wrk` (already in active use, e.g. `~/wrk/dotfiles`) replaces the old `Projects`-style folder as the general working-files location, but is treated as a plain convention-only directory, not a tracked XDG category.
|
||||
|
||||
## User Stories
|
||||
|
||||
1. As the machine owner, I want the standard XDG user folders renamed to short names (`doc`, `dwn`, `mus`, `pic`, `vid`, `.desktop`), so that fish-completion on my home directory has shorter, easier-to-disambiguate shared prefixes than the full XDG default names.
|
||||
2. As the machine owner, I want `Templates` and `Public` (both unused) collapsed into a single hidden `.ignoreme` folder, so that apps respecting `XDG_TEMPLATES_DIR`/`XDG_PUBLICSHARE_DIR` don't scatter files directly into `$HOME`, without needing two separate unused folders.
|
||||
3. As the machine owner, I want the nested `Pictures/Screenshots` folder lowercased to `pic/screenshots` in the same pass as the `Pictures→pic` rename, so that the screenshot folder matches the rest of the short-folder naming convention without a separate migration step.
|
||||
4. As the machine owner, I want `~/wrk` to have no XDG variable pointing at it, so that a non-standard, barely-recognized XDG extension (`XDG_PROJECTS_DIR`) doesn't get tracked for a directory that already works fine as a plain convention.
|
||||
5. As the machine owner, I want `user-dirs.dirs` tracked directly in the bare dotfiles repo like any other plain dotfile, so that the desired short names are recorded and restorable on a fresh machine without needing a code-generation step.
|
||||
6. As the machine owner, I want `dot setup folders` to migrate content out of any legacy full-named folder into its short-named replacement automatically when the legacy folder is empty, so that re-running setup on a fresh install requires no manual folder shuffling.
|
||||
7. As the machine owner, I want `dot setup folders` to stop and ask for explicit confirmation before moving anything out of a legacy folder that actually has content in it, so that I never silently lose files to an automated migration I forgot was going to run.
|
||||
8. As the machine owner, I want confirmation to be satisfiable via a `--yes` flag rather than an interactive prompt, so that the same command works identically whether I'm running it by hand or from an automated/tested context.
|
||||
9. As the machine owner, I want a filename collision between a legacy folder and an already-populated short-named target to never be silently overwritten, so that re-running the migration after a partial/interrupted prior run can't destroy a file just because both sides happen to have a same-named entry.
|
||||
10. As the machine owner, I want to be told which files were skipped due to a collision and have the legacy folder left in place when that happens, so that I have a clear, actionable signal that something needs manual attention instead of silent partial data loss.
|
||||
11. As the machine owner, I want `dot setup folders` to notify running apps of the directory changes via `xdg-user-dirs-update` after migrating, so that session-long apps pick up the new paths without requiring a full logout/login.
|
||||
12. As the machine owner, I want to run `dot setup` with no arguments to perform every machine-setup task (folders plus future ones like extra groups) in one command, so that setting up a fresh machine doesn't require remembering and running each task individually.
|
||||
13. As the machine owner, I want to also be able to run `dot setup folders` on its own, so that I can re-run just this one task in isolation (e.g. after a confirmation was declined) without re-running unrelated setup tasks.
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
- **Subcommand family**: `dot setup`, following the project's existing nested-subcommand dispatch convention (`help`-then-`argparse`, `_dot_<name>_usage`). Bare `dot setup` (no arguments) runs every machine-setup task unconditionally (folders, plus future tasks such as extra groups, mirroring the old bash `bin/dot init`'s dual-mode: no-args ran everything, an explicit keyword ran just one task). `dot setup folders` runs just the folders task.
|
||||
- **Folder mapping** (identical to the old repo's `setup_folders`, no changes): `Desktop→.desktop`, `Documents→doc`, `Downloads→dwn`, `Music→mus`, `Pictures→pic`, `Videos→vid`, `Templates→.ignoreme`, `Public→.ignoreme`. `Templates` and `Public` both point at the *same* `.ignoreme` folder, as before.
|
||||
- **Nested screenshots rename**: as part of the same `Pictures→pic` migration pass, the nested `Screenshots` folder (currently created empty by KDE/Spectacle defaults) is renamed to lowercase `screenshots`, so the result is `pic/screenshots`. This is folded into the folders task rather than deferred to the separate Spectacle-keybind work, since it's the same naming-convention concern and falls out for free once `Pictures/*` is moved into `pic/`.
|
||||
- **`wrk` is out of the XDG mapping**: no `XDG_PROJECTS_DIR` (or any other XDG variable) is written for it. It's a plain, convention-only directory. The currently-existing ad hoc `~/Projects` folder (created by this machine's diverged, untracked `user-dirs.dirs`) is left alone — out of scope for the folders task, since it was never one of the 8 standard XDG categories the task manages, and it's empty and harmless.
|
||||
- **`user-dirs.dirs` is tracked directly** as a plain dotfile in the bare repo (not generated/overwritten by `dot setup folders` from a hardcoded table each run) — unlike KDE's rc files (tracked via a separate declarative-manifest mechanism, see the `dot-kde` spec), `user-dirs.dirs` has no volatile/machine-specific fields, so it fits the same direct-tracking treatment as any other plain dotfile (`.bashrc`, etc.). The tracked file is the single source of truth for the desired short names.
|
||||
- **`dot setup folders` still needs a small hardcoded table** mapping each of the 8 standard XDG categories to its legacy default folder name (`Documents`, `Downloads`, etc.) — this is used purely to locate content left behind by a fresh XDG-defaults install and merge it into the already-tracked short-named target; it is not the source of truth for the target names themselves (that's the tracked `user-dirs.dirs`).
|
||||
- **Migration safety, per legacy folder**:
|
||||
- Empty (strict check: any file at all, including dotfiles/metadata like a stray KDE `.directory` file, counts as non-empty) → merge silently, no prompt.
|
||||
- Non-empty → print what would be moved and require an explicit `--yes` flag before proceeding. No interactive prompt.
|
||||
- Collisions (a same-named entry exists in both the legacy folder and its short-named target) → use no-clobber semantics (e.g. `mv -n`) so a colliding file is never silently overwritten; report which files were skipped; leave the legacy folder in place (don't remove it) if any collision occurred, rather than deleting a folder that still holds something that couldn't be merged.
|
||||
- **Post-migration step**: run `xdg-user-dirs-update` (no arguments) once folder moves are complete, to notify running apps/portals via its D-Bus signal. This is safe against the hand-tracked file — `user-dirs.dirs`'s own header documents that local edits are preserved across runs of the tool.
|
||||
|
||||
## Testing Decisions
|
||||
|
||||
- **Guiding principle**: test the folders task's own logic (mapping, empty-vs-non-empty gating, `--yes` behavior, collision handling, idempotency) through the real CLI entry point, not the internals of `mv`/`mkdir` themselves.
|
||||
- **Primary seam**: full CLI invocation of `dot setup folders` (and bare `dot setup`), run against a scratch `$HOME` per test case — the existing project convention (see `dot install`'s tests). No new seam is introduced.
|
||||
- **External command handling**: `xdg-user-dirs-update` is faked out via a `PATH`-prepended fake binary that logs its invocation (and exit code), exactly mirroring how `sudo`/`pacman` are faked for `dot install`'s tests. Real `mkdir`/`mv`/`rmdir` run for real against the scratch `$HOME` — no need to fake filesystem operations themselves.
|
||||
- **Cases to cover**: fresh migration of empty legacy folders (no `--yes` needed); a legacy folder with real content refuses without `--yes` and proceeds with it; the nested `Pictures/Screenshots→pic/screenshots` rename; a stray dotfile (e.g. a fake `.directory`) in an otherwise-"empty" legacy folder still triggers the confirmation gate; a filename collision between legacy and target is skipped (not overwritten), reported, and leaves the legacy folder in place; re-running `dot setup folders` after a clean migration is a no-op (idempotency); bare `dot setup` runs the folders task as part of running everything; `dot setup folders help` prints usage and touches nothing.
|
||||
- **Prior art**: `tests/dot.fish`'s existing scratch-`$HOME`-plus-`fishtape` pattern, and specifically the fake-`sudo`/fake-`pacman`-via-`PATH` technique used for `dot install`.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- The **extra groups** task (`dot setup groups` or similar, porting the old `.extra_groups`/`setup_users` behavior) — it will share the same `dot setup` dispatcher and dual-mode (bare-runs-everything vs. named-task) shape decided here, but its own design (group list format, idempotency, etc.) was not addressed in this spec.
|
||||
- Any KDE-side settings (caps-lock/Escape swap, screenshot keybinds, Lock Session rebind) — covered separately by the `dot-kde` spec/design.
|
||||
- Removing the currently-existing, now-orphaned `~/Projects` folder — explicitly left alone, not cleaned up by this feature.
|
||||
- Any `~/.github/README.md` command-table row or `~/.github/keybindings.md` update — not applicable here (no keybind changes), but the README row is still required by the project's standard "adding a subcommand" checklist at implementation time.
|
||||
|
||||
## Further Notes
|
||||
|
||||
- The old bash `setup_folders`'s naive `mv $from/* $to` has a latent bug this design deliberately avoids: an unquoted glob against an empty directory can misbehave, and it has no collision protection at all. The no-clobber-plus-report behavior specified here is a deliberate improvement over the old script's behavior, not a straight port.
|
||||
- This spec covers only the `folders` task; `dot setup` itself (the dispatcher, `_dot_setup_usage`, wiring into `commands/`, the completions/help-glob duplication point noted in the project's `CLAUDE.md`) needs to exist as scaffolding for this task to attach to, even though its only other planned task (extra groups) is out of scope here.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
spec: dot-kde
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
Extend the subcommand-discovery mechanism to glob one directory level
|
||||
deeper, so a `dot` subcommand can live as `commands/<name>/<name>.fish`
|
||||
alongside a companion file (e.g. a Python helper), not just as a flat
|
||||
`commands/<name>.fish`. This mechanism exists in two places today
|
||||
(`dot.fish`'s `__dot_help` and `completions/dot.fish`'s
|
||||
`__dot_custom_subcommands`), intentionally duplicated rather than shared
|
||||
(fish autoload constraints) — both must be updated together and stay in
|
||||
sync. Existing flat-file subcommands must keep working unchanged.
|
||||
|
||||
This is pure prefactoring: no KDE-specific behavior is introduced here.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] `dot help` lists a subcommand that lives at `commands/<name>/<name>.fish`
|
||||
- [ ] `dot <name>` sources and dispatches to `commands/<name>/<name>.fish`'s `_dot_<name>` function
|
||||
- [ ] Tab-completion (`__dot_custom_subcommands`) lists a nested-directory subcommand
|
||||
- [ ] Existing flat-file subcommands (`dot install`) are still discovered and dispatched correctly
|
||||
- [ ] `tests/dot.fish` covers a nested-directory dummy command dispatching correctly, alongside the existing flat-file dispatch case
|
||||
53
.config/dot/.claude/tasks/0001-kde-schema-backed-save.md
Normal file
53
.config/dot/.claude/tasks/0001-kde-schema-backed-save.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
spec: dot-kde
|
||||
blocked-by: 0000-nested-subcommand-discovery
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
Stand up `dot kde` itself: the fish dispatcher plus its Python helper,
|
||||
living together under `commands/kde/` per the nested-subcommand layout
|
||||
from the prior task. Establish the manifest file (flat text file directly
|
||||
under `~/.config/dot/`, one `identifier=value` line each, split on the
|
||||
first `=` only; identifier split on the first two `.`s into
|
||||
`file.group.key`, leaving the key free to contain further dots or spaces).
|
||||
|
||||
Implement the KConfigXT schema-backed mechanism: reads and writes go
|
||||
through `kreadconfig6`/`kwriteconfig6`, and the "default" value for a
|
||||
setting comes from its `.kcfg` schema. Build the `(rcfile → [kcfg files])`
|
||||
mapping table by scanning the system's KConfigXT schema directory for
|
||||
files that statically declare their target rc file
|
||||
(`<kcfgfile name="...">`), plus a small hand-maintained list for the
|
||||
exceptions that only declare their target file at runtime
|
||||
(`<kcfgfile arg="true">` — `kwin.kcfg` is a known example). The schema
|
||||
directory location must be overridable (e.g. via an environment variable),
|
||||
defaulting to the real system path, so tests can point it at a fixture
|
||||
directory of synthetic `.kcfg` files instead.
|
||||
|
||||
Structure identifier resolution as a dispatchable decision (rc file is
|
||||
`kglobalshortcutsrc` → shortcuts; else resolves via the mapping table →
|
||||
schema-backed; else → freeform) even though only the schema-backed branch
|
||||
is implemented yet — later tasks add the other two branches without
|
||||
restructuring this.
|
||||
|
||||
Implement `dot kde save` for schema-backed settings, in both modes:
|
||||
run with no arguments, refresh every already-declared manifest entry's
|
||||
value from the live system; run with an explicit identifier, read its
|
||||
current live value and add it to the manifest as a new declared entry.
|
||||
Add `dot kde help` and `dot kde save help`, following the project's
|
||||
check-for-`help`-before-`argparse` convention at each dispatch level.
|
||||
|
||||
Add README rows for `dot kde help`, `dot kde save <identifier>`, and
|
||||
`dot kde save` (no arguments).
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] `dot kde` and `dot kde save` are discoverable via `dot help` and dispatch correctly
|
||||
- [ ] Manifest parsing splits correctly on the first `=` (values may contain `=`) and the first two `.`s of the identifier (keys may contain dots/spaces)
|
||||
- [ ] The `(rcfile → [kcfg files])` mapping table is derived by scanning a schema directory for `<kcfgfile name="...">`, plus the hand-maintained exceptions list for `arg="true">` schemas
|
||||
- [ ] The schema directory is overridable via an environment variable, defaulting to the real system path
|
||||
- [ ] `dot kde save <identifier>` reads the current live value via `kreadconfig6` and adds a new declared entry to the manifest
|
||||
- [ ] `dot kde save` with no arguments refreshes every already-declared manifest entry's stored value from the live system, leaving undeclared settings untouched
|
||||
- [ ] `dot kde help` and `dot kde save help` print usage without touching the manifest or invoking `kreadconfig6`/`kwriteconfig6`
|
||||
- [ ] Tests run against a scratch `$HOME` and a fixture `.kcfg` schema directory, exercising manifest read/write, identifier parsing, and mapping-table-driven default lookup, per the project's scratch-`$HOME`-plus-`fishtape` convention
|
||||
- [ ] README has rows for `dot kde help`, `dot kde save <identifier>`, and `dot kde save`
|
||||
24
.config/dot/.claude/tasks/0002-kde-schema-backed-apply.md
Normal file
24
.config/dot/.claude/tasks/0002-kde-schema-backed-apply.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
spec: dot-kde
|
||||
blocked-by: 0001-kde-schema-backed-save
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
Implement `dot kde apply` for schema-backed settings: read every entry in
|
||||
the manifest and write its declared value onto the live system via
|
||||
`kwriteconfig6`. Re-running it against an already-applied system must be a
|
||||
no-op with no unintended side effects — this is the idempotence the
|
||||
feature depends on for safe re-runs after a KDE update or on a freshly
|
||||
built machine. Add `dot kde apply help`, following the project's
|
||||
check-for-`help`-before-`argparse` convention.
|
||||
|
||||
Add a README row for `dot kde apply`.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] `dot kde apply` pushes every manifest entry's declared value onto the live system via `kwriteconfig6`
|
||||
- [ ] Re-running `dot kde apply` against a system already matching the manifest changes nothing (idempotent)
|
||||
- [ ] `dot kde apply help` prints usage without writing anything
|
||||
- [ ] Tests run against a scratch `$HOME`, exercising apply over a manifest with schema-backed entries, verifying resulting rc-file contents and idempotence on a second run
|
||||
- [ ] README has a row for `dot kde apply`
|
||||
27
.config/dot/.claude/tasks/0003-kde-schema-backed-diff.md
Normal file
27
.config/dot/.claude/tasks/0003-kde-schema-backed-diff.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
spec: dot-kde
|
||||
blocked-by: 0001-kde-schema-backed-save
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
Implement `dot kde diff`'s broad, read-only scan for schema-backed
|
||||
settings: walk every `(rcfile, group, key)` reachable through the
|
||||
mapping table built in the prior task, compare each live value
|
||||
(`kreadconfig6`) against its schema-declared default, and report every
|
||||
mismatch. Each reported mismatch is tagged as declared (its identifier is
|
||||
present in the manifest — an intentional, already-tracked deviation) or
|
||||
undeclared (never explicitly declared). `diff` never writes anything.
|
||||
Add `dot kde diff help`, following the project's
|
||||
check-for-`help`-before-`argparse` convention.
|
||||
|
||||
Add a README row for `dot kde diff`.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] `dot kde diff` reports every schema-backed setting whose live value differs from its schema-declared default
|
||||
- [ ] Each reported mismatch is tagged declared or undeclared based on manifest presence
|
||||
- [ ] `dot kde diff` makes no writes under any circumstances
|
||||
- [ ] `dot kde diff help` prints usage without scanning
|
||||
- [ ] Tests run against a scratch `$HOME` and fixture `.kcfg` schema directory, covering: a declared mismatch, an undeclared mismatch, and a setting matching its default (not reported)
|
||||
- [ ] README has a row for `dot kde diff`
|
||||
32
.config/dot/.claude/tasks/0004-kde-freeform-mechanism.md
Normal file
32
.config/dot/.claude/tasks/0004-kde-freeform-mechanism.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
spec: dot-kde
|
||||
blocked-by: [0002-kde-schema-backed-apply, 0003-kde-schema-backed-diff]
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
Add the freeform mechanism as a dispatch branch across `save`, `apply`,
|
||||
and `diff`: for settings with no KConfigXT schema (e.g. `kxkbrc`'s
|
||||
`Options=` line), read and write via `kreadconfig6`/`kwriteconfig6`, with
|
||||
"default" defined as "the key is absent" rather than any schema-declared
|
||||
value. In the identifier-resolution decision from the first schema-backed
|
||||
task, this is the fallback branch: an identifier whose `(rcfile, group,
|
||||
key)` doesn't resolve through the mapping table is freeform. Because
|
||||
there's no schema to enumerate, freeform settings can only be checked by
|
||||
`diff` when already declared in the manifest — they never participate in
|
||||
undeclared broad-scan discovery.
|
||||
|
||||
As the real-world validation for this task, bring the machine's live,
|
||||
already-hand-set `kxkbrc` caps-lock/Escape swap
|
||||
(`Options=caps:escape_shifted_capslock`) under tracking via
|
||||
`dot kde save`, and confirm `dot kde apply`/`dot kde diff` behave
|
||||
correctly against it.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] An identifier whose `(rcfile, group, key)` has no schema match is treated as freeform rather than erroring
|
||||
- [ ] `dot kde save <identifier>` and `dot kde save` (refresh) work for freeform entries
|
||||
- [ ] `dot kde apply` writes freeform entries via `kwriteconfig6`, idempotently
|
||||
- [ ] `dot kde diff` reports a freeform mismatch when its identifier is already declared in the manifest, and never surfaces an undeclared freeform setting via broad scan
|
||||
- [ ] Tests run against a scratch `$HOME`, covering freeform save/apply/diff using a fixture rc file with no corresponding schema
|
||||
- [ ] The live `kxkbrc` caps-lock/Escape swap is tracked via `dot kde save` and the manifest committed to the dotfiles repo
|
||||
38
.config/dot/.claude/tasks/0005-kde-shortcuts-mechanism.md
Normal file
38
.config/dot/.claude/tasks/0005-kde-shortcuts-mechanism.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
spec: dot-kde
|
||||
blocked-by: [0002-kde-schema-backed-apply, 0003-kde-schema-backed-diff]
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
Add the shortcuts mechanism as a dispatch branch across `save`, `apply`,
|
||||
and `diff`: identifiers rooted at `kglobalshortcutsrc` are resolved not by
|
||||
editing the rc file directly but through KDE's `kglobalaccel` D-Bus
|
||||
service — `shortcut(actionId)` for the current value, `defaultShortcut
|
||||
(actionId)` for the default, and `setShortcut(actionId, keys, flags)`
|
||||
with `flags = NoAutoloading` for writes (so a declared value always wins
|
||||
over any previously saved shortcut). `actionId` is the 4-element
|
||||
`[componentUnique, actionUnique, componentFriendly, actionFriendly]`
|
||||
tuple; only the two `Unique` fields are stored in the manifest, and the
|
||||
two friendly-name fields are resolved dynamically at call time by looking
|
||||
up the component's shortcut list.
|
||||
|
||||
Per the spec's testing decisions, this mechanism is deliberately excluded
|
||||
from the automated test suite (it depends on a live, already-running
|
||||
session service that isn't practically substitutable without disproportionate
|
||||
mock infrastructure) — verify it manually against the real session instead.
|
||||
|
||||
As the real-world validation, apply the planned screenshot/session-lock
|
||||
keybind changes (Spectacle bindings, moving Lock Session off `Meta+L` to
|
||||
`Meta+X`) through `dot kde save`/`dot kde apply`, and update the
|
||||
corresponding rows in `keybindings.md` in the same change, per the
|
||||
project's cross-cutting keybindings convention.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] An identifier whose rc file is `kglobalshortcutsrc` dispatches to the `kglobalaccel` D-Bus mechanism rather than the schema-backed or freeform paths
|
||||
- [ ] `dot kde save <identifier>` and `dot kde save` (refresh) read a shortcut's current value via `shortcut(actionId)`, resolving the friendly-name fields dynamically
|
||||
- [ ] `dot kde apply` writes a declared shortcut via `setShortcut(actionId, keys, NoAutoloading)`, verified manually to take effect immediately in the running session
|
||||
- [ ] `dot kde diff` reports a declared shortcut mismatch by comparing against `defaultShortcut(actionId)`, verified manually
|
||||
- [ ] The Spectacle and Lock-Session (`Meta+X`) keybind changes are applied through `dot kde save`/`apply` and tracked in the manifest
|
||||
- [ ] `keybindings.md` is updated to reflect the new bindings in the same change
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
spec: dot-setup-folders
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
A new `dot setup` subcommand family, following the project's existing
|
||||
nested-subcommand dispatch convention. Bare `dot setup` (no arguments) runs
|
||||
every machine-setup task unconditionally; `dot setup <task>` runs just that
|
||||
one task. The only task that exists yet is `folders`.
|
||||
|
||||
`dot setup folders` brings the 8 standard XDG user directories under the
|
||||
project's short-name convention (`Desktop→.desktop`, `Documents→doc`,
|
||||
`Downloads→dwn`, `Music→mus`, `Pictures→pic`, `Videos→vid`, `Templates` and
|
||||
`Public` both →`.ignoreme`). The desired short names live in a tracked
|
||||
`user-dirs.dirs` file (a plain dotfile, not generated from a table each run).
|
||||
A separate small hardcoded table maps each of the 8 standard XDG categories
|
||||
to its legacy full-named folder, used only to locate content an XDG-defaults
|
||||
install would have left behind, and merge it into the already-tracked
|
||||
short-named target.
|
||||
|
||||
This slice covers the core happy path: a legacy folder found empty (strictly:
|
||||
no entries at all, including dotfiles/metadata) is merged into its
|
||||
short-named target silently, with no confirmation needed. As part of the same
|
||||
`Pictures→pic` pass, a nested `Screenshots` folder is renamed to lowercase
|
||||
`screenshots`, landing at `pic/screenshots`. After all folder moves complete,
|
||||
run `xdg-user-dirs-update` (no arguments) once to notify running apps/portals.
|
||||
`~/wrk` gets no XDG variable of its own and is out of scope for any mapping;
|
||||
the existing ad hoc `~/Projects` folder is left alone.
|
||||
|
||||
Non-empty legacy folders and filename collisions are out of scope for this
|
||||
slice (covered by later tasks) — for now it's acceptable for a non-empty
|
||||
legacy folder to be handled in whatever minimal way unblocks the empty-folder
|
||||
path (e.g. left untouched with a message), since the confirmation gate and
|
||||
collision safety are built out next.
|
||||
|
||||
Wire the new command into the project's standard subcommand checklist: a
|
||||
`_dot_setup_usage` help function reachable via `dot setup help` (and
|
||||
`dot setup folders help` for the nested task), the completions/help-glob
|
||||
duplication point, and a README command-table row.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] `dot setup folders` on a fresh scratch `$HOME` (all 8 legacy folders
|
||||
present and empty) renames them to their short-name targets per the
|
||||
mapping table, including `Pictures/Screenshots→pic/screenshots`, and
|
||||
leaves the tracked `user-dirs.dirs` short names in place
|
||||
- [ ] The fake `xdg-user-dirs-update` (PATH-prepended, logging its invocation
|
||||
per the project's existing fake-`sudo`/fake-`pacman` testing pattern)
|
||||
is invoked exactly once after a successful migration
|
||||
- [ ] Bare `dot setup` on a fresh scratch `$HOME` runs the `folders` task as
|
||||
part of running everything
|
||||
- [ ] `dot setup folders help` and `dot setup help` print usage and make no
|
||||
filesystem changes
|
||||
- [ ] Re-running `dot setup folders` after a clean migration is a no-op
|
||||
(idempotent)
|
||||
- [ ] `~/.github/README.md` has a command-table row for `dot setup`
|
||||
(and its `folders` task) with paths relative to `$HOME`
|
||||
- [ ] `~/.config/dot/tests/dot.fish` covers the above cases and
|
||||
`fishtape ~/.config/dot/tests/dot.fish` passes
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
spec: dot-setup-folders
|
||||
blocked-by: 0006-setup-dispatcher-and-folders-core
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
Extend `dot setup folders`'s migration so a legacy folder found non-empty
|
||||
(any entry at all, including a stray dotfile or KDE metadata like a
|
||||
`.directory` file, counts as non-empty) stops and prints what would be moved,
|
||||
then refuses to proceed unless an explicit `--yes` flag was passed on the
|
||||
command line — no interactive prompt. With `--yes`, the migration proceeds
|
||||
for that folder the same way the empty-folder path already does.
|
||||
|
||||
This applies uniformly across all 8 mapped categories, including the nested
|
||||
`Pictures/Screenshots→pic/screenshots` rename from the prior slice: a
|
||||
non-empty `Screenshots` folder is also gated behind the same confirmation
|
||||
rule.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] A legacy folder with real content (a real file, not just an empty
|
||||
directory) refuses to migrate without `--yes`, prints what would have
|
||||
been moved, and leaves the folder and its contents untouched
|
||||
- [ ] The same legacy folder migrates successfully when `--yes` is passed
|
||||
- [ ] A legacy folder containing only a stray dotfile/metadata file (e.g. a
|
||||
fake `.directory`) is still treated as non-empty and triggers the same
|
||||
confirmation gate
|
||||
- [ ] `~/.config/dot/tests/dot.fish` covers the above cases and
|
||||
`fishtape ~/.config/dot/tests/dot.fish` passes
|
||||
32
.config/dot/.claude/tasks/0008-folders-collision-handling.md
Normal file
32
.config/dot/.claude/tasks/0008-folders-collision-handling.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
spec: dot-setup-folders
|
||||
blocked-by: 0007-folders-non-empty-confirmation
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
Make the `--yes`-confirmed merge from the prior slice collision-safe: when a
|
||||
legacy folder and its short-named target both contain an entry with the same
|
||||
name, use no-clobber move semantics so the target's existing file is never
|
||||
silently overwritten. Report which files were skipped due to a collision, and
|
||||
leave the legacy folder in place (don't remove it) whenever any collision
|
||||
occurred during that folder's migration, rather than deleting a folder that
|
||||
still holds something that couldn't be merged.
|
||||
|
||||
This closes the gap left by the old bash `setup_folders`'s naive `mv $from/*
|
||||
$to`, which had no collision protection at all.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] A filename collision between a legacy folder and its already-populated
|
||||
short-named target is skipped, not overwritten (the target's existing
|
||||
file is preserved byte-for-byte)
|
||||
- [ ] The skipped collision is reported to the user
|
||||
- [ ] The legacy folder is left in place (not removed) when a collision
|
||||
occurred, even though `--yes` was given and other non-colliding files
|
||||
in it were moved
|
||||
- [ ] Re-running `dot setup folders` after a collision was reported and left
|
||||
in place behaves consistently (doesn't lose the previously-skipped
|
||||
file, doesn't re-move already-migrated files)
|
||||
- [ ] `~/.config/dot/tests/dot.fish` covers the above cases and
|
||||
`fishtape ~/.config/dot/tests/dot.fish` passes
|
||||
@@ -138,6 +138,14 @@ Tests live at `~/.config/dot/tests/dot.fish`, run with
|
||||
scoped to that directory, and don't assume an unrelated repo (e.g. a
|
||||
separate skills-source checkout elsewhere) is the tracked copy just because
|
||||
it also holds a copy of the same files.
|
||||
- `~/.claude/` and this project's own `.claude/` (e.g. `~/.config/dot/.claude/`)
|
||||
are two different directories that both happen to exist. Project-relative
|
||||
paths referenced in specs, task breakdowns, or other project docs — like
|
||||
`.claude/spec/<slug>.md` or `.claude/tasks/<NNNN>-<slug>.md` — are relative
|
||||
to this project directory (`~/.config/dot/.claude/...`), not to
|
||||
`$HOME/.claude/`. Writing to `$HOME/.claude/tasks/` instead of
|
||||
`~/.config/dot/.claude/tasks/` silently lands files in Claude Code's own
|
||||
global config dir instead of the project.
|
||||
|
||||
## Keybindings
|
||||
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
# Migration findings: ~/wrk/dotfiles (old) → ~/.dotfiles (new)
|
||||
|
||||
Exploratory session comparing the archived i3/X11/bash dotfiles repo
|
||||
(`~/wrk/dotfiles`, GitHub, 2023-2025) against the current bare-repo setup
|
||||
(`~/.dotfiles`, Gitea, started 2026-07-03) on a fresh CachyOS + KDE Plasma
|
||||
(Wayland) machine. Goal: not a literal port — for each old feature, decide
|
||||
whether CachyOS/KDE already covers it for free (skip) or whether it needs an
|
||||
equivalent tracked in the new repo (port). Nothing below has been
|
||||
implemented yet; this is a planning doc only.
|
||||
|
||||
## Resolved — no action needed (already covered by KDE/CachyOS defaults)
|
||||
|
||||
- **Package management split** (old: `pacman.gui/nogui` + `aur.gui/nogui` +
|
||||
Makefile/aurman installer). New `dot install` + flat `packages/pacman`
|
||||
stays as-is — no plan to port the old list wholesale, just add packages as
|
||||
needed.
|
||||
- **Touchpad `xorg.conf`** — `kcminputrc`/System Settings has caused no
|
||||
issues; not tracking it.
|
||||
- **i3 tiling paradigm** (focus-by-direction, split/layout toggle, floating
|
||||
toggle, resize, move-by-pixel, gaps, borders) — dropped entirely. No KWin
|
||||
tiling script (Polonium/Bismuth/Krohnkite) installed or wanted; primary
|
||||
tiling-like workflow now happens in tmux. KDE's native floating +
|
||||
quick-tile (`Meta+Arrow`) is accepted as-is.
|
||||
- **i3 workspace switch/move** (`Super+1-9,0` / `Super+Shift+1-9,0`) —
|
||||
already matched by existing KDE defaults: `Meta+1-9` (Switch to Desktop
|
||||
N), `Meta+Shift+1-9` i.e. `Meta+!/@/#/...` (Window to Desktop N).
|
||||
- **Desktop count** — KDE has 9 virtual desktops configured; confirmed
|
||||
sufficient, no change to 10.
|
||||
- **Workspace/window assignment rules** (`assign firefox → ws1`, `plexamp →
|
||||
ws10`) — skipped. `kwinrulesrc` stays empty for now.
|
||||
- **Kill/reload/restart WM keys** — functionally covered by KDE defaults
|
||||
(`Alt+F4` close, `Meta+Ctrl+Esc` kill window).
|
||||
- **Terminal launch** (`Super+Return` → alacritty) — already identically
|
||||
bound: `Meta+Return` → Alacritty, confirmed in `kglobalshortcutsrc`.
|
||||
- **App launcher** (rofi) — superseded by KRunner (default `Alt+Space`/`Alt+F2`).
|
||||
- **Media keys** (volume, mic mute, play/pause/next/prev) — already covered
|
||||
natively via hardware key bindings, exceeding the old `wpctl`+`playerctl`
|
||||
setup.
|
||||
- **Brightness/backlight keys** — already covered via hardware
|
||||
`Monitor Brightness Up/Down` bindings (powerdevil).
|
||||
- **picom, Xresources, dracula color theme** — dropped. Not using dracula
|
||||
going forward; kwin compositor replaces picom with no config needed.
|
||||
- **rofi power menu** (lock/shutdown/restart/switch-user) — covered by
|
||||
existing KDE defaults: `Meta+L` (Lock Session), `Ctrl+Alt+Del` (Show
|
||||
Logout Screen = full power menu). *Note: `Meta+L` will be reassigned, see
|
||||
below — Lock Session needs to move to `Meta+X`.*
|
||||
- **polybar → Plasma panel** — nearly the entire module set already exists
|
||||
as stock, **unmodified** Plasma panel defaults on this machine: workspace
|
||||
indicator → Pager applet, battery → Battery applet (machine has `BAT0`),
|
||||
backlight → Brightness applet, date/time → Digital Clock applet, volume →
|
||||
system tray audio, now-playing → Media Controller applet (present, just
|
||||
nothing to show — no MPRIS player installed currently). Only non-exact
|
||||
match is polybar's centered window-title label (closest KDE equivalent is
|
||||
the icon-only taskbar) — decided not worth adding a dedicated Window Title
|
||||
applet. Whole row needs no tracking; see also the panel-layout finding
|
||||
below (it's CachyOS's own shipped default, reproduces automatically).
|
||||
|
||||
## Resolved — needs porting (design agreed, not yet built)
|
||||
|
||||
- **Extra groups** (old: `.extra_groups` → `video`, `docker` via
|
||||
`setup_users` in `bin/dot init`). Missing in new repo; not urgently needed
|
||||
yet but a real gap.
|
||||
- **Architecture decision**: new `dot` subcommand, e.g. `dot setup` (name
|
||||
tentative), separate from `dot init`. `dot init` stays scoped to the
|
||||
one-shot bootstrap (clone + checkout) and explicitly refuses to re-run;
|
||||
`dot setup` is idempotent/re-runnable and is the new home for
|
||||
machine-setup tasks (extra groups, folder layout, future ones), mirroring
|
||||
the old `bin/dot init`'s `setup_users`/`setup_folders` sub-task split.
|
||||
- **Folder naming / XDG dirs** (old: `setup_folders` renamed
|
||||
`Desktop→.desktop`, `Documents→doc`, `Downloads→dwn`, `Music→mus`,
|
||||
`Pictures→pic`, `Videos→vid`, `Templates/Public→.ignoreme`).
|
||||
- **Decision**: restore the short-name convention (better for fish
|
||||
autocompletion — shorter shared prefixes, e.g. `doc`/`dwn` only share one
|
||||
character vs `Documents`/`Downloads`).
|
||||
- Replace old `Projects`-style folder with **`wrk`** (matches the existing
|
||||
`~/wrk` directory already in active use, e.g. `~/wrk/dotfiles`).
|
||||
- `user-dirs.dirs` currently untracked and diverged (has full names +
|
||||
an ad hoc `XDG_PROJECTS_DIR=$HOME/Projects` not in the old file at all).
|
||||
Needs to be regenerated to the short-name convention (with `wrk`) and
|
||||
then tracked, as part of the `dot setup` folders task.
|
||||
- **Caps-lock/Escape swap** — user confirmed this needed manual
|
||||
configuration (`kxkbrc`: `Options=caps:escape_shifted_capslock`), it is
|
||||
**not** a KDE default. Needs tracking. No kcfg schema backs this setting —
|
||||
it's a freeform string; "default" = the `Options=` line being absent
|
||||
entirely. Simple to declare directly, no diffing tooling needed for this
|
||||
one.
|
||||
- **Screenshots** (old: `Print`/`Ctrl+Print`/`Shift+Print` via `maim`+`xclip`
|
||||
→ `~/pic/screenshots/`, save + clipboard copy).
|
||||
- **New keybinds**: `Meta+L` = full-screen capture, `Ctrl+Meta+L` = select
|
||||
region, `Shift+Meta+L` = window capture — all via **Spectacle** (built-in
|
||||
capture + clipboard; `maim`/`xclip` not needed, `xclip` isn't even
|
||||
installed).
|
||||
- **Consequence**: `Meta+L` is currently KDE's default Lock Session
|
||||
shortcut — must be freed and Lock Session rebound to **`Meta+X`**.
|
||||
- **Folder**: rename Spectacle's default save-folder name from
|
||||
`Screenshots` (capital) to lowercase `screenshots`, matching the rest of
|
||||
the short-folder convention.
|
||||
- **Open detail**: exact Spectacle shortcut action IDs (likely
|
||||
`FullScreenScreenShot`, `RectangularRegionScreenShot`,
|
||||
`ActiveWindowScreenShot`) need to be verified via System Settings →
|
||||
Shortcuts at implementation time — only `CurrentMonitorScreenShot` and
|
||||
`OpenWithoutScreenshot` show up in the current `kglobalshortcutsrc` dump
|
||||
(both unset), the others aren't customized yet so don't appear there.
|
||||
|
||||
## KDE config-tracking architecture (cross-cutting decision)
|
||||
|
||||
**Problem**: KDE rc files (`kxkbrc`, `kglobalshortcutsrc`, `kwinrc`,
|
||||
`plasma-org.kde.plasma.desktop-appletsrc`, `kdeglobals`, etc.) mix real user
|
||||
intent with large amounts of machine-specific/volatile noise (timestamps,
|
||||
UUIDs, window state, plugin caches). Whole-file tracking (what naive
|
||||
dotfiles repos do, e.g. `dnephin/dotfiles`) produces noisy diffs and risks
|
||||
clobbering machine-specific state.
|
||||
|
||||
**Considered and rejected (for now)**: `chezmoi_modify_manager`-style
|
||||
filtered source-of-truth + merge script. Powerful (tracks a minimal "intent"
|
||||
INI fragment + per-file ignore/set rules, merges onto the live file), but
|
||||
it's real tooling to build from scratch outside of chezmoi, and not
|
||||
justified yet for the ~2 settings currently in scope.
|
||||
|
||||
**Decision**: track KDE settings as a declarative list of key/value pairs
|
||||
applied imperatively via `kwriteconfig6`, run through `dot setup` (or
|
||||
wherever machine-setup tasks land, see above) — not one-off hand-written
|
||||
`kwriteconfig6` calls accumulating over time.
|
||||
|
||||
**Auto-detection tooling to build** (exploratory design only — not
|
||||
implemented):
|
||||
|
||||
- **Command**: `dot config kde` — deliberately dispatchable, implies a
|
||||
`dot config <target>` family with room for non-KDE targets later.
|
||||
- **Language**: Python 3 (already installed) for XML/kcfg parsing — nested
|
||||
`<group>`/`<entry>`/`<default>` structures are painful to parse in
|
||||
fish/`xmllint` one-liners. Invoked from a fish wrapper
|
||||
(`~/.config/dot/commands/config.fish` → `_dot_config` → sub-dispatch to
|
||||
KDE logic), following the existing help-then-argparse /
|
||||
`_dot_<name>_usage` convention.
|
||||
- **Coverage**: as broad as possible across known KDE rc files, not just
|
||||
`kwinrc` — files/settings with nothing customized are expected to return
|
||||
empty, that's fine.
|
||||
- **Per-file-type handling** (three different mechanisms, no single
|
||||
approach covers everything):
|
||||
1. **`kglobalshortcutsrc`** — self-describing, no schema needed. Each line
|
||||
is `Action=Current,Default,FriendlyName`; diff field 1 vs field 2,
|
||||
report only mismatches. Fully automatic.
|
||||
2. **KConfigXT schema-backed settings** — real schemas exist at
|
||||
`/usr/share/config.kcfg/*.kcfg` (41 files on this machine) with
|
||||
`<default>` tags per `<entry>` inside `<group>` blocks. **Caveat**: not
|
||||
every kcfg statically declares its target rc file — some use
|
||||
`<kcfgfile arg="true" />` (e.g. `kwin.kcfg`), meaning the target file is
|
||||
supplied at runtime by the owning app, not in the XML. Full automatic
|
||||
discovery isn't possible in all cases; a curated `(rcfile → [kcfg
|
||||
files])` mapping table needs to be hardcoded from domain knowledge —
|
||||
confirmed acceptable. Known `kwinrc` mapping so far:
|
||||
`virtualdesktopssettings.kcfg`, `kwindecorationsettings.kcfg`,
|
||||
`workspaceoptions_kwinsettings.kcfg`, several accessibility kcfg files,
|
||||
plus `kwin.kcfg` itself (needs manual mapping, `arg="true"`). For each
|
||||
entry: read the live value via
|
||||
`kreadconfig6 --file <rcfile> --group <group> --key <key>` and compare
|
||||
against the schema's `<default>`.
|
||||
3. **Freeform/schema-less string settings** (e.g. `kxkbrc`'s `Options=`
|
||||
line) — no kcfg exists; "default" simply means the key/line is absent.
|
||||
Trivial, no diffing tooling needed, just declare directly.
|
||||
4. **Plasma panel layout** (`plasma-org.kde.plasma.desktop-appletsrc`) —
|
||||
not schema-based at all; generated once from a shipped `layout.js`.
|
||||
Confirmed CachyOS ships its **own** look-and-feel/layout
|
||||
(`/usr/share/plasma/look-and-feel/CachyOS-Nord/`), not vanilla KDE
|
||||
Breeze — so the current panel *is* "default" by construction and
|
||||
reproduces automatically on any fresh CachyOS install. No tracking
|
||||
needed, no diffing possible/necessary.
|
||||
5. **Empirical fallback** (not needed yet, noted for completeness): for
|
||||
anything not covered by the above, spin up a scratch
|
||||
`HOME`/`XDG_CONFIG_HOME`, let the app initialize its config fresh, diff
|
||||
against the real file.
|
||||
|
||||
**Status**: design only, per explicit instruction — do not implement until
|
||||
asked.
|
||||
|
||||
## Also noted, not yet actioned
|
||||
|
||||
- Once any of the above keybind changes are actually made (screenshot
|
||||
rebinds, `Meta+L`→`Meta+X` lock move, etc.), remember the project's own
|
||||
convention: add/update rows in `~/.github/keybindings.md` for each changed
|
||||
keybind, per `~/.config/dot/CLAUDE.md`.
|
||||
|
||||
## dot voice — hands-free dictation (shelved, 2026-07-04)
|
||||
|
||||
Built and then fully reverted a `dot voice` subcommand for hands-free
|
||||
dictation into Claude Code: local Silero VAD (torch-free, onnxruntime
|
||||
only) segmenting mic audio into utterances, each POSTed to a
|
||||
`whisper-server` instance running remotely on a Proxmox host with an
|
||||
NVIDIA GPU (Vulkan backend), transcribed text buffered at the cursor via
|
||||
`ydotool`, submitted on a spoken "send it" and cancelled on "scratch
|
||||
that".
|
||||
Iterated through several accuracy levers in one session: dropped then
|
||||
restored a literal-vocabulary `initial_prompt` (helped once on the
|
||||
stronger model), a deterministic post-transcription `replacements.txt`
|
||||
for persistent single-word misses, per-request `temperature`/
|
||||
`temperature_inc`, beam search (`-bs`/`-bo`, ruled out as a factor),
|
||||
and a quantization bump from `q5_0` to full fp16 `medium.en` (helped
|
||||
substantially).
|
||||
Also fixed a real bug along the way: whisper-server can return multi-
|
||||
segment text joined by newlines, and `ydotool type` sends an embedded
|
||||
`\n` as a literal Enter keypress — this was silently submitting partial
|
||||
dictation mid-sentence. Fixed by collapsing all whitespace before typing.
|
||||
Despite all of that, real-world accuracy over the laptop's built-in mic
|
||||
was still not good enough for daily use — small word-substitution and
|
||||
dropped-word errors persisted even with the best config found (fp16,
|
||||
no beam search, prompt restored).
|
||||
**Shelved reason**: audio input quality was the one variable never
|
||||
tested — everything tuned this session was server/decoding-side. The
|
||||
user's only better-microphone option is their desktop, which doesn't
|
||||
have this dotfiles setup yet.
|
||||
**If resumed**: test with a real microphone (headset/USB) before any
|
||||
further server-side tuning — it's suspected to matter more than any of
|
||||
the software changes made so far. Also worth trying `large-v3-turbo`
|
||||
given the Proxmox GPU had comfortable headroom even at fp16 `medium.en`
|
||||
(~0.1–0.5s per utterance).
|
||||
**State**: fully reverted, nothing left in the tree or installed
|
||||
packages list. The full implementation existed as local commit
|
||||
`745e417f19a02bc589c5b32853e629993adaa01f` ("dotcli: Voice dictation
|
||||
software using whisper"), never pushed, then hard-reset away — not
|
||||
recoverable via normal git history, only via reflog for a limited time
|
||||
if urgently needed. The KDE global shortcut (`Meta+Ctrl+Space` → `dot
|
||||
voice arm`) was configured in System Settings and was **not** undone by
|
||||
this revert — check System Settings → Shortcuts → Custom Shortcuts if
|
||||
this work is ever picked back up or fully abandoned.
|
||||
Reference in New Issue
Block a user