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:
119
tests/contracts/gate_contract_tests.md
Normal file
119
tests/contracts/gate_contract_tests.md
Normal 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.
|
||||
120
tests/contracts/resolver_contract_tests.md
Normal file
120
tests/contracts/resolver_contract_tests.md
Normal 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.
|
||||
```
|
||||
135
tests/contracts/specialist_contract_tests.md
Normal file
135
tests/contracts/specialist_contract_tests.md
Normal 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 |
|
||||
Reference in New Issue
Block a user