Skip to content

blueprint-react-node: trigger() in operation runners throws "app is not defined" #14

Description

@ahmad-ajmal

Summary

In an app scaffolded from blueprint-react-node, any operation runner that calls the trigger(type, payload, capability) helper throws before a task is queued:

Operation "find-online-copy" threw: app is not defined

This breaks the documented app→agent seam. Operation runners get (args, ctx, { store, trigger }), and trigger is the only way an author can enqueue agent work from a2app.schema.mjs.

Root cause

toolkits/blueprint-react-node/template/server.mjs:229:

trigger: (type, payload, capability) => app.trigger({ type, payload: payload ?? {}, ...(capability ? { capability } : {}) }),

The file never declares app. The adapter instance is created further down as:

const a2app = createA2App(binding, { ... });   // line 308

The comment above the wrapper says "app is assigned further down and read at call time", but the binding is actually named a2app, so the closure throws a ReferenceError on first call.

server.mjs is system-owned and hash-locked, so app authors can't patch it without failing the ownership gate. The only workaround is to stop using trigger, which is what the agent did in the report below.

Repro

  1. Scaffold a blueprint-react-node app.
  2. In a2app.schema.mjs, declare an event and an operation runner that triggers it:
    events: [{ type: "book.find_online_copy" }],
    operationRunners: {
      "find-online-copy": (args, _ctx, { store, trigger }) => {
        const book = store.get("books", args?.book);
        if (!book) return { ok: false, reason: "no such book" };
        const { taskId } = trigger("book.find_online_copy", { book: book.id }, "web-search");
        book.onlineSearchTask = taskId;
        store.put("books", book);
        return { ok: true, book: book.id, queued: taskId };
      },
    },
  3. Declare the operation in operations.json, then POST /api/ops/find-online-copy with { "book": "<id>" }.

Expected: { eventId, taskId } comes back, and the task appears in the A2App task APIs where agent-app <dir> bridge start can claim it.
Actual: Operation "find-online-copy" threw: app is not defined

Suggested fix

Point the wrapper at the real instance:

trigger: (type, payload, capability) => a2app.trigger({ type, payload: payload ?? {}, ...(capability ? { capability } : {}) }),

const a2app is in the temporal dead zone only until boot finishes, and runners execute after that, so the call-time read is safe. Also:

  • Add a template test that runs an operation runner which calls trigger(...) with a capability and asserts that a task is queued. No current test exercises this path.
  • Check the other blueprints (blueprint-go-react, blueprint-rust-react, Python) for the same wrapper.
  • Ship this as a toolkit update so existing apps get it through agent-app <dir> toolkit-sync.

Context

Found during Pi end-to-end testing of the framework (app: reading-list-full, operation find-online-copy, event book.find_online_copy).

Activity

  1. self-assigned this
    on Oct 1, 2026
  2. ahmad-ajmal commented on Oct 2, 2026

    @ahmad-ajmal
    CollaboratorAuthor

    fixed in PR #16

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions