chore: child to parent singleton bleed - #14
Draft
brunozoric wants to merge 25 commits into
Draft
Conversation
brunozoric
force-pushed
the
bruno/refactor/child-parent-singleton-bleed
branch
2 times, most recently
from
June 25, 2026 08:17
de91da1 to
93b9f3f
Compare
Describes the singleton bleed-through bug where child container registrations pollute a parent's cached singleton, and the fix: cache singletons per-resolving-container instead of per-owning-container. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds singleton variants of all childContainer.test.ts scenarios to ensure cross-resolution behavior is bulletproof under singleton scoping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add child decorator chain application to the singleton scoping fix - Add ProductRegistry example with parent/child/grandchild showing before/after behavior with 6 products and 2 decorators - Add registry.test.ts to impacted test list (found by code review) - Add 6 additional test scenarios: decorator chain, resolution ordering, singleton dependency chains - Document as breaking semantic change requiring semver bump Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 tasks covering: failing tests, Container.ts fix, existing test updates, singleton cross-resolution tests, decorator chain tests, and documentation updates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cold review findings: falsy singleton cache bug, reflect-metadata
side-effect in types.ts, circular { multiple: true } stack overflow,
dead prettier scripts, undocumented composite/resolveAll behavior.
6 tasks covering: cache fix, reflect-metadata cleanup, prettier
removal, circular depth guard, builder fluency, AGENTS.md docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds LifetimeScope.Global design (owner-context resolution, walk-up cache lookup, downward sharing) and fixes 20 issues found across 4 review passes including incorrect assertions, missing breaking tests, and table inaccuracies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a child container registers an additional plugin and resolves a
parent-registered singleton registry (with { multiple: true } deps),
the child's plugin leaks into the parent's cached singleton instance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Validates that a parent-registered singleton's cached instance is not affected by later parent registrations when resolved from a child whose own singleton is already cached. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AGENTS.md: add registry test directory to test structure, add singleton bleed-through to known issues - Design spec: mark regression tests 1-2 as implemented/failing, renumber remaining planned tests - Implementation plan: mark Task 1 complete with actual commit hashes and file paths - PR description: split tests section into committed (failing) and planned, add current status section Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Proves that registerInstance in a child container does not pollute the parent's resolution — instance registrations are per-container and isolated, unlike the singleton cache bleed bug. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add the ContainerToken inheritance issue to AGENTS.md known issues, test structure, and the singleton scoping design doc. Instance registrations are isolated (no bleed), but child containers inherit the parent's self-registration by default. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add the 5 new ContainerToken tests to committed tests section, current status, and test plan checklist. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The rebase left both sides of the merge in pnpm-workspace.yaml: the new settings block was added but the old one was never removed, leaving orphaned indented lines dangling under frozenLockfile and duplicate keys. The result was invalid YAML - pnpm could not load its configuration at all, so every command failed on a fresh clone. Rewrite the file as a single deduplicated block, validating every key against pnpm 12.3.4 (which ships as a native binary and warns on unrecognized keys). Tighten the settings while here: - allowBuilds: esbuild true -> false. esbuild is not installed (it is only an optional peer of rslib; this repo builds via rspack), so this was granting lifecycle-script rights to a package absent from the tree. core-js and node-gyp are likewise pinned false so they cannot gain build rights if they ever appear transitively. - autoInstallPeers: false. pnpm defaults this to true and silently installs missing peers as real packages; with strictPeerDependencies an unmet peer is now a hard error instead of an unreviewed dependency. This is recorded in the lockfile, hence the lockfile change - the package set is unchanged (493 entries before and after), only peer-resolution bookkeeping differs. - minimumReleaseAgeIgnoreMissingTime: false. Keeps a registry that cannot date its releases from bypassing both the age gate and trustPolicy. - strictStorePkgContentCheck / trustLockfile / verifyDepsBeforeRun: pin store content verification, force lockfile re-verification against the supply-chain policies on every install, and refuse to run scripts against a node_modules that does not match the lockfile. - saveExact: pin exact versions for anything added later. No age-gate or trust exceptions are configured; this package has no first-party dependencies that would need them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
brunozoric
force-pushed
the
bruno/refactor/child-parent-singleton-bleed
branch
from
September 11, 2026 08:24
8346446 to
b95ae9b
Compare
Revision 2 of the design. Records five review findings against the first revision and the decision for each: the decorator chain walk double-applied ancestor decorators (use resolveFrom.applyDecorators once), transient decorator behavior contradicted the table (decorators follow the requesting container on every non-global path), global scope was order-dependent (cache in the owning container), the falsy cache check (defensive only), and stale line/commit references. Adds a branch-hygiene section for the engines/CI mismatch and stale cache-key-collision material. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds 28 tests across three files that encode the revision 2 spec: singletonBleed.test.ts, childContainer/singletonCrossResolution.test.ts and singletonDecoratorChain.test.ts. 20 fail today because the fix is not implemented; the two existing registry regression tests keep failing as before. No passing test changed and no source changed. Global scope tests are deferred until the inGlobalScope() API exists. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copies .claude/skills/handoff from webiny-js and rewrites it for this repo: pnpm lint/build/test as the check gate, the docs table points at AGENTS.md, docs/*-design.md, docs/superpowers/plans, pr/<branch>.md, bugs/ and .changeset, handoff files go to docs/handoff/, and tests may fail only when they pin a designed-but-unimplemented behavior. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ems in spec Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Singletons registered in a parent container are polluted by child containers. The fix is designed and pinned by failing tests; the implementation in
src/Container.tsis not started.Design:
docs/2026-05-26-per-container-singleton-scoping-design.md(revision 2).Issues
resolveRegistrationcaches in the owning container but resolves dependencies from the requesting container. The first child to resolve a parent singleton writes its own view (extra{ multiple: true }entries, overridden deps) into the parent's cache, and every other container gets it.if (existing)instead ofif (existing !== undefined). Unreachable today sincenewalways yields an object, but wrong on its face.Possible fixes
Pick one context container per resolution and use it for cache, deps and decorators:
resolveRegistration:const context = scope === Global ? this : resolveFrom, thencontext.instances,context.resolveInternal(..., context),context.applyDecorators(..., context).tryResolveFromCurrentContainerandresolveMultiple: callresolveFrom.applyDecoratorsinstead ofthis.applyDecoratorsfor instance and factory registrations.LifetimeScope.GlobalandRegistrationBuilder.inGlobalScope().Alternatives rejected in the spec: smart caching by dependency diff, fixing only
{ multiple: true }, walk-up cache for globals.Open decisions
child.resolve(G).s !== child.resolve(S). Spec needs to state this.inGlobalScope(). This is the main migration cost.Breaking change
child.resolve(X) === parent.resolve(X)no longer holds for singletons. Migrate shared resources to.inGlobalScope(). Major release, changeset still to add.Tests
registry/registry.test.ts(2),singletonBleed.test.ts,childContainer/singletonCrossResolution.test.ts,singletonDecoratorChain.test.ts.singletons.test.ts, one inregistry/registry.test.ts, one incontainer.test.ts("should resolve instance from parent container if not found in child container"). All assert cross-container identity.inGlobalScope()exists:globalScope.test.ts(8 scenarios listed in the spec).containerToken.test.tsdocuments a separate inheritance limitation; unaffected.Branch hygiene
engines.nodeis>=24but CI runs Node 22.x.bugs/anddocs/SINGLETON_CACHE_KEY_COLLISION_TESTS.mddescribe a bug already fixed in fix: ensure implementations resolve correctly in minified builds #12.🤖 Generated with Claude Code