Skip to content

Latest commit

Β 

History

History
17 lines (14 loc) Β· 4.5 KB

File metadata and controls

17 lines (14 loc) Β· 4.5 KB

Caliber Learnings

Accumulated patterns and anti-patterns from development sessions. Auto-managed by caliber β€” do not edit manually.

  • [env:project] Always run unset GITHUB_TOKEN && gh pr create ... (and the same for gh pr merge) when scripting GitHub PR ops in this repo. The shell exports a GITHUB_TOKEN that conflicts with gh's own keyring auth β€” leaving it set causes silent permission mismatches. The chained pattern used per phase here: unset GITHUB_TOKEN && gh pr create ... && unset GITHUB_TOKEN && gh pr merge --merge --delete-branch && git checkout master && git pull --ff-only.

  • [convention:project] PR / commit messages reference the audit by the literal heading CONVERSION.md Β§ Phase audit (2026-05-02) β€” Phase N (or ## Phase audit (...) β€” Phase N in PR bodies). Keep this exact wording so future searches can grep audit work consistently.

  • [pattern:project] Sugar-Bits / Candy-Shine immutable models use a private mutate(...) helper. When a field is nullable (e.g. ?\Closure $validate, ?string $err), the helper can't tell "caller passed null" from "caller omitted argument" with ?? $this->field. Add a paired bool $XSet = false sentinel parameter β€” callers wanting to set null pass validate: null, validateSet: true. See TextInput::withValidator() and TextArea::withValidator() for the canonical shape. Exception: See candy-core/src/Concerns/Mutable.php for the standard trait; libs with sentinel-bool needs (TextArea, Style) override mutate() in-class and document the exception.

  • [pattern:project] When changing the default behaviour of an existing public method (e.g. enabling OSC 8 hyperlinks by default in Renderer::renderLink()), don't "fix the test" by mirroring the new bytes β€” split it: keep the old byte-exact assertion behind the explicit opt-out (->withHyperlinks(false)), then add a new test for the new default. Preserves the regression guard while documenting the toggle.

  • [gotcha:project] gh pr create prints Warning: N uncommitted changes when CALIBER_LEARNINGS.md files exist in the parent directory tree (../.caliber/, ../CALIBER_LEARNINGS.md, sibling repo learnings). These are sibling-repo artifacts β€” ignore the warning, do not add them to this repo's commits. The PR still creates successfully.

  • [pattern:assert-golden-ansi] Use assertGoldenAnsi for any new render() test. Fixture files live in tests/fixtures/ with a .golden extension. Re-record goldens with UPDATE_GOLDENS=1 vendor/bin/phpunit after intentional output changes. Mirrors: docs/repo_map_step_28.md.

  • [anti-pattern:vim-keybindings-per-lib] Do NOT add new vim keybindings to per-lib branching logic. Vim mode is handled by candy-forms/src/Vim/VimKeyHandler (shared). Always add new bindings to VimAction enum + VimKeyHandler so candy-forms, sugar-prompt, sugar-bits, and sugar-readline all benefit at once. Mirrors: docs/repo_map_step_24.md.

  • [pattern:sugar-bits:async-tick-driven] Timer, Stopwatch, and AnimatedProgress are tick-driven components. Their subscriptions() methods return null β€” they manage their own recurring ticks via Cmd::tick() inside start(). This is intentional: the tick closure captures $this->id and self-reissues until the timer stops. Do NOT add these to ReactPHP event loops expecting subscriptions() to return a stream. See Timer::tick(), Stopwatch::tick(), AnimatedProgress::scheduleTick() for the pattern.

  • [pattern:sugar-bits:class-alias-transitional] sugar-bits contains 8 deprecated alias re-exports: Cursor, TextInput, TextArea, Viewport, ItemList, FilePicker, Scrollbar, Spinner. These are class_alias() shims to SugarCraft\Forms\* from candy-forms. They exist for backward compatibility during migration. The canonical home for these components is candy-forms β€” sugar-bits is a re-export layer with no independent versioning.

  • [security:sugar-bits:c0-sanitization] All user-supplied text fields that render to the terminal are C0-sanitized via SugarCraft\Core\Util\Sanitize::controlChars(). This includes: Tabs labels, Progress fullChar/emptyChar, Help key/desc bindings, Tree cell content. The sanitizer strips \x00-\x08\x0b\x0c\x0e-\x1f and replaces \n\r\t with spaces while preserving ESC for SGR sequences. Centralized in candy-core/src/Util/Sanitize.php for single-point security auditing.

  • Lang class now extends SugarCraft\Core\I18n\Lang β€” t() method inherited from base; NAMESPACE and DIR are the only per-lib constants.