Skip to content

Feat/script stack#55

Merged
devzeebo merged 6 commits into
mainfrom
feat/script-stack
Jul 13, 2026
Merged

Feat/script stack#55
devzeebo merged 6 commits into
mainfrom
feat/script-stack

Conversation

@devzeebo

Copy link
Copy Markdown
Owner

Summary

Motivation

  • Related issue/rune: #

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change (fix or feature that changes existing behavior)
  • Documentation
  • Refactor / cleanup
  • Test coverage
  • Chore / tooling

Checklist

  • Raw go/npx were not used — everything went through make / npm run
  • make lint passes
  • make test passes
  • make build passes
  • Tests added/updated for new behavior
  • Docs updated where relevant (README / docs)
  • Commit messages are short, lowercase, imperative
  • No force-push to main; rebased on latest main
  • Self-reviewed the diff

Notes for reviewers

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Introduced a script-and-decorator stack model for running work items.
    • Added configurable execution flows, shared script context, state updates, retries, short-circuiting, and error handling.
    • Work items now support ordered flows and decorator tagging.
  • Bug Fixes

    • Improved dispatch handling for unknown scripts, decorators, execution errors, and normalized outcomes.
  • Documentation

    • Updated runner and orchestrator guides with the new execution model and examples.
    • Added beginner-friendly and detailed script-stack guides.
  • Chores

    • Publishing now discovers packages dynamically and orders them based on dependencies.

Walkthrough

The PR replaces work-item handler dispatch with script and decorator stack execution. WorkItems use kind and flow, task agents and Bifrost mappings adopt the new shape, runner documentation is updated, and publishing now discovers packages and sorts them by dependencies.

Changes

Script stack migration

