Skip to content

docs: add a stability policy and record the internal/git structure decision - #417

Merged
jongio merged 3 commits into
mainfrom
docs/1-0-stability-and-versions
Aug 27, 2026
Merged

docs: add a stability policy and record the internal/git structure decision#417
jongio merged 3 commits into
mainfrom
docs/1-0-stability-and-versions

Conversation

@jongio

@jongio jongio commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Two commits, both writing down decisions that were previously implicit.

1. Stability policy (docs/stability.md)

grut is at v0.8.0 with real usage (v0.7.1 pulled ~1,800 downloads) and no written statement of what is stable. That means every config key, JSON field, and exit code is a contract by accident.

The doc names the surface deliberately:

  • command and flag names
  • --json output shapes (ten commands, fourteen flags counting subcommands)
  • --check exit codes on clean, doctor, status
  • config TOML keys
  • the extension manifest, permission model, and Lua/WASM host functions
  • default keybindings

And what stays free to change: everything under internal/ (Go already prevents outside code from importing it, so it was never an API), TUI rendering, and log formats.

The more useful half is the pre-1.0 checklist. Freezing a surface nobody has written down does not achieve much, and right now:

  • only 2 of the 10 --json commands have their shape documented
  • the --check exit codes are undocumented
  • the extension manifest has no schema version
  • there is no stated platform or Go version floor

Also refreshes the pinned-install examples in README and CONTRIBUTING, which still used v0.1.0.

2. Why internal/git is not split (internal/git/doc.go)

doc.go carried a TODO(arch) saying the package should be split into sub-packages per #167. I looked at doing it and concluded it should not be done, so the TODO is replaced with the reasoning rather than left inviting someone to try.

It is not possible as written. Every domain file holds methods on *Client, and Go does not allow declaring methods on a type owned by another package. A split means either fragmenting Client into per-domain clients, which destroys the single GitClient interface that the AI middleware implements and every panel consumes, or demoting the methods to free functions, which rewrites every call site while the sub-packages still import git for the Client type.

The shared state is load-bearing. One OpQueue and one Cache serialise and cache across domains, because a branch operation and a stash operation must not race. Per-domain packages each need that state back, so the coupling relocates rather than dissolves.

The cost is not being felt. Roughly 4,000 lines over 30 files, averaging under 140 lines each, already separated and named by domain. The largest is undo.go at 452 lines, within the repo's own 500-line guidance. The file layout is the split the review asked for.

#167 was an automated review that counted files and domains. That heuristic is a reasonable smell test, but here it fires on a package whose cohesion is deliberate. The doc now records the signal actually worth watching: per-file size, not file count.

Verification

go build ./..., go vet ./..., gofmt -l clean, go test ./... green. All doc links resolve.

jongio and others added 3 commits August 27, 2026 14:52
grut is at v0.8.0 with real usage and no written statement of what is stable,
so every config key, JSON field and exit code is currently a contract by
accident. docs/stability.md names the surface deliberately: command and flag
names, --json output shapes, --check exit codes, config keys, the extension
manifest and host functions, and default keybindings. It also names what stays
free to change, notably everything under internal/ (which Go already prevents
outside code from importing) and TUI rendering.

The more useful half is the list of what has to happen before 1.0 is worth
tagging. Only two of the ten commands that emit --json have their shape
documented, the --check exit codes are undocumented, and the extension
manifest has no schema version. Freezing a surface nobody has written down
does not achieve much.

Also updates the pinned-install examples in README and CONTRIBUTING, which
still used v0.1.0 and implied the project was seven minor versions younger
than it is.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 98ae0c9c-bdbe-418e-8488-15082edca7dc
doc.go carried a TODO(arch) saying internal/git should be split into
sub-packages per issue #167. Acting on it is not possible as written, and the
TODO invited someone to try.

Every domain file holds methods on *Client, and Go does not allow declaring
methods on a type owned by another package. A split would mean fragmenting
Client into per-domain clients, which destroys the single GitClient interface
the AI middleware implements and every panel consumes, or demoting the methods
to free functions, which rewrites every call site while the sub-packages still
import git for the Client type.

The shared state also argues against it. One OpQueue and one Cache serialise
and cache across domains, because a branch operation and a stash operation
must not race. Per-domain packages would each need that shared state back, so
the coupling relocates rather than dissolves.

And the cost is not being felt: roughly 4,000 lines over 30 files, under 140
lines each, already separated and named by domain. The file layout is the
split the review asked for.

