OCWS Bug Lessons¶
Post-mortems and lessons learned from real bugs found in the OCWS codebase. Each file covers one class of problem, what went wrong, and the pattern to avoid.
Shell Correctness¶
| File | Topic |
|---|---|
| shell-function-syntax.md | Corrupted function definitions crashing scripts at load time |
| case-wildcard-swallows-all.md | Wildcard * in the wrong case arm making all named branches unreachable |
| multiline-command-substitution.md | Unquoted line breaks inside $(...) silently terminating commands early |
| redundant-action-calls.md | Calling a superset action before dispatching to specific ones (runs twice) |
| shell-missing-set-e.md | Missing set -e allowing silent failure propagation |
| source-cd-changes-cwd.md | source-ing a script that calls cd permanently changes the caller's working directory |
Positional Arguments and Scope¶
| File | Topic |
|---|---|
| positional-args-already-consumed.md | Re-reading $2/$3 after they were already captured at the top of the script |
| function-param-indexing.md | Using the wrong $N index inside a function |
| variable-declared-before-use.md | Passing a variable to jq before it is declared |
Paths and Portability¶
| File | Topic |
|---|---|
| relative-paths-in-scripts.md | ./scripts/... paths that only work from the project root |
| hardcoded-absolute-paths.md | Absolute developer machine paths baked into portable scripts |
| hardcoded-absolute-paths-again.md | Additional hardcoded path instances |
| fallback-to-hardcoded-path.md | Walk-up path resolution that silently falls back to a hardcoded dev path |
| portable-script-paths.md | General portable path patterns |
Process and File Safety¶
| File | Topic |
|---|---|
| kill-self-on-startup.md | pkill -f matching and killing the script that just launched |
| non-atomic-file-write.md | Modifying a live file and building a replacement simultaneously (race condition) |
| atomic-file-writes.md | Proper atomic file write patterns |
| sed-i-on-source-files.md | sed -i at runtime rewriting committed widget source files |
| same-file-redirect-truncation.md | Same-file redirect truncation before read |
Side Effects and Data¶
| File | Topic |
|---|---|
| double-side-effect.md | Clipboard copy running after satty --copy-to-clipboard already handled it |
| json-string-concatenation.md | Building JSON by string concatenation (malformed output) |
| sysmon-output-ordering.md | Output ordering mismatch in sysmon parsing |
Security¶
| File | Topic |
|---|---|
| c-system-command-injection.md | system() with unsanitized argv/metadata/clipboard content |
| eval-unsanitized-input.md | eval on user-supplied strings (arbitrary code execution) |
| ipc-command-injection.md | IPC command injection via unsanitized values |
C and Memory Safety¶
| File | Topic |
|---|---|
| c-sscanf-buffer-overflow.md | sscanf %s into fixed buffer causes stack overflow |
| c-unchecked-realloc.md | Unchecked realloc return value causes use-after-free |
IPC and Event System¶
| File | Topic |
|---|---|
| ipc-variable-mapping.md | Event Bus variable mapping mismatches |
| event-driven-ipc.md | Event-driven vs polling IPC patterns |
zigshell-cairo-pango Widget DSL¶
| File | Topic |
|---|---|
| zigshell-cairo-pango-config-syntax.md | Config file syntax pitfalls |
| zigshell-cairo-pango-css-include-inside-css.md | CSS include inside CSS block |
| zigshell-cairo-pango-css-section-vs-include.md | CSS section vs include differences |
| zigshell-cairo-pango-css-selectors.md | CSS selector compatibility |
| zigshell-cairo-pango-icon-theme-chain.md | Icon resolution depends on GTK icon theme |
| zigshell-cairo-pango-nerd-font-tabs.md | Nerd Font tab rendering issues |
| zigshell-cairo-pango-ocws-css-not-gtk.md | ocws.css uses web CSS, not valid GTK CSS |
| zigshell-cairo-pango-private-scope.md | Private variable scope in widgets |
| zigshell-cairo-pango-rendering-and-icons.md | Rendering and icon display quirks |
| zigshell-cairo-pango-triggers.md | Trigger conditions and evaluation order |
| zigshell-cairo-pango-variable-naming.md | Variable naming conventions and collisions |
| zigshell-cairo-pango-widget-architecture.md | Widget architecture and lifecycle |
| widget-css-selector-mismatch.md | CSS selector mismatch between widget and theme |
| widget-division-by-zero.md | Division by zero in widget expressions |
| widget-execterm-not-valid.md | ExecTerm() not valid in all contexts |
| widget-interval-vs-scanner-step.md | Interval vs scanner step timing differences |
| widget-unclosed-if-parens.md | Unclosed parentheses in If() chains cause silent failure |
Theme Engine and Build¶
| File | Topic |
|---|---|
| theme-engine-and-installer.md | Theme engine and installer interaction issues |
| theme-engine-templates.md | Template rendering edge cases |
Other¶
| File | Topic |
|---|---|
| autostart-ordering.md | Service startup order in autostart |
| build-absolute-temp-dirs.md | Build system absolute temp directory handling |
| c-utilities-data-sources.md | C utility data source patterns |
| easing-functions-ui.md | Easing function behavior in UI animations |
| foot-terminal-configuration.md | Foot terminal configuration nuances |
| gtk-layer-shell.md | GTK layer shell protocol usage |
| ocws-c-native-abstractions.md | C native abstraction patterns |
Quick Reference: Detection Commands¶
# Syntax check all scripts
find scripts/ dotfiles/ -name "*.sh" -exec bash -n {} \; && echo "All OK"
# Run shellcheck on everything
find scripts/ dotfiles/ -name "*.sh" | xargs shellcheck
# Find eval on variables
grep -rn '\beval\b.*\$' scripts/ --include="*.sh"
# Find hardcoded developer paths
grep -rn '/media/\|/home/[a-z]' scripts/ dotfiles/ --include="*.sh" | grep -v '#'
# Find sed -i on committed files
grep -rn 'sed -i' scripts/ --include="*.sh" | grep -v '\.bak\|\.tmp'
# Find bare relative paths
grep -rn '^\s*\./scripts/' scripts/ --include="*.sh"
# Validate all generated JSON
find ~/.config/ocws -name "*.json" -exec jq empty {} \; 2>&1