# @x12i/graphenix-authoring-format

**Authoring graph format** — the Design/Edit document editors and generators validate in memory before save.

Validates `AuthoringGraphDocument`, `ConceptDocument` (shadow), deterministic model cases, task/finalizer nodes, phase utility strategies, and `profileChoice` keys via `@x12i/ai-profiles`.

**Task-node vocabulary:** [GLOSSARY.md](../../GLOSSARY.md) · **Format scope:** [FORMAT-SCOPE.md](../../docs/FORMAT-SCOPE.md) · **Design brief:** [GRAPH-FORMAT-FINALIZATION.md](../../docs/GRAPH-FORMAT-FINALIZATION.md)

**Naming:** `@x12i/*` is npm scope only. JSON uses `graphenix.*` profiles and plain kinds `task` / `finalizer`.

---

## Task node design shape

```txt
Task node (design authoring)
│
├─ taskVariable.*              task substance
├─ inputs / inputsConfig       input bindings
├─ smartInput / executionMapping
│
├─ taskConfiguration
│   ├─ prePhase (aiTaskStrategies.pre + preInputStrategy)
│   ├─ mainPhase (executionStrategies — `[]` = explicit plain MAIN)
│   │     └─ skillProfile.inputSynthesis (aiTaskProfile.inputSynthesis)
│   └─ postPhase (aiTaskStrategies.post)
│
└─ graphenix.executable/v1 → preActionModel / skillModel / postActionModel
```

`preActions[]` / `postActions[]` are plan-phase action lists — not synonyms for `aiTaskStrategies.pre`.

---

## Example authoring graph (JSON)

Content pipeline task with PRE-phase utility strategy `synthesis` + plain MAIN:

```json
{
  "formatVersion": "2.0.0",
  "id": "graph:content-pipeline",
  "revision": "1.0.0",
  "name": "Content pipeline",
  "graph": {
    "nodes": [
      {
        "id": "node:audience-insights",
        "kind": "task",
        "layout": { "x": 120, "y": 200 },
        "parameters": {
          "profile": "graphenix.task-node/v1",
          "nodeType": "task",
          "skillKey": "professional-answer",
          "taskConfiguration": {
            "executionStrategies": [],
            "aiTaskStrategies": {
              "pre": "synthesis",
              "preInputStrategy": "execution-memory-only"
            }
          }
        }
      },
      {
        "id": "node:finalizer:package",
        "kind": "finalizer",
        "parameters": {
          "profile": "graphenix.finalizer-node/v1",
          "nodeType": "finalizer",
          "finalizerType": "aggregate",
          "config": { "sections": [] }
        }
      }
    ],
    "metadata": {
      "extensions": {
        "graphenix.executable/v1": {
          "profileVersion": "1.0.0",
          "modelConfig": {
            "version": "graph-model-config/v1",
            "cases": [{
              "id": "default",
              "modelConfig": {
                "preActionModel": { "kind": "profileChoice", "key": "cheap/default" },
                "skillModel": { "kind": "profileChoice", "key": "vol/default" },
                "postActionModel": { "kind": "profileChoice", "key": "cheap/default" }
              }
            }]
          }
        }
      }
    }
  }
}
```

Fixtures: `createMinimalExecutableGraph()`, `createContentPipelineReferenceGraph()` (canonical).

---

## Who should use this

| Role | Use this package? |
| ---- | ----------------- |
| Graph designer / editor / generator | **Yes** |
| Compiler service | Upstream validation (also depends on this) |
| Engine at runtime | **No** — engine receives compiled plans |

---

## Install

```bash
npm install @x12i/graphenix-authoring-format @x12i/graphenix-core
```

---

## Example (API)

```ts
import { validateGraph } from "@x12i/graphenix-core";
import {
  validateAuthoringGraph,
  validateConceptDocument,
  createContentPipelineReferenceGraph,
  normalizeExecutableGraph,
  stripDesignOnlyFieldsFromGraph
} from "@x12i/graphenix-authoring-format";

const doc = createContentPipelineReferenceGraph();
validateGraph(doc);
validateAuthoringGraph(doc);

validateConceptDocument({
  formatVersion: "graphenix.concept/v1",
  graphId: doc.id,
  name: doc.name
});

const normalized = normalizeExecutableGraph(doc);
const forPlan = stripDesignOnlyFieldsFromGraph(normalized);
```

---

## Two-tier validation

| Validator | Scope |
| --------- | ----- |
| `validateGraph()` | Topology, ports, task body, layout |
| `validateAuthoringGraph()` | `graphenix.executable/v1` + task-node body |
| `validateConceptDocument()` | Paired shadow document (separate API) |

---

## Key exports

| Category | APIs |
| -------- | ---- |
| Validation | `validateAuthoringGraph`, `validateConceptDocument`, `validateNormalizedExecutableGraph`, `validateGraphJobMemory`, `validateGraphJobPipeline` |
| Normalization | `normalizeExecutableGraph`, `stripDesignOnlyFieldsFromGraph`, `normalizeGraphJobMemory`, `normalizeGraphJobPipeline` |
| CRUD helpers | `addTaskNode`, `addFinalizerNode`, `setGraphModelConfig`, `setNodeModelConfig`, `connectTaskToTask` |
| Profile choice | `validateAiModelSelection`, `isKnownProfileChoice` |
| Model config authoring | `readGraphJobModelDefaults`, `mergeEffectiveModelConfig`, `normalizeNodeOverrideForPersist`, `validateNodeModelConfigOverride`, `legacyModelConfigToAuthoring` |
| Fixtures | `createMinimalExecutableGraph`, `createContentPipelineReferenceGraph`, `createPlainGraphenixGraph` |
| Task node (re-export) | `validateTaskNode`, phase config types from `@x12i/graphenix-task-node-format` |

