{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://raw.githubusercontent.com/kovrichard/portable-agent-layer/main/assets/schema/pal-settings.schema.json",
  "title": "PAL settings",
  "description": "Configuration for the Portable Agent Layer. Lives at ~/.pal/memory/pal-settings.json. All sections are optional; PAL falls back to defaults for anything absent.",
  "type": "object",
  "additionalProperties": true,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "JSON Schema reference — enables editor autocomplete, validation, and hover docs."
    },
    "identity": {
      "type": "object",
      "description": "Who the assistant is and who it serves.",
      "additionalProperties": false,
      "properties": {
        "ai": {
          "type": "object",
          "description": "The assistant's persona.",
          "additionalProperties": false,
          "properties": {
            "name": { "type": "string", "description": "Short name (e.g. the name you call the assistant)." },
            "fullName": { "type": "string", "description": "Full/formal name." },
            "displayName": { "type": "string", "description": "Uppercase label used in headers." },
            "catchphrase": { "type": "string", "description": "Greeting line. '{name}' is substituted with the ai.name value." }
          }
        },
        "principal": {
          "type": "object",
          "description": "The human the assistant works for.",
          "additionalProperties": false,
          "properties": {
            "name": { "type": "string", "description": "The principal's name." },
            "timezone": { "type": "string", "description": "IANA timezone (e.g. Europe/Budapest)." }
          }
        }
      }
    },
    "loadAtStartup": {
      "type": "object",
      "description": "Files force-loaded into session context at startup. Injected as <system-reminder> blocks.",
      "additionalProperties": false,
      "properties": {
        "files": {
          "type": "array",
          "description": "Absolute or PAL-relative file paths to inject at session start.",
          "items": { "type": "string" }
        }
      }
    },
    "dynamicContext": {
      "type": "object",
      "description": "Dynamic context sections injected at session start. Set any key to false to disable it; absent keys default to enabled.",
      "additionalProperties": { "type": "boolean" },
      "properties": {
        "wisdom": { "type": "boolean", "description": "Crystallized wisdom principles." },
        "opinions": { "type": "boolean", "description": "Tracked user opinions." },
        "relationship": { "type": "boolean", "description": "Recent relationship notes." },
        "failurePatterns": { "type": "boolean", "description": "Recurring failure patterns." },
        "projects": { "type": "boolean", "description": "Active projects list." },
        "handoff": { "type": "boolean", "description": "Handoff note from the previous session." },
        "selfModel": { "type": "boolean", "description": "The assistant's self-model synthesis." },
        "contextualSteering": { "type": "boolean", "description": "Prompt-time steering self-checks (see the steering section)." },
        "skillMatching": {
          "type": "boolean",
          "description": "Prompt-time \"Potential matching skills\" hints, matched from each skill's metadata.triggers."
        },
        "wallClock": {
          "type": "boolean",
          "description": "One line of the current time with every prompt, in identity.principal.timezone, so a session resumed hours later knows the day changed."
        },
        "repoState": {
          "type": "boolean",
          "description": "One line with every prompt saying whether the work in the current directory is committed, pushed and released; a registered project without git is named as such."
        },
        "dueReminders": {
          "type": "boolean",
          "description": "Opt-in, off by default until 1.0. Due maintenance reminders (learning analysis, relationship reflect) with every prompt until a reply names their command; then quiet until the next day."
        },
        "interactionAwareness": {
          "type": "boolean",
          "description": "Measure each user turn (pace, message length, skimming, corrections; never the text) into memory/signals/interaction/, and tell the agent when the session's pattern changes."
        },
        "reactionSampling": {
          "type": "boolean",
          "description": "Keep a rolling local sample (about 230 messages) of user message text, its reaction label and the end of the reply it answered, in memory/state/reaction-samples.json, so the reaction rules can be audited. Stores message text; set false to keep none."
        },
        "claimCheck": {
          "type": "boolean",
          "description": "At each stop, check whether the final reply claims a result (tests pass, CI green, works now) or a status (not pushed, merged, live) that no command in that turn could have shown, and log the verdict to memory/signals/claim-checks/. A claim about what is committed, pushed, merged or released is backed only by a git or gh command. Stores the claiming sentence only when nothing backed it."
        },
        "claimCheckBlocks": {
          "type": "boolean",
          "description": "Opt-in, off by default. Send a reply with an unbacked result or status claim back to the agent once, asking it to show the output or say the result is unverified. Needs claimCheck. Not on Cursor, whose stop payload carries no reply text."
        },
        "adaptationRules": {
          "type": "boolean",
          "description": "Let the rules you approved from your past corrections steer the agent: a rule triggered by your prompt is injected before the agent answers, and a reply that matches a rule's trigger is sent back to the agent once at stop. Each firing is logged to memory/adaptation/rule-events.jsonl. Set false to switch every rule off. Reply-side rules do not run on Cursor, whose stop payload carries no reply text, or on opencode, whose plugin does not run the stop check."
        },
        "steeringTestReport": { "type": "boolean", "description": "When true, the assistant notes to the user which steering self-check fired. For the dual-live test period; ships false." }
      }
    },
    "attribution": {
      "type": "object",
      "description": "Git co-author attribution opt-in.",
      "additionalProperties": false,
      "properties": {
        "enabled": { "type": "boolean", "description": "Add the PAL co-author trailer to commits." },
        "decided": { "type": "boolean", "description": "Gates the one-time attribution prompt." }
      }
    },
    "steering": {
      "type": "object",
      "description": "Contextual steering: PAL injects a short self-check into your prompt when it matches a task type. Toggle the whole feature via dynamicContext.contextualSteering.",
      "additionalProperties": false,
      "properties": {
        "disable": {
          "type": "array",
          "description": "Shipped steering rules to suppress, by tag.",
          "items": {
            "type": "string",
            "enum": ["debugging", "destructive", "refactor", "planning", "testing", "committing", "secrets"]
          },
          "uniqueItems": true
        },
        "rules": {
          "type": "array",
          "description": "Personal steering rules appended to the shipped set. A malformed entry (missing field or bad regex) is skipped, never fatal.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["tag", "pattern", "snippet"],
            "properties": {
              "tag": { "type": "string", "description": "Short identifier for this rule (also used to dedupe matches)." },
              "pattern": { "type": "string", "description": "JavaScript regex source, matched case-insensitively against the prompt." },
              "snippet": { "type": "string", "description": "The self-check text injected when the pattern matches." }
            }
          }
        }
      }
    }
  }
}
