Sprint 1: project skeleton, type system, and all architecture specs

- 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>
This commit is contained in:
2026-05-01 16:09:55 -07:00
commit b758d7ea60
35 changed files with 6344 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
# Gate Contract Tests
Template for testing any gate implementation. These tests must pass regardless of implementation method (regex, classifier, neural, hybrid).
---
## Test format
```
GateContractTest {
test_id: string
gate_id: string
input_text: string
expected: {
activated: boolean
min_confidence: float -- activated=true: confidence must be >= this
max_confidence: float -- activated=false: confidence must be <= this
}
test_class: "positive_recall" | "true_negative" | "boundary" | "idempotency"
rationale: string
}
```
---
## `is_math` gate tests
| test_id | input | expected activated | min_conf | test_class |
|---|---|---|---|---|
| G_MATH_001 | "Alice had 5 apples. She gave Bob 2." | true | 0.80 | positive_recall |
| G_MATH_002 | "What is 3 plus 7?" | true | 0.90 | positive_recall |
| G_MATH_003 | "How many apples does she have left?" | true | 0.70 | positive_recall |
| G_MATH_004 | "The cat sat on the mat." | false | — | true_negative |
| G_MATH_005 | "Alice went to the store." | false | — | true_negative |
| G_MATH_006 | "" | false | — | boundary |
| G_MATH_007 | "42" | true | 0.70 | boundary |
---
## `has_entity_reference` gate tests
| test_id | input | expected activated | min_conf | test_class |
|---|---|---|---|---|
| G_ENT_001 | "Alice gave Bob 2 apples." | true | 0.90 | positive_recall |
| G_ENT_002 | "She gave him the book." | true | 0.70 | positive_recall |
| G_ENT_003 | "Dr. Smith visited the clinic." | true | 0.90 | positive_recall |
| G_ENT_004 | "2 plus 3 equals 5." | false | — | true_negative |
| G_ENT_005 | "run the program" | false | — | true_negative |
| G_ENT_006 | "" | false | — | boundary |
---
## `has_ownership_transfer` gate tests
| test_id | input | expected activated | min_conf | test_class |
|---|---|---|---|---|
| G_OWN_001 | "Alice gave Bob 2 apples." | true | 0.90 | positive_recall |
| G_OWN_002 | "She sold the car to him." | true | 0.90 | positive_recall |
| G_OWN_003 | "He received a package." | true | 0.85 | positive_recall |
| G_OWN_004 | "The sun rose at 6am." | false | — | true_negative |
| G_OWN_005 | "Alice is happy." | false | — | true_negative |
| G_OWN_006 | "gave" | true | 0.70 | boundary |
---
## `has_temporal_relation` gate tests
| test_id | input | expected activated | min_conf | test_class |
|---|---|---|---|---|
| G_TMP_001 | "She arrived after the meeting started." | true | 0.90 | positive_recall |
| G_TMP_002 | "Before noon, Alice left." | true | 0.90 | positive_recall |
| G_TMP_003 | "Step 1: open the file. Step 2: read it." | true | 0.80 | positive_recall |
| G_TMP_004 | "Alice is 5 feet tall." | false | — | true_negative |
| G_TMP_005 | "x equals y." | false | — | true_negative |
---
## `has_logical_negation` gate tests
| test_id | input | expected activated | min_conf | test_class |
|---|---|---|---|---|
| G_NEG_001 | "Alice did not give Bob any apples." | true | 0.90 | positive_recall |
| G_NEG_002 | "She never arrived." | true | 0.90 | positive_recall |
| G_NEG_003 | "Unless it rains, the picnic continues." | true | 0.85 | positive_recall |
| G_NEG_004 | "Alice gave Bob 2 apples." | false | — | true_negative |
---
## Idempotency tests
Every gate must pass these for every gate_id:
| test_id | procedure | expected |
|---|---|---|
| G_IDEM_001 | Call gate(X) twice with identical input | Both calls return identical GateSignal |
| G_IDEM_002 | Call gate(X) after unrelated text processed | Same result as fresh call |
---
## Boundary tests (all gates)
| test_id | input | expected |
|---|---|---|
| G_BOUND_001 | "" (empty string) | Valid GateSignal with activated=false, confidence > 0 |
| G_BOUND_002 | Single character "a" | Valid GateSignal, no crash |
| G_BOUND_003 | 10,000 character string | Valid GateSignal, no crash, returns within 100ms |
| G_BOUND_004 | String with only symbols "!@#$%^&*()" | Valid GateSignal, no crash |
---
## Recall vs. precision tolerance
Gates are high-recall by design. The following tolerances apply to implementation evaluation:
- Recall must be >= 0.90 on positive test cases
- Precision on clearly negative test cases must be >= 0.80
- Precision on ambiguous test cases: no threshold (false positives are acceptable)
A gate that achieves 0.95 recall with 0.70 precision is preferred over one with 0.85 recall and 0.95 precision.

View File