---

## Strict profiles

| Node `kind` | `parameters.profile` |
| ----------- | -------------------- |
| `task` | `graphenix.task-node/v1` |
| `finalizer` | `graphenix.finalizer-node/v1` |

Extension: `graph.metadata.extensions["graphenix.executable/v1"]`

Design-only: `node.layout` — stripped via `stripDesignOnlyFieldsFromGraph()` at compile.

---

## Knowledge scope

| Field | Scope |
| ----- | ----- |
| `parameters.taskKnowledge` | Task node — forwarded on `RunTaskRequest.taskMemory.knowledge` |
| `parameters.taskPagentiKnowledge` | Task node — same scope as `taskKnowledge`; Pagenti task refs (parallel to `jobPagentiKnowledge` at graph level) |
| `graph.metadata.jobKnowledge` | Graph-run — forwarded on `RunTaskRequest.jobMemory.knowledge` |
| `graph.metadata.jobPagentiKnowledge` | Graph-run — Pagenti job refs (via `graphenix.knowledge/v1` promote path) |
| Any other `taskKnowledge` / misplaced pagenti path | **Rejected** (`TASK_KNOWLEDGE_GRAPH_SCOPE_FORBIDDEN`, `TASK_KNOWLEDGE_WRONG_PLACEMENT`) |
| Finalizer nodes | No `taskKnowledge` — finalizers do not invoke AI tasks |

Document root `taskKnowledge` and `jobKnowledge` are deprecated — use the paths above.

---

## Job memory defaults

Graph-level declarative context queries live on `graph.metadata.jobMemory.context[]` (not document root — root `jobMemory` is deprecated like `jobKnowledge`).

| Field | Role |
| ----- | ---- |
| `graph.metadata.jobMemory.context[]` | Default Memorix context queries resolved by the host into `runtime.jobMemory.context[]` before execute |

Each context entry requires `alias`, `contentType`, and `filter[]`. Duplicate aliases within one graph are rejected. Filters with `source: "input"` emit a non-fatal warning when `inputField` is empty (`validateAuthoringGraphForPersist`).

Normalization strips runtime-populated fields (`rows`, nested runtime `query` echoes) from persisted model entries.

```json
{
  "graph": {
    "metadata": {
      "jobMemory": {
        "context": [
          {
            "alias": "peer-subnets",
            "contentType": "subnet",
            "target": "entity",
            "filter": [
              { "key": "tenantId", "source": "input", "inputField": "tenantId" }
            ],
            "limit": 10
          }
        ]
      }
    }
  }
}
```

Host merge convention: `runtime.jobMemory.context = [...(resolved graph defaults), ...(resolved job contextQueries)]` with job-level entries overriding the same `alias`.

---

## Job pipeline metadata

Graph-level job identity and pipeline position live on `graph.metadata.jobPipeline` (not document root — root `jobPipeline` is deprecated like `jobMemory`).

| Field | Role |
| ----- | ---- |
| `jobTypeId` | Logical job classification — many graph implementations may share one job type |
| `upstream[]` / `downstream[]` | Job-type dependency refs (cross-entity upstream may include `entity` + `relationKey`) |
| `context[]` | Default linked-entity pulls for work-factory / `jobMemory.context` at enqueue |
| `reprocessing` | Operator-facing reprocessing policy keyed by job type |

**Identity (do not conflate):**

| Field | Meaning | Authored? |
| ----- | ------- | --------- |
| `jobId` | Per-run correlation id | Runtime only |
| `jobTypeId` | Logical job classification | `graph.metadata.jobPipeline.jobTypeId` |
| `graphId` | Pipeline implementation id | Document `id` |

Deprecated wire aliases accepted at validation: `jobType` → `jobTypeId`, `relationshipKey` → `relationKey`. Use `resolveGraphJobPipelineContract()` for canonical resolver output.

```json
{
  "graph": {
    "metadata": {
      "jobPipeline": {
        "jobTypeId": "content-pipeline.analyze",
        "label": "Analyze content",
        "upstream": [
          { "jobTypeId": "content-pipeline.ingest" }
        ],
        "context": [
          {
            "relationKey": "sourceRecord",
            "targetEntity": "content",
            "target": "entity",
            "contentTypes": ["article"],
            "limit": 5
          }
        ],
        "reprocessing": {
          "mode": "ttl",
          "ttl": { "value": 7, "unit": "days" }
        }
      }
    }
  }
}
```

---

## Related packages

| Package | Role |
| ------- | ---- |
| [GLOSSARY.md](../../GLOSSARY.md) | Phase vocabulary |
| [`@x12i/graphenix-authoring-analysis`](../authoring-analysis) | Studio save/import: `GraphProjectDocument`, purity, model-config merge |
| [`@x12i/graphenix-task-node-format`](../task-node-format) | Task body validation |
| [`@x12i/graphenix-executable-profile-format`](../executable-profile-format) | Phase model profiles |
| [`@x12i/graphenix-case-format`](../case-format) | Case condition DSL |
| [`@x12i/graphenix-plan-compiler`](../plan-compiler) | Compile authoring → plan |

**v1.2.0:** Model-config authoring APIs (`readGraphJobModelDefaults`, `mergeEffectiveModelConfig`, `normalizeNodeOverrideForPersist`, etc.) are canonical in this package. See [model-config authoring semantics](../../docs/format/guides/graphenix-model-config-authoring-semantics.md).

**v1.1.0:** Legacy flat-graph migration helpers were removed. Studio envelope validation lives in `@x12i/graphenix-authoring-analysis`.
