feat(vault): add task workflow

This commit is contained in:
2026-10-14 01:24:22 -04:00
parent ffc40e547b
commit 57bb21af1a
14 changed files with 35961 additions and 470 deletions

View File

@@ -5,7 +5,7 @@
"livePreview": false, "livePreview": false,
"readableLineLength": false, "readableLineLength": false,
"strictLineBreaks": true, "strictLineBreaks": true,
"propertiesInDocument": "hidden", "propertiesInDocument": "visible",
"showLineNumber": true, "showLineNumber": true,
"autoPairMarkdown": false, "autoPairMarkdown": false,
"useTab": false, "useTab": false,

View File

@@ -5,5 +5,6 @@
"obsidian-style-settings", "obsidian-style-settings",
"custom-font-loader", "custom-font-loader",
"obsidian-icon-folder", "obsidian-icon-folder",
"table-editor-obsidian" "table-editor-obsidian",
"obsidian-linter"
] ]

View File

@@ -0,0 +1,320 @@
{
"ruleConfigs": {
"add-blank-line-after-yaml": {
"enabled": true
},
"dedupe-yaml-array-values": {
"enabled": true,
"dedupe-alias-key": true,
"dedupe-tag-key": true,
"dedupe-array-keys": true,
"ignore-keys": []
},
"escape-yaml-special-characters": {
"enabled": true,
"try-to-escape-single-line-arrays": true
},
"force-yaml-escape": {
"enabled": true,
"force-yaml-escape-keys": []
},
"format-tags-in-yaml": {
"enabled": true
},
"format-yaml-array": {
"enabled": true,
"alias-key": true,
"tag-key": true,
"default-array-keys": true,
"force-single-line-array-style": [],
"force-multi-line-array-style": []
},
"insert-yaml-attributes": {
"enabled": true,
"text-to-insert": []
},
"move-inline-fields-to-yaml": {
"enabled": true,
"how-to-handle-full-line-fields": "Move and remove",
"how-to-handle-bracketed-fields": "Leave in place",
"how-to-handle-existing-keys": "Skip",
"inline-keys-to-ignore": []
},
"move-tags-to-yaml": {
"enabled": true,
"how-to-handle-existing-tags": "Nothing",
"tags-to-ignore": []
},
"remove-yaml-keys": {
"enabled": true,
"yaml-keys-to-remove": []
},
"sort-yaml-array-values": {
"enabled": true,
"sort-alias-key": true,
"sort-tag-key": true,
"sort-array-keys": true,
"ignore-keys": [],
"sort-order": "Ascending Alphabetical"
},
"yaml-key-sort": {
"enabled": true,
"yaml-key-priority-sort-order": [
"aliases",
"date-created",
"date-modified"
],
"priority-keys-at-start-of-yaml": true,
"yaml-sort-order-for-other-keys": "None"
},
"yaml-timestamp": {
"enabled": true,
"date-created": true,
"date-created-key": "date-created",
"date-created-source-of-truth": "file system",
"date-modified": true,
"date-modified-key": "date-modified",
"date-modified-source-of-truth": "file system",
"format": "YYYY-MM-DD HH:mm",
"convert-to-utc": false,
"update-on-file-contents-updated": "after 5 seconds"
},
"yaml-title": {
"enabled": false,
"title-key": "title",
"mode": "first-h1-or-filename-if-h1-missing"
},
"yaml-title-alias": {
"enabled": true,
"preserve-existing-alias-section-style": true,
"keep-alias-that-matches-the-filename": false,
"use-yaml-key-to-keep-track-of-old-filename-or-heading": false,
"alias-helper-key": "linter-yaml-title-alias",
"remove-alias-if-empty": true
},
"capitalize-headings": {
"enabled": true,
"style": "Title Case",
"ignore-case-words": [],
"lowercase-words": [],
"starting-word-ignore-characters": "'\"(‘“-",
"ending-word-ignore-characters": ".?!,:;'\")”’0123456789-",
"ignore-words": []
},
"file-name-heading": {
"enabled": true
},
"header-increment": {
"enabled": true,
"start-at-h2": false
},
"headings-start-line": {
"enabled": true
},
"remove-trailing-punctuation-in-heading": {
"enabled": true,
"punctuation-to-remove": ".,;:!。,;:!"
},
"footnote-after-punctuation": {
"enabled": true
},
"move-footnotes-to-the-bottom": {
"enabled": true,
"include-blank-line-between-footnotes": false
},
"re-index-footnotes": {
"enabled": true
},
"auto-correct-common-misspellings": {
"enabled": true,
"ignore-words": [],
"skip-words-with-multiple-capitals": true,
"extra-auto-correct-files": []
},
"blockquote-style": {
"enabled": true,
"style": "space"
},
"convert-bullet-list-markers": {
"enabled": true
},
"default-language-for-code-fences": {
"enabled": true,
"default-language": ""
},
"emphasis-style": {
"enabled": true,
"style": "consistent"
},
"no-bare-urls": {
"enabled": true,
"no-bare-uris": true
},
"ordered-list-style": {
"enabled": true,
"number-style": "ascending",
"list-end-style": ".",
"preserve-start": false
},
"proper-ellipsis": {
"enabled": true
},
"quote-style": {
"enabled": true,
"single-quote-enabled": true,
"single-quote-style": "''",
"double-quote-enabled": true,
"double-quote-style": "\"\""
},
"remove-consecutive-list-markers": {
"enabled": true
},
"remove-empty-list-markers": {
"enabled": true
},
"remove-hyphenated-line-breaks": {
"enabled": true
},
"remove-multiple-spaces": {
"enabled": true
},
"strong-style": {
"enabled": true,
"style": "consistent"
},
"two-spaces-between-lines-with-content": {
"enabled": false,
"line-break-indicator": " "
},
"unordered-list-style": {
"enabled": true,
"list-style": "consistent"
},
"align-table-columns": {
"enabled": true
},
"compact-yaml": {
"enabled": true,
"inner-new-lines": false
},
"consecutive-blank-lines": {
"enabled": true
},
"convert-spaces-to-tabs": {
"enabled": true,
"tabsize": 4
},
"empty-line-around-blockquotes": {
"enabled": true
},
"empty-line-around-code-fences": {
"enabled": true
},
"empty-line-around-horizontal-rules": {
"enabled": true
},
"empty-line-around-math-blocks": {
"enabled": true
},
"empty-line-around-tables": {
"enabled": true
},
"heading-blank-lines": {
"enabled": true,
"bottom": true,
"empty-line-after-yaml": true
},
"line-break-at-document-end": {
"enabled": true
},
"move-math-block-indicators-to-their-own-line": {
"enabled": true
},
"paragraph-blank-lines": {
"enabled": true
},
"remove-empty-lines-between-list-markers-and-checklists": {
"enabled": true
},
"remove-link-spacing": {
"enabled": true
},
"remove-space-around-characters": {
"enabled": true,
"include-fullwidth-forms": true,
"include-cjk-symbols-and-punctuation": true,
"include-dashes": true,
"other-symbols": ""
},
"remove-space-before-or-after-characters": {
"enabled": true,
"characters-to-remove-space-before": ",!?;:).’”]",
"characters-to-remove-space-after": "¿¡‘“(["
},
"space-after-list-markers": {
"enabled": true
},
"space-between-chinese-japanese-or-korean-and-english-or-numbers": {
"enabled": true,
"english-symbols-punctuation-before": "-+;:'\"°%$)]",
"english-symbols-punctuation-after": "-+'\"([¥$"
},
"trailing-spaces": {
"enabled": true,
"two-space-line-break": false
},
"add-blockquote-indentation-on-paste": {
"enabled": true
},
"prevent-double-checklist-indicator-on-paste": {
"enabled": true
},
"prevent-double-list-item-indicator-on-paste": {
"enabled": true
},
"proper-ellipsis-on-paste": {
"enabled": true
},
"remove-hyphens-on-paste": {
"enabled": true
},
"remove-leading-or-trailing-whitespace-on-paste": {
"enabled": true
},
"remove-leftover-footnotes-from-quote-on-paste": {
"enabled": true
},
"remove-multiple-blank-lines-on-paste": {
"enabled": true
}
},
"lintOnSave": true,
"recordLintOnSaveLogs": false,
"displayChanged": true,
"suppressMessageWhenNoChange": true,
"enableDiffPreviewView": true,
"suppressLintAllFilesConfirmationModal": false,
"suppressLintAllFilesInFolderConfirmationModal": false,
"lintOnFileChange": false,
"displayLintOnFileChangeNotice": false,
"settingsConvertedToConfigKeyValues": true,
"textAreaSettingsConvertedToListItemSettings": true,
"defaultArrayStyleMovedToCommonStyles": true,
"additionalFileExtensions": [],
"foldersToIgnore": [
"99 meta/templates"
],
"filesToIgnore": [],
"linterLocale": "system-default",
"logLevel": "ERROR",
"lintCommands": [],
"customRegexes": [],
"commonStyles": {
"aliasArrayStyle": "multi-line",
"tagArrayStyle": "single-line",
"defaultArrayStyle": "multi-line",
"minimumNumberOfDollarSignsToBeAMathBlock": 2,
"escapeCharacter": "\"",
"removeUnnecessaryEscapeCharsForMultiLineArrays": true
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
{
"id": "obsidian-linter",
"name": "Linter",
"version": "1.34.1",
"minAppVersion": "1.13.0",
"description": "Formats and styles your notes. It can be used to format YAML tags, aliases, arrays, and metadata; footnotes; headings; spacing; math blocks; regular markdown contents like list, italics, and bold styles; and more with the use of custom rule options as well.",
"author": "Victor Tao",
"authorUrl": "https://github.com/platers",
"helpUrl": "https://platers.github.io/obsidian-linter/",
"isDesktopOnly": false
}

View File

@@ -0,0 +1 @@
.linter-no-border{border:none}.linter-border-bottom{border-bottom:1px solid var(--background-modifier-border);border-top:0;margin-bottom:.75em}.custom-row-description{margin-top:0}.modal-warn,.search-zero-state{font-weight:700}.modal-heading,.search-zero-state{text-align:center}.confirm-modal-checkbox-container{align-items:center;display:flex;gap:6px;margin-top:8px}.disabled-list-entry{color:var(--text-faint)}.linter-diff-preview-view{display:flex;flex-direction:column;height:100%;min-width:0;overflow:hidden}.linter-diff-preview-empty{color:var(--text-muted);padding:16px}.linter-diff-preview-header{align-items:flex-start;border-bottom:1px solid var(--background-modifier-border);display:flex;flex:0 0 auto;gap:12px;justify-content:space-between;padding:12px 14px}.linter-diff-preview-title-group{min-width:0}.linter-diff-preview-header h3{font-size:var(--font-ui-medium);line-height:1.3;margin:0 0 6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.linter-diff-preview-actions{display:flex;flex:0 0 auto;gap:8px}.linter-diff-preview-body{flex:1 1 auto;min-height:0;overflow:auto;padding:12px 14px 16px}.linter-diff-preview-view .linter-diff-summary{color:var(--text-normal);display:inline-block;font-size:var(--font-ui-small);margin:0 0 10px;padding:5px 8px}.linter-diff-preview-view .linter-diff,.linter-diff-preview-view .linter-diff-summary{background:var(--background-secondary);border:1px solid var(--background-modifier-border);border-radius:6px}.linter-diff-preview-view .linter-diff{font-family:var(--font-monospace);font-size:var(--font-smaller);line-height:1.45;margin:0;overflow:hidden;padding:6px 0}.linter-diff-preview-view .linter-diff-line{display:grid;grid-template-columns:26px minmax(0,1fr);min-height:20px;padding:1px 10px 1px 0}.linter-diff-preview-view .linter-diff-marker{color:var(--text-muted);padding-right:8px;text-align:right;user-select:none}.linter-diff-preview-view .linter-diff-text{min-width:0;overflow-wrap:anywhere;white-space:pre-wrap}.linter-diff-preview-view .linter-diff-skip{border-bottom:1px solid var(--background-modifier-border);border-top:1px solid var(--background-modifier-border);color:var(--text-faint);font-family:var(--font-interface);font-size:var(--font-ui-smaller);padding:4px 12px;text-align:center}.linter-diff-preview-view .linter-diff-added{background:rgba(var(--color-green-rgb),.12);color:var(--text-normal)}.linter-diff-preview-view .linter-diff-removed{background:rgba(var(--color-red-rgb),.12);color:var(--text-normal)}.linter-diff-preview-view .linter-diff-context{color:var(--text-muted)}

View File

@@ -4,21 +4,21 @@
"type": "split", "type": "split",
"children": [ "children": [
{ {
"id": "6d9088d392cb8604", "id": "46cb7f7e5e1f572c",
"type": "tabs", "type": "tabs",
"children": [ "children": [
{ {
"id": "22a4c1f201a1b00e", "id": "3f7029dbd20b3ef9",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "markdown", "type": "markdown",
"state": { "state": {
"file": "00 INBOX/ai/valheim-items.md", "file": "03 wrk/tasks/0001-todo-cli.md",
"mode": "preview", "mode": "source",
"source": true "source": true
}, },
"icon": "lucide-file", "icon": "lucide-file",
"title": "valheim-items" "title": "0001-todo-cli"
} }
} }
] ]
@@ -91,7 +91,7 @@
"state": { "state": {
"type": "localgraph", "type": "localgraph",
"state": { "state": {
"file": "00 INBOX/ai/valheim-items.md", "file": "03 wrk/tasks/0001-todo-cli.md",
"options": { "options": {
"collapse-filter": true, "collapse-filter": true,
"search": "", "search": "",
@@ -126,7 +126,8 @@
} }
], ],
"direction": "horizontal", "direction": "horizontal",
"width": 338.5 "width": 338.5,
"collapsed": true
}, },
"right": { "right": {
"id": "c35c41e87bb5c18f", "id": "c35c41e87bb5c18f",
@@ -184,20 +185,6 @@
"title": "tag" "title": "tag"
} }
}, },
{
"id": "8918bf52a32e4a8e",
"type": "leaf",
"state": {
"type": "all-properties",
"state": {
"sortOrder": "frequency",
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-ghost",
"title": "all-properties"
}
},
{ {
"id": "c10908b3bd97f5fa", "id": "c10908b3bd97f5fa",
"type": "leaf", "type": "leaf",
@@ -231,14 +218,27 @@
"table-editor-obsidian:Advanced Tables Toolbar": false "table-editor-obsidian:Advanced Tables Toolbar": false
} }
}, },
"active": "22a4c1f201a1b00e", "active": "3f7029dbd20b3ef9",
"lastOpenFiles": [ "lastOpenFiles": [
"03 wrk/tasks/0001-todo-cli.md",
"99 meta/templates/task.md",
"03 wrk/tasks/Untitled.md",
"03 wrk/tasks/write-a-todo-cli.md",
"03 wrk/tasks",
"99 meta/templates/note.md",
"99 meta/templates/checkmark.md",
"AGENTS.md",
"00 INBOX/ai/valheim-items.md",
"00 INBOX/ai/hyprland-lua-migration-plan.md", "00 INBOX/ai/hyprland-lua-migration-plan.md",
"00 INBOX/ai/deadnix-architecture.md", "00 INBOX/ai/deadnix-architecture.md",
"00 INBOX/ai/valheim-items.md",
"TODO.md",
"AGENTS.md",
"00 INBOX/valheim progression.md", "00 INBOX/valheim progression.md",
"02 notes/falinks public surfaces.md",
"03 wrk/machoke.md",
"03 wrk/falinks.md",
"03 wrk/ariadne.md",
"TODO.md",
"conventional-commits.md",
"00 INBOX/ai/hyprland-config-verification-migration.md",
"technical-writing.md", "technical-writing.md",
"rust-module-prototype.md", "rust-module-prototype.md",
"00 INBOX/ai/statix-architecture.md", "00 INBOX/ai/statix-architecture.md",
@@ -250,24 +250,11 @@
"TODO.md~", "TODO.md~",
"00 INBOX/ai/xatu-rule-implementation-order.md", "00 INBOX/ai/xatu-rule-implementation-order.md",
"00 INBOX/ai/xatu-architecture-design.md", "00 INBOX/ai/xatu-architecture-design.md",
"00 INBOX/ai/nix-style-linter-plan.md",
"00 INBOX/ai/nix-style-guide-agent-customization.md",
"00 INBOX/ai/game-guest-readiness-plan.md",
"00 INBOX/ai/xatu-node-rule-static-dispatch-plan.md",
"00 INBOX/ai/nix-style-linter-pr-1-vendor-rnix.md",
"00 INBOX/ai/nix-style-linter-pr-3-let-order.md",
"00 INBOX/ai/nix-style-linter-pr-2-scaffold-cli.md",
"00 INBOX/ai/homepage-pikachu-setup-plan.md",
"00 INBOX/ai/source-driven-remote-deploy-plan.md",
"00 INBOX/ai/monorepo-work-report.md",
"00 INBOX/ai/clew-ast-exploration.md",
"00 INBOX/ai/steamcmd servers.md",
"00 INBOX/ai/clew-ast-exploration.md~", "00 INBOX/ai/clew-ast-exploration.md~",
"03 wrk/machoke.md~", "03 wrk/machoke.md~",
"03 wrk/falinks.md~", "03 wrk/falinks.md~",
"99 meta/attachments/bases/unlinked mentions.base", "99 meta/attachments/bases/unlinked mentions.base",
"00 INBOX/ai", "00 INBOX/ai",
"03 wrk/ai", "03 wrk/ai"
"99 meta/Untitled"
] ]
} }

