{
  "_meta": {
    "title": "DeckCraft — proposed core schema (draft for discussion)",
    "version": "0.1",
    "status": "PROPOSAL, NOT YET IMPLEMENTED",
    "purpose": "A single layered data model for a DeckCraft game. Replaces the current mix of cardtypes-default.json + card-behaviours.json + mechanics-effect.json + ad-hoc meta keys with an explicit, mechanic-agnostic structure.",
    "guiding_principles": [
      "Types define DATA SHAPE. Behaviours define ENGINE SEMANTICS. Effects define CARD VOCABULARY. Subtypes are AI-facing flavour only.",
      "No genre is hard-coded. A deck-builder, a trick-taking game, and a set-collection game all fit the same schema with different content.",
      "The engine can simulate a game end-to-end using only the structured fields below. No free-text interpretation required at play time.",
      "Everything a user answers in the wizard writes into exactly one place in this schema. No duplication, no silent drops.",
      "Aspirational but not speculative: every layer here answers a real need that already exists in the wizard today."
    ]
  },

  "_shape": {
    "_description": "Top-level shape of a game document. Each field is explained below.",
    "game_id":    "unique id (today: game_info.unique_id)",
    "briefing":   "Layer 1: creative context from Step 1 (name, pitch, mechanic, audience, etc.)",
    "schema":     "Layer 2: which Types / Behaviours / Effects / Subtypes this game uses",
    "economy":    "Layer 3a: resources (currencies + life-like trackers)",
    "topology":   "Layer 3b: zones (where cards live)",
    "flow":       "Layer 4: turn structure, actions, win/loss conditions — THE MISSING LAYER TODAY",
    "content":    "Layer 5: families, decks, cards (filled in Steps 3 and 4)",
    "print":      "Layer 6: physical print specs (deferred)"
  },

  "_layer_1_briefing": {
    "_description": "Creative-brief fields captured on Step 1. Mostly free-text, some structured. Already implemented today as game_briefing meta.",
    "game_name": "string",
    "elevator_pitch": "string",
    "game_description": "string",
    "mechanic": "enum: deckbuild | tcg | draft | setcollect | tricktake",
    "subgenre": "optional string, scoped to mechanic",
    "player_mode": "enum: competitive | coop | solo | team",
    "player_count_min": "int",
    "player_count_max": "int",
    "is_team_game": "bool",
    "card_volume_preset": "enum: small_game | medium_game | large_game | custom_game",
    "card_volume_data": { "soft_card_cap": "int", "hard_card_cap": "int", "min_card_count": "int" },
    "game_goal": "free-text description of winning (mirrored in structured form at flow.win_conditions)",
    "game_imaginary": "world / setting free-text",
    "game_inspired_in": "references (string)",
    "game_graphic_style": "art-direction free-text",
    "age_rating": "enum: 6+ | 8+ | 10+ | 12+ | 14+ | 18+",
    "play_time": "enum: under_15 | 15_30 | 30_60 | 60_90 | 90_120 | over_120",
    "complexity": "int 1..5",
    "product_model": "enum: one_box | collectible | living_card_game | legacy_campaign | pnp_free"
  },

  "_layer_2_schema": {
    "_description": "Which Types / Behaviours / Effects / Subtypes are ACTIVE for this specific game. The catalog itself lives in separate default JSON files; this layer just references IDs.",

    "_type_definition_lives_in": "assets/data/types-default.json (proposed split from cardtypes-default.json)",
    "_behaviour_definition_lives_in": "assets/data/behaviours-default.json (rename from card-behaviours.json)",
    "_effect_definition_lives_in": "assets/data/effects-default.json (rename from mechanics-effect.json)",
    "_subtype_definition_lives_in": "assets/data/subtypes-default.json (proposed split from cardtypes-default.json)",

    "types_enabled": [
      {
        "id": "unit",
        "fields_overrides": {
          "_description": "Per-game overrides of default fields. Rare. Engine uses the default shape unless overridden.",
          "attack": { "default": 2 }
        },
        "allowed_behaviours_overrides": {
          "_description": "Narrow the default allowed_behaviours list for this game (can only subset, never expand).",
          "narrow_to": ["Permanent", "Strike"]
        }
      },
      { "id": "event" }
    ],

    "behaviours_enabled": ["Permanent", "Strike", "Instant", "Lingering"],

    "effects_enabled": [
      "deal_damage",
      "heal",
      "draw",
      "gain_resource",
      "counter",
      "destroy"
    ],

    "subtypes_used": {
      "_description": "SOFT layer. Subtypes are named (type + forced_behaviours + description) triples. The engine ignores these; they exist to (a) give the UI preset shortcuts and (b) give the AI themed inspiration when generating cards.",
      "items": [
        { "id": "unit__striker", "parent": "unit" },
        { "id": "unit__guardian", "parent": "unit" },
        { "id": "event__damage", "parent": "event" }
      ]
    }
  },

  "_layer_3a_economy": {
    "_description": "All trackable numeric state that persists per-player during a game. Includes resources (Mana, Gold) AND life-like counters (HP, Authority, Score). Everything the engine watches for win/loss conditions lives here.",
    "trackers": [
      {
        "id": "authority",
        "label": "Authority",
        "icon": "fa-heart",
        "role": "life",
        "per_player": true,
        "starts_at": 50,
        "min": 0,
        "max": null,
        "regenerates": null,
        "visibility": "public"
      },
      {
        "id": "trade",
        "label": "Trade",
        "icon": "fa-coins",
        "role": "resource",
        "per_player": true,
        "starts_at": 0,
        "min": 0,
        "max": null,
        "regenerates": "per_turn_reset_to_zero",
        "visibility": "public"
      },
      {
        "id": "combat",
        "label": "Combat",
        "icon": "fa-fist-raised",
        "role": "resource",
        "per_player": true,
        "starts_at": 0,
        "regenerates": "per_turn_reset_to_zero",
        "visibility": "public"
      }
    ]
  },

  "_layer_3b_topology": {
    "_description": "Zones — where cards live and move between. Each zone is defined once; cards reference zones by id in actions and move effects.",
    "zones": [
      {
        "id": "draw_pile",
        "label": "Draw Pile",
        "owner": "per_player",
        "visibility": "owner",
        "ordering": "stack",
        "max_cards": null,
        "accepts_types": ["*"],
        "start_filled_from": "starter_deck"
      },
      {
        "id": "hand",
        "label": "Hand",
        "owner": "per_player",
        "visibility": "owner",
        "ordering": "none",
        "max_cards": null,
        "accepts_types": ["*"]
      },
      {
        "id": "in_play",
        "label": "In Play",
        "owner": "per_player",
        "visibility": "public",
        "ordering": "none",
        "accepts_types": ["unit", "structure"]
      },
      {
        "id": "discard",
        "label": "Discard",
        "owner": "per_player",
        "visibility": "public",
        "ordering": "stack"
      },
      {
        "id": "trade_row",
        "label": "Trade Row",
        "owner": "shared",
        "visibility": "public",
        "ordering": "none",
        "auto_refill_from": "market_deck",
        "auto_refill_to": 5
      }
    ]
  },

  "_layer_4_flow": {
    "_description": "THE MISSING LAYER IN THE CURRENT SYSTEM. Describes how a game is PLAYED, not what it is made of. An engine should be able to simulate or at least print the rules text from this alone.",

    "turn_order": "clockwise",

    "starting_hand_size": 5,
    "starting_deck_size": "auto_from_starter_deck",

    "phases": [
      {
        "id": "main",
        "label": "Main Phase",
        "repeat": "until_ended_by_player",
        "allowed_actions": [
          { "id": "play_card",     "from": "hand", "to": "in_play",  "any_number": true,  "optional": true },
          { "id": "buy_from_row",  "from": "trade_row", "to": "discard", "cost_in": "trade", "any_number": true, "optional": true },
          { "id": "attack_opponent", "cost_in": "combat", "target": "opponent", "effect": "reduce_tracker", "tracker": "authority", "optional": true }
        ]
      },
      {
        "id": "cleanup",
        "label": "Cleanup",
        "automatic": true,
        "mandatory_actions": [
          { "id": "move_all", "from": "in_play", "to": "discard" },
          { "id": "move_all", "from": "hand",    "to": "discard" },
          { "id": "draw",     "from": "draw_pile", "to": "hand", "count": 5 }
        ],
        "tracker_resets": ["trade", "combat"]
      }
    ],

    "interrupt_rules": {
      "_description": "When (if ever) can the non-active player act? Leave empty for strictly turn-based games.",
      "can_play_instants_during_opponent_turn": false
    },

    "win_conditions": [
      {
        "id": "reduce_opponent_authority",
        "label": "Reduce opponent's Authority to 0",
        "target": "opponent",
        "metric": "tracker:authority",
        "comparator": "<=",
        "threshold": 0
      }
    ],

    "loss_conditions": [
      {
        "id": "cannot_draw",
        "label": "Cannot draw a required card",
        "trigger": "on_draw_failure",
        "action": "lose_immediately"
      }
    ],

    "game_length_cap": {
      "_description": "Optional. Prevents infinite games.",
      "turn_limit": null,
      "round_limit": null
    }
  },

  "_layer_5_content": {
    "_description": "The stuff Steps 3 and 4 fill in. Listed here for completeness; the schema above is the part that's under-designed today.",

    "families": [
      {
        "id": "machine_cult",
        "label": "Machine Cult",
        "description": "Scrap-powered robots with cheap recursion.",
        "enabled": true
      }
    ],

    "decks": [
      {
        "id": "starter_deck",
        "label": "Starter Deck",
        "role": "player_starting",
        "soft_card_cap": 10,
        "hard_card_cap": 12,
        "frequency_distribution": {
          "unit": 0.6,
          "event": 0.3,
          "resource": 0.1
        },
        "start_in_zone": "draw_pile"
      },
      {
        "id": "market_deck",
        "label": "Trade Deck",
        "role": "shared_market",
        "soft_card_cap": 80,
        "hard_card_cap": 100,
        "frequency_distribution": {
          "unit": 0.5,
          "event": 0.3,
          "structure": 0.2
        },
        "feeds_into_zone": "trade_row"
      }
    ],

    "cards": [
      {
        "id": "card_001",
        "type": "unit",
        "subtype": "unit__striker",
        "family": "machine_cult",
        "name": "Scrap Blitz",
        "cost": { "trade": 2 },
        "fields": { "attack": 3, "health": 1 },
        "behaviours": ["Strike"],
        "effects": [
          { "id": "deal_damage", "trigger": "on_play", "amount": 1, "target": "opponent_authority" }
        ],
        "art_prompt": "…",
        "rules_text_localised": {
          "en": "When played: deal 1 damage. Strike."
        }
      }
    ]
  },

  "_layer_6_print": {
    "_description": "DEFERRED. Physical production specs. Captured at export/layout time, not at onboarding. Sketch of intent only.",
    "card_size": "poker | bridge | tarot | euro_mini | custom",
    "card_quality": "standard | premium",
    "back_design_id": "string",
    "copies": 1,
    "languages_supported": ["en"],
    "box_format": "tuck_box | two_piece | none",
    "manifest": "auto-generated from content.decks + content.cards"
  },



  "EXAMPLE_1_DECK_BUILDER": {
    "_description": "Concrete instance: a Star-Realms-inspired 2-player attack/defense deck-builder.",

    "game_id": "starforge-demo",

    "briefing": {
      "game_name": "StarForge",
      "elevator_pitch": "Two rival factions forge fleets from the scrap of dying stars.",
      "mechanic": "deckbuild",
      "subgenre": "combat",
      "player_mode": "competitive",
      "player_count_min": 2,
      "player_count_max": 2,
      "card_volume_preset": "medium_game",
      "age_rating": "12+",
      "play_time": "30_60",
      "complexity": 3,
      "game_goal": "Reduce opponent's Authority to zero."
    },

    "schema": {
      "types_enabled": [
        { "id": "unit" },
        { "id": "structure" },
        { "id": "event" }
      ],
      "behaviours_enabled": ["Permanent", "Strike", "Instant", "Outpost"],
      "effects_enabled": ["deal_damage", "heal", "draw", "gain_resource", "destroy"],
      "subtypes_used": { "items": [
        { "id": "unit__striker", "parent": "unit" },
        { "id": "unit__generator", "parent": "unit" },
        { "id": "event__damage", "parent": "event" }
      ]}
    },

    "economy": {
      "trackers": [
        { "id": "authority", "role": "life",     "starts_at": 50, "per_player": true,  "regenerates": null },
        { "id": "trade",     "role": "resource", "starts_at": 0,  "per_player": true,  "regenerates": "per_turn_reset_to_zero" },
        { "id": "combat",    "role": "resource", "starts_at": 0,  "per_player": true,  "regenerates": "per_turn_reset_to_zero" }
      ]
    },

    "topology": {
      "zones": [
        { "id": "draw_pile", "owner": "per_player", "ordering": "stack",  "visibility": "owner" },
        { "id": "hand",      "owner": "per_player", "ordering": "none",   "visibility": "owner" },
        { "id": "in_play",   "owner": "per_player", "ordering": "none",   "visibility": "public" },
        { "id": "discard",   "owner": "per_player", "ordering": "stack",  "visibility": "public" },
        { "id": "trade_row", "owner": "shared",     "ordering": "none",   "visibility": "public", "auto_refill_from": "market_deck", "auto_refill_to": 5 }
      ]
    },

    "flow": {
      "turn_order": "clockwise",
      "starting_hand_size": 5,
      "phases": [
        {
          "id": "main",
          "repeat": "until_ended_by_player",
          "allowed_actions": [
            { "id": "play_card",       "from": "hand",      "to": "in_play",  "any_number": true, "optional": true },
            { "id": "buy_from_row",    "from": "trade_row", "to": "discard",  "cost_in": "trade", "optional": true },
            { "id": "attack_opponent", "cost_in": "combat", "target": "opponent", "effect": "reduce_tracker", "tracker": "authority" }
          ]
        },
        {
          "id": "cleanup",
          "automatic": true,
          "mandatory_actions": [
            { "id": "move_all", "from": "in_play", "to": "discard" },
            { "id": "move_all", "from": "hand",    "to": "discard" },
            { "id": "draw",     "from": "draw_pile", "to": "hand", "count": 5 }
          ],
          "tracker_resets": ["trade", "combat"]
        }
      ],
      "win_conditions": [
        { "id": "authority_zero", "target": "opponent", "metric": "tracker:authority", "comparator": "<=", "threshold": 0 }
      ]
    }
  },



  "EXAMPLE_2_TRICK_TAKING": {
    "_description": "Concrete instance: a 4-player partnership trick-taking game. Shows the schema works for NON-combat games. Note: no resources, no attack/defense behaviours, no in_play zone.",

    "game_id": "skybid-demo",

    "briefing": {
      "game_name": "SkyBid",
      "elevator_pitch": "Two pairs of sky captains outbid each other with shifting alliances.",
      "mechanic": "tricktake",
      "player_mode": "team",
      "player_count_min": 4,
      "player_count_max": 4,
      "is_team_game": true,
      "card_volume_preset": "small_game",
      "age_rating": "10+",
      "play_time": "30_60",
      "complexity": 2,
      "game_goal": "First team to reach 500 points wins."
    },

    "schema": {
      "_note": "Trick-taking games don't use Unit / Structure / Event. They need a PROPOSED new type 'ranked_card' with rank + suit fields. That new type should live in types-default.json once this schema is adopted.",
      "types_enabled": [
        { "id": "ranked_card" }
      ],
      "behaviours_enabled": [],
      "effects_enabled": [],
      "subtypes_used": { "items": [] }
    },

    "_proposed_new_type_for_types_defaults_json": {
      "id": "ranked_card",
      "label": "Ranked Card",
      "description": "A card with a rank and suit; the base unit of trick-taking games.",
      "fields": {
        "rank":  { "type": "enum", "values": ["2","3","4","5","6","7","8","9","10","J","Q","K","A"] },
        "suit":  { "type": "enum", "values": ["hearts","diamonds","clubs","spades"] },
        "point_value": { "type": "number", "default": 0 }
      },
      "allowed_behaviours": [],
      "default_behaviours": []
    },

    "economy": {
      "trackers": [
        { "id": "team_score", "role": "score", "starts_at": 0, "per_player": false, "per_team": true, "visibility": "public" },
        { "id": "bid",        "role": "commitment", "starts_at": 0, "per_team": true,  "visibility": "public" }
      ]
    },

    "topology": {
      "zones": [
        { "id": "deck",          "owner": "shared",     "ordering": "stack" },
        { "id": "hand",          "owner": "per_player", "visibility": "owner" },
        { "id": "current_trick", "owner": "shared",     "visibility": "public" },
        { "id": "won_tricks",    "owner": "per_team",   "visibility": "owner" }
      ]
    },

    "flow": {
      "turn_order": "clockwise",
      "phases": [
        {
          "id": "deal",
          "one_time_per_round": true,
          "automatic": true,
          "mandatory_actions": [
            { "id": "shuffle", "target": "deck" },
            { "id": "deal",    "from": "deck", "to": "hand", "count": 13, "to_each_player": true }
          ]
        },
        {
          "id": "bid",
          "repeat": "once_per_player",
          "allowed_actions": [
            { "id": "place_bid", "min": 0, "max": 500 },
            { "id": "pass" }
          ]
        },
        {
          "id": "play",
          "repeat": "until_hands_empty",
          "allowed_actions": [
            { "id": "play_card", "from": "hand", "to": "current_trick", "follow_suit_if_possible": true }
          ]
        },
        {
          "id": "resolve_trick",
          "trigger": "current_trick_has_N_cards",
          "N": 4,
          "mandatory_actions": [
            { "id": "determine_winner", "by": "highest_rank_in_lead_suit_or_trump" },
            { "id": "move_all", "from": "current_trick", "to": "won_tricks.of_winner" },
            { "id": "increment_tracker", "who": "winner_team", "tracker": "team_score", "by": "sum_of_point_values_in_trick" }
          ]
        }
      ],
      "win_conditions": [
        { "id": "reach_target", "target": "any_team", "metric": "tracker:team_score", "comparator": ">=", "threshold": 500 }
      ]
    },

    "content": {
      "decks": [
        {
          "id": "main_deck",
          "label": "Standard Deck",
          "role": "shared",
          "soft_card_cap": 52,
          "hard_card_cap": 52,
          "frequency_distribution": { "ranked_card": 1.0 },
          "start_in_zone": "deck"
        }
      ]
    }
  },



  "_migration_notes": {
    "_description": "How this maps onto today's system so the change is traceable.",

    "from_today_to_proposed": {
      "cardtypes-default.json (roots)":        "-> types-default.json",
      "cardtypes-default.json (subtypes)":     "-> subtypes-default.json",
      "card-behaviours.json":                  "-> behaviours-default.json (rename only)",
      "mechanics-effect.json":                 "-> effects-default.json (rename; matches the new terminology)",
      "game_parts (is_default=1 resources)":   "-> economy.trackers catalog. Life-points become trackers with role=life, resources become trackers with role=resource.",
      "zones-default.json":                    "-> topology.zones (field names normalised)",
      "game_meta 'enabled_mechanics'":         "-> schema.effects_enabled",
      "game_meta 'card_types'":                "-> schema.types_enabled[*].*_overrides",
      "game_meta 'custom_card_subtypes'":      "-> (user-added entries in subtypes-default.json, or per-game subtypes list)",
      "game_meta 'card_behaviours'":           "-> schema.behaviours_enabled",
      "game_meta 'card_families'":             "-> content.families",
      "game_meta 'game_zones'":                "-> topology.zones",
      "game_briefing 'game_goal' (free text)": "-> KEEP AS TEXT + also structured at flow.win_conditions (derived or AI-mapped)",
      "NO EQUIVALENT TODAY":                   "-> flow.phases, flow.win_conditions, flow.loss_conditions, flow.turn_order — THE NEW LAYER"
    },

    "terminology_cleanup": {
      "card-type":          "split into `type` (root, required) and `subtype` (optional, soft)",
      "archetype":          "RETIRE. Was used for three different things in the code.",
      "mechanic (Step 1)":  "keep as-is — the genre choice",
      "mechanic (Step 2)":  "RENAME to `effect` (card vocabulary) to kill the collision",
      "resource (Step 2)":  "unify with life-points under `tracker` with a `role` field"
    }
  },

  "_open_questions": [
    "Trackers vs resources vs life-points: is it right to unify them under one 'tracker' concept with a role field? Pro: simpler model. Con: the UI separation exists for a reason (the user thinks about Mana differently from HP).",
    "Subtypes: should they be a global catalog (subtypes-default.json) or per-game? Today custom subtypes are per-game; defaults are global. Could continue.",
    "Flow actions: should we design a full DSL (conditions, sequencing) or keep it flat with a small set of action types? Flat is simpler; DSL is more expressive. Recommend flat first, extend when we hit limits.",
    "Effect params: how structured? 'deal_damage amount: 3, target: opponent_authority' is clean. But effects like 'draw a card then if it is a Unit also gain 1 Combat' get messy fast. For v1, effects should be one action each — compound effects become multiple effect entries on the card.",
    "Win conditions with multiple metrics (e.g. score >= 500 AND hand empty): do we model as composite (AND/OR), or as a list of independent conditions any of which can fire? Lean towards independent conditions — simpler.",
    "Where does game-length cap (turn limit) live? flow.game_length_cap as proposed. Or as a loss condition? The current proposal has it separate because it's game-ending but not player-attributable.",
    "Is 'flow' too ambitious for v1? Could we ship the schema/economy/topology layers first and leave flow as TODO? Yes, but that means the system still can't describe a full game until later. Recommend shipping at least a SKELETON flow (phases + win_conditions, everything else optional) from day one.",
    "Localised rules_text on cards (content.cards[].rules_text_localised) — worth designing in now or deferred? Small tax to include, huge cost to add later. Recommend designing in now."
  ]
}
