Runtime Patch Layer
Need to fix or tweak the behavior of a built-in OpenClacky method without forking the whole gem? The runtime patch layer lets you declare a prepend patch targeting a specific method. Its headline feature is optional fingerprint verification: once you provide a fingerprint, if the upstream method's source changes on a gem upgrade, your patch is automatically disabled — instead of hard-applying to new code and silently turning into a harder-to-spot bug.
Patches are now one kind of extension-container contribution — declare
contributes.patchesinext.ymland ship the patch script inside~/.clacky/ext/local/<id>/. To understand the overall model first, read the Extension System Overview. This mechanism only supports method-level overrides (prepend a new method body) and deliberately does not support line-level patch / diff.
How it works
An extension container declares the patches it contributes in ext.yml:
# ~/.clacky/ext/local/fix-search/ext.yml
id: fix-search
name: Search Timeout Fix
version: "0.1.0"
contributes:
patches:
- target: "Clacky::Tools::WebSearch#execute" # method being prepended
file: patches/timeout.rb # patch file (relative to container root)
fingerprint: "a1b2c3..." # optional: source fingerprint of the target
on_mismatch: disable # on mismatch: disable (default) | warn
At process start, OpenClacky resolves every container and, for each patch contribution:
fingerprintomitted → trust the author,requirethe patch file directly, the prepend takes effect.fingerprintgiven → recompute the target method's current source fingerprint and compare with the recorded value:- Match → apply the patch.
- Mismatch → handle per
on_mismatch(defaultdisable: not applied + warning).
That means: if you added a fingerprint, patches that may no longer apply after a gem upgrade are auto-quarantined — never "hard-applied" into a silent failure. A single failing patch quarantines only itself and doesn't affect the others.
What a Patch File Looks Like
A patch file is just a prepend module that's prepended onto the target class; you can call the original implementation with super:
# ~/.clacky/ext/local/fix-search/patches/timeout.rb
module FixSearch
def execute(query:)
@timeout = 30 # e.g. bump timeout to 30s
super # then call the original
end
end
Clacky::Tools::WebSearch.prepend(FixSearch)
target syntax:
Const::Path#method— instance methodConst::Path.method— class method- No line numbers, file paths, or regex forms.
The on_mismatch Option
Only meaningful when fingerprint is provided:
| Value | Behavior |
|---|---|
disable (default) |
Not applied, prints a warning. |
warn |
Not applied, prints a warning (different wording, similar semantics — useful to distinguish during debugging). |
There is no "force apply" option. This is deliberate — hard-applying a mismatched patch is exactly what this mechanism guards against. If you don't want drift protection, just omit
fingerprint(trust mode).
CLI Workflow
1. Scaffold
clacky ext new fix-search --full
The --full reference container includes a patches/audit.rb sample (plus the other six contributions). You can also run clacky ext new fix-search, trim ext.yml down to just contributes.patches, and write the patch file yourself.
2. Write the patch
Follow the sample above. Don't delete the trailing .prepend(...) line — that's the action that actually makes the patch take effect.
3. Verify status
clacky ext verify
Sample output:
[OK] fix-search/timeout (patch, local)
[WARN] fix-search/timeout (patch.fingerprint.mismatch) — upstream code for Clacky::Tools::WebSearch#execute changed
[OK]: loaded and prepend is in effect.[WARN]: fingerprint mismatch, handled peron_mismatch(default not applied); you need to look at the new method and decide whether to rewrite the patch.
Workflow After an Upgrade
After a gem upgrade, if your patch carries a fingerprint, strongly consider running clacky ext verify immediately:
- See which patches report a fingerprint mismatch.
- Compare against the new method source and update the patch logic.
- Recompute and fill in the new fingerprint (or temporarily omit
fingerprintto run in trust mode).
When Not to Use a Patch
- Adding a new method / new class: there's no "drift" risk here, so skip the patch mechanism — write a skill or monkey-patch inside your own project.
- Changing a constant / config default: use
~/.clacky/config.ymlor an env var, don't touch code. - A large cross-method overhaul: this most likely warrants forking the gem or opening a PR, not stacking multiple patches — the more patches, the higher the chance they all break on upgrade.
The patch mechanism fits best when: one very specific method has one very localized fix.
⚠️ Patch files are arbitrary Ruby that mutate runtime behavior directly — the highest supply-chain risk of all. Before installing someone's extension, check which patches it contributes and which methods they change.