# @x12i/graphenix-task-node-format

Task node **authoring** contract: substance, bindings, phase utility strategies, conditions, knowledge, and metadata validation.

**Canonical vocabulary:** [GLOSSARY.md](../../GLOSSARY.md) at monorepo root (do not use “Synthesis PRE”).  
When task run phase terms change, update this README per [GLOSSARY § README sync policy](../../GLOSSARY.md#readme-sync-policy-task-run-phases).

## Install

```bash
npm install @x12i/graphenix-task-node-format @x12i/graphenix-core
```

## Task node design shape

```txt
Task node (design authoring)
│
├─ taskVariable.*              task substance (question, output contract)
├─ inputs / inputsConfig       input bindings
├─ smartInput                  render-time path config (smart-inputs token)
├─ executionMapping            output into execution memory
│
├─ taskConfiguration
│   ├─ prePhase (legacy: aiTaskStrategies.pre — path-driven synthesis reads)
│   ├─ mainPhase (legacy: executionStrategies — `[]` = explicit plain MAIN)
│   │     ├─ skillProfile.inputSynthesis (legacy: aiTaskProfile.inputSynthesis)
│   │     └─ llmRuntime (optional — server tools / OpenRouter mode for main skill)
│   └─ postPhase (legacy: aiTaskStrategies.post)
│
└─ model override (graphenix.executable/v1) — see @x12i/graphenix-executable-profile-format
    └─ preActionModel / skillModel / postActionModel  (phase model profiles)
```

## Three layers (do not conflate)

| Layer | What it answers | Legacy fields |
| ----- | --------------- | ------------- |
| **Phase topology** | pre / main / post run shape | runtime only |
| **Phase utility strategies** | What utility runs in pre/post; MAIN wrappers | `aiTaskStrategies`, `executionStrategies` |
| **Skill input synthesis profile** | Synthesis inside MAIN (`synthesized-context` UI) | `aiTaskProfile.inputSynthesis` |
| **Phase model profiles** | Which AI profile runs each phase | `preActionModel`, `skillModel`, `postActionModel` |

An empty `executionStrategies: []` means explicit plain MAIN. Omitting the field is not the same as `[]` for strict graphs.

**Critical:** `aiTaskStrategies.pre: "synthesis"` is a **PRE-phase utility strategy key**. `aiTaskProfile.inputSynthesis.enabled` is the **skill input synthesis profile** — a different feature. SubNets uses the former, not the latter.

**Plan output:** `preActions[]` / `postActions[]` are compiled execution units (plan artifact) — not synonyms for `aiTaskStrategies.pre`.

## TypeScript types (canonical names)

Legacy JSON keeps `aiTaskStrategies` for migration. Prefer these names in code and validator messages:

| Type | Legacy JSON |
| ---- | ----------- |
| `PrePhaseUtilityConfig` | `aiTaskStrategies.pre` (PRE reads from `inputSynthesis.sources` or runtime default trio) |
| `PostPhaseUtilityConfig` | `aiTaskStrategies.post` |
| `MainPhaseConfig` | `executionStrategies` only |
| `SkillInputSynthesisProfile` | `aiTaskProfile.inputSynthesis` (`skillProfile.inputSynthesis`) |

## Examples (JSON)

### SubNets-style PRE-phase utility (§2)

```json
"taskConfiguration": {
  "executionStrategies": [],
  "aiTaskStrategies": {
    "pre": "synthesis",
    "post": "validate"
  }
}
```

PRE-phase utility strategy `synthesis` enables the PRE unit; read scope from `aiTaskProfile.inputSynthesis.sources` or runtime default trio (`jobVariables`, `taskVariables`, `input`). `inputsConfig` and `smartInput.paths` are **MAIN only**. **`preInputStrategy` is rejected** on new graphs (`LEGACY_PRE_INPUT_STRATEGY`).

### Skill input synthesis profile (§3 — not SubNets)

```json
"taskConfiguration": {
  "executionStrategies": [],
  "aiTaskProfile": {
    "inputSynthesis": {
      "enabled": true,
      "synthesisMode": "contextual",
      "contextSourcePolicy": "execution-memory-only"
    }
  }
}
```

No `aiTaskStrategies.pre` — different code path from PRE-phase utility `synthesis`.

### Optional fact-guard (authoring — sub-actions only)

```json
"taskConfiguration": {
  "aiTaskStrategies": { "pre": "synthesis", "post": "polish" },
  "factGuard": {
    "preSynthesis": {
      "enabled": true,
      "policyId": "prompt-context-synthesizer",
      "onViolation": "fallback"
    }
  }
}
```

Opt-in only — absent or `enabled: false` leaves compile unchanged. Validator requires `policyId` when `enabled: true`. See [GLOSSARY §6](../../GLOSSARY.md#6-fact-guard-optional-sub-action-integrity).

### Optional `llmRuntime` (main skill server-tool extensions)

Extends the **main skill invocation** only — requires `skillKey`. Not model selection; not PRE `webScope`.

```json
"taskConfiguration": {
  "executionStrategies": [],
  "llmRuntime": {
    "providerRuntime": "openrouter",
    "serverTools": {
      "webSearch": {
        "mode": "required",
        "engine": "auto",
        "maxResults": 5,
        "maxTotalResults": 15,
        "requireCitations": true
      }
    },
    "serverToolInstructionMode": "minimal",
    "output": {
      "toolMetadata": {
        "destination": "execution",
        "key": "aiTooling",
        "mode": "merge",
        "include": ["serverTools", "citations", "openrouterRuntime"]
      }
    }
  }
}
```

Compiles to `mainSkill.llmRuntime` on the node plan. Use `executionMapping.map` with allowlisted `response.metadata.*` paths to map tool metadata into `executionMemory.aiTooling.*`.

### Full task node `parameters` (substance + bindings + strategies)

```json
{
  "profile": "graphenix.task-node/v1",
  "nodeType": "task",
  "skillKey": "professional-answer",
  "taskVariable": {
    "question": "What is the professional answer?",
    "outputContract": { "type": "object" }
  },
  "inputsConfig": {
    "record": { "path": "input.record" }
  },
  "smartInput": {
    "paths": [{ "path": "input.record", "label": "Record" }]
  },
  "executionMapping": {
    "path": "executionMemory.steps.node:answer",
    "mode": "replace"
  },
  "taskConfiguration": {
    "executionStrategies": [],
    "aiTaskStrategies": {
      "pre": "synthesis"
    }
  },
  "conditions": {
    "jsonConditions": [{ "path": "runtime.mode", "eq": "live" }]
  }
}
```

Phase model profiles (`preActionModel`, `skillModel`, `postActionModel`) are validated by `@x12i/graphenix-executable-profile-format` — see that README.

## API

```ts
import { validateTaskNode } from "@x12i/graphenix-task-node-format";

const errors = validateTaskNode(node, graphDocument);
```

Typically called via `validateAuthoringGraph()` from `@x12i/graphenix-authoring-format`.

## Related

- [GLOSSARY.md](../../GLOSSARY.md)
- [@x12i/graphenix-authoring-format](../authoring-format/README.md)
- [@x12i/graphenix-executable-profile-format](../executable-profile-format/README.md) — phase model profiles
