Skip to content

Execution kernel: support cheap preflight gates/lazy providers so ubiquitous host hooks do not need a second handler model #595

Description

@ScriptedAlchemy

Problem

ScriptedAlchemy/cargo-hauler had to move its ubiquitous shell hooks out of routed events and into config-declared fast-path handlers because every non-cargo shell command paid the full rendered event-route startup cost.

Measured in cargo-hauler #90:

  • routed tool/before: ~0.10–0.11 s, ~64 MB RSS;
  • routed tool/after: ~0.09–0.10 s, ~64 MB RSS;
  • the route loaded the full rendering runtime before it could decide that ls, git, etc. were irrelevant.

The current workaround in cargo-hauler/agent-bundle.config.ts uses hooks.beforeTool.handler / hooks.afterTool.handler pointing at src/hooks/fast-path/*. Those entries are deliberately tiny, avoid React/Effect, and only load the real rewrite/telemetry path when the command contains cargo/hauler tokens.

This solves the performance problem but creates exactly the architectural split #592 is trying to remove: semantic lifecycle events live in src/events/**, while high-frequency hooks need a second handler system because the normal execution kernel cannot short-circuit before expensive route/runtime/provider initialization.

Direction

Add a framework-owned preflight/gate phase to the common execution kernel.

Conceptually:

native host event
   ↓
envelope decode + canonicalization
   ↓
cheap preflight gate       <-- no RSC/React/providers unless requested
   ├─ continue immediately
   ├─ deny immediately
   └─ execute route
          ↓
     providers/layout/rendering

The gate is part of the same event route/application graph, not another host-hook registration model.

Possible authoring forms:

export const preflight = ({ canonical }) => {
  const command = commandFrom(canonical.payload);
  return mentionsCargo(command) ? 'execute' : { outcome: 'continue' };
};

export default async function BeforeTool(props) {
  // full route only runs for relevant events
}

or a convention such as a statically compiled gate export. Exact API is open.

Lazy context/providers

The same issue appears with providers. The framework currently constructs provider values per request before route execution. A high-frequency event that does not need a daemon probe, state store, lineage lookup, etc. should not pay for those values.

Support one or both of:

  • lazy provider resolution on first access;
  • route/gate declarations of required providers;
  • a preflight context that contains only canonical event data, signal, terminal/host capability metadata, and explicitly cheap values.

Preserve deterministic provider ordering once providers are actually materialized.

Compiler/runtime requirement

The emitted hook entry must be physically cheap, not merely logically early inside the full bundle. The compiler should be able to emit a small preflight shell that imports the heavier route/runtime only after the gate says execute.

This is similar to middleware edge-gating in a web framework: the optimization has to exist in the entry graph, not after the application bundle is already loaded.

Relationship to config-declared hooks

Do not remove config handlers until routed events can meet the same latency/runtime characteristics. After this lands, config-declared handlers can remain an escape hatch for genuinely raw/native behavior, but should not be required for ordinary semantic events that need a cheap rejection path.

Acceptance

  • A src/events/tool/before route can reject/continue a non-matching event before loading the rendered route runtime.
  • The generated preflight entry imports no React/RSC renderer or application providers until execution is requested.
  • Preflight sees the same canonical event payload the route would see; no host-native parser duplication is required.
  • Providers can be avoided for routes/gates that do not read them.
  • Host envelope validation, capability translation, timeout handling, and outcome validation remain framework-owned.
  • Performance fixture demonstrates a no-op shell event near plain Node/process startup cost and materially below a full rendered route.
  • cargo-hauler can move src/hooks/fast-path/shell-before.ts / shell-after.ts back under the canonical event-route model without regressing perf(test): drop rstest maxWorkers pins, isolate per RSTEST_WORKER_ID #90.

Consumer evidence

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmeta-frameworkAgent Bundle compiler-coupled meta-framework

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions