chore: update

This commit is contained in:
2026-09-24 23:11:47 -04:00
parent 7cc066b55d
commit 80db2b61e6
5 changed files with 46 additions and 1109 deletions

View File

@@ -4,41 +4,21 @@
"type": "split",
"children": [
{
"id": "e581539f950ca1bd",
"id": "6d9088d392cb8604",
"type": "tabs",
"children": [
{
"id": "0c587e5452c16787",
"id": "22a4c1f201a1b00e",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "TODO.md",
"file": "00 INBOX/valheim progression.md",
"mode": "source",
"source": true
},
"icon": "lucide-file",
"title": "TODO"
}
}
]
},
{
"id": "72e6501450a46859",
"type": "tabs",
"children": [
{
"id": "06bc3d52246b600b",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "00 INBOX/ai/steamcmd servers.md",
"mode": "source",
"source": true
},
"icon": "lucide-file",
"title": "steamcmd servers"
"title": "valheim progression"
}
}
]
@@ -111,7 +91,7 @@
"state": {
"type": "localgraph",
"state": {
"file": "TODO.md",
"file": "00 INBOX/valheim progression.md",
"options": {
"collapse-filter": true,
"search": "",
@@ -134,7 +114,7 @@
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"scale": 1,
"scale": 0.6666666666666666,
"close": true
}
},
@@ -251,43 +231,42 @@
"table-editor-obsidian:Advanced Tables Toolbar": false
}
},
"active": "0c587e5452c16787",
"active": "22a4c1f201a1b00e",
"lastOpenFiles": [
"00 INBOX/how to setup a steamcmd server.md",
"00 INBOX/ai/steamcmd servers.md",
"03 wrk/ariadne.md",
"00 INBOX/ai/ariadne-cli-skeleton.md",
"00 INBOX/ai/ariadne-project-goals.md",
"00 INBOX/ai/statix-architecture.md",
"00 INBOX/ai/nix-statix-deadnix-rules.md",
"00 INBOX/let order name resolution.md~",
"00 INBOX/nix name resolution.md",
"00 INBOX/let order name resolution.md",
"00 INBOX/nix name resolution.md~",
"00 INBOX/valheim progression.md",
"TODO.md",
"00 INBOX/ai/dictionary-cli.md",
"00 INBOX/ai/dictionary-skill.md",
"03 wrk/falinks.md",
"03 wrk/machoke.md",
"00 INBOX/home assistant agent integration.md",
"02 notes/falinks public surfaces.md",
"00 INBOX/ai/deadnix-architecture.md",
"TODO.md~",
"00 INBOX/ai/xatu-rule-implementation-order.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/ariadne-pipeline-stage-boundary.md",
"00 INBOX/ai/ariadne-project-goals.md",
"02 notes/steamcmd server setup.md",
"00 INBOX/how to setup a steamcmd server.md",
"00 INBOX/ai/clew-ast-exploration.md~",
"03 wrk/machoke.md~",
"02 notes/machoke public surface.md",
"03 wrk/falinks.md~",
"99 meta/templates/checkmark.md",
"AGENTS.md",
"99 meta/attachments/bases/unlinked mentions.base",
"99 meta/templates/note.md",
"AGENTS.md.md",
"00 INBOX/ai",
"03 wrk/ai",
"03 wrk/brain.md",
"01 sources/second brain setup for doing.md",
"00 INBOX/ai/hoopa design rewrite proposal 2.md",
"00 INBOX/ai/hoopa decisions.md",
"00 INBOX/ai/second brain setup for doing.md",
"00 INBOX/ai/hoopa design rewrite proposal.md",
"00 INBOX/ai/hoopa design review findings.md",
"00 INBOX/ai/hoopa follow up decisions.md",
"01 sources/obsidian and ai were made for each other.md",
"99 meta/Untitled",
"03 wrk",
"02 notes",
"01 sources",
"30 wrk"
"99 meta/Untitled"
]
}

View File

@@ -1,791 +0,0 @@
# Nix Statix and Deadnix Rules
## Purpose
This note rewrites the current `statix` and `deadnix` findings as possible `nix-style` rules.
Use the `Decision` line on each rule for `keep`, `discard`, `update`, or `need-more-info`.
## Source Baseline
- `statix` rule names come from `nix run --inputs-from . nixpkgs#statix -- list`.
- `deadnix` checks come from `nix run --inputs-from . nixpkgs#deadnix -- --help`.
- `deadnix` reports categories instead of named rules.
- At collection time, `src/dotfiles/apps.nix` ran `statix fix .` and `deadnix --edit` in `nix run .#fix`.
- At collection time, `statix.toml` disabled `repeated_keys`.
## Statix Rules
### `bool_comparison`
Decision: keep
Source: `statix` `W01`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Do not compare a boolean expression to `true` or `false`.
Use the expression directly for true checks.
Use `!` for false checks.
Avoid:
```nix
x == true
x == false
```
Use:
```nix
x
!x
```
#### Rationale
Boolean literal comparisons add noise without changing behavior.
Direct predicates make conditions easier to scan.
### `empty_let_in`
Decision: keep
Source: `statix` `W02`.
Status at collection time: enabled.
Autofix: yes when the `let` body has no comments.
#### Rule
Do not keep a `let` expression that declares no bindings.
Replace it with its body.
Avoid:
```nix
let in value
```
Use:
```nix
value
```
#### Rationale
An empty `let` adds syntax without naming a dependency.
It usually remains after editing or debugging.
### `manual_inherit`
Decision: Keep
Source: `statix` `W03`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Use `inherit` when an attribute assignment repeats the same name as its value.
Avoid:
```nix
{
package = package;
}
```
Use:
```nix
{
inherit package;
}
```
#### Rationale
`inherit` states that the attribute comes from the current scope.
It removes duplicated names.
### `manual_inherit_from`
Decision:
Source: `statix` `W04`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Use `inherit (source)` when an assignment selects an attribute with the same name.
Avoid:
```nix
package = pkgs.package;
```
Use:
```nix
inherit (pkgs) package;
```
#### Rationale
`inherit (source)` states that the value comes from an attribute set.
It avoids repeated selector names.
### `legacy_let_syntax`
Decision: keep
Source: `statix` `W05`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Do not use legacy `let { ... }` syntax.
Use documented Nix expressions.
Avoid:
```nix
let {
body = x + y;
x = 2;
y = 3;
}
```
Use:
```nix
rec {
body = x + y;
x = 2;
y = 3;
}.body
```
#### Rationale
Legacy `let` syntax is undocumented.
Documented forms are clearer to tools and readers.
### `collapsible_let_in`
Decision: keep
Source: `statix` `W06`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Collapse a `let` expression when its body is another `let` expression.
Put both binding groups in one `let` when scope permits it.
Avoid:
```nix
let
a = 1;
in
let
b = 2;
in
a + b
```
Use:
```nix
let
a = 1;
b = 2;
in
a + b
```
#### Rationale
Nested `let` expressions add structure when they do not add a scope boundary.
A single binding group shows the local dependencies together.
### `eta_reduction`
Decision: keep
Source: `statix` `W07`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Remove a lambda that only passes its argument to another function.
Keep the target function name when that preserves behavior.
Avoid:
```nix
map (x: double x) values
```
Use:
```nix
map double values
```
#### Rationale
The wrapper lambda adds no behavior.
The direct function reference exposes the operation.
### `useless_parens`
Decision: keep
Source: `statix` `W08`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Remove parentheses around expressions when they do not affect parsing.
Avoid:
```nix
let
double = (x: x * 2);
in
(value)
```
Use:
```nix
let
double = x: x * 2;
in
value
```
#### Rationale
Redundant parentheses make expressions harder to scan.
Required precedence parentheses remain valid.
### `empty_pattern`
Decision: keep
Source: `statix` `W10`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Use `_` instead of an empty variadic function pattern when the argument is ignored.
Do not apply this rule to NixOS-style modules that declare `imports`.
Avoid:
```nix
{ ... }: value
```
Use:
```nix
_: value
```
#### Rationale
`_` directly marks an ignored argument.
An empty variadic pattern looks like partial destructuring.
### `redundant_pattern_bind`
Decision: keep
Source: `statix` `W11`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Do not bind a variadic pattern when the pattern captures no fields.
Use a single argument name instead.
Avoid:
```nix
args @ { ... }: args.nixpkgs
```
Use:
```nix
args: args.nixpkgs
```
#### Rationale
The empty pattern adds no destructuring value.
A named argument shows that the whole argument is used.
### `unquoted_uri`
Decision: keep
Source: `statix` `W12`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Quote URI literals.
Do not use Nix's special unquoted URI syntax.
Avoid:
```nix
url = github:owner/repository;
```
Use:
```nix
url = "github:owner/repository";
```
#### Rationale
Quoted strings work for all URI forms.
They avoid ambiguity with punctuation after the URI.
### `empty_inherit`
Decision: keep
Source: `statix` `W14`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Remove `inherit` statements that inherit no names.
Avoid:
```nix
inherit;
```
Use no replacement.
#### Rationale
An empty `inherit` has no effect.
It usually remains after a refactor.
### `deprecated_to_path`
Decision: keep
Source: `statix` `W17`.
Status at collection time: enabled.
Autofix: no.
#### Rule
Do not use `builtins.toPath` or unqualified `toPath`.
Use explicit path construction instead.
Avoid:
```nix
builtins.toPath "/path"
```
Use:
```nix
/. + "/path"
./. + "/relative-path"
```
#### Rationale
`toPath` is deprecated.
Explicit path construction states whether the result is absolute or relative.
### `bool_simplification`
Decision: keep
Source: `statix` `W18`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Use `!=` instead of negating an equality comparison.
Avoid:
```nix
!(x == y)
```
Use:
```nix
x != y
```
#### Rationale
The direct operator expresses inequality without nested boolean syntax.
### `useless_has_attr`
Decision: keep
Source: `statix` `W19`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Use `or` when an `if` expression only checks for an attribute before selecting it.
Avoid:
```nix
if x ? a then x.a else default
```
Use:
```nix
x.a or default
```
#### Rationale
The `or` form is the Nix operator for attribute defaults.
It avoids repeating the attribute path.
### `repeated_keys`
Decision: update so that it doesn't conflict with the custom module convention.
Source: `statix` `W20`.
Status at collection time: disabled in `statix.toml`.
Autofix: no.
#### Rule
Group repeated attribute path prefixes in a nested attribute set.
Apply this rule only when at least three sibling assignments share the same first key.
Do not apply it inside `rec` attribute sets.
Avoid:
```nix
{
services.a = 1;
services.b = 2;
services.c = 3;
}
```
Use:
```nix
{
services = {
a = 1;
b = 2;
c = 3;
};
}
```
#### Rationale
Grouping repeated prefixes reduces visual repetition.
The local config disables this rule, so it needs an explicit decision before promotion.
### `empty_list_concat`
Decision: keep
Source: `statix` `W23`.
Status at collection time: enabled.
Autofix: yes.
#### Rule
Remove list concatenation with an empty list.
Avoid:
```nix
[] ++ values
values ++ []
```
Use:
```nix
values
```
#### Rationale
Concatenating `[]` changes no values.
It usually remains after conditional list construction changed.
## Deadnix Checks
### Unused `let` bindings
Decision: keep
Source: `deadnix` default check.
Status at collection time: active in `nix run .#fix`.
Autofix: yes.
#### Rule
Remove `let` bindings when no live expression uses their names.
Do not keep a binding only because another dead binding uses it.
Use `# deadnix: skip` only for intentional unused declarations.
Avoid:
```nix
let
unused = 1;
in
value
```
Use:
```nix
value
```
#### Rationale
Unused bindings hide the dependencies that matter.
They also make refactors look unfinished.
### Unused `inherit` names
Decision: keep
Source: `deadnix` default check.
Status at collection time: active in `nix run .#fix`.
Autofix: yes.
#### Rule
Remove names from `inherit` statements when no live expression uses those names.
Remove the whole `inherit` statement when it becomes empty.
Avoid:
```nix
let
inherit (builtins) unused;
in
value
```
Use:
```nix
value
```
#### Rationale
Unused inherited names create the same noise as unused `let` bindings.
An empty result also overlaps with `statix` `empty_inherit`.
### Unused lambda arguments
Decision: keep
Source: `deadnix` default check.
Status at collection time: active in `nix run .#fix`.
Autofix: yes.
#### Rule
Mark an unused single lambda argument with `_`.
Do not keep a meaningful name for an ignored argument.
Avoid:
```nix
arg: value
```
Use:
```nix
_: value
```
#### Rationale
`_` states that the argument is intentionally ignored.
A named unused argument implies missing logic.
### Unused lambda pattern names
Decision: keep
Source: `deadnix` default check.
Status at collection time: active in `nix run .#fix`.
Autofix: yes.
#### Rule
Remove unused names from lambda attribute patterns.
Keep only names used by the body or defaults.
Use `...` when callers can pass extra attributes.
Avoid:
```nix
{ used, unused, ... }: used
```
Use:
```nix
{ used, ... }: used
```
#### Rationale
Pattern names usually declare dependencies.
Unused names overstate the function's inputs.
### Unused lambda pattern aliases
Decision: keep
Source: `deadnix` default check.
Status at collection time: active in `nix run .#fix`.
Autofix: yes.
#### Rule
Remove a lambda pattern alias when the body does not use the alias.
Keep the alias only when code reads the whole argument set.
Avoid:
```nix
args @ { used, ... }: used
```
Use:
```nix
{ used, ... }: used
```
#### Rationale
The alias says the function needs the complete input set.
Keeping it unused makes the function contract look wider than it is.
### Ignored underscore bindings
Decision: keep
Source: `deadnix` `--no-underscore` option.
Status at collection time: inactive; current fixer does not pass `--no-underscore`.
Autofix: no separate rule.
#### Rule
Treat bindings whose names start with `_` as intentionally ignored.
Optionally skip unused diagnostics for those names.
Avoid, when the skip policy is active:
```nix
let
_unused = expensiveValue;
in
value
```
Use only for intentional placeholders.
#### Rationale
An underscore prefix is a common marker for intentionally unused names.
Skipping all underscore bindings can hide real dead code.
### Used underscore bindings
Decision: keep
Source: `deadnix` `--warn-used-underscore` option.
Status at collection time: inactive; current fixer does not pass `--warn-used-underscore`.
Autofix: no.
#### Rule
Do not use a binding whose name starts with `_`.
Rename it to a normal name when code references it.
Avoid:
```nix
let
_package = pkgs.hello;
in
_package
```
Use:
```nix
let
package = pkgs.hello;
in
package
```
#### Rationale
The underscore prefix communicates that the name is intentionally unused.
Using it contradicts that signal.
## Notes for Decisions
- `statix` rules are mostly syntax rewrites.
- `deadnix` rules need scope analysis and dead-code reachability.
- `deadnix` does not report `rec` attribute-set bindings as removable findings.
- `statix` `repeated_keys` is present in the installed rule list but disabled locally.
- Rules that change module argument patterns need extra care around NixOS modules and `callPackage` functions.