Layer / File(s) Summary
WorkItem and script contracts
orchestrator-v2/packages/interfaces-work/...
WorkItems replace name with flow, validation is updated, and script-stack types and result guards are exported.
Script context and stack execution
orchestrator-v2/packages/runner/src/script-context.ts, script-stack.ts, conventions/*, script-stack.spec.ts
Scripts and decorators are resolved, composed, executed with mutable RPC-backed context, normalized to outcomes, and tested for ordering, retries, short-circuiting, and lookup failures.
Runner registration and dispatch wiring
orchestrator-v2/packages/runner/src/{runner,dispatch-handler,script-agent,index}.ts, runner.spec.ts
Runner registries and dispatch use scripts, decorators, conventions, and legacy script adaptation instead of work-item handlers.
Task-agent and WorkItem source integration
orchestrator-v2/packages/agent-3-task/..., examples/lvl3/*, packages/work-item-source-bifrost/...
Task agents, examples, fixtures, and Bifrost mappings use ScriptFn registration and kind plus decorator flow tags.
Script-stack documentation
orchestrator-v2/README.md, orchestrator-v2/docs/..., orchestrator-v2/packages/runner/README.md
Documentation describes script stacks, decorators, conventions, context state, dispatch, registration, and updated runner APIs.

Dynamic package publishing

Layer / File(s) Summary
Package discovery and dependency ordering
orchestrator-v2/publish.js
Publishing discovers eligible packages, topologically sorts internal dependencies, and uses discovered paths for versioning, publishing, and restoration.

Sequence Diagram(s)

sequenceDiagram
  participant Runner
  participant DispatchHandler
  participant ScriptStack
  participant ScriptContext
  participant Script
  Runner->>DispatchHandler: receive WorkItem dispatch
  DispatchHandler->>ScriptStack: resolve kind, flow, and conventions
  DispatchHandler->>ScriptContext: create live WorkItem context
  DispatchHandler->>ScriptStack: execute composed stack
  ScriptStack->>Script: invoke decorators and script
  Script->>ScriptContext: persist state updates
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description is just a template with placeholders and no actual PR summary. Replace the template text with a brief summary of the actual changes and motivation.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change, pointing to the new script stack feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devzeebo devzeebo marked this pull request as ready for review July 10, 2026 21:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
orchestrator-v2/publish.js (1)

47-69: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard against directories without a package.json (and packages without a name).

discoverPublishablePackages unconditionally reads join(dir, "package.json") for every subdirectory under packagesDir. Any non-package folder (build output, tooling, .turbo, etc.) makes readFile throw and aborts the entire publish. Additionally, a package.json without a name passes the filter on Line 61 (pkg.name?.includes("example") is falsy) and gets pushed with name: undefined, which later becomes an undefined key in packageByName and a bogus entry in the topo sort.

♻️ Proposed hardening
     const dir = join(packagesDir, entry.name);
     const pkgPath = join(dir, "package.json");
+    let pkg;
+    try {
       // oxlint-disable-next-line no-await-in-loop
-    const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
+      pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
+    } catch {
+      continue; // not a publishable package directory
+    }

-    if (pkg.private || pkg.name?.includes("example")) {
+    if (!pkg.name || pkg.private || pkg.name.includes("example")) {
       continue;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orchestrator-v2/publish.js` around lines 47 - 69, Harden
discoverPublishablePackages by checking whether each directory contains
package.json before reading it, skipping directories where the file is absent or
unreadable instead of aborting discovery. Also skip parsed packages whose
pkg.name is missing or not a valid publishable name, then apply the existing
private/example filters before pushing entries so packageByName and dependency
sorting never receive undefined names.
orchestrator-v2/packages/runner/src/script-stack.spec.ts (1)

114-142: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Given functions duplicate when-function setup and fire off unawaited promises.

Three given functions (nested_decorators, skip_decorator, flaky_script_and_retry_decorator) fully duplicate the registry/work-item setup found in their corresponding when functions, then fire composeStack(...).then(...) or executeScriptStack(...).then(...) as unawaited promises. These fire-and-forget calls race with the when functions and can overwrite this.result non-deterministically. The given functions should only set up preconditions; execution should live exclusively in the when functions.

♻️ Proposed refactor: move execution out of given functions

Example for nested_decorators / executing_nested_stack:

 function nested_decorators(this: Context) {
   const order: string[] = [];
   this.workItem = { ...baseWorkItem(), flow: ["outer", "inner"] };
   this.scripts = new Registry<ScriptFn>();
   this.decorators = new Registry<DecoratorFn>();

   this.scripts.register("hunt", async () => {
     order.push("script");
     return "done";
   });

   this.decorators.register("outer", async (_wi, _ctx, next) => {
     order.push("outer-before");
     await next();
     order.push("outer-after");
   });

   this.decorators.register("inner", async (_wi, _ctx, next) => {
     order.push("inner-before");
     await next();
     order.push("inner-after");
   });

-  this.result = order;
-  const stack = resolveStack(this.workItem, this.scripts, this.decorators, []);
-  void composeStack(this.workItem, scriptContext, stack.script, stack.decorators)().then(() => {
-    this.result = order;
-  });
+  this.result = order; // store the order array reference for the when function
 }

 async function executing_nested_stack(this: Context) {
-  const order: string[] = [];
-  this.workItem = { ...baseWorkItem(), flow: ["outer", "inner"] };
-  this.scripts = new Registry<ScriptFn>();
-  this.decorators = new Registry<DecoratorFn>();
-
-  this.scripts.register("hunt", async () => {
-    order.push("script");
-    return "done";
-  });
-
-  this.decorators.register("outer", async (_wi, _ctx, next) => {
-    order.push("outer-before");
-    await next();
-    order.push("outer-after");
-  });
-
-  this.decorators.register("inner", async (_wi, _ctx, next) => {
-    order.push("inner-before");
-    await next();
-    order.push("inner-after");
-  });
-
   const stack = resolveStack(this.workItem, this.scripts, this.decorators, []);
   await executeScriptStack(this.workItem, scriptContext, stack);
-  this.result = order;
 }

Apply the same pattern to skip_decorator/executing_short_circuit and flaky_script_and_retry_decorator/executing_with_retry.

Also applies to: 182-201, 224-256

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orchestrator-v2/packages/runner/src/script-stack.spec.ts` around lines 114 -
142, Remove the duplicated registry and work-item setup and all fire-and-forget
execution from the given functions nested_decorators, skip_decorator, and
flaky_script_and_retry_decorator. Keep those functions limited to establishing
preconditions, and move composeStack/executeScriptStack invocation and result
assignment exclusively into executing_nested_stack, executing_short_circuit, and
executing_with_retry, awaiting completion before setting this.result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@orchestrator-v2/packages/interfaces-work/src/types.ts`:
- Around line 68-69: Update the flow-entry validation in isWorkItem to require
each entry to be a non-empty string, matching missingWorkItemFields by checking
typeof entry === "string" and entry.length > 0; this ensures empty flow values
are rejected before dispatch reaches resolveStack.

In `@orchestrator-v2/packages/runner/src/dispatch-handler.ts`:
- Around line 26-31: Update the dispatch subscription callback and the
post-acceptance flow in handleDispatch so errors are not silently swallowed.
Preserve the initial accepted response, then wrap createScriptContext,
executeScriptStack, and related stack.rpc.call execution in try/catch; on
failure, send a terminal workItem.fail or workItem.pause outcome through the
existing RPC mechanism. Add a default case to the execution switch, and ensure
the outer catch logs or reports failures rather than using catch(() =>
undefined).

---

Nitpick comments:
In `@orchestrator-v2/packages/runner/src/script-stack.spec.ts`:
- Around line 114-142: Remove the duplicated registry and work-item setup and
all fire-and-forget execution from the given functions nested_decorators,
skip_decorator, and flaky_script_and_retry_decorator. Keep those functions
limited to establishing preconditions, and move composeStack/executeScriptStack
invocation and result assignment exclusively into executing_nested_stack,
executing_short_circuit, and executing_with_retry, awaiting completion before
setting this.result.

In `@orchestrator-v2/publish.js`:
- Around line 47-69: Harden discoverPublishablePackages by checking whether each
directory contains package.json before reading it, skipping directories where
the file is absent or unreadable instead of aborting discovery. Also skip parsed
packages whose pkg.name is missing or not a valid publishable name, then apply
the existing private/example filters before pushing entries so packageByName and
dependency sorting never receive undefined names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ff16c326-c8f8-475f-a2c8-a95e58003a87

📥 Commits

Reviewing files that changed from the base of the PR and between 898563c and d91a110.

📒 Files selected for processing (30)
  • orchestrator-v2/README.md
  • orchestrator-v2/docs/runner.md
  • orchestrator-v2/docs/temporal/script-stack-caveman.md
  • orchestrator-v2/docs/temporal/script-stack-eli5.md
  • orchestrator-v2/docs/temporal/script-stack.md
  • orchestrator-v2/examples/lvl3/doSomething.ts
  • orchestrator-v2/examples/lvl3/runner.ts
  • orchestrator-v2/packages/agent-3-task/src/augment.spec.ts
  • orchestrator-v2/packages/agent-3-task/src/augment.ts
  • orchestrator-v2/packages/agent-3-task/src/create-task-agent.ts
  • orchestrator-v2/packages/agent-3-task/src/run-task-agent.spec.ts
  • orchestrator-v2/packages/agent-3-task/src/run-task-agent.ts
  • orchestrator-v2/packages/interfaces-work/src/index.ts
  • orchestrator-v2/packages/interfaces-work/src/types.ts
  • orchestrator-v2/packages/orchestrator/src/test-helpers.ts
  • orchestrator-v2/packages/runner/README.md
  • orchestrator-v2/packages/runner/src/conventions/fail-on-error.ts
  • orchestrator-v2/packages/runner/src/dispatch-handler.ts
  • orchestrator-v2/packages/runner/src/execute-work-item.ts
  • orchestrator-v2/packages/runner/src/index.ts
  • orchestrator-v2/packages/runner/src/runner.spec.ts
  • orchestrator-v2/packages/runner/src/runner.ts
  • orchestrator-v2/packages/runner/src/script-agent.ts
  • orchestrator-v2/packages/runner/src/script-context.ts
  • orchestrator-v2/packages/runner/src/script-stack.spec.ts
  • orchestrator-v2/packages/runner/src/script-stack.ts
  • orchestrator-v2/packages/runner/src/work-item-execution-context.ts
  • orchestrator-v2/packages/work-item-source-bifrost/src/bifrost-work-item-source.spec.ts
  • orchestrator-v2/packages/work-item-source-bifrost/src/bifrost-work-item-source.ts
  • orchestrator-v2/publish.js
💤 Files with no reviewable changes (2)
  • orchestrator-v2/packages/runner/src/execute-work-item.ts
  • orchestrator-v2/packages/runner/src/work-item-execution-context.ts

Comment thread orchestrator-v2/packages/interfaces-work/src/types.ts Outdated
Comment thread orchestrator-v2/packages/runner/src/dispatch-handler.ts
…covery.

Reject empty flow entries before resolveStack, report dispatch failures via workItem.fail instead of swallowing errors, dedupe script-stack test setup, and skip invalid package directories during publish.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@orchestrator-v2/publish.js`:
- Around line 63-72: Update the error handlers surrounding package reads and
JSON.parse in the package discovery flow to log failures before continuing,
ensuring parse errors include the affected package path and error details.
Preserve the existing continue behavior for unreadable or invalid package files
while making skipped packages distinguishable from directories without
package.json.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 736d2ce5-2832-44ac-bdaf-b5969ce96236

📥 Commits

Reviewing files that changed from the base of the PR and between d91a110 and 8e18e6f.

📒 Files selected for processing (4)
  • orchestrator-v2/packages/interfaces-work/src/types.ts
  • orchestrator-v2/packages/runner/src/dispatch-handler.ts
  • orchestrator-v2/packages/runner/src/script-stack.spec.ts
  • orchestrator-v2/publish.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • orchestrator-v2/packages/runner/src/script-stack.spec.ts
  • orchestrator-v2/packages/interfaces-work/src/types.ts
  • orchestrator-v2/packages/runner/src/dispatch-handler.ts

Comment on lines +63 to +72
} catch {
continue;
}

let pkg;
try {
pkg = JSON.parse(contents);
} catch {
continue;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Log when packages are skipped due to read or parse failures.

Both catch blocks silently continue, making it impossible to distinguish "no package.json here (expected)" from "package.json is corrupted (unexpected)." A corrupted package.json in a real package would silently drop it from the publish order, potentially breaking dependent packages. At minimum, log the JSON parse failure.

🛡️ Suggested improvement
     try {
       // oxlint-disable-next-line no-await-in-loop
       contents = await readFile(pkgPath, "utf-8");
     } catch {
       continue;
     }

     let pkg;
     try {
       pkg = JSON.parse(contents);
     } catch {
+      console.warn(`Skipping ${entry.name}: package.json is not valid JSON`);
       continue;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch {
continue;
}
let pkg;
try {
pkg = JSON.parse(contents);
} catch {
continue;
}
} catch {
continue;
}
let pkg;
try {
pkg = JSON.parse(contents);
} catch {
console.warn(`Skipping ${entry.name}: package.json is not valid JSON`);
continue;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orchestrator-v2/publish.js` around lines 63 - 72, Update the error handlers
surrounding package reads and JSON.parse in the package discovery flow to log
failures before continuing, ensuring parse errors include the affected package
path and error details. Preserve the existing continue behavior for unreadable
or invalid package files while making skipped packages distinguishable from
directories without package.json.

@devzeebo devzeebo merged commit a23c815 into main Jul 13, 2026
1 check passed
@devzeebo devzeebo deleted the feat/script-stack branch July 13, 2026 18:46
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.

1 participant