Repository navigation
feat(plugin-contract): prove reload after unload #34
Description
Feature description
The portable plugin lifecycle assertions prove repeated source (load, load) and exact unload, but no case proves reload after unload (load, unload, load). A plugin can pass every current case and still be broken for the rest of the shell after its second load.
Related Code
tests/_support/plugin-contract/scenario.zsh on main (1dc51f1) walks source, source, unload in its ordinary scenario and never loads again after unloading. The README example has the same shape.
Reproduced today against a copy of the shipped fixture changed the way z-shell/z-a-meta-plugins#54 was broken: unload leaves the callback defined as an inert stub instead of removing it, and load only defines the callback when it is absent.
mutant: repeated-source check PASSES (does not catch it)
mutant: reload surface DIFFERS (caught by a reload case)
mutant: effect after reload MISSING (caught by an effect check)
The unmodified fixture passes the same load, unload, load walk: the reloaded surface equals the first load, its hook is registered again, and a second unload is exact.
Additional Context
This is the remaining mechanical deliverable of z-shell/.github#585, whose documentation half landed in z-shell/.github#687 and z-shell/wiki#927 (the standard now says: "In a separate clean-shell case, load, unload, and load again. Prove that the second load restores the same declared load surface as the first and that the restored resources perform their intended behavior."). #18, which added the assertion set, is closed.
Proposed change:
- Add a
reloadscenario totests/_support/plugin-contract/scenario.zsh: load, snapshot, unload, load again, snapshot, assertplugin_restoredbetween the two loaded snapshots, then observe the effect of a restored resource (call it and check what it did), not only its presence. - Give the fixture a callback with an observable effect, so the effect check has something to observe.
- Add the scenario to
tests/plugin-contract.zunitin non-interactive and interactive shells, and a negative case with a fixture broken only after reload that must fail. - Show reload in the README's lifecycle example.
Acceptance criteria:
- A fixture that is correct only on its first load fails the new case, and the shipped fixture passes it.
- The reload case checks an observable effect, not presence or exit status.
./zunit --tap testspasses on the native job.- The README lifecycle example includes reload after unload.
Self-service
- I'd be willing to address this documentation request myself.
Have you read the Contributing Guidelines?
- I have read the Contributing Guidelines.
Are you familiar with the Contributor Covenant Code of Conduct?
- I have read the Contributor Covenant Code of Conduct.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
ss-o commentedon Sep 30, 2026
Project 28 triage pass. Blank values filled on this issue (existing values left unchanged):
Feature. A new assertion scenario (reload after unload) for the portable lifecycle contract.Medium. Plugin suites that use ZUnit's lifecycle assertions cannot currently catch a plugin that is broken only after reload (the mutant in the body passes today's repeated-source check).S. One scenario, one fixture effect, suite cases and a README example in one repository.P1 - High. It is the remaining deliverable of docs(standard): lifecycle verification omits reload after unload .github#585, the last open sub-issue of P1 initiative [initiative]: Deliver clean portable Zsh plugin architecture .github#556, so it blocks important work (High band).Human delivery. Ordinary feature work.No area label: this repository defines no canonical
area:*label that fits (onlyarea:ci); adding one is label maintenance, not triage.Next action: implement on a
feature-34branch with a failing reload case first, then the fixture change.test(plugin-contract): act on the review of the reload case
ss-o commentedon Sep 30, 2026
Progress: #35 implements every acceptance criterion here, on head
8799b23. All hosted checks pass, and a fallback review of record is on that head: #35 (review)Open decision (maintainer): the second review round found that the reload case never unloads after the second load, so a plugin whose unload leaks only after a reload passes every case. This is outside the criteria above. The choice is to extend #35 (one more push and review round) or to record it as a follow-up issue and merge as is.
Status:
In Progressuntil that decision is made; the pull request isIn Review.test(plugin-contract): prove reload after unload (#34) (#35)
ss-o commentedon Sep 30, 2026
Next action: maintainer decision on closure. #35 (merged, 6349b24) meets every acceptance criterion listed here: the
reloadscenario fails a fixture correct only on its first load (reload-stale), checks an observable effect, passes the native job, and the README shows reload. #35 usedRefsby maintainer decision; the post-reload unload gap it found is #36.