The render tag wrote `self:` attributes directly into @scopes[0] under
key 'self', which shadowed the SelfDrop. `self[var]` lookups inside the
snippet then did strict key access on the bound object instead of
walking the scope chain, resolving to nil for any key not in the object.
SelfDrop now holds an optional `bound_self`; `[]` and `key?` consult it
first via duck typing (matches lib/liquid/variable_lookup.rb), then fall
through to the scope-chain walk on miss. Render#render_tag routes
attribute key Expression::SELF to inner_context.self_drop.bound_self=
instead of the generic scope write. Lookup order is bound-first;
existing self: users' hits stay intact, previously-nil misses now
resolve via fallthrough.
PR #2060 introduced the SelfDrop and bare-bracket prohibition but had
no coverage for the {% render 'snippet', self: obj %} interaction.
Adds 8 tests including two-deep nested renders with leak detection
and a composite chain combining renders, top-level self[var], and
regular variables.
Discovered while investigating SFR strict-parser migration parity
diffs.