- src/types.hpp: complete UCWM type system in C++20 — 19 enums, 11 facet data types, all core structs (CanonicalObject, Constraint, Facet, GateSignal, WorldState, etc.) with full JSON round-trip serialization - src/main.cpp: smoke test — constructs apple-problem WorldState by hand, serializes to JSON - tests/test_types.cpp: 19 tests, 123 assertions, all passing - CMakeLists.txt: CMake + CPM build with nlohmann/json, spdlog, Catch2 - schemas/: JSON Schema contracts for all UCWM data types - gates/, specialists/, resolver/, synthesis/: language-agnostic interface contracts and domain specs for all pipeline layers - docs/: architecture, vocabulary, decision matrices, roadmap (6 phases, 28 sprints), sprint_001, implementation_constraints Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.7 KiB
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 buildsucceeds with zero errors and zero warnings./build/ucwmruns and prints a JSON-serialized WorldState to stdout./build/testsruns and all type tests pass- Every field from
schemas/objects.json,schemas/facets.json,schemas/constraints.json,schemas/routing.json,schemas/provenance.json, andschemas/world_state.jsonhas 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)
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.
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<T> in C++. Never use raw nulls or sentinel values.
std::optional<SurfaceSpan> surface_span;
std::optional<std::string> canonical_label;
std::optional<std::string> merged_into;
std::optional<float> 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.
struct TemporalFacetData {
std::optional<std::string> time_point;
std::optional<std::string> interval_start;
std::optional<std::string> interval_end;
bool is_recurring = false;
std::optional<std::string> recurrence_pattern;
std::vector<std::string> 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<std::string> provenance_refs;
FacetData data; // kind is implicit in the variant index
};
WorldState as maps
struct WorldState {
std::string state_id;
WorldStateStage stage;
std::optional<std::string> input_text;
std::unordered_map<std::string, CanonicalObject> objects;
std::unordered_map<std::string, Facet> facets;
std::unordered_map<std::string, Constraint> constraints;
std::unordered_map<std::string, Provenance> provenance;
std::optional<RoutingDecision> routing_decision;
std::vector<GateSignal> gate_signals;
std::vector<ResolutionEntry> resolution_log;
std::vector<ContradictionRecord> open_contradictions;
std::optional<std::string> synthesis_answer;
std::optional<float> synthesis_confidence;
std::vector<std::string> 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.
// 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:
- Construct the post-proposal WorldState from
examples/word_problem_001.jsonby hand (hardcoded, not parsed — parsing the fixture comes in Sprint 2) - Serialize it to JSON using nlohmann/json
- Print the JSON to stdout
- 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:
- All acceptance criteria checkboxes are checked
- A second person (or the same person on a fresh read) can understand every field in
types.hppwithout referring back to the schemas - The JSON output of
main.cppmatches the structure ofexamples/word_problem_001.jsonfor 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.