@@ -0,0 +1,120 @@
# Resolver Contract Tests
Tests that any resolver implementation must pass.
---
## RT_001 — Arithmetic resolution
```
Input WorldState:
Objects: Q1 (value=5, unit=apples), Q2 (value=2, unit=apples), Q_result (value=null)
Constraints: C1 = quantity_difference(Q_result, Q1, Q2), status=unresolved
Expected resolved WorldState:
Q_result.QuantityFacet.value = 3
Q_result.QuantityFacet.is_exact = true
Q_result.QuantityFacet.unit = "apples"
C1.status = "resolved"
resolution_log contains entry for "resolve_arithmetic" affecting Q_result
```
---
## RT_002 — Coreference merge
```
Input WorldState:
Objects: E1 (Alice, confidence=0.97), E_she (She, confidence=0.72)
Constraints: C_coref = same_entity(E1, E_she), status=resolved
Expected resolved WorldState:
E_she.status = "merged"
E_she.merged_into = "E1"
E1.aliases contains "She"
All facet_refs and constraint_refs from E_she transferred to E1
resolution_log contains entry for "merge_coreference"
```
---
## RT_003 — Contradiction detected and logged
```
Input WorldState:
Constraints:
C1 = before(EV1, EV2), strength=hard, status=unresolved
C2 = before(EV2, EV1), strength=hard, status=unresolved
Expected:
C1.status = "contradicted"
C2.status = "contradicted"
open_contradictions contains record with constraint_refs = ["C1", "C2"]
NO silent resolution
```
---
## RT_004 — Soft constraint suspended by hard conflict
```
Input WorldState:
C1 = quantity_equals(Q1, 5), strength=hard, status=resolved
C2 = quantity_equals(Q1, 7), strength=soft, status=unresolved
Expected:
C2.status = "suspended"
resolution_log contains "suspend_soft" entry for C2
Q1.value = 5 (unchanged)
```
---
## RT_005 — No new objects created
```
Input WorldState: N objects
Expected: resolved WorldState has <= N objects (merges reduce count; splits would increase but must be explicitly supported)
```
---
## RT_006 — Resolution log completeness
```
For any non-trivial WorldState:
Every object status change must have a log entry
Every constraint status change must have a log entry
resolution_log must be non-empty
```
---
## RT_007 — Stage check
```
Input: WorldState with stage = "post_gate"
Expected: resolver raises error or returns error state, does not produce resolved output
```
---
## RT_008 — Idempotency
```
Call resolver(WorldState) → resolved_1
Call resolver(resolved_1) → resolved_2
Expected: resolved_1 and resolved_2 are logically equivalent (no new changes on second pass)
```
---
## RT_009 — Confidence propagation bounds
```
Given any resolved constraint affecting object O:
O.confidence must not increase by more than 0.15 from a single constraint
O.confidence must not decrease by more than 0.15 from a single contradiction
O.confidence must remain in [0.0, 1.0]
```
---
## RT_010 — Resolver interface replaceability
```
Given a resolver interface: resolve(world_state) → world_state
Any implementation that passes RT_001 through RT_009 is a valid resolver.
The rest of the pipeline must not depend on resolver internals.
```

View File

@@ -0,0 +1,135 @@
# Specialist Contract Tests
Template for testing any specialist implementation against its contract.
---
## Entity Specialist
### ES_001 — Two named entities detected
```
Input: "Alice gave Bob 2 apples."
Expected:
- new_objects contains at least two entities
- E_alice.canonical_label = "Alice"
- E_bob.canonical_label = "Bob"
- Constraint not_same_entity(E_alice, E_bob) emitted
- All objects have source_module = "entity"
```
### ES_002 — Coreference resolved
```
Input: "Alice arrived. She sat down."
WorldState contains E_alice from prior proposal.
Expected:
- same_entity(E_alice, E_she) constraint emitted
- Confidence >= 0.80
```
### ES_003 — Domain adherence
```
Input: "Alice gave Bob 5 apples."
Expected:
- No quantity facets emitted
- No temporal constraints emitted
- Only syntactic/semantic facets and entity constraints
```
### ES_004 — Budget respected
```
BudgetSpec: max_constraints = 3
Input: very long text with many entities
Expected:
- len(new_constraints) <= 3
```
### ES_005 — Valid refs
```
Expected for any input:
- All argument_refs in constraints resolve to IDs in new_objects or input WorldState.objects
- No dangling refs
```
---
## Quantity Specialist
### QS_001 — Exact arithmetic resolved inline
```
Input context: Q1.value=5, Q2.value=2
Constraint: quantity_difference(Q_result, Q1, Q2) with status=unresolved
Expected:
- Q_result QuantityFacet.value = 3
- Q_result QuantityFacet.is_exact = true
- Constraint status = resolved
```
### QS_002 — Units propagated
```
Input: Q1.unit="apples", Q2.unit="apples"
Expected:
- Q_result.unit = "apples"
```
### QS_003 — Domain adherence
```
Input: any text
Expected:
- No temporal facets emitted
- No entity constraints emitted
- Only quantity facets and quantity constraints
```
### QS_004 — Unresolvable emits unresolved constraint
```
Input context: Q1.value=5, Q2.value=null (not yet known)
Expected:
- quantity_difference(Q_result, Q1, Q2) emitted with status=unresolved
- Q_result.value = null
- No error, no crash
```
---
## Temporal Specialist
### TS_001 — Explicit ordering
```
Input: "Alice arrived before the meeting started."
Expected:
- before(EV_arrival, EV_meeting) emitted with strength=hard
- Both objects referenced exist in WorldState or new_objects
```
### TS_002 — Implicit sequence
```
Input: "The light turned green. The car moved."
Expected:
- before(EV_light_green, EV_car_move) emitted with strength=soft
```
### TS_003 — Time point attachment
```
Input: "The meeting started at 9am."
Expected:
- TemporalFacet on EV_meeting with time_point set
- starts_at(EV_meeting, T_9am) constraint emitted
```
### TS_004 — Domain adherence
```
Expected: no quantity constraints, no entity constraints emitted
```
---
## Cross-specialist invariants (apply to all specialists)
| invariant | check |
|---|---|
| Bounded output | len(new_constraints) <= BudgetSpec.max_constraints |
| Valid refs | all argument_refs resolve |
| Source module | all records have correct source_module |
| Provenance | len(provenance) >= len(new_facets) + len(new_constraints) |
| No WorldState mutation | input WorldState identical before and after call |
| Schema compliance | all outputs validate against schemas/facets.json and schemas/constraints.json |