forked from commontoolsinc/labs
-
Notifications
You must be signed in to change notification settings - Fork 0
[pull] main from commontoolsinc:main #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
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
…ergence (#2270) ## Summary This PR implements a complete overhaul of the scheduler with pull-based execution and adds comprehensive debugging tools to the shell. ### Pull-Based Scheduler - **Lazy execution model**: Computations only run when effects need their outputs, reducing unnecessary work - **Topological ordering**: Dependencies run in correct order with parent-child action ordering - **Cycle-aware convergence**: Uses settle loop with smart cycle detection - fast cycles (<16ms) converge synchronously, slow cycles yield between iterations - **Dynamic dependency discovery**: Actions can specify custom `populateDependencies` for optimized scheduling (e.g., ifElse only depends on condition initially) - **Performance controls**: Debounce/throttle APIs with auto-detection for slow actions (>50ms) ### New Debugger UI #### Scheduler Tab (New!) Visual debugging tool for understanding reactive dependency graphs: - **Graph View**: Interactive DAG visualization showing effects, computations, and their dependencies - Color-coded nodes (red effects, blue computations, white inputs) - Parent-child relationship edges with legend - Reads/writes diagnostics on each node - Zoom, pan, and collapse controls - Dirty/pending state indicators - **Table View**: Sortable list of all scheduler actions - Performance stats (run count, avg/max duration) - Baseline snapshots for comparing before/after - Debounce/throttle badges - Source location links - Inactive node tracking #### Loggers Tab (New!) Runtime logging control panel: - **Granular log control**: Enable/disable individual loggers at runtime - **Level selection**: Set log level per logger (error, warn, info, debug) - **Hierarchical organization**: Loggers grouped by namespace - **Live message counts**: Real-time counters for each logger - **Mute/unmute controls**: Quick toggles with visual feedback ### Key Features **Pull-Based Core:** - Dirty tracking (`dirty` Set, `markDirty`, `isDirty`, `clearDirty`) - Effect/computation distinction with `isEffect` option in `subscribe()` - Reverse dependency graph for transitive dirty collection - `Cell.pull()` method for demand-driven value retrieval **Builtin Optimizations:** - `RawBuiltinResult` interface for builtins to customize scheduling behavior - `ifElse` only depends on condition, not both branches - `navigateTo` self-declares as effect **Performance Diagnostics:** - Action stats tracking by source location - `mightWrite` tracking for potential writes - `writersByEntity` index for O(1) dependency lookup - Telemetry events for dependency updates ## Test Plan - [x] All scheduler tests pass (12 test groups, 88 steps) - [x] All 144 generated-patterns integration tests pass - [x] Backwards compatibility with `pullMode=false` verified - [x] Cycle convergence behavior covered by automated tests - [x] Debounce/throttle behavior covered by automated tests - [x] ifElse branch selection test added ## Migration Notes - `subscribe()` removes legacy boolean signature; use options object with `isEffect` and `rescheduling` - Prefer `cell.pull()` over `idle()+get()` for demand-driven reads - Add a sink to long-lived outputs you want to keep reactive (e.g., charms) - `get()` now accepts `{ traverseCells?: boolean }` for deep dependency capture --- * feat(scheduler): implement pull-based scheduler phases 1 & 2 Phase 1 - Effect Marking: - Add effect/computation tracking (effects, computations Sets) - Modify subscribe() to accept isEffect option - Mark sink callbacks as effects in subscribeToReferencedDocs() - Add diagnostic APIs (getStats, isEffect, isComputation, getDependents) - Build reverse dependency graph (dependents WeakMap) Phase 2 - Pull-Based Core: - Add dirty tracking (dirty Set, markDirty, isDirty, clearDirty) - Add pullMode feature flag with enable/disable methods - Modify storage change handler to branch on pullMode - Add scheduleAffectedEffects() for effect discovery - Implement pullDependencies() and runComputation() for pull mechanism - Add topologicalSortDirty() for ordering dirty computations - Update execute loop with pull mode assertion Both phases include comprehensive tests and maintain backwards compatibility with existing push-based scheduling (pullMode=false). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(scheduler): simplify pull-based scheduling execution model Refactor Phase 2 implementation based on code review feedback: - Remove pullDependencies() call from run() - run() just runs the action - Change execute() to build work queue differently in pull mode: - Collect pending effects + all their dirty computation dependencies - Topologically sort the combined set - Run all using existing run() method - Add collectDirtyDependencies() helper for transitive dirty dep collection - Remove unused methods: pullDependencies(), topologicalSortDirty(), runComputation() (~165 lines removed) This avoids the server roundtrip issue from awaiting commit() inside runComputation(). Instead, all dirty dependencies are collected upfront and run through the standard execution path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(scheduler): remove legacy boolean signature from subscribe() Remove the deprecated boolean third parameter from scheduler.subscribe(). The method now only accepts the options object format: subscribe(action, log, { scheduleImmediately?: boolean, isEffect?: boolean }) Updated all call sites across the codebase: - packages/runner/src/scheduler.ts - packages/runner/src/runner.ts - packages/runner/test/scheduler.test.ts - packages/shell/src/lib/iframe-ctx.ts - docs/specs/pull-based-scheduler/scheduler-graph-investigation.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * remove "phase 1" "phase 2" comments * lint, fmt * docs(scheduler): update implementation plan for Phase 2 refactoring Update Phase 2 tasks to reflect actual implementation: - Document collectDirtyDependencies() approach in execute() - Document removal of legacy boolean signature from subscribe() - Remove references to pullDependencies()/runComputation() methods Update Phase 3 tasks to align with new architecture: - Cycle detection now happens during collectDirtyDependencies() - Cycle handling integrated into execute() flow - Updated method names and descriptions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): implement Phase 3 cycle-aware convergence Add compute time tracking and cycle detection to the pull-based scheduler: - Add ActionStats interface and actionStats WeakMap to track execution times - Add recordActionTime() to measure action performance - Add getActionStats() public API for diagnostics - Add detectCycles() using Tarjan's algorithm for SCC detection - Add collectStack for cycle detection during dependency collection - Add convergeFastCycle() for synchronous convergence of fast cycles (<16ms) - Add runSlowCycleIteration() for yielding slow cycles between iterations - Modify execute() to detect and handle cycles before normal execution - Add comprehensive tests for cycle detection, stats tracking, and convergence Constants: - MAX_CYCLE_ITERATIONS = 20 - FAST_CYCLE_THRESHOLD_MS = 16 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): implement Phase 4 debounce and throttle Add debounce infrastructure for slow actions: - setDebounce/getDebounce/clearDebounce API - scheduleWithDebounce for delayed scheduling - Auto-debounce detection for actions >50ms avg after 3 runs - Debounce option in subscribe() Add throttle (staleness tolerance) for computations: - setThrottle/getThrottle/clearThrottle API - isThrottled check in execute() to skip recently-run actions - Throttled actions stay dirty for future pulls - Throttle option in subscribe() Key difference: - Debounce: wait until triggers stop, run after T ms quiet - Throttle: accept staleness up to T ms, skip if ran recently All 111 tests pass (19 new tests for debounce/throttle). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs(scheduler): add Phase 5 push-triggered filtering plan New phase before migration that combines pull and push mode strengths: - Pull mode builds conservative "might need to run" work set - Push mode knows what actually changed (via determineTriggeredActions) - Running their intersection gives precision without losing pull semantics Key components: - Track "mightWrite" per action (accumulated historical writes) - Track "pushTriggered" set per execution cycle - Filter work set to skip actions not triggered by actual changes - Edge cases: scheduleImmediately, first run, cycles bypass filter Renumbers old Phase 5 (Migration) to Phase 6. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): implement Phase 5 push-triggered filtering Combine pull mode's conservative work set with push mode's precision: - Pull mode builds superset of what might need to run - Push mode knows what actually changed via storage notifications - Run intersection: only actions triggered by actual changes Implementation: - mightWrite WeakMap: accumulates all paths an action has ever written - pushTriggered Set: tracks actions triggered by storage changes each cycle - scheduledImmediately Set: tracks actions that bypass filtering - shouldFilterAction(): decides whether to skip an action - filterStats: tracks filtered vs executed counts for diagnostics Bypass conditions (action always runs): - Scheduled with scheduleImmediately: true - First run (no prior mightWrite) - Triggered by actual storage change (in pushTriggered) Diagnostic API: - getMightWrite(action): inspect accumulated writes - getFilterStats(): get { filtered, executed } counts - resetFilterStats(): reset statistics All 112 tests pass (7 new tests for filtering). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Update docs/specs/pull-based-scheduler/scheduler-implementation-plan.md Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * fix(scheduler): fix fast cycle convergence and add missing test assertions - Fix convergeFastCycle to check both dirty AND pending sets for cycle members (actions get re-added to pending when they write, not dirty) - Call handleError when fast cycle iteration limit is reached (was only logging before, inconsistent with slow cycle behavior) - Clean up both dirty and pending after hitting limit to stop the cycle - Fix "should enforce iteration limit" test to subscribe both actions before awaiting idle (required for cycle detection) - Add assertion to verify error handler is called on cycle limit - Fix "should detect multiple independent cycles" test to properly declare read/write dependencies (was using empty arrays) - Change meaningless toBeGreaterThanOrEqual(0) to toBe(2) assertion - Disable memory provider logger 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fmt * feat(cell): add pull() method for demand-driven value retrieval Add Cell.pull() method that registers a temporary effect and waits for the scheduler to process it. This ensures all dependencies are computed before returning the value, providing consistent behavior across both push and pull scheduling modes. Key features: - Registers as effect with scheduleImmediately: true - Waits for scheduler.idle() before resolving - Unsubscribes after value is retrieved (one-shot) - Works in both push mode (equivalent to idle() + get()) and pull mode (triggers dependency computation) Use case: Replace `await idle(); cell.get()` pattern with cleaner `await cell.pull()` that works correctly in pull-based scheduling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * lint * feat(scheduler): enable pull mode by default and fix tests Enable pull-based scheduling by default (pullMode = true) and update all tests that assume push-mode behavior to explicitly disable pull mode. Tests updated: - scheduler.test.ts: basic scheduler, event handling, reactive retries, cycle-aware convergence describe blocks - runner.test.ts: runRecipe, setup/start describe blocks - recipes.test.ts: Recipe Runner describe block - when-unless.test.ts: when and unless describe block - llm-dialog.test.ts: llmDialog describe block - fetch-data-mutex.test.ts: fetch-data mutex describe block - cell.test.ts: Cell describe block - html-recipes.test.ts: recipes with HTML describe block The pattern for push-mode tests is to add: runtime.scheduler.disablePullMode(); in the beforeEach hook. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix pull scheduler dirty propagation and dependents * feat(scheduler): disable pull mode by default and migrate tests to use .pull() - Change pullMode default to false in scheduler for stability - Add deepTraverse helper to Cell.pull() for schemaless cells - Remove disablePullMode() from test setup in cell, llm-dialog, fetch-data-mutex, and html-recipes tests - Migrate tests to use .pull() instead of runtime.idle() + .get() - Keep explicit push mode tests in scheduler.test.ts and cell.test.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * lint * remove debugging output * change fetch test to pull result to trigger action * fix(scheduler): improve pull mode cycle detection and error recovery - Use mightWrite in collectDirtyDependencies to handle error recovery: After a computation throws before writing, mightWrite preserves knowledge of what paths it could write, ensuring it's still pulled when effects need its output - Add path overlap check in getSuccessorsInWorkSet to fix spurious cycle detection: Previously only compared space/id, now also checks arraysOverlap(write.path, read.path) to avoid false positives - Update cell.test.ts to use pull() instead of runtime.idle() + get() for the argumentCell test case 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(runner): pull handler-returned recipes and add persistent effect test - Add resultCell.pull() when handlers return recipes to ensure execution (nothing else would read from them by default) - Add test verifying pull() doesn't create persistent effects after completion 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(runner): track potential writes in ReactivityLog for diffAndUpdate Add markReadAsPotentialWrite metadata marker to track reads that may result in writes. This is useful for operations like diffAndUpdate which reads values to compare before conditionally writing them back. Changes: - Add markReadAsPotentialWrite metadata marker (scheduler.ts) - Extend ReactivityLog type with optional potentialWrites array - Update txToReactivityLog to populate potentialWrites when flag is set - Use markReadAsPotentialWrite in diffAndUpdate so Cell.set tracks all properties it compares, even ones that don't actually change This enables the scheduler to know which paths an action might write to, not just which paths it actually wrote to. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * update mightWrite with potential writes as well * docs(scheduler): add Phase 5b for parent-child action ordering Add implementation plan for tracking parent-child relationships between actions to ensure proper execution order in pull mode: - Parent actions must run before their children - When parent unsubscribes children, remove them from work set - Prevents stale execution when lifts return recipes that get replaced This addresses the two failing recipe tests in pull mode: - "should execute recipes returned by handlers" - "should handle recipes returned by lifted functions" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): implement Phase 5b parent-child action ordering Track parent-child relationships between actions to ensure proper execution ordering in pull mode. When a child action is created during parent execution, the parent must run first so it can potentially unsubscribe stale children. Implementation: - Add executingAction, actionParent, actionChildren tracking properties - Record parent-child relationship in subscribe() when called during action execution - Clean up relationships in unsubscribe() - Pass actionParent to topologicalSort() to add parent→child edges - Track executingAction in run() method Tests added for: - Parent executes before child - Child skipped when parent unsubscribes it - Parent-child ordering when both become dirty - Nested grandparent-parent-child ordering - Cleanup on unsubscribe 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): enable pull mode by default and fix handler-returned recipes - Enable pull mode by default in scheduler (pullMode = true) - Fix handler-returned recipes to use .sink() instead of .pull() so they become effects that re-run when inputs change - Update test to verify handler-returned recipes continue running as long-lived charmlets 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(tests): add sinks to keep charms reactive in pull mode In pull mode, charms need an effect (sink) pulling from them to stay reactive when inputs change. Tests that set values externally and expect UI updates need to create a sink on the result cell. This is the correct approach rather than adding a blanket sink to all recipes - the test explicitly declares its intent to observe reactivity. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(tests): use pull() in pattern harness for pull mode compatibility In pull mode, .get() returns cached values without triggering computation of dirty dependencies. Changed the pattern harness to use .pull() which properly establishes an effect and waits for the scheduler to compute all transitive dependencies. This fixes 27/28 failing generated-patterns integration tests. One test (counter-when-unless-operators) still fails due to a deeper issue with nested lift computations through references. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(scheduler): use mightWrite for dependency chain and topological sort When a lift function returns undefined on first run, no write is recorded in the journal. This broke the dependency chain in pull mode because: 1. updateDependents only looked at actual writes 2. topologicalSort only used actual writes for ordering Fix: Initialize mightWrite from declared writes during subscribe, and use mightWrite (instead of just log.writes) in both updateDependents and topologicalSort. Adds test case that reproduces the nested lift scenario where inner lift initially returns undefined. Fixes counter-when-unless-operators test which has nested lifts where the inner lift initially returns undefined. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fmt * feat(builtins): add isEffect option to raw() for effect built-ins - Add `isEffect` field to Module interface - Add optional `options` parameter to `raw()` with `isEffect` flag - Pass `isEffect` through to scheduler.subscribe() in instantiateRawNode - Mark navigateTo as an effect since it triggers navigation side effects 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(llm-dialog): mark as effect and pull dependencies in startRequest llmDialog is an effect (triggered by user action), not a computation. In pull mode, it needs to ensure its dependencies are computed before reading them. Changes: - Mark llmDialog as isEffect: true in registration - Add pulls for inputs, pinnedCells, and their referenced cells at the start of startRequest The helper functions (buildToolCatalog, buildAvailableCellsDocumentation) remain synchronous so they can be shared with llm/generateText/generateObject which are computations that need reactive dependency tracking. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(llm-dialog): mark as effect for pull mode support llmDialog is an effect (triggered by user action), not a computation. Mark it as isEffect: true in registration. Note: Explicit pull() calls were removed because they conflict with the handler's transaction lifecycle. The handler runs synchronously with a transaction, but pull() is async. A better solution for pulling dependencies in effect handlers is needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(llm-dialog): decouple startRequest from handler transaction - Remove tx parameter from startRequest, use pull() and editWithRetry() instead to support pull-based scheduling - Pull inputs, pinnedCells, and individual context/pinned cells at start of startRequest to ensure they're computed in pull mode - Use editWithRetry() for the write to result.pinnedCells - Remove isEffect marking since llmDialog is event-handler driven, not an effect 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs(specs): add userland handler pull design for pull-based scheduler Design spec for ensuring event handler inputs are current in pull mode. Key decisions: - Treat handlers as one-time actions in the unified scheduler loop - Topological sort naturally orders handler inputs before handlers - Per-stream event queues preserve FIFO ordering - Dependency re-validation handles dynamic references - Event handlers get priority among actions at same topo level - Throttled/debounced deps allowed to be stale 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): pull handler dependencies before running in pull mode Implements the userland handler pull design to ensure event handlers don't run until their dependencies are computed. Changes: - Add traverseCells flag to validateAndTransform() and Cell.get() to recursively read into nested Cells and capture all dependencies - Add populateDependencies callback to addEventHandler() for schema-based dependency discovery without coupling scheduler to schema module - In pull mode, check if handler dependencies are dirty before running; if so, re-queue the event and process pending actions first - Use global FIFO ordering for events across all streams 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fmt * test(scheduler): add tests for handler dependency pulling Add tests verifying that in pull mode, event handlers wait for their computed dependencies to be pulled before running: - Handler as only reader of computed output - Multiple dirty dependencies pulled before handler runs - Chained dependencies (source -> computed1 -> computed2 -> handler) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(scheduler): add test for handler A triggering lift read by handler B Tests the scenario where: - Handler A writes to a lift's input and queues an event to handler B - Handler B depends on the lift's output (via populateDependencies) - The scheduler should pull the lift before running handler B This validates that handler B sees the fresh lift output (10) rather than the stale value (0). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(scheduler): check mightWrite to find actions that write to dependencies When checking handler dependencies in pull mode, also iterate over dirty actions and check their mightWrite to find actions that WRITE to the entities the handler reads. Previously only checked triggers which maps actions by what they READ. Also updates the dynamic lift test to: - Use scheduleImmediately: false for the lift (tests cold-start pull) - Send a link to liftOutput as the event to handler B - Handler B has populateDependencies that reads liftOutput 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): pass event to populateDependencies callback Update populateDependencies to receive the event value, allowing handlers to resolve dependencies from links in the event itself. Changes: - EventHandler.populateDependencies now takes (tx, event) instead of (tx) - Store event value in eventQueue alongside the action closure - Test updated to use runtime.getImmutableCell() to resolve the event link and register the dependency dynamically - Removed incorrect triggers check (was looking for actions that READ from the same entity, but we need actions that WRITE to it) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(runner): include event in populateDependencies for handler dependency tracking - recipe-binding.ts: Use isWriteRedirectLink instead of isLegacyAlias to handle both legacy $alias format and new sigil link format when writing lift outputs - cell.ts: Use getAsWriteRedirectLink in export() for proper cell serialization - runner.ts: Extract mergeEventIntoInputs helper and use it in populateDependencies so the scheduler can discover dependencies from event data (e.g., cell references passed as events) - Add test for handler A creating lift and sending output to handler B 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(scheduler): rename scheduleImmediately to rescheduling with inverted semantics The scheduler's subscribe() option has been renamed and its semantics inverted: Old: scheduleImmediately?: boolean (default: false) - true = "run now" - false = "just register triggers, wait for changes" New: rescheduling?: boolean (default: false) - false = "first time → schedule immediately" (default) - true = "action already ran → just register triggers" This makes the common case (first-time subscription) require no option, and explicitly marks the re-subscription case after an action completes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): implement pull-based scheduling for lazy computations Pull-based scheduling makes computations lazy - they only run when an effect (sink, pull, event handler) needs their value. This prevents unnecessary computation and aligns with reactive programming principles. Key changes: - Effects now trigger queueExecution() when subscribed - idle() resolves based on scheduled flag, not pending size - subscribe() with rescheduling:false adds to dirty+pending without immediate scheduling (computations wait for pull) - Removed scheduledFirstTime bypass in shouldFilterAction - Tests updated to call pull() to trigger computation chains Known issue: workSet filtering has || true workaround - removing it breaks "should not run lifts until something pulls" test. The collectDirtyDependencies mechanism needs investigation to properly find computation dependencies from effect reads. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(scheduler): properly handle pull-based scheduling with event dependencies Key fixes for pull-based scheduling: 1. workSet now only includes effects (not all pending computations) - Computations are lazy and only run when pulled by effects - This makes "should not run lifts until something pulls" test pass 2. Event handlers that have dirty dependencies now work correctly - When an event is deferred due to dirty deps, those deps are tracked - eventBlockingDeps are added to workSet so they run before the event - This makes "should wait for lift before handler" test pass 3. idle() resolution based on effectivePendingSize - In pull mode, idle resolves when no effects are pending - Orphaned computations don't block idle resolution Known issue: 3 tests fail due to broken dependency chain in nested recipes (outer lift doesn't read inner lift's cell). This is a dependency tracking issue at the recipe/cell layer, not a scheduler issue. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(scheduler): complete pull-based scheduling with dynamic dependency discovery Replace static ReactivityLog with PopulateDependencies callbacks that discover dependencies dynamically. This ensures computations only run when pulled by effects that need their outputs. Key changes: - subscribe() now takes a PopulateDependencies callback instead of ReactivityLog - Add resubscribe() for re-subscribing after an action completes - Discover dependencies during execute() before running actions - Track potentialWrites in reactivity logs for bridging - Fix findAllWriteRedirectCells() to follow sourceCell chains This completes the lazy evaluation model where computations are only executed when their outputs are actually needed by effects, reducing unnecessary work. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(scheduler): remove redundant pushTriggered filtering The pushTriggered tracking and shouldFilterAction logic was redundant with the existing pending/dirty set checks. Verified by testing that commenting out shouldFilterAction had no effect on behavior. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(scheduler): track throttled actions in filterStats Count throttled actions as filtered in filterStats for diagnostics. This provides visibility into how often throttling kicks in. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(scheduler): remove redundant .writes fallbacks mightWrite always contains everything in dependencies.writes (and more), so the fallbacks to .writes were never triggered. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs(scheduler): clarify mightWrite vs dependencies.writes distinction mightWrite is cumulative (all paths ever written + potentialWrites) for conservative dependency graph building. dependencies.writes is current run only, used for ordering within an execution cycle where historical writes would create false dependency edges. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(module): attach source location to function implementations Parse V8 stack traces to extract the caller's file:line:col and attach it to function implementations via .src property. Works in both Deno and Chrome for debugging purposes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(scheduler): replace Tarjan's cycle detection with settle loop Remove explicit cycle detection (~200+ lines) in favor of simpler implicit handling: - Remove detectCycles(), convergeFastCycle(), runSlowCycleIteration() - Remove slowCycleState WeakMap and related infrastructure - Add parent-child aware topological sort (prefer parents in cycles) - Add settle loop for conditional dependencies (ifElse pattern) - Maintain true pull-based semantics (lazy evaluation preserved) The settle loop re-collects dependencies after running computations, handling cases where conditional logic changes which branch is active. This is more robust than static cycle detection for dynamic dependency graphs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(scheduler): simplify action validity check in execution loops Replace complex parent-child sibling tracking logic with a simple check for whether an action is still scheduled (in computations or effects). This handles all cases where running one action unsubscribes another that's in the current workSet, without needing to track specific parent-child relationships. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(scheduler): unify execution and settle loops Consolidate the main execution loop and settle loop into a single unified loop structure. This eliminates code duplication and ensures all validation checks (throttle, loop counter, isStillScheduled) are consistently applied in both the initial execution and settle phases. The settle loop now wraps the entire execution logic: - First iteration: processes initial seeds + their dirty deps - Subsequent iterations: re-collects dirty deps from all effects This fixes a bug where throttle checks were missing from the settle loop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(scheduler): fold pendingDependencyCollection into settle loop Move the processing of newly subscribed actions (pendingDependencyCollection) into the settle loop itself, eliminating the separate POST-LOOP section. Now each settle iteration: 1. Processes pendingDependencyCollection (sets up deps for new subscriptions) 2. Collects dirty dependencies 3. Runs work This removes ~100 lines of duplicated execution logic while maintaining the same behavior for dynamic sub-recipes created during execution. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(scheduler): properly handle cycles and dirty effects - Include dirty effects in initialSeeds so cycle-skipped or throttled effects get picked up on subsequent executions - Track computations in early settle iterations for cycle detection - When hitting max iterations, break cycles by: - Clearing dirty/pending for computations in early iterations that are still in the last workSet (likely part of the cycle) - Running all remaining dirty effects (so they're not lost) - Skipping throttled actions to preserve their dirty state - Improve done-check to consider both pending AND dirty effects - Remove TODO about debouncing - this cycle-breaking approach handles it 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(scheduler): add cycle-aware debounce for slow cycling actions Adds adaptive debounce that triggers when an action runs 3+ times within a single execute() call that takes >100ms total. The debounce delay is set to 2× the execute time, helping break slow cycles earlier than the hard 20-iteration limit. - Track runs per action within each execute() cycle - Apply adaptive debounce only to slow, repeatedly-running actions - Respect noDebounce option for actions that opt out - Add tests for the mechanism 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test(scheduler): add comprehensive tests for cycle-aware debounce Additional test coverage for the cycle-aware debounce feature: - Verify existing higher debounce is not reduced by cycle debounce - Verify multiple actions are tracked independently - Verify run tracking resets between execute() cycles - Verify clearDebounce removes cycle-applied debounce 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * style(scheduler): auto-format line wrapping * chore(test): remove unused errorCaught variable * Revert "chore(test): remove unused errorCaught variable" This reverts commit 2cb3c2de0f5b9dc55372600bf12dfbc346f79218. * chore(test): remove unused errorCaught variable * docs(scheduler): consolidate spec docs into single README Replace the original investigation and planning documents with a single comprehensive README that describes the current implementation: - Background on why pull-based scheduling - Effects vs computations, dirty propagation - Execution flow with settle loop - Cycle handling (simplified approach, no Tarjan's) - Event handler dependencies - Debounce/throttle infrastructure - Debugging guide The removed documents are preserved in git history for reference. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(runner): use .name instead of .src for function source locations Use Object.defineProperty to set function .name property instead of custom .src property for tracking source locations. This makes the source info visible in standard debugging tools and stack traces. Actions and handlers now have prefixed names (action:path, handler:path) for easier identification in scheduler logs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(patterns): ensure dynamically instantiated recipes have stable identity When a lift dynamically instantiates recipes and pushes them to an array, downstream lifts that read computed values from those recipes need the recipes to have stable identity so the scheduler can properly track and execute them before the downstream reads. Changes: - Use .for(index) on dynamically instantiated subtotalGroup recipes to give them stable identity based on array position - Fix typing in totalItems lift to use { itemCount: number }[] instead of SubtotalGroupArgs[] - Use Cell.push() instead of spread+set pattern in appendValueToList - Remove unused imports and debug console.log statements Also adds runner change to traverse cells in argument schema during dependency capture, and a minimal repro test in recipes.test.ts that demonstrates the failure mode with push-based scheduling (sink + idle). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * ct step pulls from result cell (fix CLI integration test) * feat(shell): add Scheduler tab to debugger with dependency graph visualization - Add SchedulerGraphView component using Dagre for graph layout - Add getGraphSnapshot() to scheduler for exporting dependency graph - Add scheduler graph types to telemetry (SchedulerGraphNode, SchedulerGraphEdge) - Add historical edge tracking in DebuggerController - Name all actions for debugging: sink, raw, action, pull, readResult - Add .src backup property to actions since .name can be finicky - Support push/pull mode toggle in graph view - Show node stats (run count, avg time) with glow animation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(shell): add zoom, collapse, and grouping to scheduler debug UI - Add zoom controls (25%-200%) with reset button - Add triggered node size boost when zoomed out below readable threshold - Export parent-child relationships in scheduler graph snapshot - Add collapse by parent functionality with +/- toggle - Add visual grouping with background rectangles around parent-child clusters 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(scheduler): track parent-child relationships for sink actions - Add parent-child tracking to resubscribe() method, fixing sinks created during parent action execution not having parentId set - Add test verifying sink parent-child relationship tracking - Fix zoom-around-center for scheduler graph view (preserve scroll position when zooming instead of resetting to top-left) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(shell): improve scheduler graph ID display - Show last 4 chars of entity ID + path instead of "did:key:..." prefix - Add SVG title element for full ID tooltip on hover - Smarter truncation that preserves meaningful parts of action names 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(shell): auto-collapse new parents in scheduler graph - Always check for new parents with children to collapse, not just on first load - Remove hasAutoCollapsed flag since we now check continuously 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(shell): infer parent-child relationships from entity IDs When sinks don't have explicit parent relationships (created outside of action execution, e.g., by the renderer), infer grouping by matching entity IDs in action names. Sinks are grouped with the computation/action that operates on the same entity. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): add reads/writes diagnostics to graph visualization Show cell paths each action reads and writes in node tooltips. This helps debug missing dependency edges by revealing: - What cells an action declares as reads - What cells an action declares as writes When hovering over a node, the tooltip now shows: - Full action ID - Reads: list of cell paths - Writes: list of cell paths (from mightWrite) Also fixes unrelated type error in DebuggerView.ts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): add input nodes for source cells in graph Show cells that are read but not written by any action as input nodes. These represent recipe inputs or external data sources. - Add input node type (green, badge I) - Find source entities: reads with no matching write - Draw edges from input nodes to actions that read them - Custom tooltip for input nodes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): show parent-child edges and add legend * feat(scheduler): improve graph zoom and show all subscribed nodes Zoom improvements: - Continuous zoom with Ctrl/Cmd + mousewheel (Google Maps style) - Zoom around cursor position - Initial zoom to fit full graph on first load - No min/max limits on zoom level Show all nodes: - Add scheduler.subscribe telemetry event - Auto-refresh graph on subscribe/run/invocation events - Shows nodes before they execute (pending state) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): add dependencies.update telemetry event Emit scheduler.dependencies.update when updateDependents is called, including the actual read/write cell paths. Graph auto-refreshes on this event to capture dependency changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(shell): remove unused rect variable in zoom handler 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * workaround deno fmt bug * fmt * fix merge * behave better when items is undefined * feat(shell): add table view to scheduler debugger and fix issues - Add table view with sortable columns (Type, Action, Runs, Total, Avg, Last) - Fix browser stack trace parsing for http:// URLs and hashed filenames - Add re-entrancy guard to prevent infinite telemetry update loop - Add comprehensive parseStackFrame tests for browser formats 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): persist action stats by source location and show inactive nodes - Change actionStats from WeakMap<Action> to Map<string> keyed by action ID (source location) so stats persist across action recreation - Add getActionId() helper method to Scheduler for consistent ID generation - Add inactive node type for actions with stats that are no longer registered - Show inactive nodes in graph (gray) and table views to preserve historical data - Add hierarchical parent-child grouping in table with aggregated stats - Add expand/collapse for parent rows showing childrens individual times - Add detail pane to table view (click row to see node details) - Fix preview not showing in detail pane (access via module.implementation.preview) This ensures slow actions remain visible in the debugger even after being unsubscribed, making it easier to identify performance issues. * feat(shell): add Loggers tab to debugger and improve log granularity - Add new Loggers tab to DebuggerView with: - Reset baseline / Sample buttons for capturing log counts - Delta display showing changes relative to baseline - Enable/disable toggles for individual loggers - Expandable breakdown of log events by key - Improve scheduler logging by replacing generic "schedule" keys: - schedule-resubscribe, schedule-resubscribe-path - schedule-unsubscribe - schedule-notification - schedule-change, schedule-change-skip, schedule-change-match - schedule-change-trigger, schedule-trigger - schedule-dep-collect, schedule-dep-error (and variants) - schedule-execute, schedule-execute-pull - Improve storage logging: - storage-source-read, storage-source-parse - storage-datauri-parse - storage-commit-writes (specific event for commits with writes) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(shell): clarify logger toggle with mute/unmute icons Change the logger enable/disable toggle to use mute/unmute speaker icons (🔊/🔇) instead of filled/empty circles (●/○). This makes it clearer that the toggle controls console output, not whether the logger is actively tracking events. The previous UI was confusing because: - Loggers with high event counts showed as "disabled" (gray circle) - Loggers with 0 events showed as "enabled" (green circle) The new icons make it clear that you're muting/unmuting console output, while event counting continues regardless. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(shell): add level selector and improve logger controls Each logger row now has two controls: - Level dropdown (debug/info/warn/error) to set minimum console output level - Enable/disable toggle (●/○) to completely enable/disable console output This matches the Logger class API which has both: - `disabled` property (boolean) - master switch for console output - `level` property (LogLevel) - minimum level filter for output Changed back to ●/○ icons for enable/disable since it's now clear from context that this controls the logger's enabled state, with the level dropdown handling the filtering. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fmt * add slow get logging * fix logger * feat(shell): improve scheduler view with table default and node navigation - Make table the default view instead of graph - When switching to graph with selection, zoom to 50% and center on node - Add adjacent nodes (dependents/dependencies) to detail pane with click navigation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * deno lock * perf(scheduler): add writersByEntity index for O(1) dependency lookup - Add writersByEntity Map to index actions by the entities they write to - Add actionWriteEntities WeakMap for cleanup tracking - Rewrite updateDependents to use entity-based lookup instead of iterating all actions (O(reads × writers_per_entity) vs O(reads × actions)) - Add scheduler benchmarks to measure performance Benchmark results show scheduler ops are fast (~6-7µs per subscribe/resubscribe). The bottleneck is in cell creation (~730µs per cell), not scheduling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * bench(scheduler): add granular overhead benchmarks Break down cell overhead into getCell vs set to identify bottlenecks: - getCell only: ~19µs per call (fast) - set on existing cells: ~590µs per call (slow) The bottleneck is in cell.set(), not cell creation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * bench(scheduler): add cell internals and commit benchmarks New benchmarks to isolate performance bottlenecks: Cell layer internals (per 100 ops): - createRef (simple): 1.0ms (10µs each) - createRef (complex): 6.0ms (60µs each) - resolveLink: 2.0ms (20µs each) - tx.readValueOrThrow: 12.2ms (122µs each) <- expensive! - diffAndUpdate: 21.7ms (217µs each) <- biggest cost Storage commit: - Empty commit: 507µs - 100 raw tx.write + commit: 14.4ms (144µs/entity) - Commit after 100 cell.set(): 44.8ms (448µs/entity) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * bench(runner): factor out storage benchmarks and add overhead microbenchmarks - Create storage.bench.ts with storage layer benchmarks: - Write operations (tx.write, writeOrThrow, writeValueOrThrow) - Read operations (tx.read, readOrThrow, readValueOrThrow) - Entity creation overhead - Path depth comparison - Commit overhead - Overhead microbenchmarks - Add overhead microbenchmarks to cell.bench.ts: - isRecord check - JSON.stringify comparison - Remove cell/storage benchmarks from scheduler.bench.ts to keep it focused on scheduler-specific performance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(shell): add baseline/snapshot pattern to scheduler debugger - Hide zoom controls when in table mode - Replace Refresh/Clear History with Reset Baseline/Snapshot buttons - Show delta stats (runs and time since baseline) in table and detail views - Add totals for runs and time since baseline in toolbar - Add [All|Δ] toggle to sort by lifetime totals vs delta since baseline 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * bench(runner): add entity creation breakdown benchmarks Add microbenchmarks to isolate entity creation overhead: - Write phase vs commit phase timing - First write vs subsequent writes comparison - Entity creation overhead measurement 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(scheduler): trigger execution on mode toggle Queue scheduler execution when switching between pull and push modes to ensure pending work is processed immediately after the mode change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(runner): fix URI type error in storage benchmarks Inline template literal to preserve the `${string}:${string}` type instead of assigning to a variable which widens to `string`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(scheduler): add array reactivity tests for pull mode Add tests to verify array reactivity in pull mode: - Direct sink on array with push/set works correctly - Computation triggered by array push with explicit pull works - Bug repro: sink on computed result not triggered when source array grows The failing test demonstrates the root cause of the Notes UI bug where the list doesn't update after creating a new note. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(scheduler): register triggers in subscribe() for immediate reactivity - Add trigger registration in subscribe() when immediateLog is provided - This ensures computations are notified of storage changes immediately - Only set cancel function when triggers were actually registered - Fix test to use fresh transaction for push (avoid consistency error) The pull mode scheduler correctly marks computations dirty when source cells change and uses scheduleAffectedEffects() to find and schedule dependent effects. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(scheduler): mark effects dirty when they read from dirty computations When an effect resubscribes, check if any non-throttled dirty computations write to entities it reads. If so, mark the effect dirty so it can pull those computations and see fresh data. This fixes a bug where pattern remounts wouldn't see updated data: - Pattern unmounts (computation unsubscribed) - Data changes while unmounted - Pattern remounts with NEW computation (different function reference) - Sink runs once with stale cached data - Sink calls resubscribe but never re-runs via scheduler The fix uses the existing writersByEntity index for efficient lookup. Throttled computations are skipped - they'll trigger via storage changes when the throttle expires. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(shell): persist scheduler baseline stats across tab switches - Move baselineStats from SchedulerGraphView component state to DebuggerController so it survives when switching debugger tabs - Always show lifetime totals in toolbar (Total: X runs Yms) - Show delta since baseline separately when baseline exists (Δ: +X runs) Fixes issues where: 1. Reset baseline + snapshot data disappeared when switching to logger tab and back (component was destroyed/recreated) 2. Totals weren't showing all-time stats, only delta since baseline 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fmt * feat(shell): show debounce/throttle badges in scheduler table view - Add debounceMs and throttleMs fields to SchedulerGraphNode interface - Populate these fields in getGraphSnapshot() from actionDebounce/actionThrottle maps - Display colored badges in the table view action name column: - Purple "D:Xms" badge for debounced actions - Cyan "T:Xms" badge for throttled actions - Badges include tooltips explaining the timing behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(runner): allow builtins to customize scheduling dependencies Add RawBuiltinResult interface so builtins can return custom populateDependencies and isEffect alongside their action. This enables builtins like ifElse to only depend on the condition for initial scheduling, avoiding unnecessary computation of unselected branches. - Add RawBuiltinResult type with action, isEffect, populateDependencies - Update instantiateRawNode to handle new return format - Move navigateTo's isEffect inside its definition - ifElse now only reads condition in populateDependencies - Add test for ifElse branch selection behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(shell): refactor template to avoid deno fmt crash Extract renderBaselineStats helper to avoid deeply nested ternaries in class attributes combined with multiline function calls, which triggers a dprint-core indent tracking bug in deno fmt. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * turn off pull mode for merge * fix(test): update potentialWrites tests to use nested paths The tests incorrectly expected per-property potentialWrites entries, but diffAndUpdate reads at the object level being written to. Updated tests to use key("nested").set() to verify potentialWrites correctly tracks the path being written to. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(scheduler): queue execution for computations in push mode - In push mode, computations need to trigger execution when subscribed, not just effects. Without this, newly subscribed computations never run. - In pull mode, when an effect resubscribes and there are pending computations whose dependencies haven't been collected yet, conservatively assume they might affect the effect and queue execution. - Remove redundant queueExecution() call from sink() in cell.ts - the scheduler now handles this correctly in both push and pull modes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor scheduler logging and helpers * fix scheduler trigger cleanup * make scheduler telemetry serializable --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )