Files
ucwm/docs/sprint_001.md

205 lines
6.7 KiB
Markdown
Raw Permalink Normal View History

# 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<T>` in C++. Never use raw nulls or sentinel values.
```cpp
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.
```cpp
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
```cpp
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.
```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.