feat: CLASSIFICATION function-header field + shadow-classification lint #163
No Reviewers
Labels
Clear labels
Area/CI
Area/Completions
Area/Components
Area/Config
Area/Docs
Area/Functions
Area/Integrations
Area/Prompt & Theme
Area/Scripts
Area/Tests
Compat/Breaking
Kind/Bug
Kind/Chore
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Performance
Kind/Refactor
Kind/Security
Kind/Testing
good first issue
help wanted
.github/workflows and repository automation
completions/ - tab-completion scripts
The opinionated-component system (C1-C6)
config.fish and conf.d/ - startup and environment
docs/manual/ and the generated manual, man page, and site
functions/ - user-facing functions and helpers
integrations/ - third-party tool wiring
themes/ and prompt appearance
scripts/ - standalone helper scripts
tests/ - the syntax lint and functional suite
Breaking change that won't be backward compatible
Something is not working
Tooling, dependencies, and housekeeping
Documentation changes
Improve existing functionality
New functionality
Makes existing behavior faster or lighter
Restructures code without changing behavior
This is security issue
Issue or pull request related to testing
Well-scoped and self-contained; a good place to start
Maintainer is looking for someone to pick this up
Priority
Critical
1
The priority is critical
Priority
High
2
The priority is high
Priority
Medium
3
The priority is medium
Priority
Low
4
The priority is low
Reviewed
Confirmed
1
Issue has been confirmed
Reviewed
Duplicate
2
This issue or pull request already exists
Reviewed
Invalid
3
Invalid issue
Reviewed
Won't Fix
3
This issue won't be fixed
Status
Blocked
1
Something is blocking this issue or pull request
Status
Need More Info
2
Feedback is required to reproduce issue or to continue work
Status
Abandoned
3
Somebody has started to work on this but abandoned work
Milestone
No items
No Milestone
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: rootiest/fish-config#163
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Started from a
Ctrl+Alt+Ukeybind fix and grew into a systematic audit ofevery function's interaction with the C1 command-shadow system (
ls→eza,cat→bat,rm→trash,cd→zoxide, etc.), plus a new lint that keeps itthat way going forward.
pretty-historyrenameconf.d/tricks.fishshadowedhistoryitself to add timestamps, whichbroke
_replace_command_token.fish(Ctrl+Alt+Uon an empty prompt dumpedthe entire history with timestamps instead of recalling one command).
Renamed the shadow to
pretty-historysohistoryis never shadowed —every function that reads history now gets real builtin semantics, no
builtin historydefensive-coding required.CLASSIFICATIONfunction-header fieldNew optional header label (schema:
docs/function-classification-schema.md,introduced to contributors in
CONTRIBUTING.md) with a closed tag set:uses-shadow(name)— calls a shadow bare, wants the override.bypasses-shadow(name)— callscommand/builtinexplicitly, needsstock behavior.
self-limiting(name)— calls a shadow bare, but the shadow's own logic(a flag check, tty/pipe auto-detection) already neutralizes the override,
so no tag would technically be required for safety, but it's
documented anyway: if that internal logic ever changes, every site is
one grep away instead of silently wrong.
destructive,network,blocking-prompt— general hazard flags.Tagged across all 65 functions/
conf.dentries that interact with ashadow, using an agy-delegated first pass (fully reviewed and corrected by
hand — several judgment errors caught: false
blocking-promptfrom pipedread, a missed--yesescape hatch,destructiveon a function's owntemp-file cleanup where the schema explicitly excludes that).
Real bugs found and fixed along the way
rm: three functions (fc.fish,dng2avif.fish,_scrollback_prune_junk.fish) sent their own scratch/junk files to theuser's trash instead of wiping them.
cd:mkcd.fishandmkrep.fish(9 sites, including 8rollback-to-original-directory calls on error paths) routed through
zoxide's shadow — a failed
mkreprun's error-path cleanup couldsilently land somewhere zoxide guessed instead of the directory it was
trying to return to.
less:config-help.fish's--manpath explicitly checkstype -q lessfor real-less-only flag syntax (-R +N), then calledit bare, routing through our
$PAGER → ov → less → more → catfallback chain instead.
cp/bash:_fish_deps_install.fish/_fish_deps_update.fish'sbinary-upgrade path
cps over an already-installed binary with noexistence guard — our
cpshadow forces-iunconditionally (a plainalias, not flag-aware like
rm's), so this could hang waiting on aconfirmation prompt with nothing there to answer it. Same two files
piped
curlinto barebash, invoking our shell-switch wrapper.mv:agents-init.fish's AGENTS.md/CLAUDE.md relocation (4 sites)called
mvbare; each was already guarded bytest -fon thedestination, so lower severity, but fixed to
command mvanyway ratherthan relying on that guard being the only thing standing between it and
an unattended hang.
New: shadow-classification lint (
tests/run-tests.fishPhase 1b)Runtime auto-unwrapping isn't viable in fish (no hook finer than shadowing
itself), so this is a static check: flags a bare shadow-command call in
functions/*.fishwith no matchinguses-shadow/self-limitingin thatfunction's own header. CI-blocking. Running the first draft of this
surfaced the
less/cp/bash/mvbugs above before they shipped.Testing
fish tests/run-tests.fish: 759/759 assertions, including the new196/196 shadow-classification check.
python3 docs/verify-manual.py: 83/84 — the one failure is the expecteddrift in the CI-generated
docs/fish-config.mdconcat, regenerated andauto-committed by CI on merge (same as every prior docs-touching PR).
CLASSIFICATIONtag mechanically cross-checked against the actualcode it describes.