Repository navigation
Conversation
Add src/host/patch-method.ts (patchMethod), now the only way the plugin wraps a method on a host object, and move the four instance patch sites in src/models-discovery.ts onto it: the three listings hidden by hideResponsesRoute, decorateModelDiscovery, decorateModelInfo and decoratePlaneAdapters. On the real host the three llm-level disposers never restored: ctx.llm is cordis's traceable proxy, every method read returns a new shadow proxy, so the `===` identity checks were never true and each reload stacked a wrapper over the last. The helper reads identity from own descriptors instead. A dispose restores only while its wrapper is still current (otherwise it becomes a pass-through), deletes the own property when the original was inherited, and a same-site reinstall wraps the unpatched method rather than the earlier install, so either disposal order ends at the exact original. decoratePlaneAdapters becomes one patch per (adapter, method) that composes the adapter's plane routes in registry order, which keeps today's nesting on pi-ai's shared adapter. rewrapOnAdaptersUpdated is built and tested but wired to no site yet. test/reload.test.ts runs apply A, apply B, dispose A, dispose B (and the reverse order) for every site on own and inherited originals, on the real host (tier A) and through apply(); test/patch-method.test.ts covers the helper's contract. The existing per-site pins in test/models-discovery.test.ts stay unchanged; five new cases there record the deliberate edge changes on hosts that refuse the patch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
|
The fix exists. Generated by Claude Code |
What the review found, and what changed: - rewrapOnAdaptersUpdated reinstalls its site inside the plugin's own teardown and on every foreign registry change. Its JSDoc in src/host/patch-method.ts now says so, counts the reinstalls a release causes, says a caller disposes it before releasing its adapters, and notes that a throw from a reinstall is logged by the host. - The plan of record said all three P-H edge changes are unreachable on the real host for one reason; the third (the live-reload listing) is unreachable for a different one. docs/improvement-plan.md now gives each its reason. - The tier-A reload cases for the listing and discovery sites proved the row only structurally. test/reload.test.ts now gives every site a tier-A probe whose decorated answer must differ from the host's own, so "survivor still decorates" and "last dispose restores" can fail. - "patchMethod is the only way the plugin wraps a host method" was asserted in docs and enforced by nothing. test/architecture.test.ts now has a textual tripwire over everything apply() reaches, with a self-check of planted violations, and the apply()-twice reload case requires a patch record on every own function apply() leaves on the stand-in. - The tripwire missed several write forms (Object.assign, Object.defineProperty/defineProperties, Reflect.setPrototypeOf, Object.setPrototypeOf, and an Object.* target reached through a property path such as registration?.adapter or this.llm). The patterns now catch them, and the self-check plants each one (17 planted, up from 13). Nothing new in src/ matches. - The docs said "any Reflect.* write" where the pattern matches a fixed set. The test docstring, AGENTS.md and the plan's P-H section now name the four Reflect and four Object methods matched, and why a prototype swap is listed. No runtime behaviour changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
|
Second review of P-H This phase's first reviews ran on the general model because the dedicated reviewer was unavailable at the time. It has now had the dedicated review. Rounds (lenses: correctness, rules, test matrix)
All findings were low severity. Fixed
Declined Verification did not uphold one round-1 finding. Every upheld finding was fixed, and none is left open. There is no runtime behaviour change; the only stricter thing is the architecture tripwire. The full gate passes: check, unit suite, mock suite, build and bundle gate. Commit: 3cfacd9 Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…nt-mendel-bctz9n-p-h
…nt-mendel-bctz9n-p-h
…nt-mendel-bctz9n-p-h
Phase P-H of docs/improvement-plan.md, stacked on #110.
What changed
src/host/patch-method.ts(new)patchMethodis now the only way the plugin wraps a method on a host object. A dispose restores only while the own descriptor still holds its wrapper; otherwise the wrapper becomes a pass-through to its unpatched method. When the original was inherited, a dispose deletes the own property; when it was own, a dispose redefines its exact descriptor. A same-site reinstall (sameid) wraps the true unpatched method, never the earlier install, so either disposal order ends at the exact original. Identity is read from own descriptors, never with===on a property read. 100% per-file coverage floor.hideResponsesRoutelistProviders,listConfigurableProvidersandlistModelsonllmgo through the helper.decorateModelDiscoveryllm.discoverModelsgoes through the helper.decorateModelInfollm.resolveModelInfogoes through the helper.decoratePlaneAdapters[INTENDED_CHANGE]characterization row.rewrapOnAdaptersUpdateddecoratePlaneAdapterswould flip the plugin-first[KNOWN_DEFECT]rows, and P-R1 deletes that site.test/reload.test.ts(new)apply().test/patch-method.test.ts(new)test/models-discovery.test.tsdocs/improvement-plan.md(P-H status and landed record; the globalfetchstacking is left for P6),docs/engineering-notes.md(the measured identity trap),AGENTS.md,README.md,CONTRIBUTING.md.Why: on the real host the three
llm-level disposers never restored.ctx.llmis cordis's traceable proxy, and every read of a function-valued property returns a new shadow proxy, sollm.x === decoratedwas never true. Each reload stacked a wrapper that kept the previous config's decisions, and a disable left them installed. The plane-adapter disposer restored without checking, so disposing A wrote the prototype's method over B.Out of scope: the
globalThis.fetchswap inlifecycle.ts(a global, not a host service, now on P6's work list) and thellmFacadeProxy inresponses-provider.ts(a view, not a patch).Behaviour changes
None. No wrapper's answer changes on a host that accepts the patch. The
docs/improvement-plan.mdP-H entry records two things. First, three edge cases that the realLlmRuntimeand pi-ai adapter cannot reach: a refused listing define, a refused or frozen plane-adapter method, and a reinstall'sregisteredRoutes. Second, that a reload or a disable on the real host now restores the methods instead of stacking wrappers.Review
Round 3's findings were all low severity and were fixed in tests, test comments and the plan of record, with no runtime change. Each new regression probe was checked by mutating the source, watching exactly the new cases fail, and restoring the source byte-for-byte.
Verification
pnpm run checkvp test runvp test run --config vitest.mock.config.tsvp test run --coveragesrc/host/patch-method.tsat 100/100/100/100pnpm run buildscripts/check.tsNote: the
catalog freshnesscheck fails on every branch until the vendor-catalog refresh lands on main. That failure is unrelated to this change.Generated by Claude Code