An extension adopting the optional-integration recipe should verify all of
the following before release. The reference implementation of a two-branch
harness is the pi-repair-layer package smoke test
(scripts/package-smoke.mjs in that repo): it compiles the consumer once,
then runs the emitted JS in a clean project with and without the package
installed.
- Activation registers the adapted definition (not the raw object) and emits no fallback note.
- Strictly valid input passes through unchanged.
- Every configured alias/selector repair produces valid arguments (e.g.
{file_path: "/x"}→{path: "/x"}viaprepareArguments). - Unconfigured content and similar-but-unknown keys remain unchanged.
- Unrepairable input throws before the executor runs.
- An existing owner
prepareArgumentsshim is still called.
- Activation succeeds — no error escapes the factory.
- The registered definition is the raw one (identity: the same object,
no
prepareArgumentsadded). - Exactly one stderr note is emitted, naming the extension and the tool.
- Strictly valid input behaves identically to the adapter branch.
- Decide explicitly about invalid input: in fallback mode pi's native
validation runs TypeBox
Value.Convertfirst, which silently coerces some invalid values (null→"null") instead of repairing or rejecting. If that is unacceptable for the tool, the dependency should be hard, not optional.
- A module-not-found error naming a different (transitive) module is rethrown, not treated as absence — simulate by breaking a transitive dependency of the installed package.
- Compiled (Bun-binary) pi: recipe always falls back safely, even with the
package installed — dynamic
import()cannot resolve npm siblings there. - Git-installed or cross-scope consumers: fallback; offer
optionalDependencies. - Node-based pi installs with the package npm-installed in the same scope: adapter branch.
- If
onOutcomeor feedback is used: telemetry and persisted entries contain no argument values or note text (rule IDs, stages, policy, outcome, and fingerprints only). - If
RepairLifecyclefeedback is used: concurrent identical calls receive one note each, and stale notes do not leak to later calls.