# Sprint 1 — Skeleton ## Goal The C++ project compiles cleanly. All core UCWM types are defined as C++ structs. A `WorldState` can be constructed in code and serialized to JSON. No logic runs yet — this sprint is purely about getting the type system right before anything else is built on top of it. **If the types are wrong here, everything built on them will be wrong. Take the time to get them right.** --- ## Deliverables | File | Description | |---|---| | `CMakeLists.txt` | Build system. CPM dependencies. Targets for main and tests. | | `src/types.hpp` | All core UCWM structs: CanonicalObject, all Facet types, Constraint, GateSignal, RoutingDecision, WorldState, Provenance, SpecialistError. | | `src/main.cpp` | Smoke test: constructs a minimal WorldState for the apple problem by hand, serializes to JSON, prints debug output. | | `tests/test_types.cpp` | Unit tests: construct each type, verify field access, verify JSON round-trip. | --- ## Acceptance criteria - [ ] `cmake -B build && cmake --build build` succeeds with zero errors and zero warnings - [ ] `./build/ucwm` runs and prints a JSON-serialized WorldState to stdout - [ ] `./build/tests` runs and all type tests pass - [ ] Every field from `schemas/objects.json`, `schemas/facets.json`, `schemas/constraints.json`, `schemas/routing.json`, `schemas/provenance.json`, and `schemas/world_state.json` has a corresponding C++ field - [ ] No raw owning pointers — use `std::optional`, `std::variant`, `std::vector`, `std::unordered_map` - [ ] All string IDs typed as `std::string` (upgrade to a newtype wrapper in a later sprint if needed) --- ## Out of scope - Any gate logic - Any specialist logic - Any resolver logic - Any synthesis logic - JSON Schema validation (structs mirror the schemas, but no runtime validation yet) - Neural models of any kind - Performance optimization --- ## Dependencies (pulled via CPM) ```cmake CPMAddPackage("gh:nlohmann/json@3.11.3") CPMAddPackage("gh:gabime/spdlog@1.13.0") CPMAddPackage("gh:catchorg/Catch2@3.5.4") ``` --- ## Type design notes ### Use `std::variant` for sum types Object kind, facet kind, constraint status, object status — these are closed enumerations in the schema. Use `enum class` for simple enums, `std::variant` when the type carries different data per variant. ```cpp enum class ObjectKind { Entity, Event, Concept, Proposition, RelationInstance, ProcedureStep, CodeObject, Quantity, Claim, State }; enum class ObjectStatus { Proposed, Active, Merged, Split, Invalidated }; enum class ConstraintStrength { Hard, Soft, Probabilistic, Defeasible }; enum class ConstraintStatus { Unresolved, Resolved, Contradicted, Suspended }; enum class Polarity { Positive, Negative }; ``` ### Use `std::optional` for nullable fields Schema fields that are not in `required` should be `std::optional` in C++. Never use raw nulls or sentinel values. ```cpp std::optional surface_span; std::optional canonical_label; std::optional merged_into; std::optional probability; // only when strength=Probabilistic ``` ### Facets as a variant All facet types share a base set of fields, then add domain-specific fields. Represent as a `std::variant` or a struct with a `std::variant` payload. ```cpp struct TemporalFacetData { std::optional time_point; std::optional interval_start; std::optional interval_end; bool is_recurring = false; std::optional recurrence_pattern; std::vector temporal_order_refs; }; // ... one struct per facet kind ... using FacetData = std::variant< TemporalFacetData, SpatialFacetData, CausalFacetData, LogicalFacetData, OwnershipFacetData, QuantityFacetData, SyntacticFacetData, SocialFacetData, PlanningFacetData >; struct Facet { std::string facet_id; std::string object_ref; float confidence; std::string source_module; std::vector provenance_refs; FacetData data; // kind is implicit in the variant index }; ``` ### WorldState as maps ```cpp struct WorldState { std::string state_id; WorldStateStage stage; std::optional input_text; std::unordered_map objects; std::unordered_map facets; std::unordered_map constraints; std::unordered_map provenance; std::optional routing_decision; std::vector gate_signals; std::vector resolution_log; std::vector open_contradictions; std::optional synthesis_answer; std::optional synthesis_confidence; std::vector derivation_trace; }; ``` ### JSON serialization Use `nlohmann/json`'s `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` macro or manual `to_json`/`from_json` overloads. Every type needs both directions for the debug trace and for loading fixtures. ```cpp // Example pattern for each type: void to_json(nlohmann::json& j, const CanonicalObject& o); void from_json(const nlohmann::json& j, CanonicalObject& o); ``` --- ## File layout after Sprint 1 ``` ucwm/ CMakeLists.txt cmake/ CPM.cmake src/ types.hpp ← all structs and enums main.cpp ← smoke test tests/ test_types.cpp ← type construction + JSON round-trip tests ``` --- ## Smoke test behavior (`main.cpp`) `main.cpp` should: 1. Construct the post-proposal WorldState from `examples/word_problem_001.json` by hand (hardcoded, not parsed — parsing the fixture comes in Sprint 2) 2. Serialize it to JSON using nlohmann/json 3. Print the JSON to stdout 4. Print a simple debug header showing object count, constraint count Expected output: ``` === UCWM Smoke Test === WorldState stage: post_proposal Objects: 7 Facets: 0 Constraints: 0 [full JSON follows] ``` --- ## Definition of done Sprint 1 is done when: 1. All acceptance criteria checkboxes are checked 2. A second person (or the same person on a fresh read) can understand every field in `types.hpp` without referring back to the schemas 3. The JSON output of `main.cpp` matches the structure of `examples/word_problem_001.json` for the post-proposal stage --- ## Next sprint preview Sprint 2 builds the keyword gates and rule-based object proposal on top of these types. None of that can start until `types.hpp` is locked.