feat(FN-2734): merge fusion/fn-2734

- docs(FN-2734): complete Step 7 — cross-link routing docs

Fusion-Task-Id: FN-2734
This commit is contained in:
Fusion
2026-04-29 09:52:00 -07:00
committed by gsxdsm
parent 9ed8402d63
commit ea90bd6030
6 changed files with 213 additions and 7 deletions

View File

@@ -744,6 +744,50 @@ In `packages/engine/src/ipc/ipc-protocol.ts`:
+ InProcessRuntime
```
## Task Routing Architecture
Task dispatch routing is resolved in two layers:
1. **Task routing resolution** (`packages/engine/src/effective-node.ts`)
- `resolveEffectiveNode(task, settings)` applies precedence:
1. `Task.nodeId``task-override`
2. `ProjectSettings.defaultNodeId``project-default`
3. no node set → `local`
2. **Runtime selection** (`packages/engine/src/project-manager.ts`)
- `child-process` isolation always uses `ChildProcessRuntime`
- `in-process` isolation uses `RemoteNodeRuntime` when the registered project host node is remote
- otherwise uses `InProcessRuntime`
### Dispatch flow in scheduler
On dispatch (`packages/engine/src/scheduler.ts`), scheduler:
- resolves effective node/source,
- persists `effectiveNodeId` + `effectiveNodeSource` on the task,
- logs activity: `Node routing resolved: <node|local> (source: <source>)`.
### Active-task node-override guard
`packages/core/src/node-override-guard.ts` enforces immutable routing overrides for active tasks:
- `validateNodeOverrideChange()` blocks node override updates while task column is `in-progress`
- returns reason `task-in-progress`
`TaskStore.updateTask()` applies this guard before persisting `nodeId` changes.
### Unavailable-node policy status
`unavailableNodePolicy` is a validated/stored project setting (`block` default, `fallback-local` allowed) and is exposed in dashboard/CLI controls.
Current implementation note: scheduler dispatch does **not yet** enforce health-based `block`/`fallback-local` behavior; node-health enforcement is reserved for a follow-up path (see scheduler `nodeHealthMonitor` reserved comment).
### Routing activity visibility
Routing decisions are visible in task activity/log entries and in task metadata (`effectiveNodeId`, `effectiveNodeSource`), and surfaced in dashboard routing UI + `fn task show` output.
See also:
- [Settings Reference → Node Routing settings](./settings-reference.md#node-routing-settings-project-scope)
- [Task Management → Node Routing](./task-management.md#node-routing)
- [Multi-Project → Node Routing](./multi-project.md#node-routing)
---
## 12) Settings Hierarchy
@@ -840,3 +884,5 @@ Git behavior is implemented primarily in engine executor/merger + dashboard/CLI
- **Pi extension:** `packages/cli/src/extension.ts`
- **Runtime abstraction:** `packages/engine/src/project-runtime.ts`
- **Multi-project orchestrator:** `packages/engine/src/hybrid-executor.ts`
- **Task routing resolver:** `packages/engine/src/effective-node.ts`
- **Node override guard:** `packages/core/src/node-override-guard.ts`

View File

@@ -301,6 +301,7 @@ Task lifecycle and task operations.
```bash
fn task create "Fix login race condition"
fn task create "Fix bug" --attach screenshot.png --depends FN-010
fn task create "Investigate flaky runner" --node edge-runner
fn task plan "Design a new authentication flow"
```
@@ -312,6 +313,11 @@ fn task show FN-001
fn task logs FN-001 --follow --limit 50 --type tool
```
`fn task show <id>` includes routing context when available:
- task node override
- project default node fallback
- unavailable-node policy value
### Execution and status
```bash
@@ -323,6 +329,18 @@ fn task pause FN-001
fn task unpause FN-001
```
### Node routing controls
```bash
fn task set-node FN-001 edge-runner
fn task clear-node FN-001
```
Notes:
- `set-node` resolves either node name or node ID.
- `set-node` and `clear-node` are blocked while the task is in progress.
- Use `fn node list` / `fn node show <name>` to discover node IDs and status.
### Collaboration and guidance
```bash
@@ -497,6 +515,8 @@ Show and manage settings.
```bash
fn settings
fn settings set maxConcurrent 4
fn settings set defaultNodeId node_abc123
fn settings set unavailableNodePolicy fallback-local
fn settings export [--scope global|project|both] [--output <file>]
fn settings import <file> [--scope global|project|both] [--merge] [--yes]
```
@@ -583,6 +603,7 @@ Subcommands: `search`, `install`.
| `--dev` | `fn dashboard`, `fn desktop` |
| `--attach` | `fn task create` |
| `--depends` | `fn task create` |
| `--node` | `fn task create` |
| `--feedback` | `fn task refine` |
| `--yes` | confirmation-skipping flows (`task plan`, `settings import`, git pull/push, etc.) |
| `--limit`, `-l` | `fn task import` (default: 30, max: 100), `fn skills search` (default: 10, max: 50) |

View File

@@ -112,7 +112,15 @@ Memory view provides a multi-file editor for project and daily memory files.
## Task Detail Modal
Inspect task definition, logs, comments, documents, workflow outcomes, and model overrides from a single modal.
Inspect task definition, logs, comments, documents, workflow outcomes, model overrides, and task routing from a single modal.
The **Routing** tab shows:
- effective node
- routing source (task override vs project default vs local)
- unavailable-node policy value
- per-task node override controls (locked while task is active)
Project-wide routing defaults are configured in **Settings → Node Routing**.
![Task detail modal](./screenshots/task-detail.png)

View File

@@ -80,6 +80,53 @@ Projects can run with:
- **`in-process`** (default): low overhead, shared process
- **`child-process`**: stronger isolation with independent process boundary
## Node Routing
Multi-project deployments use two related node fields at different layers:
1. **Project runtime placement** (`projects.nodeId` in `~/.fusion/fusion-central.db`)
- Decides where a project runtime is hosted in multi-project orchestration.
2. **Task dispatch default** (`defaultNodeId` in project settings)
- Decides where tasks route when they do not have a per-task override.
These fields are intentionally distinct.
### Runtime placement (`projects.nodeId`)
`ProjectManager` uses project registration data plus isolation mode to pick runtime type:
- `isolationMode: "child-process"` → always `ChildProcessRuntime`
- `isolationMode: "in-process"` + remote `projects.nodeId``RemoteNodeRuntime`
- `isolationMode: "in-process"` + local/unset/missing node assignment → `InProcessRuntime`
So `projects.nodeId` is a **project host-node assignment**, not a per-task override.
### Task routing defaults (`defaultNodeId` + `Task.nodeId`)
Within a project runtime, effective task routing resolves as:
1. task override (`Task.nodeId`)
2. project default (`defaultNodeId`)
3. local execution
This allows each project to maintain independent routing behavior even when managed from one central registry.
### Unavailable node policy in multi-project context
`unavailableNodePolicy` is project-scoped and can be set differently per project (`block` or `fallback-local`).
Current behavior: scheduler dispatch records effective node/source for each task, but health-based block/fallback enforcement is not yet applied in the scheduler dispatch path.
### Example: different node defaults per project
- **Project A** (`projects.nodeId` assigned to remote host): runtime executes via `RemoteNodeRuntime`; `defaultNodeId=edge-a` routes unpinned tasks to edge-a.
- **Project B** (`projects.nodeId` unset): runtime stays local `InProcessRuntime`; `defaultNodeId=edge-b` still marks its task dispatch default independently.
See also:
- [Settings Reference → Node Routing settings](./settings-reference.md#node-routing-settings-project-scope)
- [Task Management → Node Routing](./task-management.md#node-routing)
- [Architecture → Task Routing Architecture](./architecture.md#task-routing-architecture)
## Auto-Migration from Single-Project
On first run after upgrade:

View File

@@ -125,12 +125,8 @@ Defaults from `DEFAULT_PROJECT_SETTINGS`; key scope from `PROJECT_SETTINGS_KEYS`
| `maxWorktrees` | `number` | `4` | Max git worktrees. |
| `pollIntervalMs` | `number` | `15000` | Scheduler poll interval (ms). |
| `heartbeatMultiplier` | `number` | `1` | Global multiplier applied to all agent heartbeat intervals. Configured from the Agents screen (not Settings). |
| `defaultNodeId` | `string` | `undefined` | Optional project default node ID. When set, tasks without a per-task `nodeId` override are routed to this node. |
| `unavailableNodePolicy` | `"block" \| "fallback-local"` | `"block"` | Routing policy when a selected node is unavailable/unhealthy. `"block"` stops execution until the node is healthy; `"fallback-local"` runs the task on the local node instead. Applies to both project-default node routing and per-task node overrides. |
CLI usage:
- `fn settings set defaultNodeId <node-id>`
- `fn settings set unavailableNodePolicy <block|fallback-local>`
| `defaultNodeId` | `string` | `undefined` | Optional project default execution node for task dispatch. When set, tasks without a per-task `nodeId` override resolve to this node (`routing source: project-default`). See [Task Management → Node Routing](./task-management.md#node-routing). |
| `unavailableNodePolicy` | `"block" \| "fallback-local"` | `"block"` | Project routing policy value used by dashboard/CLI routing controls. Current scheduler dispatch records effective routing but does not yet apply health-based block/fallback enforcement in the dispatch path. See [Architecture → Task Routing Architecture](./architecture.md#task-routing-architecture). |
| `groupOverlappingFiles` | `boolean` | `true` | Serialize execution when file scopes overlap. |
| `overlapIgnorePaths` | `string[]` | `[]` | Optional project-relative file or directory paths to exclude from overlap blocking (for example `docs` or `generated/openapi.json`). Entries are trimmed, deduplicated, and must not be absolute or contain `..` traversal. |
@@ -226,6 +222,30 @@ CLI usage:
| `showQuickChatFAB` | `boolean` | `false` | Show floating quick-chat button (chat remains available via More menu). |
| `experimentalFeatures` | `Record<string, boolean>` | `{}` | Project-scoped experimental feature flags. |
### Node Routing settings (project scope)
Node routing controls in the project settings table are configured from **Settings → Node Routing** in the dashboard or via CLI:
- `fn settings set defaultNodeId <node-id>`
- `fn settings set unavailableNodePolicy <block|fallback-local>`
Routing precedence for task dispatch is:
1. per-task override (`Task.nodeId`)
2. project default (`defaultNodeId`)
3. local execution
### Project Default Node vs central project node assignment
Fusion also stores `projects.nodeId` in the **central registry database** (`~/.fusion/fusion-central.db`). That value is a multi-project runtime placement field used by `ProjectManager` (for selecting remote vs local project runtime), not the same setting as `defaultNodeId` task dispatch routing.
- `defaultNodeId` (project settings): task-level dispatch default
- `projects.nodeId` (central registry): which node hosts the project runtime in multi-project mode
See also:
- [Task Management → Node Routing](./task-management.md#node-routing)
- [Multi-Project → Node Routing](./multi-project.md#node-routing)
- [Architecture → Task Routing Architecture](./architecture.md#task-routing-architecture)
### Remote Access settings (project-scoped)
Remote access settings are project-only (stored in `.fusion/config.json`), not global.

View File

@@ -245,6 +245,70 @@ Each task may override:
Overrides are configured from the task model tab or task creation actions.
## Node Routing
Tasks execute on an effective node selected by routing precedence:
1. **Per-task node override** (`Task.nodeId`)
2. **Project default node** (`defaultNodeId` in project settings)
3. **Local execution** (no node configured)
At dispatch time, scheduler routing is persisted on the task as:
- `effectiveNodeId`
- `effectiveNodeSource` (`task-override`, `project-default`, or `local`)
### Per-task node override
You can set or clear a task override from:
- Task detail modal → **Routing** tab
- Quick/create flows that support node selection
- Bulk task actions
- CLI:
- `fn task set-node <task-id> <node-name-or-id>`
- `fn task clear-node <task-id>`
- `fn task create "..." --node <node-name-or-id>`
- Pi extension tool `fn_task_update` with `nodeId`
### Active-task blocking
Node override changes are blocked while a task is active/in progress. Core validation (`validateNodeOverrideChange`) returns `reason: "task-in-progress"` and users must pause/stop or wait for completion before changing routing.
### Task detail routing summary
The Routing tab shows:
- Effective node (with health indicator when known)
- Routing source (override vs project default vs local)
- Unavailable-node policy value (`block` or `fallback-local`)
- Lock banner when routing is currently immutable for an active task
### Activity log entries
When a task is dispatched, task activity/log records include routing decisions such as:
- `Node routing resolved: <node-or-local> (source: <source>)`
Use `fn task show <id>` or task logs to inspect current node routing context.
### Examples
```bash
# Route one task to a specific remote node
fn task set-node FN-204 edge-runner
# Remove override and return to project default routing
fn task clear-node FN-204
# Create a task with node override immediately
fn task create "Reproduce flaky node error" --node edge-runner
# Inspect routing summary from CLI
fn task show FN-204
```
See also: [Settings Reference → Node Routing settings](./settings-reference.md#node-routing-settings-project-scope) and [Architecture → Task Routing Architecture](./architecture.md#task-routing-architecture).
## Review Level
Review levels control the rigor of the review process for a task: