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
…t1b' into claude/vigilant-mendel-bctz9n-p-h Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The pairing gate main added (test/docs-pairing.test.ts) counts the 'Changed in 1.x' list items per language; P-H's English bullet about disable and reload restoring the wrapped host methods had no Chinese twin, so the merge left the pair at 46/45. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The plan's P-H status row and its section heading now carry a `fix:` title, "disabling or reloading the plugin restores the host methods it wrapped", and record why it is no longer `refactor:`: main squash-merges, the pull request's title is the release signal, and release-please hides `refactor`, so the changelog would never carry the restore on a disable or a reload that the README's "Changed in 1.x" notes announce. - fixed: rules-docs-3-PH-RULES-4 — status row and section heading retitled with the reason; the pull request's own title and its "Behaviour changes" section move at the post-restack PR-body pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
|
Restacked onto main 1.8.0. This branch now merges the restacked P-T1b (#110) and so carries Conflicts and how they were resolvedReal conflict:
Merged clean, checked by hand:
Dropped
Review rounds
No code changed after the merge. The two follow-up commits only touch docs. Live re-measures still owed
Reviews that ran on the general model because the dedicated reviewer was unavailable: all twelve passes. These were the resolutions, both-kept and rules-docs reviews in rounds 1 to 3, plus the verify pass after each of rounds 1, 2 and 3. Generated by Claude Code |
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.mdandREADME.zh-CN.md(the paired "Changed in 1.x" bullet),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
One, user-visible: disabling or reloading the plugin now restores the host methods it wrapped. As the README pair words it: the model listing, model discovery and model-info decorations, and the plane adapters' fill, come off on a disable; a live reload replaces them instead of stacking another layer, so a setting removed in the reload stops applying. Before this change, on the real host a reload stacked a wrapper that kept the previous config's decisions, and a disable left them installed.
test/models-discovery.test.ts, on hosts the realLlmRuntimeand pi-ai adapter never are: a refused listing define (the listing install is now all-or-nothing), a refused or frozen plane-adapter method (skipped), and a reinstall'sregisteredRoutes(read from the host's own listing).The plan of record retitles P-H from
refactor:tofix: disabling or reloading the plugin restores the host methods it wrapped, because main squash-merges, the pull request's title is the release signal, and release-please hidesrefactor. This pull request's title is not changed in this pass; the retitle is still owed.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.Restacked onto main 1.8.0
The branch now merges the restacked P-T1b (#110), which carries
main1.8.0 through its own parent chain; P-H merges nomainitself.Scope now: one way to patch a host method, on top of the restacked P-T1b (both harness channels, no pi patch).
src/host/patch-method.ts(patchMethod,rewrapOnAdaptersUpdated): a dispose restores only while its own descriptor still holds its wrapper, deletes an inherited original's own property, and a same-site reinstall wraps the unpatched method. 100% per-file coverage floor.src/models-discovery.tssites (the three listings, discovery, model-info, plane-adapter fill) migrated to it, with the three pinned edge changes above.test/reload.test.ts: apply A, apply B, dispose A, dispose B for every site, in both orders, on own and inherited originals, and on tier A on both channels; also a reload with changed config andapply()twice.Reflect/Objectwrites and assignments ontollm, an adapter,settingsandglobalThis.AGENTS.mdmap lines.Dropped, because the restacked base now does it or the decision changed: the pinned-cell gate (the alpha cluster with pi 1.3.0 and the harness's patch) is superseded by the owner's no-patch decision; the alpha floor cell (pi 1.3.0 unpatched, cordis family at the record) and the rc floor cell (pi 1.1.0) stand in for it. Nothing in the helper, its sites or its tripwires was dropped.
Decisions applied: D14'' and D22'' (which replace D14' and D22'): tier A in
test/reload.test.tsruns on whichever channel is installed, newest and floor, with no pi patch. D24 and D25 do not touch P-H's files.Merge: the only conflict was
docs/improvement-plan.md: P-T1b's plan, its D14''/D22'' rows and status table, plus P-H's own landed phase-6 record, its pinning-phase exception, its test-plan entries, the P6 fetch-restore item and "P-H before P-R1". The other P-H files merged clean and carry P-H's side unchanged.Live re-measures CI must confirm: none specific to P-H. The
e2e-hostjob runs thenpmcell of both channels on this pull request (it touchessrc/models-discovery.tsandvite.config.ts); the alphanpmcell should show the inheritedHOST_LIMITATION(the host'sdsh-app-bootcannot save a config edit under the cordis npm now resolves), so its three save rows skip, as on P-T1b.The Verification table above predates the restack; CI on the pushed head (ff2db1d) is the current record.
Generated by Claude Code