docs: Add specs and tasks for previously scoped work in todo.md
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user