@x12i/graphenix-plan-format
ExecutableGraphPlan format — validates the engine-facing artifact produced by @x12i/graphenix-plan-compiler.
Defines and checks what a correct compiled plan looks like: resolved phase model profiles, ordered execution units per run phase, finalizer plans, embedded normalized graph snapshot, and forbidden runtime fields.
This package does not compile from authoring graphs — it only validates plan documents. Use @x12i/graphenix-plan-compiler to produce plans.
Canonical vocabulary: GLOSSARY.md at monorepo root.
Finalization brief: COMPILE-FORMAT-FINALIZATION.md · execute/trace: EXECUTE-TRACE-FORMAT-FINALIZATION.md.
When task run phases or plan-phase terms change, update this README and GLOSSARY.md §5 together.
Who should use this
| Role | Use this package? |
|---|---|
| Execution engine | Yes — validate plan before run |
| Compiler / execution prep | Yes — validate output before handoff |
| Run history / observability | Yes — bind traces to validated plan shape |
| Graph designer / Studio | Only if showing compile preview |
| Authoring-only services | No — use @x12i/graphenix-authoring-format |
Install
npm install @x12i/graphenix-plan-format
Types (ExecutableGraphPlan, ExecutableGraphPlanV2, ExecutionUnitPlanV2, …) live in @x12i/graphenix-executable-contracts.
Lifecycle position
AuthoringGraphDocument
│ compileAuthoringGraphToExecutablePlan() (static — no runtime)
│ compileExecutablePlanV2(authoring, runtime) (execute path only)
▼
ExecutableGraphPlan ← THIS PACKAGE validates
Execute contract: validateExecutablePlanV2 requires complete nodePlans[].modelSlots for AI tasks (job ⊕ node merge happens at compile — execute must not re-read partial node taskConfiguration.modelConfig).
Task run phases (runtime topology)
One task node invocation is three run phases (GLOSSARY §1):
| Run phase | Meaning | Typical plan units |
|---|---|---|
| prePhase | Optional utility work before the skill | externalPreUtility (v2) / preAction (v1) |
| mainPhase | Skill / MAIN execution | mainSkill, pipelinePhase, localSkill (v2) / skill (v1) |
| postPhase | Optional utility work after the skill | externalPostUtility (v2) / postAction (v1) |
This is phase topology — not model choice and not catalog action ids.
Design → plan mapping (do not conflate)
Authoring declares phase utility strategies on the task node. The compiler expands them into plan execution units:
prePhase.strategyKey (design: aiTaskStrategies.pre)
│ compile
▼
PRE-phase execution units (plan: externalPreUtility / preAction)
mainPhase.executionStrategies + skill input synthesis profile (design)
│ compile
▼
MAIN-phase execution units (plan: mainSkill, pipelinePhase, …)
postPhase.strategyKey (design: aiTaskStrategies.post)
│ compile
▼
POST-phase execution units (plan: externalPostUtility / postAction)
| Design (authoring) | Plan (this package) | Glossary |
|---|---|---|
aiTaskStrategies.pre |
PRE-phase units with strategyKey / actionKey |
PRE-phase utility strategy → plan PRE-phase action list |
executionStrategies, aiTaskProfile.inputSynthesis |
mainSkill, pipelinePhase, … |
MAIN-phase wrappers / skill input synthesis profile |
aiTaskStrategies.post |
POST-phase units | POST-phase utility strategy → plan POST-phase action list |
preActions[] / postActions[] (optional on design) |
Same shape at plan layer | Plan-phase action lists — not synonyms for aiTaskStrategies.pre |
Explicit rule: strategyKey: "synthesis" (PRE-phase utility) ≠ inputSynthesis.enabled (MAIN skill input synthesis profile). The plan may contain pipelinePhase units from the latter without any PRE-phase utility unit.
PRE synthesis (2.4.0): inputsConfig and smartInput.paths are MAIN only. PRE invokeContract.reads / unitParams.memoryPaths come from aiTaskProfile.inputSynthesis.sources or are empty ([]) for runtime default trio. When sources is authored, reads and memoryPaths must be identical.
Each execution unit carries a phase model profile binding (GLOSSARY §4):
modelSlot |
Run phase |
|---|---|
preActionModel |
PRE-phase model profile |
skillModel |
MAIN-phase model profile |
postActionModel |
POST-phase model profile |
finalizerModel |
Finalizer (graph output) |
Utility strategy = what runs. Model profile = which AI profile runs it.
Plan document shape
v1 — graphenix.executable-plan/v1
ExecutableGraphPlan
├─ format, planId, createdAt
├─ source (graphId, graphHash, …)
├─ profileRegistry (@x12i/ai-profiles version)
├─ runtimeBinding (jobId, mode, environment, hashes)
├─ graph — embedded NormalizedExecutableGraphDocument
├─ caseSelection — graph + per-node selected cases
├─ nodePlans[nodeId]
│ ├─ selectedCases, modelSlots (preActionModel / skillModel / postActionModel)
│ └─ executionUnits[] — ordered preAction → skill → postAction
└─ finalizerPlans (if applicable)
v1 unit kinds: preAction | skill | postAction | finalizer.
v2 — graphenix.executable-plan/v2 (preferred for new engines)
ExecutableGraphPlanV2
├─ format, planId, createdAt, planHash
├─ source, profileRegistry, runtimeBinding, runtimeExpectations
├─ normalizedGraph — embedded or ref
├─ policies (directModelPolicy, fallback, trace, …)
├─ nodeIndex[], deferredGates[]
├─ nodePlans[nodeId] — NodeExecutionPlan
│ ├─ invokeContract (pipeline snapshot from authoring)
│ ├─ modelSlots, selectedCases
│ └─ executionUnits[] — ordered by run phase
└─ finalizerPlans[nodeId] — FinalizerExecutionPlan
v2 unit kinds include:
unitKind |
Typical run phase | invokeMode |
|---|---|---|
externalPreUtility |
prePhase | externalRunTask |
mainSkill |
mainPhase | mainRunTask |
pipelinePhase |
mainPhase (wrapper / synthesis inside MAIN) | pipelinePhase |
localSkill |
mainPhase | local |
externalPostUtility |
postPhase | externalRunTask |
finalizer |
graph output | finalizer |
validation, mapping, callGraph |
special | none / … |
Each ExecutionUnitPlanV2 exposes: unitId, nodeId, unitKind, invokeMode, order, actionKey, strategyKey, skillKey, modelSlot, modelSelection, modelSource, invokeContract, retry/fallback/trace policies, and sourcePath (JSON pointer into embedded graph).
Per-task node plan (diagram)
NodeExecutionPlan (one task node, one runTask wave)
│
├─ prePhase units[] externalPreUtility / preAction
│ modelSlot: preActionModel
│
├─ mainPhase units[] mainSkill | pipelinePhase | localSkill
│ modelSlot: skillModel (pipelinePhase may share MAIN profile)
│
└─ postPhase units[] externalPostUtility / postAction
modelSlot: postActionModel
Order is strict: PRE units → MAIN units → POST units. Validators reject missing MAIN skill, duplicate orders, and slot/kind mismatches.
Examples (JSON)
v2 plan header (minimal)
{
"format": "graphenix.executable-plan/v2",
"planId": "plan:abc123",
"createdAt": "2026-06-06T12:00:00.000Z",
"planHash": "sha256:…",
"source": {
"graphId": "graph:content-pipeline",
"graphRevision": "1.0.0",
"graphHash": "sha256:…",
"formatVersion": "2.0.0"
},
"profileRegistry": {
"package": "@x12i/ai-profiles",
"version": "3.2.0",
"registryHash": "sha256:…"
},
"runtimeBinding": {
"jobId": "job-001",
"mode": "live",
"environment": "prod",
"sourceGraphHash": "sha256:…",
"profileRegistryHash": "sha256:…"
}
}
v2 nodePlans — PRE + MAIN execution units (content pipeline)
Compiled from authoring aiTaskStrategies.pre: "synthesis" + executionStrategies: [] on node:audience-insights. No POST units unless design declares aiTaskStrategies.post:
"nodePlans": {
"node:audience-insights": {
"nodeId": "node:audience-insights",
"nodeType": "task",
"executionClass": "aiTask",
"skillKey": "professional-answer",
"selectedCases": {
"graphModelCaseId": "default"
},
"modelSlots": {
"preActionModel": {
"selection": { "kind": "profileChoice", "key": "cheap/default" },
"source": "graphCase"
},
"skillModel": {
"selection": { "kind": "profileChoice", "key": "vol/default" },
"source": "graphCase"
}
},
"executionUnits": [
{
"unitId": "unit:node:audience-insights:pre:0",
"nodeId": "node:audience-insights",
"unitKind": "externalPreUtility",
"invokeMode": "externalRunTask",
"order": 0,
"strategyKey": "synthesis",
"actionKey": "synthesis",
"modelSlot": "preActionModel",
"modelSelection": { "kind": "profileChoice", "key": "cheap/default" },
"sourcePath": "/graph/nodes/node:audience-insights/parameters/taskConfiguration/aiTaskStrategies/pre"
},
{
"unitId": "unit:node:audience-insights:main:1",
"nodeId": "node:audience-insights",
"unitKind": "mainSkill",
"invokeMode": "mainRunTask",
"order": 1,
"skillKey": "professional-answer",
"modelSlot": "skillModel",
"modelSelection": { "kind": "profileChoice", "key": "vol/default" },
"sourcePath": "/graph/nodes/node:audience-insights/parameters/skillKey"
}
],
"sourcePath": "/graph/nodes/node:audience-insights"
}
}
v2 nodePlans — PRE / MAIN / POST
When authoring declares aiTaskStrategies.post, the compiler appends POST-phase units (same mapping as PRE via externalPostUtility).
v2 MAIN pipelinePhase from skill input synthesis profile (not PRE utility)
When authoring uses aiTaskProfile.inputSynthesis.enabled (not aiTaskStrategies.pre):
{
"unitId": "unit:node:task:pipeline:0",
"unitKind": "pipelinePhase",
"invokeMode": "pipelinePhase",
"order": 0,
"modelSlot": "skillModel",
"sourcePath": "/graph/nodes/node:task/parameters/taskConfiguration/aiTaskProfile/inputSynthesis"
}
v1 plan unit shape (legacy engines)
"executionUnits": [
{
"unitId": "unit:1:pre",
"unitKind": "preAction",
"order": 0,
"actionKey": "synthesis",
"modelSlot": "preActionModel",
"modelSelection": { "kind": "profileChoice", "key": "cheap/default" }
},
{
"unitId": "unit:1:skill",
"unitKind": "skill",
"order": 1,
"skillKey": "professional-answer",
"modelSlot": "skillModel",
"modelSelection": { "kind": "profileChoice", "key": "vol/default" }
}
]
Example (API)
import {
validateExecutablePlan,
validateExecutablePlanV1,
validateExecutablePlanV2
} from "@x12i/graphenix-plan-format";
import type { ExecutableGraphPlan } from "@x12i/graphenix-executable-contracts";
const result = validateExecutablePlan(plan);
if (!result.valid) {
console.error(result.errors.map((e) => `${e.path}: ${e.message}`));
}
// Or target a specific format version:
validateExecutablePlanV2(planV2);
validateExecutablePlanV1(planV1);
validateExecutablePlan() auto-selects v1 vs v2 from plan.format.
Typical compile + validate flow:
import { compileExecutablePlan } from "@x12i/graphenix-plan-compiler";
import { validateExecutablePlan } from "@x12i/graphenix-plan-format";
const plan = compileExecutablePlan(authoringGraph, runtime, {
profileRegistry: { package: "@x12i/ai-profiles", version: "3.2.0" }
});
const validation = validateExecutablePlan(plan);
if (!validation.valid) throw new Error("Invalid plan");
Key exports
| API | Purpose |
|---|---|
validateExecutablePlan(plan) |
Validate v1 or v2 (detects plan.format) |
validateExecutablePlanV1(plan) |
Validate graphenix.executable-plan/v1 only |
validateExecutablePlanV2(plan) |
Validate graphenix.executable-plan/v2 only |
Types and constants are imported from @x12i/graphenix-executable-contracts:
| Symbol | Value / role |
|---|---|
EXECUTABLE_PLAN_FORMAT |
"graphenix.executable-plan/v1" |
EXECUTABLE_PLAN_FORMAT_V2 |
"graphenix.executable-plan/v2" |
ExecutableGraphPlan |
v1 plan document |
ExecutableGraphPlanV2 |
v2 plan document |
NodeExecutionPlan |
Per-node v2 plan with executionUnits |
ExecutionUnitPlanV2 |
Single v2 execution unit |
ExecutionUnitPlan |
v1 unit (deprecated for new work) |
What validation enforces
- Plan metadata:
format,planId,createdAt, source graph refs, profile registry ref - Embedded graph is normalized (
validateNormalizedExecutableGraphvia authoring-format) - Case selection frozen on plan — graph + node cases with explanations
- Model slots resolved per node;
profileChoicekeys well-formed; direct model policy (v2) - Execution units: ordering, unique ids, kind
modelSlotalignment, required MAIN skill - Optional fact-guard on sub-action
unitParams(synthesis,polish,audit);enabled: truerequirespolicyId;mainSkillforbidden — see GLOSSARY §6 - Forbidden fields never on plan: credentials, trace events, provider responses, execution logs, …
- v2:
invokeModematchesunitKind, invoke contracts, deferred gates, finalizer plans
Validation errors use glossary-friendly paths (e.g. PRE/MAIN/POST phase model slots). Do not label plan units as “Synthesis PRE” — use PRE-phase utility strategy vs skill input synthesis profile per GLOSSARY.md.
Dependencies
| Package | Why |
|---|---|
@x12i/graphenix-executable-contracts |
Plan and unit types, error codes |
@x12i/graphenix-authoring-format |
Validates embedded normalized authoring graph |
Peer (via compiler, not required at runtime for validation alone): @x12i/graphenix-core.
Related packages
| Package | Role |
|---|---|
| GLOSSARY.md | Canonical phase + strategy + model vocabulary |
@x12i/graphenix-plan-compiler |
Produces plans from authoring + runtime |
@x12i/graphenix-authoring-format |
Design authoring; phase utility strategies |
@x12i/graphenix-task-node-format |
Task-node body validation (design layer) |
@x12i/graphenix-executable-profile-format |
Phase model profiles on authoring graph |
@x12i/graphenix-trace-format |
Run evidence bound to a validated plan |
| Compiler role guide | End-to-end compile client guide |
README sync policy
When documenting or changing task run phases, update these together:
| Layer | Package README |
|---|---|
| Design task body + utility strategies | task-node-format/README.md |
| Phase model profiles (authoring) | executable-profile-format/README.md |
| Compiled plan + execution units | this file — include example JSON |
| Compile pipeline | plan-compiler/README.md |
| Terms of record | GLOSSARY.md |