GridPlacement Parity Contract — 6.0 C# Core ↔ 5.1 GDScript
Purpose
This document is the canonical source of truth for what “parity” means between:
- GridPlacement 6.0 C# (canonical)
- GridBuilding/GridPlacement 5.1 GDScript (legacy-stable)
It defines:
- Scope (what parity is required for)
- Non-goals (what parity is explicitly not required for)
- Vertical slices (how we implement parity incrementally)
- Acceptance tests (how we prove parity)
- Update rules (where to record mappings when things move)
Canonical Principles
- 6.0 C# Core is canonical for behavior and tests.
- 5.1 is legacy-stable: we keep it loading/working and we backport only small, high-value behavior when it clearly reduces risk.
- No plugin-agnostic composition framework in 5.1.
GBInjectorSystemis the canonical 5.1 composition root and remains so.
- 5.1 keeps
GBEnums(GDScript) as a stable public API surface.- This reduces
class_nameproliferation and avoids churn in legacy demos/tests.
- This reduces
- Parity is expressed at the contract level; enum/value names may differ by track.
- 6.0 uses
GridMode(C#) as the canonical mode enum.- Canonical type:
GridBuilding.Core.Types.GridMode(cs/Core/Types/Mode.cs). - See
cs/Core/Types/GridModeExtensions.cs,cs/Core/Services/Mode/IModeService.cs,cs/Core/Services/Mode/ModeService.cs.
- Canonical type:
- Breaking change (6.0): no C#
GBEnums.GBEnumsis a 5.1 GDScript-only API surface.- All 6.0 C# code should reference
GridMode(and other explicit types) directly.
- Parity is contract-level:
- Parity is required for Core/service/domain behavior.
- Parity is not required for Godot Node/Resource names, scene graphs, or editor conveniences.
- No cross-language runtime bridge:
- 5.1 parity is achieved via GDScript translation into pure
RefCountedlogic modules (deterministic IO), not by calling 6.0 C# at runtime.
- 5.1 parity is achieved via GDScript translation into pure
Mode naming parity (5.1 ↔ 6.0)
5.1 (GDScript) uses GBEnums.Mode while 6.0 (C#) uses GridMode. Preferred conceptual mapping:
GBEnums.Mode.OFF→GridMode.OffGBEnums.Mode.INFO→GridMode.InfoGBEnums.Mode.BUILD→GridMode.PlaceGBEnums.Mode.MOVE→GridMode.MoveGBEnums.Mode.DEMOLISH→GridMode.Remove
Input wiring acceptance tests (composition chain)
These are integration wiring contracts (not full gameplay parity): they prove that each track can boot its composition root and route a representative input event through the configured adapter/interpreter pipeline.
5.1 GDScript (Godot) chain
Contract:
GBInjectorSystem → GBServiceFactory → GBServiceRegistry → GBInputService → PositioningInputAdapter
Evidence (GdUnit integration test):
demos/grid_building_dev/godot/test/grid_building/systems/placement/integration/node_adapter_service_wiring_integration_test.gd
What it asserts:
- The test environment boots and provides a
GBCompositionContainer. - The container exposes a non-null
GBServiceRegistry. - The registry exposes a non-null
GBInputService. - A forwarded engine input event triggers a positioning-domain signal (adapter/interpreter path is live).
6.0 C# (Godot) chain
Contract:
GBUserScopeRoot → ServiceRegistry (C#) → CoreInputService → CoreInputRoutingService → interpreters/adapters
Evidence (GoDotTest):
demos/grid_building_dev/godot_cs/InputPipelineSmokeGoDotTests.cs
What it asserts:
GBUserScopeRootboots standalone and provides aServiceRegistry.CoreInputServiceis registered and contains a non-null internalCoreInputRoutingServicerouter.- An
InputEventActiontranslated through the bridge is routed and handled by the interpreter pipeline.
What parity IS (required)
Parity is required for:
- Placement validation semantics
- same “valid/invalid” decision outcomes for the same inputs
- same key failure categories (bounds, collisions, rule failures)
- Indicator decision semantics
- given a placement validation outcome, the same indicator state decision is produced (valid/invalid/mixed/etc.)
- Placement execution semantics (minimal)
- the same high-level result categories (success/failure) and core invariants
In practice, parity is expressed as:
- a small set of deterministic acceptance tests per vertical slice
- a stable data contract for inputs/outputs (even if 5.1 uses Dictionaries internally)
What parity is NOT (explicitly not required)
Parity is not required for:
- Godot Node names, scene layout, exported property names (unless they are part of the locked 5.1 public API)
- Demo scene UX parity
- Full test-suite parity (5.1 keeps a curated, small test slice)
- DI/composition parity (5.1 stays on InjectorSystem)
Plugin boundary: JSON & resource loading
Parity does not require both tracks to share the same JSON loading architecture.
5.1 GDScript (grid_building) boundary
- 5.1 uses Godot-native resource loading as the canonical path (
res://resources,.tres/.tscn), plus any existing plugin-specific configuration loaders. - 5.1 does not need a generalized “safe JSON loader” framework.
- 5.1 does not need to support headless/server JSON workflows.
- If a game wants JSON-driven configuration, JSON parsing belongs in game/host code using Godot’s JSON APIs.
- Parity requirement (5.1): it is sufficient that the game can convert JSON into the plugin’s expected Resources (e.g.,
Placeableresources) and then call into the plugin using those resources.
6.0 C# (GridPlacement) boundary
- 6.0 Core should remain engine-agnostic and should not own Godot
Resourceloading. - 6.0 should not provide a generic JSON file IO subsystem as part of the plugin boundary.
- If JSON is involved, the expected boundary is:
- Host/engine layer owns IO + asset resolution (e.g.,
AssetLoader/ safe loader service) - Core owns schema/contract interpretation and validation (pure types in/pure results out)
- Host/engine layer owns IO + asset resolution (e.g.,
In-scope vertical slices (incremental parity plan)
We implement parity one vertical slice at a time.
Slice A — Placement Validate → Indicator Decision → Place (core)
Goal: a user can attempt to place an item; the validation and indicator decisions match 6.0 behavior.
Minimal contract (Slice A)
Inputs (conceptual):
- User identity:
UserId(6.0 canonical type; 5.1 uses owner-style identity but conceptually maps 1:1) - Target: grid position or world position that can be converted to grid
- Placeable definition: placeable id + footprint/variant + any required metadata
- Rotation: canonical rotation representation for the placement workflow
- Grid bounds: region/limits used to validate in-bounds placement
- Occupancy/collision representation: current occupied cells and collision rules
Outputs (contract-level):
- Validation outcome: valid/invalid + reason category (bounds, collision/overlap, rule failure)
- Indicator decision: the canonical indicator state decision for the validation outcome
- Execution result category: success/failure (minimal), plus stable identifiers/coordinates where available
Minimal service graph expectations
6.0 C# (canonical):
- Core services own the Slice A behavior under the service + adapter architecture.
- Loading/asset resolution is out-of-scope (host/engine responsibilities).
5.1 GDScript (legacy):
GBInjectorSystemremains the canonical composition root.- Slice A parity is implemented by:
- pure
RefCountedlogic (validation and indicator decision) plus - thin Node/scene adapters that keep the 5.1 public API stable.
- pure
- JSON parsing (if any) remains game-level code; the plugin consumes Resources.
Done when (Slice A)
- 6.0 has a deterministic Core test set covering the Slice A contract.
- 5.1 has a small deterministic GDScript test set that mirrors the same cases.
- A short doc example shows the same inputs produce equivalent outcomes.
Slice B — Targeting / cursor state transitions (optional)
Parity applies to:
- cursor → grid snapping rules
- target cell transitions
Parity does not require node-level APIs to match.
Slice C — Manipulation (move/rotate/demolish) (optional)
Larger slice; defer until slices A/B are stable.
Acceptance tests (how we prove parity)
Rules:
- Tests must be deterministic.
- Prefer contract-level tests over heavy scenes.
- 6.0 tests are canonical; 5.1 tests mirror only the chosen slice’s contract.
Done when (per slice):
- a) 6.0 Core tests exist and pass
- b) a mirrored 5.1 GDScript test set exists and passes
- c) documented examples show the same inputs produce equivalent outcomes
Canonical tracker map (where to look)
Use these documents as the “map,” but treat this file as the canonical contract:
- 6.0 overall execution plan:
docs/Roadmap/ROADMAP.md - 5.1 legacy lane plan:
docs/5.1/gdscript/ROADMAP.md - Canonical type mapping:
docs/6.0/Core/GRIDBUILDING_CLASS_MAPPING.md - Legacy Godot (_Legacy/) port scoring:
docs/Migration/MIGRATION_CLASS_PRIORITY.md - Workflow guidance for users migrating:
docs/5.1/gdscript/GDSCRIPT_5_1_TO_6_0_MIGRATION.md - Architecture/migration narrative:
docs/6.0/Core/SERVICE_PATTERN_MIGRATION.md
Update rules (to prevent drift)
When parity decisions change:
- Update this file first.
- If types move, update:
docs/6.0/Core/GRIDBUILDING_CLASS_MAPPING.md- any impacted migration docs referenced above
- Keep duplication low: other docs should link here rather than restating the contract.