View File

@@ -1,304 +0,0 @@
# Deadnix Architecture
## Purpose
This note explains how `deadnix` finds unused Nix code.
It focuses on architecture, not whether each check belongs in our linter.
Use this note when deciding whether our linter needs scope analysis.
## What Deadnix Is
`deadnix` is a dead-code scanner for Nix.
It finds bindings that introduce names which no live code uses.
It is not a general style linter.
It does not have many independent rules.
It implements one analysis family: unused declarations.
Like `statix`, it parses source text with `rnix`.
It does not evaluate Nix.
It does not import files.
It reasons over local syntax and local scope only.
## Minimal Dead-Code Vocabulary
- A binding introduces a name.
- A scope defines where introduced names are visible.
- A use is an identifier occurrence that refers to a binding.
- Shadowing happens when an inner scope reuses an outer name.
- Dead code is a binding that no live code uses.
- A fixed point is the final result after repeated analysis no longer changes.
Dead-code analysis needs more state than a simple syntax lint.
It must know which names a scope introduces and where those names can be used.
## Component Map
`deadnix` has fewer layers than `statix`.
The command-line entry point, file walk, parsing, analysis, reporting, and editing live in one small tool.
The important concepts are separate modules:
- settings control analysis options;
- scopes describe where names are introduced;
- bindings describe individual declared names;
- usage search answers whether a name appears in a live body;
- dead-code results connect a binding to its scope;
- reports render findings;
- edits remove or rename unused declarations.
## Main Pipeline
```mermaid
flowchart TD
A[Parse CLI options] --> B[Collect input files]
B --> C[Read each file]
C --> D[Parse with rnix]
D --> E[Skip file on parse errors]
D --> F[Run dead-code analysis]
F --> G[Print reports unless quiet]
G --> H[Apply edits when --edit is set]
H --> I[Exit 1 when --fail and findings exist]
```
The CLI accepts files or directories.
Directory mode walks `.nix` files.
Hidden files require an explicit option.
Excludes filter paths before analysis.
The tool skips dead-code diagnostics when a file has parse errors.
It reports parse errors to the user and continues with other files.
## Analysis Settings
The analysis has four important switches.
`no_lambda_arg` disables checks for single lambda arguments.
This can avoid noise when an API requires an argument name.
`no_lambda_pattern_names` disables checks for names in attribute-set function patterns.
This matters for `callPackage`-style code, where argument names can be external API.
`no_underscore` ignores bindings that start with `_`.
This treats `_name` as an intentional unused placeholder.
`warn_used_underscore` reports underscore-prefixed bindings that code actually uses.
This enforces the convention that `_name` means unused.
These settings are architecture-relevant because dead-code policy depends on API context.
A repository linter needs a way to express that context.
## Scope Model
`deadnix` recognizes four scope types.
### Lambda pattern scope
A lambda pattern is a function argument written as an attribute set pattern.
It can introduce field names and an optional alias for the whole argument set.
Example shape:
```nix
args @ { pkgs, lib, ... }: body
```
This scope can introduce `args`, `pkgs`, and `lib`.
### Lambda argument scope
A single lambda argument introduces one name.
Example shape:
```nix
pkgs: body
```
This scope introduces `pkgs`.
### Let scope
A `let` expression introduces bindings and inherited names.
Those names are available in binding expressions and in the body.
Example shape:
```nix
let
inherit (pkgs) hello;
value = hello;
in
value
```
This scope introduces `hello` and `value`.
### Recursive attribute-set scope
A `rec` attribute set introduces names that sibling attributes can reference.
`deadnix` models this scope, but treats its bindings as non-removable.
That conservative choice avoids deleting public-looking attributes from recursive sets.
## Binding Model
A binding stores three facts.
It stores the identifier name.
It stores the syntax node that declared the name.
It stores whether the binding is mortal.
A mortal binding can become a dead-code finding.
A non-mortal binding participates in name lookup but will not be removed.
Single lambda arguments that already start with `_` are non-mortal.
Recursive attribute-set bindings are also non-mortal.
A binding also checks for the `deadnix: skip` pragma.
The pragma applies to a nearby following declaration.
The scanner searches backward from the declaration and stops after a blank-line boundary.
## Usage Search
Usage search asks whether a given name appears inside a syntax subtree.
The search is scope-aware.
When it enters a child scope that binds the same name, it stops following that branch.
That prevents an inner binding from counting as a use of an outer binding.
Attribute paths get special handling.
Ordinary attribute keys do not count as uses.
Dynamic or string attribute names can contain expressions, so the search looks inside those parts.
`inherit` also gets special handling.
A scope can inherit names from an outer scope.
The analysis checks whether the inherited expression uses the searched name.
## Fixed-Point Analysis
`deadnix` repeats the scan until the result set stops changing.
This matters because dead code can depend on other dead code.
A binding can appear used during the first pass because another binding references it.
If the referring binding is dead, the referenced binding is also dead.
The loop tracks dead declaration nodes.
During later scans, uses inside already-dead bodies do not keep other bindings alive.
This fixed-point loop is the central architectural difference from simple local syntax lints.
It makes dead-code results transitive.
## Reporting Model
A result stores the scope and binding for one finding.
The display text combines both parts.
Examples include unused lambda pattern, unused lambda argument, and unused let binding.
Human output uses `ariadne`.
The reporter groups ranges by line and labels the exact binding names.
It uses different colors for different scope kinds.
JSON output exists behind a feature flag.
It emits file, message, line, column, and end column.
`NO_COLOR` disables colored human output.
## Edit Model
`deadnix --edit` converts findings into text edits.
Each edit has a start offset, end offset, and replacement string.
The edit behavior depends on scope kind.
For unused lambda pattern names, it removes the name and nearby comma or whitespace.
For unused pattern aliases, it removes the alias syntax.
For unused lambda arguments, it renames the argument with an underscore prefix.
For unused `let` assignments, it deletes the assignment.
For unused inherited names, it deletes only the inherited name.
After those edits, it parses the edited source again.
Then it removes empty `let in` constructs and empty `inherit` statements.
This cleanup pass prevents the first edit batch from leaving invalid or useless scaffolding.
## Error Handling
If a path cannot be stated or read, the CLI reports the file error and continues.
If a file has parse errors, the CLI reports those errors and skips dead-code analysis for that file.
The `--fail` flag changes exit status when findings exist.
Without `--fail`, findings do not make the command fail.
This design separates report generation from CI policy.
## Architectural Strengths
The scope model is explicit.
Each scope type declares which bindings it introduces and where those bindings are visible.
The fixed-point loop catches transitive dead code.
A binding used only by dead code also becomes dead.
The usage search handles shadowing.
That prevents common false positives in nested functions and nested `let` expressions.
The edit model is tied to analysis results.
It knows whether a binding came from a lambda, pattern, `let`, or `inherit`.
The tool has few moving parts.
That makes the analysis easy to follow compared to a general multi-rule linter.
## Architectural Limits
`deadnix` does not evaluate Nix.
It cannot know which names external callers depend on.
This matters for module arguments, package function arguments, and public attribute sets.
It has global switches rather than project-specific rule contexts.
For example, all lambda pattern names can be checked or skipped.
The tool does not know that one directory contains `callPackage` functions.
The analysis is local to one file.
It cannot see that another file imports a function and relies on its argument names.
The fixer performs text edits with byte ranges.
It must handle punctuation and whitespace carefully.
Pattern edits are harder than simple assignment deletion.
The tool has one analysis family.
It is not a framework for many unrelated rules.
## Lessons for Our Linter
Dead-code checks need a scope pass.
A per-node rule interface is not enough when a diagnostic depends on name visibility.
Represent scopes explicitly.
Each scope type should provide introduced bindings and visible bodies.
Track shadowing before reporting unused names.
A text search for an identifier is not sufficient.
Use fixed-point analysis for transitive dead code.
One pass misses names used only by dead declarations.
Separate policy from mechanics.
The mechanics can find unused names.
Repository policy decides whether module arguments, package arguments, or underscore names count.
Treat autofix as scope-specific.
Removing a pattern name is not the same operation as deleting a `let` assignment.
Run cleanup after edits.
Dead-code fixes can create empty `let` expressions and empty `inherit` statements.
## Open Design Questions for Our Linter
- Should dead-code analysis be part of `nix-style-linter`, or a separate command?
- Should policy vary by path, file kind, or suppression comment?
- Should recursive attribute-set members ever become removable findings?
- Should underscore names mean ignored, or should used underscore names be errors?
- Should the linter report unused names without offering autofix first?
- Should cleanup rules run as ordinary rules or as a post-fix pass?

View File

@@ -1,102 +0,0 @@
# Hyprland Lua Migration Plan
## Problem Statement
The Hyprland compositor config uses Home Manager's hyprlang output.
Hyprland deprecates hyprlang for the main compositor config.
The repository needs to generate a Lua config without changing desktop behavior.
## Solution
Keep the config owned by Nix and Home Manager.
Switch the Hyprland Home Manager config output to Lua.
Convert only the settings whose shape changes under Lua.
This is not a desktop redesign.
The change preserves the current monitors, binds, window rules, environment variables, and UWSM startup.
## User Stories
1. As the operator, I want Hyprland to load `hyprland.lua`, so that startup no longer warns about hyprlang deprecation.
2. As the operator, I want existing keyboard binds to keep working, so that the desktop behavior stays unchanged.
3. As the operator, I want host monitor layout to stay unchanged, so that the physical display setup does not move.
4. As the operator, I want session environment variables to stay in the compositor config, so that Wayland and cursor behavior stays unchanged.
5. As the operator, I want auxiliary module binds to keep working, so that launcher, lock, screenshot, clipboard, notifications, and recording flows stay usable.
## Implementation Decisions
1. Use Home Manager's Lua config generator.
Set the Hyprland config type to Lua and keep package ownership unchanged.
2. Keep ordinary compositor options declarative in Nix.
Move input, general, decoration, xwayland, animation, and layout settings into the Lua config structure.
3. Remove hyprlang-only variables from generated config.
Keep modifier and terminal values as Nix locals or helper inputs.
4. Add minimal local helpers for Lua calls.
Use helpers only for repeated bind, dispatcher, command, environment, and rule shapes.
5. Convert all bind strings to Lua bind call data.
Preserve key combinations, dispatchers, commands, repeat behavior, and locked-session behavior.
6. Convert contributed binds in their owning modules.
Keep each bind near the module that owns the tool it launches.
7. Convert monitor strings to Lua monitor records.
Preserve output names, modes, positions, scale values, and rotation.
8. Convert the screenshot window rule to the Lua rule shape.
Preserve the Satty class matcher and floating behavior.
9. Convert environment strings to Lua environment calls.
Preserve the Wayland flag and cursor variables.
10. Delete or update comments that describe hyprlang syntax.
Keep comments that explain behavior or non-obvious invariants.
## Testing Decisions
1. Run the repository Nix formatting and style checks for modified Nix files.
2. Evaluate or build the affected host configuration.
This proves Home Manager accepts the Lua settings structure.
3. Confirm the generated config file uses Lua.
The expected output is `hyprland.lua`, not `hyprland.conf`.
4. Verify Hyprland accepts the generated Lua config.
Use Hyprland config verification when the installed build supports it.
Otherwise reload the compositor and inspect errors.
5. Smoke test the changed desktop surface.
Test one bind from each source area after reload.
6. Check the explicit monitor layout on the host that declares it.
Confirm resolution, position, scale, and rotation stayed unchanged.
## Out of Scope
- Do not split the config into Lua modules.
- Do not hand-write a full Lua config.
- Do not add new Hyprland behavior.
- Do not change UWSM startup.
- Do not redesign binds, monitor layout, or visual settings.
- Do not change hyprlock, hypridle, waybar, mako, or rofi settings beyond their Hyprland binds.
## Further Notes
Home Manager documents `configType = "lua"` for Hyprland.
It maps settings to `hl.*` calls.
It supports `_args` for multi-argument calls.
It supports raw Lua expressions with `lib.generators.mkLuaInline`.
The riskiest parts are binds, monitor declarations, and window rules.
Those parts changed shape more than scalar config sections.
Assumption: the pinned Home Manager input contains the documented Lua Hyprland generator.
Validate this during the configuration build.