Replaces the TODO with that reasoning plus the signal actually worth watching,
which is per-file size rather than file count.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 98ae0c9c-bdbe-418e-8488-15082edca7dc
Task 20 reads as panel wiring, but AnalyzeBranches is not reachable from any
panel. Point at #418 so the next person does not rediscover that from scratch.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 98ae0c9c-bdbe-418e-8488-15082edca7dc
@github-actions

Copy link
Copy Markdown
Contributor

Benchmarks: darwin-arm64

📊 Results vs main (same-runner) , click to expand

Note

No significant regressions on darwin-arm64 (threshold: 40%, same-runner comparison).

@github-actions

Copy link
Copy Markdown
Contributor

Benchmarks: linux-amd64

📊 Results vs main (same-runner) , click to expand
goos: linux
goarch: amd64
pkg: github.com/jongio/grut/internal/ai
cpu: AMD EPYC 7763 64-Core Processor                
                                       │ bench-baseline.txt │         bench-current.txt         │
                                       │       sec/op       │   sec/op     vs base              │
RedactContent/small_500B-4                      120.0µ ± 2%   112.9µ ± 2%  -5.93% (p=0.002 n=6)
RedactContent/medium_5KB-4                      1.591m ± 3%   1.568m ± 1%  -1.48% (p=0.004 n=6)
RedactContent/large_50KB-4                      16.95m ± 5%   16.88m ± 1%       ~ (p=0.240 n=6)
RedactContent_NoMatch/small_500B-4              173.0µ ± 5%   163.5µ ± 4%  -5.51% (p=0.002 n=6)
RedactContent_NoMatch/medium_5KB-4              1.960m ± 5%   1.884m ± 3%  -3.88% (p=0.002 n=6)
RedactContent_NoMatch/large_50KB-4              20.39m ± 2%   20.29m ± 1%       ~ (p=0.589 n=6)
ShouldExcludeFile/match_builtin-4               67.75n ± 3%   68.10n ± 2%       ~ (p=0.240 n=6)
ShouldExcludeFile/match_user_pattern-4          2.903µ ± 1%   2.897µ ± 5%       ~ (p=0.457 n=6)
ShouldExcludeFile/no_match-4                    2.013µ ± 1%   2.010µ ± 0%       ~ (p=0.448 n=6)
NewRedactor-4                                   788.5n ± 2%   786.3n ± 2%       ~ (p=0.699 n=6)
geomean                                         76.63µ        75.25µ       -1.81%

                                       │ bench-baseline.txt │            bench-current.txt            │
                                       │        B/op        │     B/op       vs base                  │
RedactContent/small_500B-4                  2.879Ki ±  0%     2.879Ki ±  0%       ~ (p=0.727 n=6)
RedactContent/medium_5KB-4                  36.43Ki ±  0%     36.30Ki ±  0%  -0.38% (p=0.041 n=6)
RedactContent/large_50KB-4                  370.3Ki ±  0%     370.3Ki ±  0%       ~ (p=0.900 n=6)
RedactContent_NoMatch/small_500B-4            5.000 ±   ?       0.000 ±   ?       ~ (p=0.061 n=6)
RedactContent_NoMatch/medium_5KB-4            76.00 ± 84%       63.00 ± 94%       ~ (p=0.504 n=6)
RedactContent_NoMatch/large_50KB-4            126.0 ± 93%       123.0 ± 93%       ~ (p=0.210 n=6)
ShouldExcludeFile/match_builtin-4             0.000 ±  0%       0.000 ±  0%       ~ (p=1.000 n=6) ¹
ShouldExcludeFile/match_user_pattern-4        0.000 ±  0%       0.000 ±  0%       ~ (p=1.000 n=6) ¹
ShouldExcludeFile/no_match-4                  0.000 ±  0%       0.000 ±  0%       ~ (p=1.000 n=6) ¹
NewRedactor-4                               1.320Ki ±  0%     1.320Ki ±  0%       ~ (p=1.000 n=6) ¹
geomean                                                   ²                  ?                    ² ³
¹ all samples are equal
² summaries must be >0 to compute geomean
³ ratios must be >0 to compute geomean

                                       │ bench-baseline.txt │         bench-current.txt          │
                                       │     allocs/op      │ allocs/op   vs base                │
