Skip to content

feat(scope-eval): add new package for evaluating operations in isolated scopes#145

Open
taras wants to merge 9 commits intomainfrom
feat/scope-eval
Open

feat(scope-eval): add new package for evaluating operations in isolated scopes#145
taras wants to merge 9 commits intomainfrom
feat/scope-eval

Conversation

@taras
Copy link
Member

@taras taras commented Feb 3, 2026

Motivation

When testing Effection operations, we often need to:

  1. Capture operation results as values instead of exceptions
  2. Evaluate operations in isolated scopes to inspect their side effects
  3. Access context values set by those operations

Approach

@effectionx/scope-eval

useEvalScope()

Create isolated scopes for evaluating operations:

import { createContext } from "effection";
import { useEvalScope } from "@effectionx/scope-eval";

const context = createContext<string>("my-context");
const evalScope = yield* useEvalScope();

// Evaluate an operation that sets context
yield* evalScope.eval(function*() {
  yield* context.set("Hello World!");
});

// Inspect the context value
evalScope.scope.get(context); // => "Hello World!"

box() / unbox()

Utilities for capturing operation results:

import { box, unbox } from "@effectionx/scope-eval";

// Capture operation result as Ok/Err
const result = yield* box(function* () {
  return yield* someOperation();
});

if (result.ok) {
  console.log(result.value);
} else {
  console.log(result.error);
}

// Extract value (throws on error)
const value = unbox(result);

Policy Clarification

Also clarifies that use* resource functions (like useEvalScope()) are exempt from the Stateless Streams policy, since they follow Effection's convention for scope-bound resources.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 3, 2026

Open in StackBlitz

npm i https://pkg.pr.new/thefrontside/effectionx/@effectionx/scope-eval@145

commit: e1fbf8e

@claude
Copy link

claude bot commented Feb 3, 2026

Policy Compliance Assessment

Summary

Metric Count
Total items reviewed 3
Compliant 2
Violations 1
Violations details

Critical Violations

Item Policy Issue Fix
scope-eval/box.ts Stateless Stream Operations The box() function uses function* directly instead of the *[Symbol.iterator] pattern, which could lead to immediate execution when called rather than deferred execution Wrap the implementation in an object with *[Symbol.iterator]() method

Advisory Notes

  • The test file (scope-eval/eval-scope.test.ts) properly follows the No-Sleep Test Synchronization policy by using deterministic patterns like withResolvers() and direct assertions without sleep() calls.
  • The useEvalScope() function in eval-scope.ts correctly uses function* as it's not a stream operation but a resource creation function, so the Stateless Stream Operations policy does not apply to it.
  • The unbox() function is a synchronous utility (not an Operation), so it correctly does not use the *[Symbol.iterator] pattern.

Detailed Analysis

✅ No-Sleep Test Synchronization Policy (Recommended)

Status: COMPLIANT

All tests in scope-eval/eval-scope.test.ts follow the recommended pattern:

  • No sleep() calls used for waiting/synchronization
  • Uses deterministic operations and direct assertions
  • Tests are properly structured with immediate expectations after operations complete

⚠️ Stateless Stream Operations Policy (Recommended)

Status: VIOLATION

The box() function in scope-eval/box.ts:24-30 violates the policy:

Current implementation:

export function* box<T>(content: () => Operation<T>): Operation<Result<T>> {
  try {
    return Ok(yield* content());
  } catch (error) {
    return Err(error as Error);
  }
}

Issue: This function returns an Operation<T> but uses function* directly, which means it could execute immediately when called rather than deferring execution until yield*.

Required fix: Wrap in *[Symbol.iterator] pattern:

export function box<T>(content: () => Operation<T>): Operation<Result<T>> {
  return {
    *[Symbol.iterator]() {
      try {
        return Ok(yield* content());
      } catch (error) {
        return Err(error as Error);
      }
    }
  };
}

This ensures that box() returns an object that can be stored as a constant and reused without side effects, matching the pattern used throughout the @effectionx/stream-helpers package.


Recommendation

The PR should address the box() function violation before merge to maintain consistency with the Stateless Stream Operations policy.

@cowboyd
Copy link
Member

cowboyd commented Feb 3, 2026

Policy bot ftw 💪🏻

We should agree on what template the agent will follow when it creates a PR summary. Parts of it are really helpful, but others are just noise. For example, saying how many tests are there for each function doesn't really contribute anything and causes eyes to glaze over.

If we can train it to stick to our PR template that would be very helpful. In particular, we have developed a culture of leading with why not how. Hence Motivation, not Summary

It just makes it so much faster to review, and with the volume of PRs increasing (a good thing) we should make our PR descriptions as slick as possible so they can slide right through.

taras added a commit that referenced this pull request Feb 3, 2026
- Remove verbose summary tables and counts
- Only list violations, not compliant items
- Keep feedback concise and actionable
- When compliant, just say 'No violations found.'

Addresses feedback from #145
@claude
Copy link

claude bot commented Feb 3, 2026

Policy Review

1 violation found:

  • scope-eval/box.ts:23-28 - Stateless Stream Operations: Duplicate box() implementation using function* should be removed. Only the *[Symbol.iterator] pattern version (lines 29-39) should remain, as operations returning Operation<T> must use this pattern for deferred execution and reusability.

