{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://github.com/Entelligentsia/agentic-skills/forge/config.schema.json",
  "title": "ForgeConfig",
  "description": "Schema for .forge/config.json — the per-project Forge configuration file. Canonical single-source schema (FORGE-S25-T12 reconciled sdlc-config.schema.json into this file and deleted the sibling). Generated by /forge:init and refreshed by /forge:update.",
  "type": "object",
  "required": ["version", "project", "paths"],
  "properties": {
    "version": {
      "type": "string",
      "description": "Config schema version (e.g. '1.0'). Used for migration support."
    },
    "project": {
      "type": "object",
      "required": ["prefix", "name"],
      "properties": {
        "prefix": {
          "type": "string",
          "description": "Short prefix for task/sprint/bug IDs (e.g. ACME)"
        },
        "name": {
          "type": "string",
          "description": "Human-readable project name"
        },
        "description": {
          "type": "string",
          "description": "One-line project description"
        }
      }
    },
    "stack": {
      "type": "object",
      "description": "Detected/declared project technology stack. Optional fields nullable because not every project ships e.g. a frontend or task queue.",
      "properties": {
        "languages": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Primary languages detected"
        },
        "framework":         { "type": ["string", "null"], "description": "Primary backend framework" },
        "frontendFramework": { "type": ["string", "null"], "description": "Frontend framework if detected" },
        "database":          { "type": ["string", "null"], "description": "Primary database" },
        "taskQueue":         { "type": ["string", "null"], "description": "Task queue if detected (celery, sidekiq, etc.)" },
        "containerisation":  { "type": ["string", "null"], "description": "Container tooling if detected" }
      }
    },
    "commands": {
      "type": "object",
      "description": "Resolved project tooling commands. Workflows reference these via {COMMAND_TEST} / {LINT_COMMAND} placeholders.",
      "properties": {
        "test":          { "type": "string" },
        "build":         { "type": "string" },
        "lint":          { "type": "string" },
        "syntaxCheck": {
          "type": "object",
          "additionalProperties": { "type": "string" }
        },
        "migrate":       { "type": "string" },
        "migrateCheck":  { "type": "string" }
      }
    },
    "paths": {
      "type": "object",
      "required": ["engineering", "store"],
      "properties": {
        "engineering":     { "type": "string", "default": "engineering" },
        "store":           { "type": "string", "default": ".forge/store" },
        "workflows":       { "type": "string", "default": ".forge/workflows" },
        "commands":        { "type": "string", "default": ".claude/commands" },
        "templates":       { "type": "string", "default": ".forge/templates" },
        "customCommands":  { "type": "string", "default": "engineering/commands", "description": "Directory for project-specific custom pipeline phase commands. Files here are workflow scripts invoked directly by the orchestrator via the phase workflow field." },
        "forgeRef":        { "type": "string", "description": "Pinned Forge plugin reference (tag or commit) the project was generated against. Used to resolve the plugin root via cache lookup." }
      }
    },
    "pipeline": {
      "type": "object",
      "description": "Optional pipeline customisation for the default pipeline",
      "properties": {
        "skipPhases": {
          "type": "array",
          "items": { "type": "string" }
        },
        "maxReviewIterations": {
          "type": "integer",
          "minimum": 1,
          "default": 3
        },
        "includeTokenDataInBugReports": {
          "type": "boolean",
          "description": "If set, silently opt in (true) or out (false) of appending sprint token usage data to Forge bug reports. When absent, the user is prompted at report time."
        }
      }
    },
    "pipelines": {
      "type": "object",
      "description": "Named pipeline definitions. Each key is a pipeline name that tasks can reference via their `pipeline` field. The `default` pipeline is used when a task has no `pipeline` field. Projects define custom pipelines to route tasks to specialized agent commands.",
      "additionalProperties": {
        "type": "object",
        "required": ["phases"],
        "properties": {
          "description": {
            "type": "string",
            "description": "Human-readable description of when to use this pipeline"
          },
          "phases": {
            "type": "array",
            "description": "Ordered list of pipeline phases. Each phase maps to a slash command.",
            "items": {
              "type": "object",
              "required": ["command", "role"],
              "properties": {
                "command": {
                  "type": "string",
                  "description": "Slash command name (without /) to invoke for this phase"
                },
                "role": {
                  "type": "string",
                  "enum": ["plan", "review-plan", "implement", "review-code", "validate", "approve", "commit"],
                  "description": "Semantic role of this phase in the pipeline lifecycle"
                },
                "model": {
                  "type": "string",
                  "description": "Optional model override for this phase"
                },
                "maxIterations": {
                  "type": "integer",
                  "description": "Max revision loop iterations for review phases",
                  "default": 3
                },
                "on_revision": {
                  "type": "string",
                  "description": "Command name of the phase to re-invoke on 'Revision Required'. Defaults to the nearest preceding non-review phase if omitted."
                },
                "workflow": {
                  "type": "string",
                  "description": "Optional path to a workflow file. When set, the orchestrator reads and follows this file directly instead of deriving the workflow from the command name. Use for custom phase commands in engineering/commands/."
                }
              }
            }
          }
        }
      }
    },
    "installedSkills": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Marketplace skill names installed during forge:init or recommended by forge:health. Used to wire skill invocations into generated personas and workflows.",
      "examples": ["vue-best-practices", "stripe-integration", "claude-api"]
    },
    "sprint": {
      "type": "object",
      "description": "Optional sprint execution configuration",
      "properties": {
        "execution": {
          "type": "object",
          "properties": {
            "mode": {
              "type": "string",
              "enum": ["sequential", "wave-parallel", "full-parallel"],
              "default": "sequential"
            },
            "maxConcurrentAgents": {
              "type": "integer",
              "default": 3
            }
          }
        }
      }
    },
    "calibrationBaseline": {
      "type": "object",
      "description": "Calibration baseline written by /forge:init after skill generation. Records the state at calibration time so /forge:calibrate can detect drift.",
      "properties": {
        "lastCalibrated":   { "type": "string", "description": "ISO date (YYYY-MM-DD) of the last calibration run" },
        "version":          { "type": "string", "description": "Plugin version string at calibration time (e.g. 0.9.3)" },
        "masterIndexHash":  { "type": "string", "description": "SHA-256 hex digest of the semantic content of engineering/MASTER_INDEX.md at calibration time" },
        "sprintsCovered":   { "type": "array", "items": { "type": "string" }, "description": "Sprint IDs whose tasks and outcomes were included in the calibration baseline" }
      }
    },
    "calibrationHistory": {
      "type": "array",
      "description": "Audit trail of calibration runs. Each entry records the drift categories detected and the patches applied.",
      "items": {
        "type": "object",
        "required": ["date", "version", "categories", "patches"],
        "additionalProperties": false,
        "properties": {
          "date":       { "type": "string", "description": "ISO date (YYYY-MM-DD) of this calibration run" },
          "version":    { "type": "string", "description": "Plugin version at calibration time" },
          "categories": { "type": "array", "items": { "type": "string" }, "description": "Drift categories detected: technical, business, retrospective, acceptance-criteria" },
          "patches": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["target", "type", "applied"],
              "additionalProperties": false,
              "properties": {
                "target":  { "type": "string", "description": "Regeneration target (e.g. personas:engineer, skills:supervisor-skills)" },
                "type":    { "type": "string", "enum": ["regenerate"], "description": "Patch type (currently only 'regenerate' is supported)" },
                "applied": { "type": "boolean", "description": "Whether this patch was applied (true if approved and succeeded, false if skipped or failed)" }
              }
            }
          }
        }
      }
    }
  },
  "additionalProperties": false
}
