{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "urn:manifest:config:manifest-config",
  "title": "Manifest Build Configuration",
  "$comment": "SOURCE OF TRUTH (Binding): machine contract for manifest.config YAML/keys. Prose: docs/spec/config/manifest.config.md. Index: docs/SOURCE_OF_TRUTH_INDEX.md.",
  "description": "Schema for manifest.config.yaml / manifest.config.yml / .manifestrc.yaml. The runtime-level TypeScript config (manifest.config.ts) is validated structurally by the loader, not by this schema — it contains JavaScript functions and class references that cannot be expressed in JSON.",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "Optional pointer to this schema for editor IntelliSense."
    },
    "src": {
      "type": "string",
      "description": "Glob pattern for source .manifest files.",
      "default": "**/*.manifest"
    },
    "output": {
      "type": "string",
      "description": "Directory where compiled IR JSON files are written.",
      "default": "ir/"
    },
    "prismaSchema": {
      "type": "string",
      "description": "Optional path to a Prisma schema file used for property alignment checks. When omitted, Manifest searches prisma/schema.prisma, schema.prisma, and db/schema.prisma."
    },
    "validation": {
      "type": "object",
      "additionalProperties": false,
      "description": "Config G2 — CI exit policy + optional additive rule registry. Does not change language diagnostic severities (a block is still a block).",
      "properties": {
        "failOn": {
          "type": "string",
          "enum": ["block", "warn", "never"],
          "default": "block",
          "description": "When to exit non-zero: 'block' = errors only (default); 'warn' = errors or warnings; 'never' = always exit 0 after reporting (report-only)."
        },
        "rules": {
          "type": "object",
          "additionalProperties": false,
          "description": "Optional additive lint rules (off by default). Emit CONFIG_VALIDATION_RULE_* diagnostics.",
          "properties": {
            "missing-policy": {
              "type": "string",
              "enum": ["off", "warn", "error"],
              "default": "off",
              "description": "Entity has commands but no targeting policies and empty defaultPolicies."
            },
            "unused-entity": {
              "type": "string",
              "enum": ["off", "warn", "error"],
              "default": "off",
              "description": "Entity has no store, no commands, and is not referenced by relationships."
            },
            "orphan-relationship": {
              "type": "string",
              "enum": ["off", "warn", "error"],
              "default": "off",
              "description": "belongsTo/ref without hasMany/hasOne inverse on the parent."
            }
          }
        }
      }
    },
    "mergeIntegrity": {
      "$ref": "#/definitions/MergeIntegrityConfig",
      "description": "Config G3 — multi-module merge collision policy for compile --merge / --all."
    },
    "provenance": {
      "$ref": "#/definitions/ProvenanceConfig",
      "description": "Config G4 — IR provenance stamps (deterministic compiledAt, lockfile, failIfStale)."
    },
    "runtime": {
      "$ref": "#/definitions/RuntimeConfig",
      "description": "Config G7 — central runtime knobs for generate (executionMode, determinism.deterministicMode)."
    },
    "driftGates": {
      "type": "object",
      "additionalProperties": false,
      "description": "Config G10 — declarative CI drift gates enforced by `manifest ci-gate`.",
      "properties": {
        "effectiveConfigSnapshot": {
          "type": "string",
          "description": "Path to a committed effective-config snapshot (from `manifest config inspect --json`)."
        },
        "failOnConfigDrift": {
          "type": "boolean",
          "description": "Compare live effective config to the snapshot. Defaults to true when effectiveConfigSnapshot is set."
        },
        "failOnGeneratedDrift": {
          "type": "boolean",
          "default": false,
          "description": "Run generate --all --check and fail when committed artifacts drift."
        },
        "pinIrSchemaVersion": {
          "type": "string",
          "description": "When set, every *.ir.json under the config output must declare this version string."
        }
      }
    },
    "projections": {
      "type": "object",
      "description": "Per-projection configuration. Each key matches a registered projection name (e.g. 'nextjs', 'routes').",
      "additionalProperties": false,
      "properties": {
        "analytics": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'analytics' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "contract-tests": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'contract-tests' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "convex": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'convex' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "dart": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'dart' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "defaults": {
          "type": "object",
          "additionalProperties": true,
          "description": "Shared options merged under every named projection's `options` (per-projection keys win). Consumed by `resolveProjectionOptions` / `manifest generate --all`. Not a projection target."
        },
        "drizzle": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'drizzle' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "dynamodb": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'dynamodb' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "elasticsearch": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'elasticsearch' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "enabled": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "description": "When set, `manifest generate --all` runs only these projection names (order preserved). Absent = every declared projection block. Not a projection target."
        },
        "express": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'express' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "graphql": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'graphql' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "health": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'health' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "hono": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'hono' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "jsonschema": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'jsonschema' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "kysely": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'kysely' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "llm-context": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'llm-context' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "materialized-views": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'materialized-views' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "mermaid": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'mermaid' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "mongoose": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'mongoose' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "nextjs": { "$ref": "#/definitions/NextJsProjectionConfig" },
        "openapi": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'openapi' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "prisma": { "$ref": "#/definitions/PrismaProjectionConfig" },
        "prisma-store": { "$ref": "#/definitions/PrismaStoreProjectionConfig" },
        "pydantic": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'pydantic' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "react-query": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'react-query' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "remix": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'remix' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "routes": { "$ref": "#/definitions/RoutesProjectionConfig" },
        "storybook": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'storybook' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "sveltekit": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'sveltekit' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "terraform": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'terraform' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "wiring": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'wiring' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        },
        "zod": {
          "type": "object",
          "additionalProperties": false,
          "description": "Configuration for the 'zod' projection. Consumed by `manifest generate --all`, which reads `output` (the target directory/path) and `options` (passed verbatim to the projection generator). Option keys are projection-specific TypeScript interfaces and are not individually validated here.",
          "properties": {
            "output": {
              "type": "string",
              "description": "Directory or path hint where this projection's artifacts are written. A projection with no `output` is skipped by `manifest generate --all`."
            },
            "options": {
              "type": "object",
              "additionalProperties": true,
              "description": "Projection-specific options passed verbatim to the projection generator."
            }
          }
        }
      }
    },
    "env": {
      "$ref": "#/definitions/EnvironmentMapping",
      "description": "Environment variable mapping for stores, auth, adapters, and custom settings."
    },
    "hooks": {
      "$ref": "#/definitions/HooksConfig",
      "description": "Git pre-commit hook settings consumed by `manifest install-hooks`."
    },
    "plugins": {
      "type": "array",
      "description": "Third-party plugin declarations loaded by the CLI. Inspected via `manifest plugins`.",
      "items": { "$ref": "#/definitions/PluginDeclaration" }
    },
    "naming": {
      "description": "Identifier naming policy. Legacy: 'snake_case' or { table, column, pluralizeTables } (physical projection convention only; normalization off). Expanded: { normalization, entities, fields, … } — see NamingNormalizationConfig.",
      "oneOf": [
        { "$ref": "#/definitions/NamingConvention" },
        { "$ref": "#/definitions/NamingNormalizationConfig" }
      ]
    }
  },
  "definitions": {
    "HooksConfig": {
      "type": "object",
      "additionalProperties": false,
      "description": "Git pre-commit (`manifest install-hooks`) plus Config G8 build lifecycle hooks.",
      "properties": {
        "skipInCi": {
          "type": "boolean",
          "description": "Skip running the generated hook in CI environments.",
          "default": true
        },
        "provider": {
          "type": "string",
          "enum": ["husky", "simple-git-hooks"],
          "description": "Git hook manager the pre-commit hook is installed into.",
          "default": "husky"
        },
        "runFmt": {
          "type": "boolean",
          "description": "Run `manifest fmt` from the generated pre-commit hook.",
          "default": true
        },
        "runValidate": {
          "type": "boolean",
          "description": "Run `manifest validate` from the generated pre-commit hook.",
          "default": true
        },
        "lifecycle": {
          "$ref": "#/definitions/LifecycleHooksConfig",
          "description": "Config G8 — scripts around compile/generate (not git hooks)."
        }
      }
    },
    "LifecycleHooksConfig": {
      "type": "object",
      "additionalProperties": false,
      "description": "Config G8 — build lifecycle script paths (relative to project cwd).",
      "properties": {
        "beforeCompile": {
          "type": "array",
          "description": "Scripts run before `manifest compile` (fail-closed on non-zero exit).",
          "items": { "type": "string", "minLength": 1 }
        },
        "afterGenerate": {
          "type": "array",
          "description": "Scripts run after a successful `manifest generate` / `generate --all`.",
          "items": { "type": "string", "minLength": 1 }
        }
      }
    },
    "MergeIntegrityConfig": {
      "type": "object",
      "additionalProperties": false,
      "description": "Config G3 — collision policy when compiling multiple .manifest files into one IR. Default remains strict error (no silent overwrite).",
      "properties": {
        "onDuplicateEntity": {
          "type": "string",
          "enum": ["error", "lastWins"],
          "default": "error",
          "description": "When two files declare the same entity name: fail (default) or keep the last file in topological order."
        },
        "onDuplicateCommand": {
          "type": "string",
          "enum": ["error", "lastWins"],
          "default": "error",
          "description": "When two files declare the same command (entity-qualified): fail (default) or keep the last declaration."
        },
        "moduleOrder": {
          "type": "string",
          "enum": ["lexicographic"],
          "default": "lexicographic",
          "description": "Deterministic module order. Only lexicographic topo order is supported."
        },
        "allowCrossModuleRefs": {
          "type": "boolean",
          "default": true,
          "description": "Allow relationships/stores to target entities declared in another file."
        },
        "forbidCycles": {
          "type": "boolean",
          "const": true,
          "default": true,
          "description": "Cycle detection in `use` graphs. Must be true; false is rejected."
        }
      }
    },
    "ProvenanceConfig": {
      "type": "object",
      "additionalProperties": false,
      "description": "Config G4 — IR provenance policy. IR always includes required provenance fields; this controls timestamps, lockfile, and staleness.",
      "properties": {
        "stamp": {
          "type": "boolean",
          "default": true,
          "description": "When false, skip lockfile write. IR still receives required provenance (schema)."
        },
        "fields": {
          "type": "array",
          "description": "Requested stamp fields. Required IR fields always stamp. gitSha is accepted but not written yet (no IR field).",
          "items": {
            "type": "string",
            "enum": ["sourceHash", "generatorVersion", "irSchemaVersion", "gitSha"]
          }
        },
        "deterministic": {
          "type": "boolean",
          "default": false,
          "description": "Use a fixed compiledAt (1970-01-01T00:00:00.000Z) so irHash is stable across identical compiles."
        },
        "lockfile": {
          "type": "string",
          "minLength": 1,
          "description": "Path for provenance.lock.json (relative to project cwd)."
        },
        "failIfStale": {
          "type": "boolean",
          "default": false,
          "description": "With deterministic + lockfile: fail when contentHash matches lock but irHash drifted."
        }
      }
    },
    "RuntimeConfig": {
      "type": "object",
      "additionalProperties": false,
      "description": "Config G7 — generation fan-in for runtime factory + nextjs dispatcher (determinism, stores, defaultContext, concurrency).",
      "properties": {
        "executionMode": {
          "type": "string",
          "enum": ["inline", "externalExecutor"],
          "default": "inline",
          "description": "When set, fills projections.nextjs.options.dispatcher.executionMode if that key is unset."
        },
        "determinism": {
          "type": "object",
          "additionalProperties": false,
          "description": "Runtime determinism knobs consumed by the generated createManifestRuntime factory.",
          "properties": {
            "deterministicMode": {
              "type": "boolean",
              "default": false,
              "description": "Emit RuntimeOptions.deterministicMode on the generated factory (adapter actions fail closed)."
            },
            "forbidWallClock": {
              "type": "boolean",
              "default": false,
              "description": "When true, emit RuntimeOptions.now as a fixed function (seed ms, or 0)."
            },
            "seed": {
              "type": "number",
              "description": "Fixed now() ms when forbidWallClock; also seeds generateId sequence prefix."
            }
          }
        },
        "stores": {
          "type": "string",
          "description": "Import path for ManifestRuntimeConfig store bindings. Fans into projection runtimeConfigImport when unset (nextjs/express/hono/remix/sveltekit)."
        },
        "defaultContext": {
          "type": "object",
          "additionalProperties": true,
          "description": "Merged under createManifestRuntime caller context (caller wins)."
        },
        "concurrency": {
          "type": "object",
          "additionalProperties": false,
          "description": "Top-level parallel runCommand budget for generated RuntimeEngine factories.",
          "properties": {
            "maxParallelCommands": {
              "type": "integer",
              "minimum": 1,
              "description": "Emit RuntimeOptions.maxParallelCommands. Excess top-level calls fail closed with PARALLEL_COMMAND_LIMIT (nested reaction/saga calls do not consume the budget)."
            }
          }
        }
      }
    },
    "PluginDeclaration": {
      "type": "object",
      "additionalProperties": false,
      "required": ["module"],
      "description": "Declares a Manifest plugin for the CLI to load. Config G9: optional order + capabilities.",
      "properties": {
        "module": {
          "type": "string",
          "description": "npm package name or relative file path to the plugin module."
        },
        "options": {
          "type": "object",
          "description": "Plugin-specific options passed to the plugin at load time."
        },
        "enabled": {
          "type": "boolean",
          "description": "Whether the plugin is active.",
          "default": true
        },
        "order": {
          "type": "integer",
          "description": "Config G9 — load priority. Lower numbers load first. Omitted entries sort after any ordered plugin (ties break by module name)."
        },
        "capabilities": {
          "type": "array",
          "description": "Config G9 — capability tags this declaration advertises (storeAdapter, auditSink, builtin, cliCommand, projection, or host tags).",
          "items": { "type": "string", "minLength": 1 }
        }
      }
    },
    "EnvVarDefinition": {
      "type": "object",
      "description": "Definition of a single environment variable referenced by the application.",
      "additionalProperties": false,
      "required": ["name"],
      "properties": {
        "name": {
          "type": "string",
          "description": "Environment variable name (e.g. DATABASE_URL)."
        },
        "description": {
          "type": "string",
          "description": "Human-readable description of the variable's purpose."
        },
        "required": {
          "type": "boolean",
          "description": "Whether the variable must be set at startup. Defaults to true.",
          "default": true
        },
        "default": {
          "type": "string",
          "description": "Default value if the variable is not set."
        },
        "example": {
          "type": "string",
          "description": "Example value for .env.example generation."
        }
      }
    },
    "EnvironmentMapping": {
      "type": "object",
      "description": "Grouped environment variable definitions for preflight validation.",
      "additionalProperties": false,
      "properties": {
        "stores": {
          "type": "object",
          "additionalProperties": { "$ref": "#/definitions/EnvVarDefinition" }
        },
        "auth": {
          "type": "object",
          "additionalProperties": { "$ref": "#/definitions/EnvVarDefinition" }
        },
        "adapters": {
          "type": "object",
          "additionalProperties": { "$ref": "#/definitions/EnvVarDefinition" }
        },
        "custom": {
          "type": "object",
          "additionalProperties": { "$ref": "#/definitions/EnvVarDefinition" }
        }
      }
    },
    "NextJsProjectionConfig": {
      "type": "object",
      "description": "Configuration for the Next.js App Router projection.",
      "additionalProperties": false,
      "properties": {
        "output": {
          "type": "string",
          "description": "Directory where generated TypeScript files are written.",
          "default": "generated/"
        },
        "options": { "$ref": "#/definitions/NextJsProjectionOptions" }
      }
    },
    "NextJsProjectionOptions": {
      "type": "object",
      "description": "Surface-specific options for the Next.js projection. Every key here has a default exported from src/manifest/projections/nextjs/defaults.ts.",
      "additionalProperties": false,
      "properties": {
        "authProvider": {
          "type": "string",
          "enum": ["clerk", "nextauth", "custom", "none"],
          "description": "Auth provider that wraps generated route handlers.",
          "default": "none"
        },
        "authImportPath": {
          "type": "string",
          "description": "Module path for the auth helpers (auth(), getServerSession, etc.).",
          "default": "@/lib/auth"
        },
        "databaseImportPath": {
          "type": "string",
          "description": "Module path for the database client used by direct-read routes.",
          "default": "@/lib/database"
        },
        "responseImportPath": {
          "type": "string",
          "description": "Module path for manifestErrorResponse / manifestSuccessResponse / normalizeCommandResult helpers.",
          "default": "@/lib/manifest-response"
        },
        "runtimeImportPath": {
          "type": "string",
          "description": "Module path for createManifestRuntime. Only used when dispatcher.executionMode is 'inline'.",
          "default": "@/lib/manifest-runtime"
        },
        "includeTenantFilter": {
          "type": "boolean",
          "description": "When true, generated read routes filter by tenantIdProperty and POST handlers resolve a tenantId before executing.",
          "default": false
        },
        "includeSoftDeleteFilter": {
          "type": "boolean",
          "description": "When true, generated read routes filter by `${deletedAtProperty}: null`.",
          "default": false
        },
        "tenantIdProperty": {
          "type": "string",
          "description": "Name of the tenant scope property used in WHERE clauses and tenant context.",
          "default": "tenantId"
        },
        "deletedAtProperty": {
          "type": "string",
          "description": "Name of the soft-delete timestamp property.",
          "default": "deletedAt"
        },
        "appDir": {
          "type": "string",
          "description": "App Router base directory. Generated route pathHints are relative to this.",
          "default": "app/api"
        },
        "strictMode": {
          "type": "boolean",
          "description": "Whether generated TypeScript should be strict-mode friendly.",
          "default": true
        },
        "includeComments": {
          "type": "boolean",
          "description": "Whether to emit explanatory comments above generated handlers.",
          "default": true
        },
        "indentSize": {
          "type": "integer",
          "minimum": 1,
          "maximum": 8,
          "description": "Spaces of indentation in generated code.",
          "default": 2
        },
        "unauthorizedStatus": {
          "type": "integer",
          "minimum": 400,
          "maximum": 499,
          "description": "HTTP status returned when the auth helper rejects the request OR when it throws (invalid/expired token). Auth failures MUST NEVER surface as 500.",
          "default": 401
        },
        "generatedDir": {
          "type": "string",
          "description": "Base directory for generated non-route artifacts (types, client, hooks, shared-runtime). Individual paths can be overridden via the 'paths' object.",
          "default": "src"
        },
        "paths": {
          "type": "object",
          "additionalProperties": false,
          "description": "Fine-grained overrides for individual artifact pathHints. When set, takes precedence over generatedDir.",
          "properties": {
            "typesFile": {
              "type": "string",
              "description": "pathHint for ts.types artifact. Default: '${generatedDir}/types/manifest-generated.ts'"
            },
            "clientFile": {
              "type": "string",
              "description": "pathHint for ts.client artifact. Default: '${generatedDir}/lib/manifest-client.ts'"
            },
            "hooksDir": {
              "type": "string",
              "description": "Base directory for subscription hooks. Default: '${generatedDir}/hooks'"
            },
            "sharedRuntimeFile": {
              "type": "string",
              "description": "pathHint for nextjs.sharedRuntime artifact. Default: '${generatedDir}/lib/manifest-shared-runtime.ts'"
            }
          }
        },
        "tenantProvider": {
          "type": "object",
          "additionalProperties": false,
          "description": "Override the default userTenantMapping.findUnique pattern with a project-supplied tenant lookup helper.",
          "properties": {
            "importPath": { "type": "string", "description": "Module path for the lookup helper." },
            "functionName": {
              "type": "string",
              "description": "Named export to call (e.g. getTenantIdForOrg)."
            },
            "lookupKey": {
              "type": "string",
              "enum": ["orgId", "userId"],
              "description": "Which auth-context field to pass as the lookup key."
            }
          },
          "required": ["importPath", "functionName", "lookupKey"]
        },
        "dispatcher": { "$ref": "#/definitions/DispatcherOptions" },
        "concreteCommandRoutes": { "$ref": "#/definitions/ConcreteCommandRoutesOptions" },
        "readRoutes": { "$ref": "#/definitions/ReadRoutesOptions" },
        "realtime": { "$ref": "#/definitions/RealtimeOptions" },
        "naming": {
          "$ref": "#/definitions/NamingConvention",
          "description": "Naming convention for database accessor names (database.<accessor> in generated read routes). Applies table-name resolution to the entity name — use when the database client exposes physical table names (Kysely, raw SQL) instead of Prisma model delegates. Response field names stay camelCase (the API contract is independent of physical DB naming). Explicit accessorNames entries override it."
        },
        "accessorNames": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Explicit per-entity database accessor overrides, e.g. { \"OrderLine\": \"order_lines\" } → database.order_lines.findMany(...). Takes precedence over 'naming'."
        },
        "routeSegments": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Explicit per-entity URL path segment overrides for generated routes and client fetch paths, e.g. { \"OrderLine\": \"order-lines\" } → app/api/order-lines/list/route.ts. The value may contain '/' for a multi-segment domain path, e.g. { \"Event\": \"events/event\" } → app/api/events/event/list/route.ts (replaces hand-rolled domain-remap passes). Default: lowercased entity name."
        },
        "routeCasing": {
          "type": "string",
          "enum": ["lowercase", "kebab-case", "snake_case", "preserve"],
          "description": "Casing applied to the default URL route segment derived from each entity name (when no explicit routeSegments override is given). 'lowercase' (default) is the legacy flattened form (PrepTask → preptask); 'kebab-case' → prep-task, 'snake_case' → prep_task, 'preserve' keeps the entity name verbatim.",
          "default": "lowercase"
        },
        "dateSerialization": {
          "type": "string",
          "enum": ["date", "iso-string"],
          "description": "How date/datetime scalars are typed in the generated ts.types surface. 'date' (default) emits Date; 'iso-string' emits string, matching JSON/HTTP transport where dates serialize to ISO-8601 strings. Non-breaking.",
          "default": "date"
        },
        "emitCompanions": {
          "type": "boolean",
          "description": "Emit the companion modules generated code imports (runtime factory, response helpers, database client, auth/tenant shims) so 'manifest generate' output compiles without hand-authored glue. A companion is skipped when its configured import path is a package specifier rather than a local alias. Set to false to keep hand-writing them.",
          "default": true
        },
        "runtimeConfigImport": {
          "type": "string",
          "description": "Import specifier for the app's manifest.config (default export, ManifestRuntimeConfig shape), relative to the emitted runtime factory module. When set, the generated createManifestRuntime composes a storeProvider from config.stores. Leave unset for zero-config projects (the factory uses the engine's built-in stores). Only consulted when emitCompanions is true."
        },
        "apiBasePath": {
          "type": "string",
          "description": "URL prefix for the generated ts.client fetch paths and the derivation base for dispatcher command URLs. Default: derived from appDir (the Next.js routing roots src/app are stripped, so 'app/api' → '/api'). Deriving it keeps the client and the emitted routes on the same prefix; set explicitly only to target a different origin prefix. Shared with the route layout via the cross-projection route contract so client and routes cannot desync."
        },
        "dispatcherBasePath": {
          "type": "string",
          "description": "Dispatcher URL prefix for generated client command callers. Default: `${apiBasePath}/manifest` — matches where the nextjs.dispatcher surface is served. Override alongside dispatcher.path only if you relocate the dispatcher route."
        },
        "client": {
          "type": "object",
          "additionalProperties": false,
          "description": "Options for the generated TypeScript client SDK (ts.client).",
          "properties": {
            "fetchAdapter": {
              "type": "object",
              "additionalProperties": false,
              "description": "Import a host-provided fetch wrapper (auth/credentials) instead of the inline apiFetch. Same shape as the react-query projection's fetchAdapter; the named export is aliased to apiFetch so generated call sites are identical.",
              "properties": {
                "importPath": {
                  "type": "string",
                  "description": "Module to import the adapter from, e.g. '@/lib/api'."
                },
                "importName": {
                  "type": "string",
                  "description": "Exported name of the adapter (default: 'apiFetch'). Aliased to apiFetch."
                }
              },
              "required": ["importPath"]
            }
          }
        }
      }
    },
    "DispatcherOptions": {
      "type": "object",
      "additionalProperties": false,
      "description": "Configuration for the canonical write surface at POST /api/manifest/[entity]/commands/[command]. See docs/spec/config/manifest.config.md § Dispatcher.",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "When false, the nextjs.dispatcher surface emits no artifact.",
          "default": true
        },
        "executionMode": {
          "type": "string",
          "enum": ["inline", "externalExecutor"],
          "description": "Where command execution happens. 'inline' constructs createManifestRuntime per request (back-compat default). 'externalExecutor' imports the configured executor and delegates — the dispatcher does not inline runtime construction.",
          "default": "inline"
        },
        "executorImportPath": {
          "type": "string",
          "description": "Module path for the external executor. Only used in externalExecutor mode.",
          "default": "@/lib/manifest-executor"
        },
        "executorImportName": {
          "type": "string",
          "description": "Named export to call on the external executor module.",
          "default": "executeManifestCommand"
        },
        "deriveInstanceId": {
          "type": "boolean",
          "description": "When true (default), the dispatcher extracts an instanceId from body.instanceId or body.id and forwards it to runCommand / the executor. Non-create commands need this to address the target instance; create commands ignore it harmlessly.",
          "default": true
        },
        "path": {
          "type": "string",
          "description": "Dispatcher route path relative to appDir. Override only if your app uses a non-canonical prefix (e.g. /api/v1/manifest/...).",
          "default": "/manifest/[entity]/commands/[command]/route.ts"
        }
      }
    },
    "ConcreteCommandRoutesOptions": {
      "type": "object",
      "additionalProperties": false,
      "description": "Policy for the deprecated per-command 'concrete' routes (the nextjs.command surface). Opt-in: the canonical dispatcher is the single write surface by default.",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "When false (default), nextjs.command artifacts are suppressed entirely and 'manifest generate --surface all' does not emit them. Opt in only if you still need per-command routes for legacy callers.",
          "default": false
        },
        "legacyAliasesOnly": {
          "type": "boolean",
          "description": "When true (default), emitted concrete routes carry the DEPRECATED ALIAS banner pointing at the dispatcher. Set false only if you intentionally treat per-command routes as a first-class surface.",
          "default": true
        }
      }
    },
    "ReadRoutesOptions": {
      "type": "object",
      "additionalProperties": false,
      "description": "Policy for direct database read routes (GET list, GET detail). Direct reads bypass the runtime engine and assume a Prisma-compatible client at databaseImportPath.",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "When false, nextjs.route and nextjs.detail surfaces emit no artifact (info diagnostic only).",
          "default": true
        },
        "directDbReads": {
          "type": "boolean",
          "description": "When false, read route stubs are emitted without inlining a Prisma call — useful for projects that route reads through a separate query layer.",
          "default": true
        }
      }
    },
    "RealtimeOptions": {
      "type": "object",
      "additionalProperties": false,
      "description": "Realtime (SSE) delivery options for entities flagged `realtime`. The generated shared-runtime singleton streams engine events over SSE; configuring an `eventBus` in the runtime factory's RuntimeOptions lets it fan events across instances (the accessor calls connectEventBus() at init when a bus is present).",
      "properties": {
        "requireEventBus": {
          "type": "boolean",
          "description": "When true, the generated shared-runtime singleton THROWS at init if no eventBus is configured on the runtime — fail-loud for deployments that advertise multi-instance realtime. When false (default), a one-time console.warn is emitted and delivery degrades to single-instance.",
          "default": false
        }
      }
    },
    "RoutesProjectionConfig": {
      "type": "object",
      "additionalProperties": false,
      "description": "Configuration for the Canonical Routes projection (routes.manifest, routes.ts).",
      "properties": {
        "output": {
          "type": "string",
          "description": "Directory where the route manifest and typed path builders are written.",
          "default": "generated/"
        },
        "options": { "$ref": "#/definitions/RoutesProjectionOptions" }
      }
    },
    "RoutesProjectionOptions": {
      "type": "object",
      "additionalProperties": false,
      "description": "Options for the Canonical Routes projection. Defaults mirror src/manifest/projections/nextjs/defaults.ts → ROUTES_DEFAULTS.",
      "properties": {
        "basePath": {
          "type": "string",
          "description": "Base path prefix prepended to every route.",
          "default": "/api"
        },
        "includeAuth": {
          "type": "boolean",
          "description": "Whether route entries carry auth=true expectations.",
          "default": true
        },
        "includeTenant": {
          "type": "boolean",
          "description": "Whether route entries carry tenant=true expectations.",
          "default": true
        },
        "manualRoutes": {
          "type": "array",
          "description": "Hand-declared routes merged into the canonical route surface.",
          "items": { "$ref": "#/definitions/ManualRouteDeclaration" }
        }
      }
    },
    "ManualRouteDeclaration": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "path", "method"],
      "properties": {
        "id": { "type": "string" },
        "path": { "type": "string" },
        "method": { "type": "string", "enum": ["GET", "POST", "PUT", "PATCH", "DELETE"] },
        "auth": { "type": "boolean", "default": false },
        "tenant": { "type": "boolean", "default": false },
        "params": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["name", "type", "location"],
            "properties": {
              "name": { "type": "string" },
              "type": { "type": "string" },
              "location": { "type": "string", "enum": ["path", "query", "body"] },
              "required": { "type": "boolean" }
            }
          }
        }
      }
    },
    "PrismaProjectionConfig": {
      "type": "object",
      "description": "Configuration for the Prisma schema projection.",
      "additionalProperties": false,
      "properties": {
        "output": {
          "type": "string",
          "description": "Path hint for the emitted schema.prisma artifact.",
          "default": "schema.prisma"
        },
        "options": { "$ref": "#/definitions/PrismaProjectionOptions" }
      }
    },
    "PrismaStoreProjectionConfig": {
      "type": "object",
      "description": "Configuration for the Prisma store metadata/registry projection (GenericPrismaStore).",
      "additionalProperties": false,
      "properties": {
        "output": {
          "type": "string",
          "description": "Directory where the store metadata/registry artifacts are written. Required for `manifest generate --all` to run this projection (projections with no top-level output are skipped)."
        },
        "options": { "$ref": "#/definitions/PrismaStoreProjectionOptions" }
      }
    },
    "PrismaStoreProjectionOptions": {
      "type": "object",
      "description": "Options for prisma-store.metadata and prisma-store.registry surfaces. Mirrors the TypeScript PrismaStoreProjectionOptions = PrismaProjectionOptions & { ...own }. NOTE: draft-07 cannot express closed-set inheritance via allOf — each branch's additionalProperties is blind to the other branch's properties, so a composed schema falsely rejects inherited options (provider, naming, ...). The inherited PrismaProjectionOptions properties are therefore inlined here. Keep in sync with PrismaProjectionOptions.",
      "additionalProperties": false,
      "properties": {
        "provider": {
          "description": "Optional Prisma datasource provider. When set, the projection emits a datasource block and a prisma.config.ts companion.",
          "enum": ["postgresql", "mysql", "sqlite", "sqlserver", "mongodb", "cockroachdb"]
        },
        "output": {
          "type": "string",
          "description": "Path hint for the emitted schema.prisma artifact. Default: 'schema.prisma'."
        },
        "urlEnvVar": {
          "type": "string",
          "description": "Environment variable name for the database URL in the emitted prisma.config.ts companion. Default: 'DATABASE_URL'. Only relevant when provider is set."
        },
        "tableMappings": {
          "description": "Per-entity table-name override. Emits @@map(\"...\").",
          "type": "object",
          "additionalProperties": { "type": "string" }
        },
        "columnMappings": {
          "description": "Per-entity, per-property column-name override. Emits @map(\"...\").",
          "type": "object",
          "additionalProperties": { "type": "object", "additionalProperties": { "type": "string" } }
        },
        "precision": {
          "description": "Per-entity, per-property decimal precision and scale.",
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "required": ["precision", "scale"],
              "additionalProperties": false,
              "properties": {
                "precision": { "type": "integer", "minimum": 1 },
                "scale": { "type": "integer", "minimum": 0 }
              }
            }
          }
        },
        "indexes": {
          "description": "Per-entity composite/named index definitions. Each entry is an array of column names or an object with fields and optional name.",
          "type": "object",
          "additionalProperties": {
            "type": "array",
            "items": {
              "oneOf": [
                { "type": "array", "items": { "type": "string" }, "minItems": 1 },
                {
                  "type": "object",
                  "required": ["fields"],
                  "additionalProperties": false,
                  "properties": {
                    "fields": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
                    "name": { "type": "string" }
                  }
                }
              ]
            }
          }
        },
        "typeMappings": {
          "description": "Per-entity, per-property Prisma scalar override. Value is a literal Prisma scalar (e.g. 'Int', 'Decimal').",
          "type": "object",
          "additionalProperties": { "type": "object", "additionalProperties": { "type": "string" } }
        },
        "foreignKeys": {
          "description": "Per-entity, per-relationship FK field name or structured config. Value is a plain string or an object with fields/references/onDelete/onUpdate.",
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": {
              "oneOf": [
                { "type": "string" },
                {
                  "type": "object",
                  "required": ["fields"],
                  "additionalProperties": false,
                  "properties": {
                    "fields": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
                    "references": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
                    "onDelete": { "type": "string" },
                    "onUpdate": { "type": "string" }
                  }
                }
              ]
            }
          }
        },
        "dbAttributes": {
          "description": "Per-entity, per-property native @db.* attribute string (without the @db. prefix). Applied after the type scalar.",
          "type": "object",
          "additionalProperties": { "type": "object", "additionalProperties": { "type": "string" } }
        },
        "fieldAttributes": {
          "description": "Per-entity, per-property array of verbatim Prisma field attributes (e.g. '@unique', '@updatedAt').",
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": { "type": "array", "items": { "type": "string" } }
          }
        },
        "naming": {
          "$ref": "#/definitions/NamingConvention",
          "description": "Automatic identifier-casing convention. Opt-in; only ever adds @map/@@map (model/field identifiers stay the IR name). Explicit tableMappings/columnMappings override it."
        },
        "relationMode": {
          "description": "Datasource relationMode. Emitted as relationMode = \"...\" on the datasource block. Use \"prisma\" when relations are enforced in the client (PlanetScale, Neon pooled, etc.) rather than via DB foreign keys.",
          "enum": ["prisma", "foreignKeys"]
        },
        "generator": {
          "description": "Fields for the emitted `generator client { ... }` block, each rendered verbatim as key = \"value\". Defaults to { provider: \"prisma-client-js\" }. Override to select the newer prisma-client generator and set output/moduleFormat/generatedFileExtension/importFileExtension.",
          "type": "object",
          "additionalProperties": { "type": "string" }
        },
        "multiSchema": {
          "description": "Preserve the model's module layout as database schemas instead of flattening into one schema. Emits @@schema(...) per model and schemas=[...] on the datasource. PostgreSQL / CockroachDB / SQL Server only.",
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": {
              "description": "Master switch. Default false (flat layout, fully back-compatible).",
              "type": "boolean",
              "default": false
            },
            "schemas": {
              "description": "Explicit datasource schema list. Order preserved; any schema used by a model but missing here is appended (sorted) so the datasource lists every referenced schema.",
              "type": "array",
              "items": { "type": "string" }
            },
            "entitySchema": {
              "description": "Per-entity schema override. Key = IR entity name; value = schema name. Takes precedence over the entity's IR module.",
              "type": "object",
              "additionalProperties": { "type": "string" }
            },
            "defaultSchema": {
              "description": "Schema for entities with neither an entitySchema override nor an IR module. Default 'public'.",
              "type": "string",
              "default": "public"
            }
          }
        },
        "autoBackRelations": {
          "description": "Auto-emit the inverse relation field on a target model for any belongsTo/ref that lacks an explicit opposite. Default false.",
          "type": "boolean",
          "default": false
        },
        "accessorNames": {
          "description": "Per-entity Prisma client delegate override (e.g. OrderLine -> order_lines).",
          "type": "object",
          "additionalProperties": { "type": "string" }
        },
        "metadataOutput": {
          "type": "string",
          "description": "Path hint for emitted prisma-model-metadata.generated.ts.",
          "default": "prisma-model-metadata.generated.ts"
        },
        "registryOutput": {
          "type": "string",
          "description": "Path hint for emitted prisma-store-registry.generated.ts.",
          "default": "prisma-store-registry.generated.ts"
        },
        "storeImportPath": {
          "type": "string",
          "description": "Import path for GenericPrismaStore in the registry artifact."
        },
        "metadataImportPath": {
          "type": "string",
          "description": "Import path for the metadata module in the registry artifact."
        },
        "softDelete": {
          "description": "Per-entity status-based soft-delete. Keyed by entity name; `field` is the IR status property name and `deletedValue` the sentinel meaning deleted. Lets an entity that soft-deletes via a status transition (instead of a deletedAt timestamp) use the generic store.",
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "required": ["field", "deletedValue"],
            "properties": {
              "field": { "type": "string" },
              "deletedValue": { "type": "string" }
            }
          }
        }
      }
    },
    "PrismaProjectionOptions": {
      "type": "object",
      "description": "Options for the Prisma schema projection. All keys are optional; omitting a key applies the documented default.",
      "additionalProperties": false,
      "properties": {
        "provider": {
          "description": "Optional Prisma datasource provider. When set, the projection emits a datasource block and a prisma.config.ts companion.",
          "enum": ["postgresql", "mysql", "sqlite", "sqlserver", "mongodb", "cockroachdb"]
        },
        "output": {
          "type": "string",
          "description": "Path hint for the emitted schema.prisma artifact. Default: 'schema.prisma'."
        },
        "urlEnvVar": {
          "type": "string",
          "description": "Environment variable name for the database URL in the emitted prisma.config.ts companion. Default: 'DATABASE_URL'. Only relevant when provider is set."
        },
        "tableMappings": {
          "description": "Per-entity table-name override. Emits @@map(\"...\").",
          "type": "object",
          "additionalProperties": { "type": "string" }
        },
        "columnMappings": {
          "description": "Per-entity, per-property column-name override. Emits @map(\"...\").",
          "type": "object",
          "additionalProperties": { "type": "object", "additionalProperties": { "type": "string" } }
        },
        "precision": {
          "description": "Per-entity, per-property decimal precision and scale.",
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "required": ["precision", "scale"],
              "additionalProperties": false,
              "properties": {
                "precision": { "type": "integer", "minimum": 1 },
                "scale": { "type": "integer", "minimum": 0 }
              }
            }
          }
        },
        "indexes": {
          "description": "Per-entity composite/named index definitions. Each entry is an array of column names or an object with fields and optional name.",
          "type": "object",
          "additionalProperties": {
            "type": "array",
            "items": {
              "oneOf": [
                { "type": "array", "items": { "type": "string" }, "minItems": 1 },
                {
                  "type": "object",
                  "required": ["fields"],
                  "additionalProperties": false,
                  "properties": {
                    "fields": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
                    "name": { "type": "string" }
                  }
                }
              ]
            }
          }
        },
        "typeMappings": {
          "description": "Per-entity, per-property Prisma scalar override. Value is a literal Prisma scalar (e.g. 'Int', 'Decimal').",
          "type": "object",
          "additionalProperties": { "type": "object", "additionalProperties": { "type": "string" } }
        },
        "foreignKeys": {
          "description": "Per-entity, per-relationship FK field name or structured config. Value is a plain string or an object with fields/references/onDelete/onUpdate.",
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": {
              "oneOf": [
                { "type": "string" },
                {
                  "type": "object",
                  "required": ["fields"],
                  "additionalProperties": false,
                  "properties": {
                    "fields": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
                    "references": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
                    "onDelete": { "type": "string" },
                    "onUpdate": { "type": "string" }
                  }
                }
              ]
            }
          }
        },
        "dbAttributes": {
          "description": "Per-entity, per-property native @db.* attribute string (without the @db. prefix). Applied after the type scalar.",
          "type": "object",
          "additionalProperties": { "type": "object", "additionalProperties": { "type": "string" } }
        },
        "fieldAttributes": {
          "description": "Per-entity, per-property array of verbatim Prisma field attributes (e.g. '@unique', '@updatedAt').",
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": { "type": "array", "items": { "type": "string" } }
          }
        },
        "naming": {
          "$ref": "#/definitions/NamingConvention",
          "description": "Automatic identifier-casing convention. Opt-in; only ever adds @map/@@map (model/field identifiers stay the IR name). Explicit tableMappings/columnMappings override it."
        },
        "relationMode": {
          "description": "Datasource relationMode. Emitted as relationMode = \"...\" on the datasource block. Use \"prisma\" when relations are enforced in the client (PlanetScale, Neon pooled, etc.) rather than via DB foreign keys.",
          "enum": ["prisma", "foreignKeys"]
        },
        "generator": {
          "description": "Fields for the emitted `generator client { ... }` block, each rendered verbatim as key = \"value\". Defaults to { provider: \"prisma-client-js\" }. Override to select the newer prisma-client generator and set output/moduleFormat/generatedFileExtension/importFileExtension.",
          "type": "object",
          "additionalProperties": { "type": "string" }
        },
        "multiSchema": {
          "description": "Preserve the model's module layout as database schemas instead of flattening into one schema. Emits @@schema(...) per model and schemas=[...] on the datasource. PostgreSQL / CockroachDB / SQL Server only.",
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": {
              "description": "Master switch. Default false (flat layout, fully back-compatible).",
              "type": "boolean",
              "default": false
            },
            "schemas": {
              "description": "Explicit datasource schema list. Order preserved; any schema used by a model but missing here is appended (sorted) so the datasource lists every referenced schema.",
              "type": "array",
              "items": { "type": "string" }
            },
            "entitySchema": {
              "description": "Per-entity schema override. Key = IR entity name; value = schema name. Takes precedence over the entity's IR module.",
              "type": "object",
              "additionalProperties": { "type": "string" }
            },
            "defaultSchema": {
              "description": "Schema for entities with neither an entitySchema override nor an IR module. Default 'public'.",
              "type": "string",
              "default": "public"
            }
          }
        },
        "autoBackRelations": {
          "description": "Auto-emit the inverse relation field on a target model for any belongsTo/ref that lacks an explicit opposite. Default false.",
          "type": "boolean",
          "default": false
        }
      }
    },
    "NamingConvention": {
      "description": "Identifier-casing convention. String shorthand 'snake_case' expands to { table: 'snake_case', column: 'snake_case', pluralizeTables: true }.",
      "oneOf": [
        { "type": "string", "enum": ["snake_case"] },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "table": {
              "description": "Case style for table/model physical names. Default 'preserve'.",
              "enum": ["snake_case", "camelCase", "PascalCase", "preserve"]
            },
            "column": {
              "description": "Case style for column/field physical names. Default 'preserve'.",
              "enum": ["snake_case", "camelCase", "preserve"]
            },
            "pluralizeTables": {
              "description": "Pluralize resolved table names. Default true.",
              "type": "boolean"
            }
          }
        }
      ]
    },
    "NamingRuleSeverity": {
      "type": "string",
      "enum": ["off", "warn", "error", "fix"],
      "description": "off=ignore; warn=keep source name + warn; error=fail compile; fix=normalize generated output (never rewrites source files)."
    },
    "NamingCasing": {
      "type": "string",
      "enum": ["preserve", "camel", "pascal", "snake", "kebab", "upper", "lower"]
    },
    "NamingCategoryRule": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "casing": { "$ref": "#/definitions/NamingCasing" },
        "mismatch": { "$ref": "#/definitions/NamingRuleSeverity" }
      }
    },
    "NamingNormalizationConfig": {
      "type": "object",
      "additionalProperties": false,
      "description": "Expanded naming policy. normalization defaults to false (backward compatible).",
      "properties": {
        "normalization": {
          "type": "boolean",
          "default": false,
          "description": "Master switch. false preserves current verbatim IR spelling."
        },
        "convention": {
          "$ref": "#/definitions/NamingConvention",
          "description": "Physical projection convention (former top-level naming value)."
        },
        "entities": { "$ref": "#/definitions/NamingCategoryRule" },
        "fields": { "$ref": "#/definitions/NamingCategoryRule" },
        "relationships": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "casing": { "$ref": "#/definitions/NamingCasing" },
            "mismatch": { "$ref": "#/definitions/NamingRuleSeverity" },
            "idSuffix": { "type": "string", "default": "Id" }
          }
        },
        "commands": { "$ref": "#/definitions/NamingCategoryRule" },
        "events": { "$ref": "#/definitions/NamingCategoryRule" },
        "collections": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "casing": { "$ref": "#/definitions/NamingCasing" },
            "mismatch": { "$ref": "#/definitions/NamingRuleSeverity" },
            "pluralization": { "type": "string", "enum": ["preserve", "automatic", "explicit"] }
          }
        },
        "tables": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "casing": { "$ref": "#/definitions/NamingCasing" },
            "mismatch": { "$ref": "#/definitions/NamingRuleSeverity" },
            "pluralization": { "type": "string", "enum": ["preserve", "automatic", "explicit"] }
          }
        },
        "separators": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "normalize": {
              "type": "array",
              "items": { "type": "string", "enum": ["underscore", "hyphen", "whitespace"] }
            }
          }
        },
        "collisions": { "type": "string", "enum": ["off", "warn", "error"] },
        "conflictingDefinitions": { "type": "string", "enum": ["off", "warn", "error"] },
        "ambiguousWordBoundaries": { "$ref": "#/definitions/NamingRuleSeverity" },
        "storageNameChange": { "type": "string", "enum": ["off", "warn", "error"] },
        "irregularPlurals": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Canonical singular → plural form for generated collection/table names only."
        },
        "aliases": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Semantic aliases (writer → author). Not guessed automatically."
        },
        "projections": {
          "type": "object",
          "additionalProperties": false,
          "description": "Projection-only legacy storage remaps; Manifest canonical names unchanged.",
          "properties": {
            "convex": { "$ref": "#/definitions/NamingProjectionStorageMappings" },
            "prisma": { "$ref": "#/definitions/NamingProjectionStorageMappings" }
          }
        }
      }
    },
    "NamingProjectionStorageMappings": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "tables": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Canonical entity name → deployed table name."
        },
        "fields": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Entity.field or Entity.relationship → deployed column name."
        }
      }
    }
  }
}