RedactContent/small_500B-4                     12.00 ± 0%     12.00 ± 0%       ~ (p=1.000 n=6) ¹
RedactContent/medium_5KB-4                     60.00 ± 0%     60.00 ± 0%       ~ (p=1.000 n=6) ¹
RedactContent/large_50KB-4                     483.0 ± 0%     483.0 ± 0%       ~ (p=1.000 n=6)
RedactContent_NoMatch/small_500B-4             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
RedactContent_NoMatch/medium_5KB-4             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
RedactContent_NoMatch/large_50KB-4             0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ShouldExcludeFile/match_builtin-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ShouldExcludeFile/match_user_pattern-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
ShouldExcludeFile/no_match-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
NewRedactor-4                                  5.000 ± 0%     5.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                   ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/jongio/grut/internal/config
                                   │ bench-baseline.txt │         bench-current.txt         │
                                   │       sec/op       │   sec/op     vs base              │
Validate/valid_defaults-4                   2.386µ ± 1%   2.373µ ± 1%       ~ (p=0.390 n=6)
Validate/with_arrays-4                      9.472µ ± 1%   9.439µ ± 1%       ~ (p=0.418 n=6)
Validate/invalid_multi_error-4              5.416µ ± 1%   5.380µ ± 2%       ~ (p=0.699 n=6)
ParseDefaultsTOML-4                         34.13µ ± 0%   34.15µ ± 1%       ~ (p=0.818 n=6)
RejectEmbeddedKeys/clean_config-4           1.564µ ± 1%   1.555µ ± 3%       ~ (p=0.420 n=6)
RejectEmbeddedKeys/with_patterns-4          9.742µ ± 2%   9.844µ ± 3%       ~ (p=0.589 n=6)
AppendEnumErr/match_first-4                 33.95n ± 2%   34.25n ± 2%       ~ (p=0.240 n=6)
AppendEnumErr/match_last-4                  34.19n ± 1%   34.28n ± 1%       ~ (p=0.937 n=6)
AppendEnumErr/no_match-4                    887.9n ± 1%   901.1n ± 1%  +1.49% (p=0.009 n=6)
geomean                                     1.592µ        1.595µ       +0.17%

                                   │ bench-baseline.txt │          bench-current.txt           │
                                   │        B/op        │     B/op      vs base                │
Validate/valid_defaults-4                  868.0 ± 0%       868.0 ± 0%       ~ (p=1.000 n=6)
Validate/with_arrays-4                     868.0 ± 0%       868.0 ± 0%       ~ (p=1.000 n=6)
Validate/invalid_multi_error-4           2.039Ki ± 0%     2.038Ki ± 0%       ~ (p=0.121 n=6)
ParseDefaultsTOML-4                      1.836Ki ± 0%     1.836Ki ± 0%       ~ (p=1.000 n=6)
RejectEmbeddedKeys/clean_config-4          0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
RejectEmbeddedKeys/with_patterns-4         0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/match_first-4                48.00 ± 0%       48.00 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/match_last-4                 48.00 ± 0%       48.00 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/no_match-4                   346.0 ± 0%       346.0 ± 0%       ~ (p=1.000 n=6)
geomean                                               ²                 -0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                   │ bench-baseline.txt │         bench-current.txt          │
                                   │     allocs/op      │ allocs/op   vs base                │
Validate/valid_defaults-4                  18.00 ± 0%     18.00 ± 0%       ~ (p=1.000 n=6) ¹
Validate/with_arrays-4                     18.00 ± 0%     18.00 ± 0%       ~ (p=1.000 n=6) ¹
Validate/invalid_multi_error-4             58.00 ± 0%     58.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseDefaultsTOML-4                        52.00 ± 0%     52.00 ± 0%       ~ (p=1.000 n=6) ¹
RejectEmbeddedKeys/clean_config-4          0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
RejectEmbeddedKeys/with_patterns-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/match_first-4                1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/match_last-4                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
AppendEnumErr/no_match-4                   12.00 ± 0%     12.00 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                               ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/jongio/grut/internal/crashlog
                                 │ bench-baseline.txt │         bench-current.txt          │
                                 │       sec/op       │    sec/op     vs base              │
ScrubPII/small_100B-4                     12.93µ ± 1%   12.91µ ±  1%       ~ (p=0.485 n=6)
ScrubPII/medium_1KB-4                     226.0µ ± 1%   226.1µ ±  0%       ~ (p=0.699 n=6)
ScrubPII/large_10KB-4                     3.081m ± 2%   3.113m ±  2%       ~ (p=0.132 n=6)
ScrubPII_NoSecrets/100B-4                 23.78µ ± 2%   23.68µ ±  1%       ~ (p=0.589 n=6)
ScrubPII_NoSecrets/1KB-4                  234.5µ ± 0%   234.0µ ± 11%       ~ (p=0.699 n=6)
ScrubPII_NoSecrets/10KB-4                 2.932m ± 3%   2.909m ±  3%       ~ (p=0.589 n=6)
ScrubLogTail/10_entries-4                 129.4µ ± 2%   129.9µ ±  0%       ~ (p=0.485 n=6)
ScrubLogTail/50_entries-4                 647.4µ ± 3%   646.6µ ±  1%       ~ (p=0.589 n=6)
ScrubLogTail/100_entries_mixed-4          10.82m ± 1%   10.90m ±  2%       ~ (p=0.310 n=6)
geomean                                   371.1µ        371.3µ        +0.06%

                                 │ bench-baseline.txt │         bench-current.txt          │
                                 │        B/op        │     B/op      vs base              │
