feat(FN-3967): document executorRuntimeEnv plugin hook
Adds `docs/PLUGIN_AUTHORING.md` documenting the `executorRuntimeEnv` hook with cross-references to the architecture docs, accompanied by tests validating the documentation contract. Fusion-Task-Id: FN-3967
This commit is contained in:
@@ -343,6 +343,7 @@ const plugin: FusionPlugin = {
|
||||
| `onTaskCompleted` | `(task: Task, ctx: PluginContext) => Promise<void> \| void` | Task reached "done" |
|
||||
| `onError` | `(error: Error, ctx: PluginContext) => Promise<void> \| void` | Error occurred in plugin execution |
|
||||
| `onSchemaInit` | `(db: Database) => Promise<void> \| void` | After enabled plugins are loaded at startup (engine/daemon/dashboard/serve) |
|
||||
| `executorRuntimeEnv` | `(taskCtx: ExecutorRuntimeTaskContext, ctx: PluginContext) => Promise<ExecutorRuntimeEnvContribution> \| ExecutorRuntimeEnvContribution` | Before executor-spawned task commands run, to contribute task-scoped env and PATH prepends |
|
||||
|
||||
### Hook Behavior
|
||||
|
||||
@@ -373,6 +374,85 @@ hooks: {
|
||||
},
|
||||
```
|
||||
|
||||
### `executorRuntimeEnv`: task-scoped executor subprocess environment
|
||||
|
||||
Use `executorRuntimeEnv` when your plugin needs to provide runtime environment values for **executor-spawned user commands** tied to a specific task.
|
||||
|
||||
This hook runs when Fusion prepares subprocess environments for executor command surfaces such as configured commands, verification commands, and step-session subprocesses.
|
||||
|
||||
It does **not** apply to internal git plumbing subprocesses used by worktree/branch management.
|
||||
|
||||
```typescript
|
||||
import type {
|
||||
ExecutorRuntimeEnvContribution,
|
||||
ExecutorRuntimeTaskContext,
|
||||
PluginContext,
|
||||
} from "@fusion/plugin-sdk";
|
||||
|
||||
function executorRuntimeEnv(
|
||||
taskCtx: ExecutorRuntimeTaskContext,
|
||||
ctx: PluginContext,
|
||||
): ExecutorRuntimeEnvContribution {
|
||||
ctx.logger.info(`Preparing env for task ${taskCtx.taskId}`);
|
||||
return {
|
||||
pathPrepend: ["/absolute/path/to/tools"],
|
||||
env: {
|
||||
MY_PLUGIN_TASK_ID: taskCtx.taskId,
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
`ExecutorRuntimeTaskContext` fields:
|
||||
|
||||
- `taskId`: Fusion task ID
|
||||
- `worktreePath`: absolute path to the task worktree
|
||||
- `rootDir`: project root directory
|
||||
- `branch?`: task branch name when available
|
||||
|
||||
`ExecutorRuntimeEnvContribution` fields:
|
||||
|
||||
- `pathPrepend?`: array of **absolute** path strings prepended to `PATH` for executor-spawned commands
|
||||
- `env?`: key/value string map merged into the subprocess environment
|
||||
- `description?`: optional human-readable note for debugging/telemetry
|
||||
|
||||
Validation and merge behavior (from engine runtime collection):
|
||||
|
||||
- `pathPrepend` must be an array of absolute path strings; non-absolute entries are rejected.
|
||||
- `env` values must be strings.
|
||||
- `env` must not include `PATH`; use `pathPrepend` instead.
|
||||
- When multiple plugins set the same `env` key, later plugins override earlier values and the engine logs a warning.
|
||||
- `pathPrepend` entries from later plugins are placed earlier in the final prepend list.
|
||||
|
||||
### Example: prepend a generated tool directory for executor commands
|
||||
|
||||
```typescript
|
||||
import { definePlugin } from "@fusion/plugin-sdk";
|
||||
import path from "node:path";
|
||||
|
||||
export default definePlugin({
|
||||
manifest: {
|
||||
id: "fusion-plugin-tooling-example",
|
||||
name: "Tooling Example",
|
||||
version: "1.0.0",
|
||||
},
|
||||
state: "installed",
|
||||
hooks: {},
|
||||
executorRuntimeEnv: (taskCtx) => {
|
||||
const toolDir = path.resolve(taskCtx.rootDir, ".fusion/tools/my-plugin/bin");
|
||||
|
||||
return {
|
||||
pathPrepend: [toolDir],
|
||||
env: {
|
||||
MY_PLUGIN_TOOL_HOME: toolDir,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
With this hook enabled, executor-spawned commands (for example verification commands or `bash` tool subprocesses in the task session) can resolve binaries from `toolDir` without mutating global process environment.
|
||||
|
||||
### Example: Notification on Task Completion
|
||||
|
||||
```typescript
|
||||
|
||||
@@ -357,7 +357,7 @@ Hybrid evaluator pipeline (FN-3389/FN-3391):
|
||||
- `TaskStore.getPluginStore()` now propagates the configured `globalSettingsDir`/central directory so all CLI and dashboard install paths resolve the same central DB
|
||||
- `PluginLoader` (`plugin-loader.ts`) loads/unloads plugin modules using the effective per-project plugin state
|
||||
- Plugin contributions now include both embedded `uiSlots` and top-level `dashboardViews`
|
||||
- Executor runtime contributions can be provided via `executorRuntimeEnv(taskCtx, ctx)`; the engine aggregates plugin-provided `pathPrepend` + `env` overlays per task and applies them only to executor-spawned user commands (configured commands, verification commands, step-session subprocesses), never to git plumbing subprocesses.
|
||||
- Executor runtime contributions can be provided via `executorRuntimeEnv(taskCtx, ctx)`; see the canonical plugin-authoring contract in [`docs/PLUGIN_AUTHORING.md` §4 "`executorRuntimeEnv`: task-scoped executor subprocess environment"](./PLUGIN_AUTHORING.md#executorruntimeenv-task-scoped-executor-subprocess-environment). The engine applies these task-scoped overlays only to executor-spawned user commands, never to git plumbing subprocesses.
|
||||
- Discovery endpoints:
|
||||
- `GET /api/plugins/ui-slots`
|
||||
- `GET /api/plugins/dashboard-views`
|
||||
|
||||
@@ -85,3 +85,20 @@ test("PLUGIN_AUTHORING TOC includes top-level dashboard views and anchors align
|
||||
assert.ok(topLevelEntry, "TOC should include section 8");
|
||||
assert.equal(topLevelEntry.title, "Registering Top-Level Dashboard Views");
|
||||
});
|
||||
|
||||
test("PLUGIN_AUTHORING documents executorRuntimeEnv hook signature in hook reference", () => {
|
||||
assert.match(
|
||||
doc,
|
||||
/\| `executorRuntimeEnv` \| `\(taskCtx: ExecutorRuntimeTaskContext, ctx: PluginContext\) => Promise<ExecutorRuntimeEnvContribution> \\| ExecutorRuntimeEnvContribution` \|/,
|
||||
);
|
||||
});
|
||||
|
||||
test("PLUGIN_AUTHORING documents executorRuntimeEnv runtime env contract and PATH injection example", () => {
|
||||
assert.match(doc, /### `executorRuntimeEnv`: task-scoped executor subprocess environment/);
|
||||
assert.match(doc, /does \*\*not\*\* apply to internal git plumbing subprocesses/);
|
||||
assert.match(doc, /pathPrepend` must be an array of absolute path strings/);
|
||||
assert.match(doc, /must not include `PATH`; use `pathPrepend` instead/);
|
||||
assert.match(doc, /later plugins override earlier values and the engine logs a warning/);
|
||||
assert.match(doc, /later plugins are placed earlier in the final prepend list/);
|
||||
assert.match(doc, /pathPrepend: \[toolDir\]/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user