View File

@@ -1,261 +0,0 @@
# Statix Architecture
## Purpose
This note explains how `statix` works as a Nix linter and fixer.
It focuses on architecture, not the value of each rule.
Use this note when deciding how complete our repository-local Nix linter should become.
## What Statix Is
`statix` is a syntax-tree linter for Nix.
It parses source text into a Nix AST, walks that tree, and runs rules on matching nodes.
It does not evaluate Nix.
It does not import files.
It does not know the final value of an attribute set.
It sees source structure, not runtime semantics.
That design makes `statix` fast and deterministic.
It also limits it to rules that the parser can prove from local syntax.
## Minimal Linter Vocabulary
- Source text is the bytes in a Nix file.
- A parser converts source text into a syntax tree.
- A syntax tree records nodes such as lambdas, lists, strings, and operators.
- A token is a leaf item such as an identifier, comment, or URI literal.
- A rule inspects one syntax element and optionally returns a diagnostic.
- A diagnostic reports a span and a message.
- A suggestion adds a replacement for that span.
- A fixer applies suggestions back to source text.
## Component Map
The upstream architecture document names four major components.
### CLI component
The CLI owns operator interaction.
It parses subcommands and options, loads configuration, discovers files, and prints output.
It supports these user flows:
- `check`: report diagnostics.
- `fix`: apply all safe suggestions.
- `single`: apply one suggestion at a position.
- `explain`: print the long explanation for a rule code.
- `dump`: print a sample configuration file.
- `list`: print all available rules.
### Library component
The library owns lint rules and the data structures that rules return.
It defines `Report`, `Diagnostic`, `Suggestion`, `Rule`, `Metadata`, `Explain`, and `Lint`.
Each rule is a small unit.
It receives one syntax element and returns zero or one report.
The library also contains helpers that build replacement syntax.
Those helpers parse small replacement fragments back into AST nodes.
This makes fixes use valid Nix syntax instead of raw string assembly.
### Virtual filesystem component
The virtual filesystem stores file paths and file contents in memory.
It assigns each file a small `FileId`.
The CLI reads files into this store before linting.
Diagnostics then carry `FileId` references instead of path strings.
The VFS supports sequential iteration and parallel iteration.
`statix check` uses parallel iteration across files.
### Macro component
The macro component reduces rule boilerplate.
A `lint` macro generates rule metadata, explanation support, and the static rule object.
A rule author writes the rule structure, metadata, documentation, and validation logic.
The macro wires that rule into the common linter interface.
## Check Pipeline
```mermaid
flowchart TD
A[Parse CLI options] --> B[Discover configuration]
B --> C[Build ignore set]
C --> D[Walk Nix files]
D --> E[Read files into VFS]
E --> F[Build active lint map]
F --> G[Parse each file with rnix]
G --> H[Walk syntax tree]
H --> I[Run rules matching each syntax kind]
I --> J[Collect reports]
J --> K[Render diagnostics]
K --> L[Exit 1 when reports exist]
```
The key optimization is the active lint map.
`statix` groups rules by `SyntaxKind` before it walks a file.
During traversal, it looks up only rules that declared interest in the current node kind.
This avoids running every rule on every node.
It also keeps each rule narrow.
## Rule Registration
Rules are built into the binary.
There is no dynamic rule loading.
A registry macro imports each rule module and builds a static `LINTS` vector.
The CLI filters that vector with configuration.
Disabled rule names disappear from the active lint map.
This model has a simple trust boundary:
- all rule code ships with the tool;
- configuration can disable rules;
- configuration cannot define new rules.
## Rule Execution Model
Each rule declares the syntax kinds it wants to inspect.
For example, one rule can ask for binary-operation nodes.
Another rule can ask for URI tokens.
The traversal visits tree entries with `preorder_with_tokens`.
That includes both nodes and tokens.
Rules can therefore inspect structured AST nodes and raw tokens.
A rule returns a `Report` when it finds a problem.
The report contains one or more diagnostics.
Each diagnostic has a source range and a message.
A diagnostic can also include a `Suggestion`.
Rules do not share state with each other.
They also do not run a whole-program analysis.
A rule sees the syntax element passed to it and whatever children or parents it chooses to inspect.
## Parse Errors
`statix` turns parser errors into reports.
Those reports use code `0` and severity `Error`.
Parse errors flow through the same output machinery as lint findings.
This lets the CLI render syntax errors and lint diagnostics with one reporter.
## Output Model
`statix` has several output formats.
The human format uses `ariadne` to render annotated source snippets.
The `errfmt` format prints one compact line per diagnostic.
The JSON format exists behind a feature flag.
The output layer receives reports and the VFS.
It converts byte ranges into line and column positions at render time.
This separates rule logic from display logic.
Rules do not need to know how the terminal or editor consumes diagnostics.
## Fix Pipeline
A fixable diagnostic contains a `Suggestion`.
A suggestion contains the range to replace and replacement syntax.
`statix fix` uses the same parser and rule walk as `check`, but filters for reports that contain suggestions.
It then sorts and filters suggestions to avoid overlapping edits.
The fixer applies selected reports to a copy of the source.
Then it parses and scans the changed source again.
It repeats this process until no more suggestions appear.
That iterative design matters because one edit can expose a later edit.
It also avoids applying stale ranges after source text changes.
## Single Fix Pipeline
The `single` command applies one fix at a requested line and column.
It converts the line and column to a byte offset.
Then it finds the first fixable report whose diagnostic range contains that offset.
This shape supports editor workflows.
An editor can request a fix for the diagnostic under the cursor.
## Configuration Model
Configuration is small.
A `statix.toml` file can disable rules by name.
It can also add ignore globs.
The CLI discovers configuration by walking ancestors from the configured path.
If no config exists, it uses defaults.
The command also respects `.gitignore` unless unrestricted mode is enabled.
Ignore handling happens before parsing.
## Architectural Strengths
`statix` has a clean separation between these concerns:
- file discovery;
- parsing;
- rule selection;
- rule execution;
- reporting;
- fixing.
Rules stay small because the framework handles traversal and output.
Fixes are safer because replacement syntax comes from parsed Nix fragments.
The lint map avoids unnecessary rule calls.
Parallel file traversal improves throughput for large trees.
The static rule registry is easy to audit.
It fits a repository-local linter well when we want deterministic behavior.
## Architectural Limits
`statix` does not evaluate Nix.
It cannot know imported module results, overlays, option merges, or generated attributes.
Most rules must remain local syntax rules.
A rule that needs semantic knowledge can easily become wrong.
Fixes rely on byte ranges in source text.
They must avoid overlapping ranges.
They must rerun after edits because previous ranges become stale.
The macro system reduces rule boilerplate, but it hides generated behavior.
That tradeoff is useful for many rules, but it can make debugging harder.
## Lessons for Our Linter
Start with a clear rule interface.
A rule should declare its name, matching syntax kinds, and diagnostic output.
Keep parsing and diagnostics separate from output.
The same diagnostic should support terminal output, editor output, and tests.
Use syntax-kind indexing when rules are local.
It prevents unnecessary calls and makes rule ownership explicit.
Treat autofix as a second contract.
A diagnostic can exist without a safe fix.
A fix must define exact replacement ranges and avoid overlap.
Rerun analysis after applying a batch of fixes.
Source ranges become invalid after edits.
Do not add Nix evaluation accidentally.
If a rule needs evaluated values, design that as a separate analysis layer.
## Open Design Questions for Our Linter
- Should our linter support fixes, or stay report-only for now?
- Should rules register by syntax kind like `statix`?
- Should our suppression model live in the parser layer or rule layer?
- Should parse errors use the same diagnostic type as style rules?
- Should editor-friendly output be part of the first complete architecture?
- Should future semantic rules use a separate pass from local syntax rules?

View File

@@ -22,6 +22,8 @@
- dictionary cli tool
- kitchen.md
- Rust project creator
- random number generator utility
- reading material helper
# migration
@@ -98,6 +100,13 @@
- https://www.youtube.com/watch?v=kzcI5F4tGiU
- https://www.youtube.com/watch?v=iKwPaB5TUdI
- https://www.youtube.com/watch?v=A-CtdQFRX64
- xatu literature
- https://rust-analyzer.github.io/book/contributing/syntax.html?utm_source=chatgpt.com
- https://github.com/dotnet/roslyn/blob/main/docs/compilers/Design/Red-Green%20Trees.md?utm_source=chatgpt.com
- https://github.com/rust-analyzer/rowan/blob/master/examples/s_expressions.rs?utm_source=chatgpt.com
- https://dl.acm.org/doi/10.1145/964001.964011
- https://web.cecs.pdx.edu/~apt/esop15.pdf?utm_source=chatgpt.com
- https://www.researchgate.net/publication/2377179_Efficient_and_flexible_incremental_parsing?utm_source=chatgpt.com
# tech debt