ScrubPII/small_100B-4                      913.5 ± 0%     913.0 ± 0%       ~ (p=0.727 n=6)
ScrubPII/medium_1KB-4                    11.89Ki ± 0%   11.89Ki ± 0%       ~ (p=0.275 n=6)
ScrubPII/large_10KB-4                    228.3Ki ± 0%   228.3Ki ± 0%       ~ (p=0.937 n=6)
ScrubPII_NoSecrets/100B-4                1.174Ki ± 0%   1.174Ki ± 0%       ~ (p=0.732 n=6)
ScrubPII_NoSecrets/1KB-4                 10.09Ki ± 0%   10.09Ki ± 0%       ~ (p=0.242 n=6)
ScrubPII_NoSecrets/10KB-4                101.0Ki ± 0%   101.1Ki ± 0%       ~ (p=0.818 n=6)
ScrubLogTail/10_entries-4                9.079Ki ± 0%   9.085Ki ± 0%       ~ (p=0.113 n=6)
ScrubLogTail/50_entries-4                45.52Ki ± 0%   45.52Ki ± 0%       ~ (p=0.790 n=6)
ScrubLogTail/100_entries_mixed-4         545.5Ki ± 0%   545.7Ki ± 0%       ~ (p=0.359 n=6)
geomean                                  20.55Ki        20.55Ki       +0.03%

                                 │ bench-baseline.txt │          bench-current.txt          │
                                 │     allocs/op      │  allocs/op   vs base                │
ScrubPII/small_100B-4                      17.00 ± 0%    17.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubPII/medium_1KB-4                      27.00 ± 0%    27.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubPII/large_10KB-4                      79.50 ± 1%    79.50 ± 1%       ~ (p=1.000 n=6)
ScrubPII_NoSecrets/100B-4                  15.00 ± 0%    15.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubPII_NoSecrets/1KB-4                   15.00 ± 0%    15.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubPII_NoSecrets/10KB-4                  15.00 ± 0%    15.00 ± 0%       ~ (p=1.000 n=6) ¹
ScrubLogTail/10_entries-4                  171.0 ± 0%    171.0 ± 0%       ~ (p=1.000 n=6) ¹
ScrubLogTail/50_entries-4                  851.0 ± 0%    851.0 ± 0%       ~ (p=1.000 n=6) ¹
ScrubLogTail/100_entries_mixed-4          1.909k ± 0%   1.909k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                    68.73         68.73       +0.00%
¹ all samples are equal

pkg: github.com/jongio/grut/internal/git
                               │ bench-baseline.txt │         bench-current.txt         │
                               │       sec/op       │   sec/op     vs base              │
ParseStatus/10_files-4                  1.831µ ± 3%   1.810µ ± 1%  -1.12% (p=0.041 n=6)
ParseStatus/100_files-4                 15.72µ ± 3%   15.87µ ± 6%       ~ (p=0.240 n=6)
ParseStatus/500_files-4                 75.91µ ± 1%   75.84µ ± 2%       ~ (p=1.000 n=6)
ParseStatus/100_renamed-4               23.27µ ± 2%   23.43µ ± 1%       ~ (p=0.310 n=6)
ParseStatusBranch-4                     1.717µ ± 6%   1.697µ ± 7%       ~ (p=1.000 n=6)
ParseDiff/10_lines-4                    1.064µ ± 1%   1.063µ ± 2%       ~ (p=1.000 n=6)
ParseDiff/100_lines-4                   6.770µ ± 2%   6.821µ ± 2%       ~ (p=0.132 n=6)
ParseDiff/1000_lines-4                  68.00µ ± 2%   68.22µ ± 1%       ~ (p=0.818 n=6)
ParseHunkHeader/simple-4                99.34n ± 1%   99.44n ± 1%       ~ (p=0.905 n=6)
ParseHunkHeader/with_context-4          105.0n ± 1%   105.3n ± 3%       ~ (p=0.719 n=6)
ParseHunkHeader/large_range-4           117.4n ± 1%   117.5n ± 1%       ~ (p=0.548 n=6)
UndoStack/record_50-4                   20.53µ ± 1%   20.54µ ± 2%       ~ (p=0.937 n=6)
UndoStack/record_overflow-4             80.69µ ± 1%   80.91µ ± 2%       ~ (p=0.699 n=6)
UndoStack/peek_undo-4                   8.475n ± 0%   8.472n ± 2%       ~ (p=0.905 n=6)
UndoStack/can_undo_redo-4               11.35n ± 2%   11.34n ± 0%       ~ (p=0.355 n=6)
JoinPaths-4                             1.207µ ± 2%   1.210µ ± 1%       ~ (p=0.853 n=6)
SplitPaths-4                            2.244µ ± 1%   2.254µ ± 2%       ~ (p=0.589 n=6)
geomean                                 1.803µ        1.805µ       +0.09%

                               │ bench-baseline.txt │          bench-current.txt           │
                               │        B/op        │     B/op      vs base                │
