You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Zoo Code repeatedly implements dependency construction, asynchronous readiness checks, and resource cleanup within individual features. This increases maintenance cost and makes initialization failures, cancellation, and shutdown ordering harder to reason about, with potential impact on extension reliability.
Context (who is affected and when)
This became apparent during the Code Index refactoring. CodeIndexWorkspaceScope was introduced to centralize workspace-level ownership, but we should evaluate a reusable approach rather than reproduce lifecycle infrastructure for every feature.
Concrete examples from the current implementation:
Construction is not readiness:CodeIndexWorkspaceScope constructs the state manager and manager synchronously and guards access to the constructed manager. Configuration loading and asynchronous initialization remain in CodeIndexManager, with additional initialization during indexing.
Wiring and checks are manual:CodeIndexServiceFactory connects the embedder, vector store, scanner, and watcher. The manager checks optional service fields, while CodebaseSearchTool separately checks configuration loading, enablement, configuration validity, and initialization. Product states such as disabled or unconfigured must remain distinct from construction readiness.
Cleanup ownership is distributed: the workspace scope creates the state manager, but the manager disposes it. CodeIndexOrchestrator requests scan cancellation and disposes watcher resources without awaiting scan settlement. Extension activation registers managers for cleanup, while deactivation also disposes their scopes through the registry. Ordering and idempotency therefore require explicit conventions.
Failure paths require separate reasoning: service recreation performs several allocations and asynchronous steps before publishing the complete graph, without a surrounding scoped rollback mechanism. Error recovery clears service references separately. These paths motivate testing cleanup after partial initialization, rather than assuming a library wrapper would fix it automatically.
The pattern is not unique to Code Index:ClineProvider constructs services, starts background initialization, and manually tears them down. Task maintains readiness promises and resource-by-resource disposal. These are comparison points, not initial migration targets.
The contributors’ discussion suggested preparing a technical brief and establishing an ADR process. No docs/adr directory or documented ADR convention was found in the inspected checkout; existing architecture documents are under docs/architecture.
Desired behavior (conceptual, not technical)
Contributors should have a consistent way to declare dependencies, expose services only after required initialization, and release resources when their owner ends. Features should expose a small public API, with explicit ownership and lifetimes for their private dependencies.
Create an ADR with Proposed status to investigate this approach. Effect is a preferred candidate for evaluation, not an approved architectural decision. The intended outcome is an evidence-based decision after a limited experiment—not an immediate project-wide migration.
Constraints / preferences (optional)
Keep the initial experiment within Code Index and preserve existing behavior, progress events, promise-based callers, and non-blocking activation.
Distinguish service readiness from completion of indexing and from disabled/unconfigured product states.
Preserve workspace separation and define how shared extension services are borrowed rather than disposed by a feature.
Do not conflate dependency/resource management with Task state transitions or persistence.
Agree on a minimal ADR location, template/numbering, and review process; keep the ADR Proposed until reviewed.
Request checklist
I've searched existing Issues and Discussions for duplicates
This describes a specific problem with clear context and impact
Acceptance criteria (optional)
Existing dependency construction, initialization, exposure, ownership, and cleanup patterns are documented with source examples.
Effect’s suitability is evaluated, distinguishing library guarantees from application-level conventions and adapter responsibilities.
typed-inject, Awilix, Knifecycle, and a no-new-runtime baseline are considered.
The relationship between Effect and XState is clarified, without assuming either replaces the other.
A bounded Code Index PoC PR is defined, including tests, success criteria, non-goals, and rollback strategy.
Migration risks, learning costs, debugging/testing implications, and compatibility with existing promise-based APIs, VS Code shutdown, and CLI execution are documented.
An ADR is created with Proposed status using an agreed convention; broader adoption remains subject to review.
Proposed approach (optional)
Investigate Effect
Evaluate declarative dependency composition, dependency-aware asynchronous initialization, scoped cleanup, structured errors, and owned background operations. Define appropriate extension, workspace, provider, and task lifetimes without committing to migrate all of them.
Readiness follows the declared dependency graph only when required initialization is included in acquisition, not detached background work.
Scoped finalizers clean up registered resources when the scope closes, including failure/interruption paths. Resources allocated inside a failing acquisition still require correctly staged acquisition and cleanup registration.
Closing a scope alone does not interrupt arbitrary pending work. Background operations need explicit ownership; existing asynchronous APIs need cooperative cancellation adapters and a defined settlement/shutdown policy.
Effect does not enforce complete feature isolation or invalidate escaped references to disposed objects. Public exports, import boundaries, and post-disposal API behavior remain architectural responsibilities.
Layer memoization is not an automatic application-wide singleton policy; sharing must match intended lifetimes.
Code Index PoC
Prepare a small, reversible PR that models one workspace’s dependencies behind a narrow feature API and moves a representative initialization/resource-owning path into Effect. Include watcher subscriptions and a cancellable background scan without rewriting indexing algorithms or unrelated features. Explicitly state coverage of external-provider and Semble paths.
Test concurrent initialization, partial initialization failure, dependent-before-dependency cleanup, repeated shutdown, shutdown during active work, workspace separation, and configuration-driven service replacement. Compare lifecycle plumbing removed against adapter code added, test clarity, debugging experience, activation time, and bundle size.
Use the results to update the proposed ADR and decide whether to adopt, narrow, defer, or reject the approach.
XState is a separate consideration
XState focuses on state machines, transitions, actors, and stateful orchestration. Effect’s role here is dependency composition, asynchronous execution, errors, concurrency, and resources. Their capabilities overlap, but they are not interchangeable or necessarily mutually exclusive.
Task state management may warrant a separate ADR, grounded in the existing Task lifecycle model, production-backed reducers, and bounded checks. The Code Index PoC must not replace those contracts.
Trade-offs / risks (optional)
Effect introduces a broader computation model than a conventional DI container. Evaluate its learning curve, maintenance and debugging costs, error/cancellation translation at promise boundaries, runtime ownership, bundle overhead, and the risk of spreading Effect-specific APIs beyond the intended boundary. Record the version evaluated and upgrade implications.
Compare against:
typed-inject: narrower, type-safe DI with injector lifetimes and disposal support; assess remaining asynchronous initialization and cancellation coordination.
Awilix: container-based DI with scoped lifetimes and disposal hooks; assess initialization and cleanup-order requirements.
Knifecycle: asynchronous dependency initialization and service shutdown management without adopting Effect’s broader model.
No new runtime: standardize explicit factories, asynchronous construction, and reusable ownership scopes around the existing design.
The ADR should explain why the chosen scope of adoption is simpler and safer than these alternatives—or conclude that Effect adds unnecessary complexity.
Problem (one or two sentences)
Zoo Code repeatedly implements dependency construction, asynchronous readiness checks, and resource cleanup within individual features. This increases maintenance cost and makes initialization failures, cancellation, and shutdown ordering harder to reason about, with potential impact on extension reliability.
Context (who is affected and when)
This became apparent during the Code Index refactoring.
CodeIndexWorkspaceScopewas introduced to centralize workspace-level ownership, but we should evaluate a reusable approach rather than reproduce lifecycle infrastructure for every feature.Concrete examples from the current implementation:
CodeIndexWorkspaceScopeconstructs the state manager and manager synchronously and guards access to the constructed manager. Configuration loading and asynchronous initialization remain inCodeIndexManager, with additional initialization during indexing.CodeIndexServiceFactoryconnects the embedder, vector store, scanner, and watcher. The manager checks optional service fields, whileCodebaseSearchToolseparately checks configuration loading, enablement, configuration validity, and initialization. Product states such as disabled or unconfigured must remain distinct from construction readiness.CodeIndexOrchestratorrequests scan cancellation and disposes watcher resources without awaiting scan settlement.Extension activationregisters managers for cleanup, while deactivation also disposes their scopes through the registry. Ordering and idempotency therefore require explicit conventions.ClineProviderconstructs services, starts background initialization, and manually tears them down.Taskmaintains readiness promises and resource-by-resource disposal. These are comparison points, not initial migration targets.The contributors’ discussion suggested preparing a technical brief and establishing an ADR process. No
docs/adrdirectory or documented ADR convention was found in the inspected checkout; existing architecture documents are underdocs/architecture.Desired behavior (conceptual, not technical)
Contributors should have a consistent way to declare dependencies, expose services only after required initialization, and release resources when their owner ends. Features should expose a small public API, with explicit ownership and lifetimes for their private dependencies.
Create an ADR with Proposed status to investigate this approach. Effect is a preferred candidate for evaluation, not an approved architectural decision. The intended outcome is an evidence-based decision after a limited experiment—not an immediate project-wide migration.
Constraints / preferences (optional)
Request checklist
Acceptance criteria (optional)
Proposed approach (optional)
Investigate Effect
Evaluate declarative dependency composition, dependency-aware asynchronous initialization, scoped cleanup, structured errors, and owned background operations. Define appropriate extension, workspace, provider, and task lifetimes without committing to migrate all of them.
Relevant primary documentation: layers, scopes and resource acquisition, runtime integration, and layer memoization.
The ADR should explicitly document these limits:
Code Index PoC
Prepare a small, reversible PR that models one workspace’s dependencies behind a narrow feature API and moves a representative initialization/resource-owning path into Effect. Include watcher subscriptions and a cancellable background scan without rewriting indexing algorithms or unrelated features. Explicitly state coverage of external-provider and Semble paths.
Test concurrent initialization, partial initialization failure, dependent-before-dependency cleanup, repeated shutdown, shutdown during active work, workspace separation, and configuration-driven service replacement. Compare lifecycle plumbing removed against adapter code added, test clarity, debugging experience, activation time, and bundle size.
Use the results to update the proposed ADR and decide whether to adopt, narrow, defer, or reject the approach.
XState is a separate consideration
XState focuses on state machines, transitions, actors, and stateful orchestration. Effect’s role here is dependency composition, asynchronous execution, errors, concurrency, and resources. Their capabilities overlap, but they are not interchangeable or necessarily mutually exclusive.
Task state management may warrant a separate ADR, grounded in the existing Task lifecycle model, production-backed reducers, and bounded checks. The Code Index PoC must not replace those contracts.
Trade-offs / risks (optional)
Effect introduces a broader computation model than a conventional DI container. Evaluate its learning curve, maintenance and debugging costs, error/cancellation translation at promise boundaries, runtime ownership, bundle overhead, and the risk of spreading Effect-specific APIs beyond the intended boundary. Record the version evaluated and upgrade implications.
Compare against:
The ADR should explain why the chosen scope of adoption is simpler and safer than these alternatives—or conclude that Effect adds unnecessary complexity.