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 forgh pr merge) when scripting GitHub PR ops in this repo. The shell exports aGITHUB_TOKENthat conflicts withgh'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 Nin 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 pairedbool $XSet = falsesentinel parameter β callers wanting to set null passvalidate: null, validateSet: true. SeeTextInput::withValidator()andTextArea::withValidator()for the canonical shape. Exception: Seecandy-core/src/Concerns/Mutable.phpfor the standard trait; libs with sentinel-bool needs (TextArea, Style) overridemutate()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 createprintsWarning: N uncommitted changeswhen 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
assertGoldenAnsifor any newrender()test. Fixture files live intests/fixtures/with a.goldenextension. Re-record goldens withUPDATE_GOLDENS=1 vendor/bin/phpunitafter 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 toVimActionenum +VimKeyHandlerso 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 returnnullβ they manage their own recurring ticks viaCmd::tick()insidestart(). This is intentional: the tick closure captures$this->idand self-reissues until the timer stops. Do NOT add these to ReactPHP event loops expectingsubscriptions()to return a stream. SeeTimer::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 toSugarCraft\Forms\*fromcandy-forms. They exist for backward compatibility during migration. The canonical home for these components iscandy-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-\x1fand replaces\n\r\twith spaces while preserving ESC for SGR sequences. Centralized incandy-core/src/Util/Sanitize.phpfor 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.