View File

@@ -0,0 +1,15 @@
---
aliases:
- "0001"
- TODO CLI
date-created: 2026-10-14 00:49
date-modified: 2026-10-14 01:22
# Allowed: open, closed
status: open
blocked-by: []
subtasks: []
---
# TODO CLI
Write a TODO CLI that enforces the task workflow in this vault deterministically. This allows agents to participate in task management with minimal cost instead of relying on markdown generation and parsing arbitrary metadata while maintaining human readability.

View File

@@ -1,5 +1,5 @@
--- ---
date-created: {{date}} {{time}} date-created: "{{date}} {{time}}"
--- ---
# {{title}} # {{title}}

View File

@@ -0,0 +1,10 @@
---
date-created: "{{date}} {{time}}"
status: open # Allowed: open, closed
blocked-by:
-
subtasks:
-
---
# {{title}}

View File

@@ -1,12 +1,16 @@
# active projects # active projects
- ariadne
- machoke
- falinks
- dotfiles - dotfiles
- nix-style / refactor - nix-style / refactor
## Inactive for now
- ariadne
- machoke
- falinks
# project ideas # project ideas
- onepacerr (requires raichu migration)
- obsidian-source-notes (`ingest-*` skills) - obsidian-source-notes (`ingest-*` skills)
- obsidian-game-backlog - obsidian-game-backlog
- whisper dictation - whisper dictation
@@ -16,7 +20,6 @@
- home assistant agent integration - home assistant agent integration
- wallpaper randomizer - wallpaper randomizer
- ebook tool (might be an overlap with `ingest-ebook`) - ebook tool (might be an overlap with `ingest-ebook`)
- amazon / actualbudget exporter
- vim-obsidian-markdown - vim-obsidian-markdown
- dictionary cli tool - dictionary cli tool
- kitchen.md - kitchen.md
@@ -24,6 +27,8 @@
- random number generator utility - random number generator utility
- reading material helper - reading material helper
- valheim progression DSL + graph - valheim progression DSL + graph
- Pi:
- vim bindings
# migration # migration
@@ -99,6 +104,7 @@
- https://www.youtube.com/watch?v=kzcI5F4tGiU - https://www.youtube.com/watch?v=kzcI5F4tGiU
- https://www.youtube.com/watch?v=iKwPaB5TUdI - https://www.youtube.com/watch?v=iKwPaB5TUdI
- https://www.youtube.com/watch?v=A-CtdQFRX64 - https://www.youtube.com/watch?v=A-CtdQFRX64
- https://www.youtube.com/watch?v=MHPRnM38Dyc
# tech debt # tech debt
@@ -144,23 +150,23 @@ Review Nix files in entrypoint order:
- [x] `configs/nixos/modules/desktop/apps/signal.nix` - [x] `configs/nixos/modules/desktop/apps/signal.nix`
- [x] `configs/nixos/modules/desktop/apps/steam.nix` - [x] `configs/nixos/modules/desktop/apps/steam.nix`
- [x] `configs/nixos/modules/desktop/apps/vencord.nix` - [x] `configs/nixos/modules/desktop/apps/vencord.nix`
- [ ] `configs/nixos/modules/desktop/session/hyprland/default.nix` - [x] `configs/nixos/modules/desktop/session/hyprland/default.nix`
- [ ] `configs/nixos/modules/desktop/session/hyprland/hypridle.nix` - [x] `configs/nixos/modules/desktop/session/hyprland/hypridle.nix`
- [ ] `configs/nixos/modules/desktop/session/hyprland/hyprlock.nix` - [x] `configs/nixos/modules/desktop/session/hyprland/hyprlock.nix`
- [ ] `configs/nixos/modules/desktop/session/login.nix` - [x] `configs/nixos/modules/desktop/session/login.nix`
- [ ] `configs/nixos/modules/desktop/session/mako.nix` - [x] `configs/nixos/modules/desktop/session/mako.nix`
- [ ] `configs/nixos/modules/desktop/session/portals.nix` - [x] `configs/nixos/modules/desktop/session/portals.nix`
- [ ] `configs/nixos/modules/desktop/session/rofi.nix` - [x] `configs/nixos/modules/desktop/session/rofi.nix`
- [ ] `configs/nixos/modules/desktop/session/waybar.nix` - [x] `configs/nixos/modules/desktop/session/waybar.nix`
- [ ] `configs/nixos/modules/desktop/utilities/audio.nix` - [x] `configs/nixos/modules/desktop/utilities/audio.nix`
- [ ] `configs/nixos/modules/desktop/utilities/clipboard.nix` - [x] `configs/nixos/modules/desktop/utilities/clipboard.nix`
- [ ] `configs/nixos/modules/desktop/utilities/osd.nix` - [x] `configs/nixos/modules/desktop/utilities/osd.nix`
- [ ] `configs/nixos/modules/desktop/utilities/recording.nix` - [x] `configs/nixos/modules/desktop/utilities/recording.nix`
- [ ] `configs/nixos/modules/desktop/utilities/screenshot.nix` - [x] `configs/nixos/modules/desktop/utilities/screenshot.nix`
- [ ] `configs/nixos/modules/desktop/utilities/theming.nix` - [x] `configs/nixos/modules/desktop/utilities/theming.nix`
- [ ] `configs/nixos/modules/desktop/utilities/userdirs.nix` - [x] `configs/nixos/modules/desktop/utilities/userdirs.nix`
- [ ] `configs/nixos/modules/services/actualbudget.nix` - [x] `configs/nixos/modules/services/actualbudget.nix`
- [ ] `configs/nixos/modules/services/copyparty.nix` - [x] `configs/nixos/modules/services/copyparty.nix`
- [ ] `configs/nixos/modules/services/homepage.nix` - [ ] `configs/nixos/modules/services/homepage.nix`
- [ ] `configs/nixos/modules/services/navidrome.nix` - [ ] `configs/nixos/modules/services/navidrome.nix`
- [ ] `configs/nixos/modules/services/reverse-proxy.nix` - [ ] `configs/nixos/modules/services/reverse-proxy.nix`
@@ -181,7 +187,7 @@ Review Nix files in entrypoint order:
- [ ] `configs/nixos/modules/shell/tmux/default.nix` - [ ] `configs/nixos/modules/shell/tmux/default.nix`
- [ ] `configs/nixos/modules/shell/yazi.nix` - [ ] `configs/nixos/modules/shell/yazi.nix`
- [ ] `src/dotfiles/guests.nix` - [ ] `src/dotfiles/guests.nix`
- [ ] `configs/nixos/guests/actualbudget.nix` - [x] `configs/nixos/guests/actualbudget.nix`
- [ ] `configs/nixos/guests/copyparty.nix` - [ ] `configs/nixos/guests/copyparty.nix`
- [ ] `configs/nixos/guests/homepage.nix` - [ ] `configs/nixos/guests/homepage.nix`
- [ ] `configs/nixos/guests/navidrome.nix` - [ ] `configs/nixos/guests/navidrome.nix`