rhizome-node/plans/plugin-dep-vis.md
2025-06-23 00:59:55 -05:00

4.7 KiB

Plugin Dependency Visibility Implementation Plan

Overview

This document outlines the implementation plan for enforcing restricted state visibility in the CustomResolver system. The goal is to ensure that each plugin can only access the states of properties it has explicitly declared as dependencies.

Current Behavior

  • All plugins currently receive the complete allStates object containing all property states
  • There's no enforcement of which states a plugin can access
  • Dependencies are declared but not used for access control

Proposed Changes

1. Update ResolverPlugin Interface

interface ResolverPlugin<T = unknown> {
  name: string;
  dependencies?: PropertyID[];  // Explicitly declare which properties this plugin depends on
  // ... rest of the interface
}

2. Modify CustomResolver Implementation

2.1 Update Reducer Method

Modify the reducer method to filter states before passing to plugins:

public reducer(
  acc: CustomResolverAccumulator,
  cur: LosslessViewOne
): CustomResolverAccumulator {
  // ... existing setup code ...

  // Process each property in execution order
  for (const propertyId of executionOrder) {
    // ... existing delta processing ...
    
    // Create filtered states object with only declared dependencies
    const visibleStates: Record<PropertyID, unknown> = {};
    
    // Add states for declared dependencies
    if (plugin.dependencies) {
      for (const dep of plugin.dependencies) {
        if (allStates[dep] !== undefined) {
          visibleStates[dep] = allStates[dep];
        }
      }
    }

    // Pass only visible states to the plugin
    propertyState.state = propertyState.plugin.update(
      propertyState.state,
      value,
      delta,
      visibleStates
    );
    
    // ... rest of the method ...
  }
}

2.2 Update Resolver Method

Similarly update the resolver method:

resolver(cur: CustomResolverAccumulator): CustomResolverResult {
  const res: CustomResolverResult = {};

  for (const [entityId, entity] of Object.entries(cur)) {
    const entityResult = { id: entityId, properties: {} };
    const allStates: Record<PropertyID, unknown> = {};

    // First pass: collect all states
    for (const [propId, propState] of Object.entries(entity.properties)) {
      allStates[propId] = propState.state;
    }

    // Second pass: resolve each property with filtered states
    for (const [propId, propState] of Object.entries(entity.properties)) {
      const plugin = propState.plugin;
      const visibleStates: Record<PropertyID, unknown> = {};
      
      // Only include declared dependencies
      if (plugin.dependencies) {
        for (const dep of plugin.dependencies) {
          if (allStates[dep] !== undefined) {
            visibleStates[dep] = allStates[dep];
          }
        }
      }

      const resolvedValue = plugin.resolve(propState.state, visibleStates);
      if (resolvedValue !== undefined) {
        entityResult.properties[propId] = resolvedValue;
      }
    }

    if (Object.keys(entityResult.properties).length > 0) {
      res[entityId] = entityResult;
    }
  }

  return res;
}

3. Add Validation

Add validation to ensure dependencies exist:

private validateDependencies(): void {
  // Existing cycle detection...
  
  // Add validation that all dependencies exist
  for (const [pluginId, plugin] of Object.entries(this.config)) {
    for (const dep of plugin.dependencies || []) {
      if (!this.config[dep]) {
        throw new Error(`Plugin '${pluginId}' depends on unknown property: ${dep}`);
      }
    }
  }
}

4. Update Tests

  1. Add tests for state visibility:

    • Test that plugins only receive their declared dependencies
    • Test that plugins can't access undeclared dependencies
    • Test that dependency validation works
    • Test that existing functionality remains unchanged
  2. Update existing tests to account for the new behavior

Migration Strategy

  1. This is a breaking change for any plugins that were accessing undeclared dependencies
  2. Add warnings in the next minor version
  3. Make the behavior opt-in initially with a flag
  4. In the next major version, make it the default

Implementation Steps

  1. Add the state filtering to reducer
  2. Add the state filtering to resolver
  3. Update dependency validation
  4. Add comprehensive tests
  5. Update documentation
  6. Add deprecation warnings for plugins accessing undeclared dependencies

Future Considerations

  1. Add a debug mode that logs when plugins access undeclared dependencies
  2. Consider adding granular permissions (read/write) for dependencies
  3. Add support for wildcard dependencies for plugins that need access to many properties