ParseStatus/10_files-4               2.641Ki ± 0%     2.641Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/100_files-4              24.66Ki ± 0%     24.66Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/500_files-4              111.9Ki ± 0%     111.9Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/100_renamed-4            32.16Ki ± 0%     32.16Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseStatusBranch-4                    928.0 ± 0%       928.0 ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/10_lines-4                 1.703Ki ± 0%     1.703Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/100_lines-4                11.02Ki ± 0%     11.02Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/1000_lines-4               116.5Ki ± 0%     116.5Ki ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/simple-4               32.00 ± 0%       32.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/with_context-4         32.00 ± 0%       32.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/large_range-4          32.00 ± 0%       32.00 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/record_50-4                22.91Ki ± 0%     22.91Ki ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/record_overflow-4          87.27Ki ± 0%     87.27Ki ± 0%       ~ (p=0.455 n=6)
UndoStack/peek_undo-4                  0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/can_undo_redo-4              0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
JoinPaths-4                          2.625Ki ± 0%     2.625Ki ± 0%       ~ (p=1.000 n=6) ¹
SplitPaths-4                         6.062Ki ± 0%     6.062Ki ± 0%       ~ (p=1.000 n=6) ¹
geomean                                           ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                               │ bench-baseline.txt │         bench-current.txt          │
                               │     allocs/op      │ allocs/op   vs base                │
ParseStatus/10_files-4                 14.00 ± 0%     14.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/100_files-4                89.00 ± 0%     89.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/500_files-4                411.0 ± 0%     411.0 ± 0%       ~ (p=1.000 n=6) ¹
ParseStatus/100_renamed-4              209.0 ± 0%     209.0 ± 0%       ~ (p=1.000 n=6) ¹
ParseStatusBranch-4                    2.000 ± 0%     2.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/10_lines-4                   9.000 ± 0%     9.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/100_lines-4                  46.00 ± 0%     46.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseDiff/1000_lines-4                 448.0 ± 0%     448.0 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/simple-4               1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/with_context-4         1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseHunkHeader/large_range-4          1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/record_50-4                  208.0 ± 0%     208.0 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/record_overflow-4            811.0 ± 0%     811.0 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/peek_undo-4                  0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
UndoStack/can_undo_redo-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
JoinPaths-4                            1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
SplitPaths-4                           7.000 ± 0%     7.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                           ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/jongio/grut/internal/markdown
                                   │ bench-baseline.txt │         bench-current.txt          │
                                   │       sec/op       │    sec/op     vs base              │
RenderStaticCached/same_width-4            537.7µ ±  1%   536.4µ ±  4%       ~ (p=1.000 n=6)
RenderStaticCached/varying_width-4         612.1µ ±  1%   613.4µ ± 10%       ~ (p=0.240 n=6)
RenderStaticCold-4                         556.5µ ± 11%   557.3µ ±  2%       ~ (p=1.000 n=6)
geomean                                    567.9µ         568.1µ        +0.03%

                                   │ bench-baseline.txt │         bench-current.txt          │
                                   │        B/op        │     B/op      vs base              │
RenderStaticCached/same_width-4            122.0Ki ± 0%   121.9Ki ± 0%       ~ (p=0.818 n=6)
RenderStaticCached/varying_width-4         143.9Ki ± 0%   144.1Ki ± 0%       ~ (p=0.515 n=6)
RenderStaticCold-4                         161.8Ki ± 1%   161.7Ki ± 1%       ~ (p=0.699 n=6)
geomean                                    141.6Ki        141.6Ki       -0.01%

                                   │ bench-baseline.txt │          bench-current.txt          │
                                   │     allocs/op      │  allocs/op   vs base                │