All other files comply with both policies:

  • No-Sleep Test Synchronization: Tests use deterministic assertions without sleep-based waiting
  • Stateless Stream Operations: All other operations correctly use the *[Symbol.iterator] pattern

@claude
Copy link

claude bot commented Feb 3, 2026

Policy Review

1 violation found:

  • scope-eval/eval-scope.ts - Stateless Stream Operations: useEvalScope() uses function* directly instead of returning an object with *[Symbol.iterator]

The function should be refactored to return { *Symbol.iterator { ... } } to enable deferred execution and reusability, matching the pattern used by box() and other stream helpers.

taras and others added 4 commits February 3, 2026 14:26
…ed scopes

Adds @effectionx/scope-eval package that provides:

- useEvalScope(): Create an isolated scope for evaluating operations
  and inspecting their side effects (like context values)
- box(): Execute an operation and capture result as Result<T>
- unbox(): Extract value from Result<T>, throwing on error

This enables testing patterns where you need to evaluate operations
in a separate scope and access context values set by those operations.
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- Remove duplicate box() function definition
- Apply Stateless Streams policy (use *[Symbol.iterator] pattern)
- Fix formatting (tabs -> spaces)
…s policy

Resource/hook functions like useScope(), useEvalScope() are designed to set up
state within a scope and are not meant to be stored and reused like stream
operations.
@claude
Copy link

claude bot commented Feb 3, 2026

Policy Review

No violations found.

All code in this PR complies with the policies defined in .policies/index.md:

  • No-Sleep Test Synchronization: Tests use deterministic patterns; no problematic sleep() usage found
  • Stateless Stream Operations: box() correctly uses *[Symbol.iterator] pattern; useEvalScope() uses function* which is acceptable for use* resource functions per the policy exception added in this PR

- Create new result package with box() and unbox() utilities
- Update scope-eval to depend on @effectionx/result
- Re-export box/unbox from scope-eval for convenience
- Move box/unbox tests to result package
@claude
Copy link

claude bot commented Feb 3, 2026

Policy Review

No violations found.

All code changes comply with the project policies:

  • No-Sleep Test Synchronization: Tests use deterministic patterns without sleep() for synchronization ✓
  • Stateless Stream Operations:
    • box() correctly uses *[Symbol.iterator]() pattern for reusable operations
    • useEvalScope() appropriately uses function* as a resource function (matches the policy exception added in this PR)
    • EvalScope.eval() method uses generator method syntax correctly

@claude
Copy link

claude bot commented Feb 3, 2026

Policy Review

No violations found.


Compliance Summary

Stateless Stream Operations Policy (.policies/stateless-streams.md):

  • box() in result/box.ts:24 correctly uses *[Symbol.iterator]() pattern
  • useEvalScope() in scope-eval/eval-scope.ts:75 correctly uses function* (exempt as use* resource function per policy clarification)
  • eval() method in scope-eval/eval-scope.ts:109 correctly uses *[Symbol.iterator]() pattern for the returned operation

No-Sleep Test Synchronization Policy (.policies/no-sleep-test-sync.md):

  • ✅ No sleep() calls found in test files
  • ✅ Tests use deterministic synchronization patterns (direct assertions, no timing dependencies)

Policy Clarification:

  • The PR includes an update to .policies/stateless-streams.md that correctly documents the use* exception for resource functions like useEvalScope(). This clarification aligns with Effection's convention and is appropriate.

@claude
Copy link

claude bot commented Feb 3, 2026

Policy Review

No violations found.

Summary:

All changes comply with the project policies:

  • Stateless Streams Policy:

    • box() correctly uses *[Symbol.iterator] pattern for reusable stream operation (result/box.ts:24-34)
    • useEvalScope() properly uses function* as a resource function with use prefix (scope-eval/eval-scope.ts:75)
    • EvalScope.eval() method correctly uses * generator method syntax (scope-eval/eval-scope.ts:109)
    • Policy clarification appropriately documents the use* exception (.policies/stateless-streams.md:19)
  • No-Sleep Test Synchronization Policy:

    • All tests use deterministic patterns
    • No sleep() calls for waiting on results
    • Tests properly use synchronous operations and unbox() for assertions

@taras taras requested a review from cowboyd February 4, 2026 15:00
@cowboyd
Copy link
Member

cowboyd commented Feb 4, 2026

Ok,Err, and Result are all available from effection core. I'm totally ok including box and unbox in scope-eval

Per feedback: Ok, Err, and Result are available from effection core,
so box and unbox can stay in scope-eval without a separate package.
@claude
Copy link

claude bot commented Feb 4, 2026

Policy Review

No violations found.

Summary

This PR adds the @effectionx/scope-eval package and updates the Stateless Stream Operations policy to clarify the exception for use* resource functions.

Policy Compliance:

  1. No-Sleep Test Synchronization (.policies/no-sleep-test-sync.md): All tests use deterministic synchronization with withResolvers() and direct operation evaluation. No sleep() calls used for waiting.

  2. Stateless Stream Operations (.policies/stateless-streams.md):

    • box() correctly implements *[Symbol.iterator]() pattern (scope-eval/box.ts:24-33)
    • useEvalScope() uses function* directly, which is now properly documented as acceptable for use* resource functions
    • Policy update clarifies this exception appropriately

The code follows all applicable policies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants