{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://neon-sprawl.local/schemas/resource-node-def.json",
  "title": "ResourceNodeDef",
  "description": "Single resource node row for catalogs (e.g. content/resource-nodes/*_resource_nodes.json). nodeDefId is stable forever—rename displayName only. See docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md.",
  "type": "object",
  "additionalProperties": false,
  "required": ["nodeDefId", "displayName", "gatherLens", "maxGathers"],
  "properties": {
    "nodeDefId": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9_]*$",
      "description": "Immutable node key; must match interactableId for prototype wiring."
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "description": "Player-facing label; safe to change without migrating state."
    },
    "gatherLens": {
      "type": "string",
      "description": "Prototype Slice 2 gather lens (one per frozen node). CI requires exactly one row per lens.",
      "enum": ["consumer_salvage", "subsurface", "bio", "urban_bulk"]
    },
    "maxGathers": {
      "type": "integer",
      "minimum": 1,
      "description": "Successful gathers before node instance is depleted (prototype Slice 2)."
    }
  }
}