RenderStaticCached/same_width-4             12.18k ± 0%   12.18k ± 0%       ~ (p=1.000 n=6) ¹
RenderStaticCached/varying_width-4          13.77k ± 0%   13.76k ± 0%       ~ (p=0.177 n=6)
RenderStaticCold-4                          5.824k ± 0%   5.823k ± 0%       ~ (p=0.468 n=6)
geomean                                     9.919k        9.918k       -0.01%
¹ all samples are equal

pkg: github.com/jongio/grut/internal/notify
                           │ bench-baseline.txt │         bench-current.txt         │
                           │       sec/op       │   sec/op     vs base              │
NotifyToastChurn-4                  328.5n ± 1%   330.4n ± 1%       ~ (p=0.258 n=6)
NotifyToastLifecycleCold-4          664.4n ± 1%   664.2n ± 8%       ~ (p=0.900 n=6)
NotifyInlineCap-4                   1.018µ ± 0%   1.018µ ± 0%       ~ (p=0.855 n=6)
geomean                             605.7n        606.8n       +0.19%

                           │ bench-baseline.txt │         bench-current.txt          │
                           │        B/op        │    B/op     vs base                │
NotifyToastChurn-4                   144.0 ± 0%   144.0 ± 0%       ~ (p=1.000 n=6) ¹
NotifyToastLifecycleCold-4           848.0 ± 0%   848.0 ± 0%       ~ (p=1.000 n=6) ¹
NotifyInlineCap-4                    104.0 ± 0%   104.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                              233.3        233.3       +0.00%
¹ all samples are equal

                           │ bench-baseline.txt │         bench-current.txt          │
                           │     allocs/op      │ allocs/op   vs base                │
NotifyToastChurn-4                   2.000 ± 0%   2.000 ± 0%       ~ (p=1.000 n=6) ¹
NotifyToastLifecycleCold-4           10.00 ± 0%   10.00 ± 0%       ~ (p=1.000 n=6) ¹
NotifyInlineCap-4                    2.000 ± 0%   2.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                              3.420        3.420       +0.00%
¹ all samples are equal

pkg: github.com/jongio/grut/internal/panels/filetree
                                               │ bench-baseline.txt │         bench-current.txt         │
                                               │       sec/op       │   sec/op     vs base              │
TreeRebuildVisible/flat_50_files-4                      259.7n ± 2%   259.3n ± 1%       ~ (p=0.333 n=6)
TreeRebuildVisible/nested_5_levels_461_nodes-4          2.858µ ± 1%   2.857µ ± 2%       ~ (p=0.974 n=6)
TreeRebuildVisible/large_1000_nodes-4                   5.348µ ± 1%   5.355µ ± 2%       ~ (p=0.284 n=6)
RenderLine/file_node-4                                  4.584µ ± 1%   4.589µ ± 1%       ~ (p=0.721 n=6)
RenderLine/dir_node_cursor-4                            6.027µ ± 0%   6.036µ ± 1%       ~ (p=0.310 n=6)
RenderLine/with_icons-4                                 4.695µ ± 1%   4.705µ ± 0%       ~ (p=0.561 n=6)
DetachedLazyLoad/50_entries-4                           148.6µ ± 2%   148.4µ ± 2%       ~ (p=0.699 n=6)
DetachedLazyLoad/23_root_entries-4                      66.34µ ± 1%   66.08µ ± 1%       ~ (p=0.310 n=6)
DisplayWidth/ascii-4                                    67.80n ± 1%   67.59n ± 2%       ~ (p=0.394 n=6)
DisplayWidth/mixed_unicode-4                            311.4n ± 1%   310.4n ± 1%       ~ (p=0.937 n=6)
TruncateToWidth/no_truncation-4                         21.43n ± 1%   21.46n ± 1%       ~ (p=0.288 n=6)
TruncateToWidth/needs_truncation-4                      841.3n ± 3%   843.5n ± 1%       ~ (p=0.589 n=6)
geomean                                                 1.879µ        1.878µ       -0.03%

                                               │ bench-baseline.txt │          bench-current.txt           │
                                               │        B/op        │     B/op      vs base                │
