{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://cleo-dev.com/schemas/v1/project-context.schema.json",
  "schemaVersion": "1.0.0",
  "title": "CLEO Project Context Schema",
  "description": "LLM agent metadata structure for project discovery. Generated by cleo init --detect, consumed via @ reference injection. Distinct from config.json (executable config) - this provides discovery metadata and LLM guidance.",
  "type": "object",
  "required": ["schemaVersion", "detectedAt", "projectTypes"],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "JSON Schema reference URI"
    },
    "schemaVersion": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$",
      "description": "Project context schema version (semver)"
    },
    "detectedAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp of detection"
    },
    "projectTypes": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["node", "python", "rust", "go", "ruby", "elixir", "java", "php", "deno", "bun", "dotnet", "bash", "unknown"]
      },
      "minItems": 1,
      "description": "All detected project types"
    },
    "primaryType": {
      "type": "string",
      "enum": ["node", "python", "rust", "go", "ruby", "elixir", "java", "php", "deno", "bun", "dotnet", "bash", "unknown"],
      "description": "Primary project type"
    },
    "monorepo": {
      "type": "boolean",
      "default": false,
      "description": "Whether project is a monorepo"
    },
    "testing": {
      "type": "object",
      "description": "Testing configuration summary.",
      "additionalProperties": false,
      "properties": {
        "framework": {
          "type": "string",
          "enum": ["bats", "jest", "vitest", "playwright", "cypress", "mocha", "ava", "uvu", "tap", "node:test", "deno", "bun", "pytest", "go", "cargo", "rspec", "junit", "custom"],
          "description": "Detected testing framework"
        },
        "command": {
          "type": "string",
          "description": "Test execution command"
        },
        "affectedCommand": {
          "type": "string",
          "minLength": 1,
          "description": "Affected-scope test command template for tool:test-affected (D11150) and a scope-aware tool:test (T12959). {projects}, {filters}, {workspaces} and {packages} expand per affected package, e.g. \"pnpm exec vitest run {projects}\". When absent, one is derived from a workspace-wide command where that is mechanical (T13125)."
        },
        "preferAffected": {
          "type": "boolean",
          "description": "false makes tool:test always run the full suite instead of the affected scope (T12959)."
        },
        "testFilePatterns": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Glob patterns for test files"
        },
        "directories": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "unit": {
              "type": "string"
            },
            "integration": {
              "type": "string"
            }
          }
        }
      }
    },
    "build": {
      "type": "object",
      "description": "Build configuration summary.",
      "additionalProperties": false,
      "properties": {
        "command": {
          "type": "string",
          "description": "Build command"
        },
        "outputDir": {
          "type": "string",
          "description": "Build output directory"
        }
      }
    },
    "directories": {
      "type": "object",
      "description": "Directory structure summary.",
      "additionalProperties": false,
      "properties": {
        "source": {
          "type": "string",
          "description": "Source code directory"
        },
        "tests": {
          "type": "string",
          "description": "Test directory"
        },
        "docs": {
          "type": "string",
          "description": "Documentation directory"
        }
      }
    },
    "conventions": {
      "type": "object",
      "description": "Coding conventions.",
      "additionalProperties": false,
      "properties": {
        "fileNaming": {
          "type": "string",
          "enum": ["kebab-case", "snake_case", "camelCase", "PascalCase"],
          "description": "File naming convention"
        },
        "importStyle": {
          "type": "string",
          "enum": ["esm", "commonjs", "mixed"],
          "description": "Module import style"
        },
        "typeSystem": {
          "type": "string",
          "description": "Type system (typescript, python-hints, rust, etc.)"
        }
      }
    },
    "llmHints": {
      "type": "object",
      "description": "LLM agent guidance for code generation and understanding.",
      "additionalProperties": false,
      "properties": {
        "preferredTestStyle": {
          "type": "string",
          "description": "Framework-specific test style guidance"
        },
        "typeSystem": {
          "type": "string",
          "description": "Type system details (e.g., 'TypeScript strict mode')"
        },
        "commonPatterns": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Framework-specific best practices"
        },
        "avoidPatterns": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Deprecated APIs, incompatible syntax, style violations to avoid"
        }
      }
    },
    "lint": {
      "type": "object",
      "description": "Lint command override (T12026).",
      "additionalProperties": false,
      "properties": {
        "command": {
          "type": "string",
          "description": "Command to run static analysis / lint checks"
        }
      }
    },
    "typecheck": {
      "type": "object",
      "description": "Type-check command override (T12026).",
      "additionalProperties": false,
      "properties": {
        "command": {
          "type": "string",
          "description": "Command to run type checking"
        }
      }
    },
    "audit": {
      "type": "object",
      "description": "Audit command override (T12026).",
      "additionalProperties": false,
      "properties": {
        "command": {
          "type": "string",
          "description": "Command to run dependency/module audit checks"
        }
      }
    },
    "security-scan": {
      "type": "object",
      "description": "Security-scan command override (T12026).",
      "additionalProperties": false,
      "properties": {
        "command": {
          "type": "string",
          "description": "Command to run security vulnerability scanning"
        }
      }
    },
    "release": {
      "type": "object",
      "description": "Release and evidence behavior configured per project.",
      "additionalProperties": false,
      "properties": {
        "integrationBranches": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "uniqueItems": true,
          "description": "Branches whose reachable commits may satisfy commit evidence atoms."
        },
        "prRequiredWorkflows": {
          "type": "array",
          "items": {
            "oneOf": [
              { "type": "string", "minLength": 1 },
              {
                "type": "object",
                "additionalProperties": false,
                "required": ["name"],
                "properties": {
                  "name": { "type": "string", "minLength": 1 },
                  "app": {
                    "oneOf": [
                      { "type": "string", "minLength": 1 },
                      { "type": "integer", "minimum": 1 }
                    ],
                    "description": "GitHub App slug or id that must post this check."
                  },
                  "workflow": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Workflow file path that must produce this check."
                  }
                }
              }
            ]
          },
          "uniqueItems": true,
          "description": "Required successful checks for PR and CI evidence atoms. An object entry pins the check to the GitHub App (and workflow file) that must post it (T12634)."
        }
      }
    },
    "evidence": {
      "type": "object",
      "description": "Evidence execution overrides (T12308).",
      "additionalProperties": false,
      "properties": {
        "gitRoot": {
          "type": "string",
          "minLength": 1,
          "description": "Checkout that git/gh evidence tools run in, relative to the CLEO store root (absolute is also accepted). Needed only when the store root is not itself a git work tree and parents more than one repository."
        },
        "ciSatisfies": {
          "type": "boolean",
          "description": "Accept required CI green on a merged PR's merge commit as testsPassed/qaPassed evidence (ci:<pr>, D11149)."
        },
        "scopeExcludes": {
          "type": "array",
          "items": { "type": "string", "minLength": 1 },
          "description": "Repo-relative paths or globs that never count as part of a change when evidence decides its scope (runtime state, e.g. .opencode/goals/**). CLEO's own installed hook files are always excluded (T13135)."
        },
        "typecheckOnlyDependents": {
          "type": "array",
          "items": { "type": "string", "minLength": 1 },
          "description": "Repo-relative globs of files whose change affects dependents only at the type level: a generated client (lib/api-zod/**), seed or data files (T13783). A package that depends on a change only through changed packages whose every changed path matches owes a typecheck (tool:typecheck or a qa-run typecheck receipt naming it) instead of a test file. Read from the default branch only."
        },
        "runtimePaths": {
          "type": "array",
          "items": { "type": "string", "minLength": 1 },
          "description": "Globs of tracked files a harness rewrites during a run (e.g. .opencode/goals/*/run.json), left out of the evidence tree hash and kept tracked (T13554). Read from the default branch only; a glob matching the project context, a goal.yaml, an oracle or a typed gate input is refused."
        },
        "ciChecks": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "tests": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 },
            "qa": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 },
            "jobs": {
              "type": "object",
              "additionalProperties": false,
              "description": "Job-name globs (* wildcard) that must each have run and succeeded in the pinned workflows before ci:<pr> attests a code task's gate.",
              "properties": {
                "tests": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 },
                "qa": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 }
              }
            },
            "covering": {
              "type": "object",
              "additionalProperties": false,
              "description": "T13175: when every required job of a gate was skipped by its change filter, jobs covering each changed path (e.g. scripts/** -> Scripts Tests) attest it instead, if they ran and succeeded. Path globs: **/ any number of whole directories, a trailing /** everything below, * within one segment, ? one character; everything else literal. A script a package test imports belongs in the CI code filter, never under a covering rule.",
              "properties": {
                "tests": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["paths", "jobs"], "properties": { "paths": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 }, "jobs": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 } } }, "minItems": 1 },
                "qa": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["paths", "jobs"], "properties": { "paths": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 }, "jobs": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 } } }, "minItems": 1 }
              }
            }
          },
          "description": "Required checks that attest testsPassed (tests) and qaPassed (qa) for ci:<pr>; each must be a subset of the required list."
        }
      }
    }
  }
}
