.agents/skills/project-knowledge/references/codebase.md
.github/workflows/vale.yml) explicitly lints AGENTS.md, .github/copilot-instructions.md,
and .agents/skills, and markdownlint-cli2's **/*.md glob in .markdownlint-cli2.yaml also
reaches .agents/skills - only the two explicit ignores entries there are excluded. Lint skill
doc changes with both vale <path> and npx markdownlint-cli2 --config .markdownlint-cli2.yaml <path> before pushing..vale.ini, each with a comment.git rebase --autosquash can stop mid-sequence with "Your local changes to
<file> would be overwritten by merge" on a plain pick that has no real content conflict
(verified 2026-07-30). Root cause: core.autocrlf=true renormalizes line endings on checkout,
which git treats as a working-tree modification that blocks the next pick. Toggle
git config core.autocrlf false for the duration of the rebase (restore it after), rather than
trying to resolve a conflict that doesn't reflect the actual diff.src/, not the repo root - run all go commands from there.go test -race is NOT supported. Concurrency-sensitive changes
must rely on CI (amd64) for race detection.src/template/bench_test.go,
src/terminal/bench_test.go, and src/prompt/bench_test.go; compare runs with benchstat.template.Init resets the parsed-template cache - a macro benchmark that calls it per iteration
measures the cold-parse path, not steady state.src/shell/scripts/ is embedded at build time (go:embed). After editing a
script, rebuild the binary before testing; oh-my-posh init <shell> --print shows the generated
output and is the fastest way to inspect what a user actually sources.src/shell/<shell>.go - a script function is dead code unless the feature switch emits its
activation line.Execute runs in bare goroutines with no recover (src/prompt/segments.go), and
template rendering re-panics runtime errors. Any panic there kills the whole process - the user
sees a completely blank prompt. So when a user reports a blank prompt: find the panic.segments.Base.env/options are unexported and
MUST survive a cache restore: overlay the restored data onto the writer initialized by
MapSegmentWithWriter, never replace the writer.runtime/cmd.RunWithEnv applies strings.TrimSpace to the complete command output before
returning it (verified 2026-07-26). If a segment encodes an empty final field as a trailing blank
line, that field is lost. Use a record format that retains a final non-whitespace delimiter or
sentinel, and validate the record before assigning parsed state.--save-cache flag (print/stream commands);
without it, stores never write on close. Redirect the location with OMP_CACHE_DIR.oh-my-posh debug command (grep for
restored segment from cache / setting entry). POSH_TRACE=1 and stderr show nothing for
print commands.store.go:init EOF error on first
read."streaming": <ms> config key. That value is ALSO each
segment's pending-timeout and overwrites segment-level timeout.stream always emits the transient prompt as a \x1e-prefixed NUL record (initial + refreshed
once all segments resolve); serve records are <id>\x1f<payload>\0.if X != nil return) pin first-render state
in a daemon. template.Cache did exactly that (pinned PWD/Folder/Code/Jobs) - fixed with
template.ResetCache() per render in startRenderCycle (flag-based rebuild; never nil the
global, abandoned segment goroutines may still read it). Audit for this pattern when extending
serve.config.Get prefers the session gob cache over POSH_THEME.renderComplete) - blocking clients (Clink) rely on this.*_fallback.go build-tag stubs for GOOS=js across process/host/mem/
load/disk/net/cpu/sensors, so it links fine into the src/wasm build without any split - it is
NOT a multi-MB cost by itself. The real, measurable cost was runtime.Terminal.Shell()'s parent-
process fallback (gopsutil/process.NewProcess) and SystemInfo() (gopsutil/load +
gopsutil/disk + Memory()/gopsutil/mem) plus TerminalWidth()'s
wayneashleyberry/terminal-dimensions (which shells out to stty via os/exec). go build -ldflags=-dumpdep (GOOS=js) showed gopsutil/process.Process retaining dozens of exported
methods' .namedata (CPUPercent, Connections, Children, ...) even though wasm code only ever
calls .Name() - all of it is reachable, not DCE'd. Splitting those three methods into
!js/js file pairs on the existing *Terminal type (mirroring terminal_root_js.go /
terminal_writable_js.go) dropped the stripped omp.wasm from 21,866,822 to 21,833,786 bytes
(~33 KB) and fully eliminated gopsutil/process, gopsutil/cpu, gopsutil/load, and
terminal-dimensions from the dumpdep graph - only the type-only gopsutil/disk.IOCountersStat
(referenced by SystemInfo.Disks, unavoidable without touching environment.go) remains._js.go splits are the only viable shape, not a new Environment type:
text/template calls reflect.Value.MethodByName with a non-constant name, so the linker keeps
every exported method of any type that gets converted to an interface in live code. render.Config
does exactly that (env := &runtime.Terminal{} assigned to the Environment interface), so ALL
of *Terminal's methods stay linked regardless of which ones the wasm path actually calls - the
only way to drop a method's cost is to replace its body for the js tag,
not to introduce a slimmer type.config/config.go and config/default.go referenced bare segments.CONST string option-keys
(e.g. segments.Source, segments.BranchTemplate) directly and unconditionally, which pulled the
entire segments package - and its transitive HCL/go-cty/golang.org/x/mod/modfile/gopkg.in/ini.v1/
cli/auth dependency tree - into every binary linking config, including src/wasm. Fix: mirror
the option-key strings as local options.Option consts in the config package itself (options is
the small standalone package the keys' type lives in - importing it alone costs nothing heavy) and
drop the segments import entirely. Verified with go list -deps ./wasm/ that segments is fully
gone from the wasm dependency graph after this change.src/dsc (the CLI-only DSC/Desired State Configuration resource wrapper) pulls in
github.com/invopop/jsonschema (→ go/ast, go/parser, go/doc) and github.com/spf13/cobra.
Both config/dsc.go (the Configuration/Resource wrapper around parsed config files) and
shell/dsc.go (the Shell/Resource wrapper around shell-rc-rewriting) imported it unconditionally,
even though DSC tracking is purely a CLI bookkeeping feature never exercised by the wasm render path
(render.Config/config.ParseBytes/config.ParseData never touch it). Fix: moved both DSC resource
types into a new src/cli/dsc package (so they live where they're actually used) and decoupled the
config package from dsc via a nil-by-default hook: config.NewDSCTracker func() config.DSCTracker,
set by cli/dsc's init(). config.Parse() (the file-loading path, config/load.go) checks the hook
and no-ops when nil (i.e. in any binary that never imports cli, such as wasm) instead of directly
constructing a dsc.Resource. shell/dsc.go's shell-config-rewriting logic moved wholesale into
cli/dsc/shell.go (renamed Shell→ still Shell, just in the new package) since nothing in shell
itself needs it - only cli/init.go's init command does.go list -deps ./wasm/ that segments, dsc, invopop/jsonschema, spf13/cobra, go/ast,
go/parser, and HCL/go-cty are all absent from the wasm link graph after this change.init(). This avoids the shared package importing the heavy
dependency at all, while keeping the CLI behavior identical when cli (or whichever package sets
the hook) is actually linked.