TreeRebuildVisible/flat_50_files-4                     0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
TreeRebuildVisible/nested_5_levels_461_nodes-4         0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
TreeRebuildVisible/large_1000_nodes-4                  0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/file_node-4                                 392.0 ± 0%       392.0 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/dir_node_cursor-4                           624.0 ± 0%       624.0 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/with_icons-4                                392.0 ± 0%       392.0 ± 0%       ~ (p=1.000 n=6) ¹
DetachedLazyLoad/50_entries-4                        34.12Ki ± 0%     34.13Ki ± 0%       ~ (p=0.816 n=6)
DetachedLazyLoad/23_root_entries-4                   14.77Ki ± 0%     14.77Ki ± 0%       ~ (p=0.667 n=6)
DisplayWidth/ascii-4                                   0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
DisplayWidth/mixed_unicode-4                           0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
TruncateToWidth/no_truncation-4                        0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
TruncateToWidth/needs_truncation-4                     544.0 ± 0%       544.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                           ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                               │ bench-baseline.txt │         bench-current.txt          │
                                               │     allocs/op      │ allocs/op   vs base                │
TreeRebuildVisible/flat_50_files-4                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
TreeRebuildVisible/nested_5_levels_461_nodes-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
TreeRebuildVisible/large_1000_nodes-4                  0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/file_node-4                                 12.00 ± 0%     12.00 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/dir_node_cursor-4                           18.00 ± 0%     18.00 ± 0%       ~ (p=1.000 n=6) ¹
RenderLine/with_icons-4                                12.00 ± 0%     12.00 ± 0%       ~ (p=1.000 n=6) ¹
DetachedLazyLoad/50_entries-4                          363.0 ± 0%     363.0 ± 0%       ~ (p=1.000 n=6) ¹
DetachedLazyLoad/23_root_entries-4                     164.0 ± 0%     164.0 ± 0%       ~ (p=1.000 n=6) ¹
DisplayWidth/ascii-4                                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
DisplayWidth/mixed_unicode-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
TruncateToWidth/no_truncation-4                        0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
TruncateToWidth/needs_truncation-4                     3.000 ± 0%     3.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                           ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/jongio/grut/internal/panels/gitdiff
                                                   │ bench-baseline.txt │         bench-current.txt         │
                                                   │       sec/op       │   sec/op     vs base              │
InlineDiffRenderExact/10_lines-4                            47.18µ ± 1%   47.41µ ± 1%       ~ (p=0.310 n=6)
InlineDiffRenderExact/100_lines-4                           410.1µ ± 2%   411.2µ ± 2%       ~ (p=0.310 n=6)
InlineDiffRenderExact/1000_lines-4                          4.116m ± 2%   4.121m ± 3%       ~ (p=0.818 n=6)
InlineDiffRenderCold-4                                      410.9µ ± 1%   410.4µ ± 1%       ~ (p=0.937 n=6)
SideBySideDiffRenderExact/10_lines-4                        85.68µ ± 1%   85.54µ ± 1%       ~ (p=0.937 n=6)
SideBySideDiffRenderExact/100_lines-4                       769.0µ ± 1%   766.4µ ± 1%       ~ (p=0.589 n=6)
SideBySideDiffRenderExact/1000_lines-4                      7.684m ± 2%   7.683m ± 3%       ~ (p=1.000 n=6)
SideBySideDiffRenderCold-4                                  770.4µ ± 1%   767.5µ ± 1%       ~ (p=0.240 n=6)
PairDiffLines/20_lines_mixed-4                              306.5n ± 1%   305.5n ± 2%       ~ (p=0.734 n=6)
PairDiffLines/200_lines_mixed-4                             2.041µ ± 3%   2.040µ ± 2%       ~ (p=0.786 n=6)
RenderViewportWithStats/small-4                             5.554µ ± 2%   5.546µ ± 2%       ~ (p=0.701 n=6)
RenderViewportWithStats/large_scrolled-4                    5.845µ ± 2%   5.836µ ± 1%       ~ (p=1.000 n=6)
DiffWordsBoundedAllocations/50_tokens-4                     30.60µ ± 2%   30.62µ ± 1%       ~ (p=0.818 n=6)
DiffWordsBoundedAllocations/200_tokens-4                    452.5µ ± 0%   452.0µ ± 1%       ~ (p=0.485 n=6)
DiffWordsBoundedAllocations/400_tokens-4                    1.787m ± 1%   1.777m ± 2%       ~ (p=0.132 n=6)
DiffWordsBoundedAllocations/over_cap_tokens-4               5.073µ ± 4%   5.065µ ± 2%       ~ (p=0.937 n=6)
DiffWordsBoundedAllocations/one_mib_over_cap-4              5.113µ ± 3%   5.054µ ± 2%       ~ (p=0.240 n=6)
DiffWordsBoundedAllocations/one_mib_single_token-4          9.849m ± 2%   9.838m ± 1%       ~ (p=0.818 n=6)
geomean                                                     102.8µ        102.6µ       -0.16%

                                                   │ bench-baseline.txt │          bench-current.txt           │
                                                   │        B/op        │     B/op      vs base                │
InlineDiffRenderExact/10_lines-4                           4.016Ki ± 0%   4.016Ki ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderExact/100_lines-4                          33.47Ki ± 0%   33.47Ki ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderExact/1000_lines-4                         334.7Ki ± 0%   334.7Ki ± 0%       ~ (p=1.000 n=6)
InlineDiffRenderCold-4                                     37.97Ki ± 0%   37.97Ki ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/10_lines-4                       15.50Ki ± 0%   15.50Ki ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/100_lines-4                      144.3Ki ± 0%   144.3Ki ± 0%       ~ (p=0.892 n=6)
SideBySideDiffRenderExact/1000_lines-4                     1.410Mi ± 0%   1.410Mi ± 0%       ~ (p=0.563 n=6)
SideBySideDiffRenderCold-4                                 148.8Ki ± 0%   148.8Ki ± 0%       ~ (p=0.636 n=6)
PairDiffLines/20_lines_mixed-4                               416.0 ± 0%     416.0 ± 0%       ~ (p=1.000 n=6) ¹
PairDiffLines/200_lines_mixed-4                            3.469Ki ± 0%   3.469Ki ± 0%       ~ (p=1.000 n=6) ¹
RenderViewportWithStats/small-4                            6.368Ki ± 0%   6.368Ki ± 0%       ~ (p=1.000 n=6) ¹
RenderViewportWithStats/large_scrolled-4                   6.376Ki ± 0%   6.376Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/50_tokens-4                    4.125Ki ± 0%   4.125Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/200_tokens-4                   18.12Ki ± 0%   18.12Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/400_tokens-4                   36.62Ki ± 0%   36.62Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/over_cap_tokens-4              18.12Ki ± 0%   18.12Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/one_mib_over_cap-4             18.12Ki ± 0%   18.12Ki ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/one_mib_single_token-4           896.0 ± 0%     896.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                    17.94Ki        17.94Ki       -0.00%
¹ all samples are equal

                                                   │ bench-baseline.txt │          bench-current.txt          │
                                                   │     allocs/op      │  allocs/op   vs base                │
InlineDiffRenderExact/10_lines-4                             122.0 ± 0%    122.0 ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderExact/100_lines-4                           1.096k ± 0%   1.096k ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderExact/1000_lines-4                          10.96k ± 0%   10.96k ± 0%       ~ (p=1.000 n=6) ¹
InlineDiffRenderCold-4                                      1.110k ± 0%   1.110k ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/10_lines-4                         217.0 ± 0%    217.0 ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/100_lines-4                       2.044k ± 0%   2.044k ± 0%       ~ (p=1.000 n=6) ¹
SideBySideDiffRenderExact/1000_lines-4                      20.44k ± 0%   20.44k ± 0%       ~ (p=0.567 n=6)
SideBySideDiffRenderCold-4                                  2.058k ± 0%   2.058k ± 0%       ~ (p=1.000 n=6) ¹
PairDiffLines/20_lines_mixed-4                               3.000 ± 0%    3.000 ± 0%       ~ (p=1.000 n=6) ¹
PairDiffLines/200_lines_mixed-4                              3.000 ± 0%    3.000 ± 0%       ~ (p=1.000 n=6) ¹
RenderViewportWithStats/small-4                              18.00 ± 0%    18.00 ± 0%       ~ (p=1.000 n=6) ¹
RenderViewportWithStats/large_scrolled-4                     19.00 ± 0%    19.00 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/50_tokens-4                      8.000 ± 0%    8.000 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/200_tokens-4                     12.00 ± 0%    12.00 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/400_tokens-4                     14.00 ± 0%    14.00 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/over_cap_tokens-4                6.000 ± 0%    6.000 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/one_mib_over_cap-4               6.000 ± 0%    6.000 ± 0%       ~ (p=1.000 n=6) ¹
DiffWordsBoundedAllocations/one_mib_single_token-4           4.000 ± 0%    4.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                      80.02         80.02       -0.00%
¹ all samples are equal

Note

No significant regressions on linux-amd64 (threshold: 15%, same-runner comparison).

@jongio
jongio merged commit dcb8684 into main Aug 27, 2026
9 checks passed
@jongio
jongio deleted the docs/1-0-stability-and-versions branch August 27, 2026 23:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant