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:
133
resolver/contract.md
Normal file
133
resolver/contract.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Resolver Interface Contract
|
||||
|
||||
The resolver is the UCWM core. It receives WorldState after specialists have attached their facets and constraints, and it produces a stabilized WorldState ready for synthesis.
|
||||
|
||||
---
|
||||
|
||||
## Design principles
|
||||
|
||||
1. **Never adds meaning.** The resolver does not infer new facts beyond what constraint propagation and arithmetic require. It resolves existing constraints; it does not guess.
|
||||
|
||||
2. **Contradiction-explicit.** When constraints contradict, the resolver records the contradiction in `open_contradictions` rather than silently discarding either side.
|
||||
|
||||
3. **Multiple hypotheses.** When ambiguity is unresolvable, the resolver maintains multiple hypotheses in WorldState rather than forcing a single resolution.
|
||||
|
||||
4. **Monotonic confidence.** Constraint reinforcement raises confidence. Contradiction lowers confidence. Confidence never increases without supporting evidence.
|
||||
|
||||
5. **Traceable.** Every operation the resolver performs is recorded in `resolution_log` with the affected refs and the reason.
|
||||
|
||||
---
|
||||
|
||||
## Input
|
||||
|
||||
```
|
||||
ResolverInput {
|
||||
world_state: WorldState -- post-specialist state, stage = "post_specialist"
|
||||
resolution_mode: string -- "single_pass" | "iterative" | "conflict_checking"
|
||||
max_iterations: integer? -- for iterative mode (default: 5)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
```
|
||||
ResolverOutput {
|
||||
resolved_state: WorldState -- stage = "resolved"
|
||||
resolution_log: ResolutionEntry[]
|
||||
open_contradictions: ContradictionRecord[]
|
||||
confidence_summary: ConfidenceSummary
|
||||
}
|
||||
|
||||
ResolutionEntry {
|
||||
operation: string -- see Operations below
|
||||
affected_refs: string[]
|
||||
result: string -- "resolved" | "contradicted" | "suspended" | "merged" | "split"
|
||||
confidence_after: float?
|
||||
note: string?
|
||||
}
|
||||
|
||||
ContradictionRecord {
|
||||
constraint_refs: string[] -- constraints that contradict each other
|
||||
description: string
|
||||
resolution_attempt: string? -- what the resolver tried before giving up
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Operations
|
||||
|
||||
The resolver applies operations in this order:
|
||||
|
||||
### 1. Validate all refs
|
||||
|
||||
Before any operation, verify all `argument_refs` and `object_refs` resolve to objects in WorldState. Emit warnings for any dangling refs. Do not proceed with a constraint that has dangling refs.
|
||||
|
||||
### 2. Apply hard constraints
|
||||
|
||||
Apply all constraints with `strength: hard`. If two hard constraints contradict, record the contradiction immediately and mark both as `contradicted`. Do not attempt to resolve hard-vs-hard contradictions silently.
|
||||
|
||||
### 3. Resolve arithmetic
|
||||
|
||||
Apply `quantity_difference`, `quantity_sum`, `quantity_product`, `quantity_quotient`, `quantity_equals` where all operands are known exact values. Update the result quantity object's QuantityFacet `value` field.
|
||||
|
||||
### 4. Propagate truth values
|
||||
|
||||
Apply `truth_value` constraints. Propagate via `implies` chains. If `implies(P1, P2)` and P1 is true, mark P2 as true. If P1 is false, no propagation (modus tollens requires explicit encoding).
|
||||
|
||||
### 5. Resolve coreference
|
||||
|
||||
Apply `same_entity` constraints. For each pair (A, B) with `same_entity(A, B)`:
|
||||
- Merge B into A (or A into B, by priority: higher confidence object survives)
|
||||
- Set the lower-confidence object to `status: merged`, `merged_into: <surviving_id>`
|
||||
- Transfer all facet_refs and constraint_refs to the surviving object
|
||||
|
||||
### 6. Detect contradictions from soft/defeasible constraints
|
||||
|
||||
Apply soft and defeasible constraints. When a soft constraint contradicts a hard constraint, suspend the soft constraint. When two soft constraints contradict, mark both with lower confidence and record in `open_contradictions`.
|
||||
|
||||
### 7. Confidence propagation
|
||||
|
||||
For each constraint that was resolved:
|
||||
- Increase confidence on objects/facets that the constraint reinforces
|
||||
- Decrease confidence on objects/facets that the constraint contradicts
|
||||
|
||||
Confidence deltas must be bounded: a single constraint may not move confidence by more than 0.15 in either direction.
|
||||
|
||||
### 8. Produce resolved WorldState
|
||||
|
||||
Update all constraint statuses. Objects with `status: proposed` that have at least one resolved constraint referencing them may be upgraded to `status: active`. Mark WorldState stage as `resolved`.
|
||||
|
||||
---
|
||||
|
||||
## Resolution modes
|
||||
|
||||
**single_pass:** Apply all operations once. Fast, suitable for simple inputs.
|
||||
|
||||
**iterative:** Apply operations repeatedly until no new resolutions occur or `max_iterations` is reached. Use for inputs with chains of implications.
|
||||
|
||||
**conflict_checking:** Extra pass after resolution: verify that no two hard constraints in `resolved` status contradict each other. Emit detailed contradiction records if found. Use when input may contain adversarial or self-contradictory statements.
|
||||
|
||||
---
|
||||
|
||||
## Invariants
|
||||
|
||||
1. **No new objects.** The resolver does not propose new objects. It may merge or split existing ones, but may not create new objects from thin air.
|
||||
|
||||
2. **Idempotent application.** Applying a resolved constraint again does not change WorldState.
|
||||
|
||||
3. **Contradiction is never silent.** Every detected contradiction must appear in `open_contradictions` or in `resolution_log` with `result: contradicted`.
|
||||
|
||||
4. **Log completeness.** Every state change must have a corresponding entry in `resolution_log`.
|
||||
|
||||
---
|
||||
|
||||
## Contract test requirements
|
||||
|
||||
- Given worldstate with `quantity_difference(Q3, Q1, Q2)`, Q1.value=5, Q2.value=2: resolver must set Q3.value=3 and mark C3 resolved
|
||||
- Given `same_entity(E1, E3)`, E1.confidence=0.97, E3.confidence=0.72: E3 must be merged into E1
|
||||
- Given `before(EV1, EV2)` and `before(EV2, EV1)` (cycle): must emit a ContradictionRecord, not silently resolve
|
||||
- `resolution_log` must be non-empty after any state change
|
||||
- All constraints must have updated `status` after resolver runs
|
||||
256
resolver/operations.md
Normal file
256
resolver/operations.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# Resolver Operations Reference
|
||||
|
||||
Detailed pseudocode for each resolver operation. These are language-agnostic specifications; any implementation must produce equivalent results.
|
||||
|
||||
---
|
||||
|
||||
## Op 1: ValidateRefs
|
||||
|
||||
```
|
||||
procedure ValidateRefs(world_state):
|
||||
warnings = []
|
||||
for each constraint C in world_state.constraints:
|
||||
for each ref in C.argument_refs:
|
||||
if ref not in world_state.objects:
|
||||
warnings.append(f"Dangling ref {ref} in constraint {C.constraint_id}")
|
||||
mark C.status = suspended
|
||||
for each facet F in world_state.facets:
|
||||
if F.object_ref not in world_state.objects:
|
||||
warnings.append(f"Dangling object_ref {F.object_ref} in facet {F.facet_id}")
|
||||
return warnings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Op 2: ApplyHardConstraints
|
||||
|
||||
```
|
||||
procedure ApplyHardConstraints(world_state):
|
||||
hard_constraints = [C for C in world_state.constraints where C.strength = "hard" and C.status = "unresolved"]
|
||||
|
||||
for each C in hard_constraints:
|
||||
conflicts = [C2 for C2 in hard_constraints
|
||||
where C2 ≠ C
|
||||
and C2.status = "unresolved"
|
||||
and DirectlyContradicts(C, C2)]
|
||||
if conflicts:
|
||||
for C2 in conflicts:
|
||||
mark C.status = "contradicted"
|
||||
mark C2.status = "contradicted"
|
||||
add ContradictionRecord(constraint_refs=[C.id, C2.id])
|
||||
else:
|
||||
mark C.status = "resolved"
|
||||
log ResolutionEntry(operation="apply_hard", affected_refs=C.argument_refs, result="resolved")
|
||||
```
|
||||
|
||||
```
|
||||
function DirectlyContradicts(C1, C2):
|
||||
-- Same type, same arguments, opposite polarity
|
||||
if C1.constraint_type = C2.constraint_type
|
||||
and C1.argument_refs = C2.argument_refs
|
||||
and C1.polarity ≠ C2.polarity:
|
||||
return true
|
||||
-- Known incompatible pairs
|
||||
if {C1.constraint_type, C2.constraint_type} = {"before", "after"}
|
||||
and C1.argument_refs = reverse(C2.argument_refs):
|
||||
return true
|
||||
return false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Op 3: ResolveArithmetic
|
||||
|
||||
```
|
||||
procedure ResolveArithmetic(world_state):
|
||||
arithmetic_types = {
|
||||
"quantity_difference": (result, minuend, subtrahend) -> minuend - subtrahend,
|
||||
"quantity_sum": (result, *addends) -> sum(addends),
|
||||
"quantity_product": (result, *factors) -> product(factors),
|
||||
"quantity_quotient": (result, dividend, divisor) -> dividend / divisor,
|
||||
"quantity_equals": (result, value) -> value
|
||||
}
|
||||
|
||||
for each C in world_state.constraints where C.constraint_type in arithmetic_types:
|
||||
if C.status ≠ "unresolved":
|
||||
continue
|
||||
|
||||
arg_objects = [world_state.objects[ref] for ref in C.argument_refs]
|
||||
result_obj = arg_objects[0]
|
||||
operands = arg_objects[1:]
|
||||
|
||||
operand_values = [GetQuantityValue(O) for O in operands]
|
||||
|
||||
if all(V is not None and is_exact(V) for V in operand_values):
|
||||
fn = arithmetic_types[C.constraint_type]
|
||||
computed = fn(*operand_values)
|
||||
|
||||
SetQuantityValue(result_obj, computed)
|
||||
result_facet = GetOrCreateQuantityFacet(result_obj)
|
||||
result_facet.value = computed
|
||||
result_facet.is_exact = true
|
||||
result_facet.derived_from_refs = [O.object_id for O in operands]
|
||||
|
||||
mark C.status = "resolved"
|
||||
log ResolutionEntry(operation="resolve_arithmetic",
|
||||
affected_refs=[result_obj.object_id],
|
||||
result="resolved",
|
||||
note=f"computed {computed}")
|
||||
else:
|
||||
-- operands not yet resolved; defer
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Op 4: PropagateTruth
|
||||
|
||||
```
|
||||
procedure PropagateTruth(world_state):
|
||||
implies_constraints = [C for C in world_state.constraints
|
||||
where C.constraint_type = "implies" and C.status = "resolved"]
|
||||
|
||||
changed = true
|
||||
while changed:
|
||||
changed = false
|
||||
for each C in implies_constraints:
|
||||
antecedent_id, consequent_id = C.argument_refs
|
||||
antecedent = world_state.objects[antecedent_id]
|
||||
consequent = world_state.objects[consequent_id]
|
||||
|
||||
if GetTruthStatus(antecedent) = "true":
|
||||
if GetTruthStatus(consequent) ≠ "true":
|
||||
SetTruthStatus(consequent, "true")
|
||||
log ResolutionEntry(operation="propagate_truth",
|
||||
affected_refs=[consequent_id],
|
||||
result="resolved",
|
||||
note=f"derived from {antecedent_id} via implies constraint {C.id}")
|
||||
changed = true
|
||||
|
||||
if GetTruthStatus(antecedent) = "false":
|
||||
-- modus tollens: do not propagate without explicit negation constraint
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Op 5: ResolveCoreference
|
||||
|
||||
```
|
||||
procedure ResolveCoreference(world_state):
|
||||
same_entity_constraints = [C for C in world_state.constraints
|
||||
where C.constraint_type = "same_entity" and C.status = "resolved"]
|
||||
|
||||
for each C in same_entity_constraints:
|
||||
id_a, id_b = C.argument_refs
|
||||
obj_a = world_state.objects[id_a]
|
||||
obj_b = world_state.objects[id_b]
|
||||
|
||||
if obj_a.confidence >= obj_b.confidence:
|
||||
survivor = obj_a
|
||||
deprecated = obj_b
|
||||
else:
|
||||
survivor = obj_b
|
||||
deprecated = obj_a
|
||||
|
||||
-- Transfer all refs from deprecated to survivor
|
||||
for ref in deprecated.facet_refs:
|
||||
world_state.facets[ref].object_ref = survivor.object_id
|
||||
survivor.facet_refs.append(ref)
|
||||
|
||||
for ref in deprecated.constraint_refs:
|
||||
constraint = world_state.constraints[ref]
|
||||
replace deprecated.object_id with survivor.object_id in constraint.argument_refs
|
||||
survivor.constraint_refs.append(ref)
|
||||
|
||||
-- Transfer aliases
|
||||
survivor.aliases += deprecated.aliases + [deprecated.canonical_label]
|
||||
|
||||
deprecated.status = "merged"
|
||||
deprecated.merged_into = survivor.object_id
|
||||
|
||||
log ResolutionEntry(operation="merge_coreference",
|
||||
affected_refs=[survivor.object_id, deprecated.object_id],
|
||||
result="merged",
|
||||
note=f"merged {deprecated.object_id} into {survivor.object_id}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Op 6: DetectSoftContradictions
|
||||
|
||||
```
|
||||
procedure DetectSoftContradictions(world_state):
|
||||
soft_constraints = [C for C in world_state.constraints
|
||||
where C.strength in {"soft", "defeasible"} and C.status = "unresolved"]
|
||||
|
||||
for each C in soft_constraints:
|
||||
-- Check against hard resolved constraints first
|
||||
hard_conflicts = [C2 for C2 in world_state.constraints
|
||||
where C2.strength = "hard" and C2.status = "resolved"
|
||||
and DirectlyContradicts(C, C2)]
|
||||
if hard_conflicts:
|
||||
mark C.status = "suspended"
|
||||
log ResolutionEntry(operation="suspend_soft",
|
||||
affected_refs=C.argument_refs,
|
||||
result="suspended",
|
||||
note=f"overridden by hard constraint {hard_conflicts[0].id}")
|
||||
continue
|
||||
|
||||
-- Check against other soft constraints
|
||||
soft_conflicts = [C2 for C2 in soft_constraints
|
||||
where C2 ≠ C and DirectlyContradicts(C, C2)]
|
||||
if soft_conflicts:
|
||||
C.confidence *= 0.7
|
||||
for C2 in soft_conflicts:
|
||||
C2.confidence *= 0.7
|
||||
add ContradictionRecord(constraint_refs=[C.id] + [C2.id for C2 in soft_conflicts],
|
||||
description="Soft constraint conflict, confidence reduced")
|
||||
else:
|
||||
mark C.status = "resolved"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Op 7: PropagateConfidence
|
||||
|
||||
```
|
||||
procedure PropagateConfidence(world_state):
|
||||
REINFORCE_DELTA = 0.05
|
||||
CONTRADICT_DELTA = 0.10
|
||||
MAX_DELTA = 0.15
|
||||
|
||||
for each constraint C where C.status = "resolved":
|
||||
for ref in C.argument_refs:
|
||||
obj = world_state.objects.get(ref)
|
||||
if obj:
|
||||
delta = min(REINFORCE_DELTA * C.confidence, MAX_DELTA)
|
||||
obj.confidence = min(1.0, obj.confidence + delta)
|
||||
|
||||
for each constraint C where C.status = "contradicted":
|
||||
for ref in C.argument_refs:
|
||||
obj = world_state.objects.get(ref)
|
||||
if obj:
|
||||
delta = min(CONTRADICT_DELTA, MAX_DELTA)
|
||||
obj.confidence = max(0.0, obj.confidence - delta)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Op 8: FinalizeState
|
||||
|
||||
```
|
||||
procedure FinalizeState(world_state):
|
||||
-- Promote high-confidence proposed objects
|
||||
for each obj in world_state.objects where obj.status = "proposed":
|
||||
resolved_constraints = [C for C in world_state.constraints
|
||||
where obj.object_id in C.argument_refs
|
||||
and C.status = "resolved"]
|
||||
if obj.confidence >= 0.75 and len(resolved_constraints) >= 1:
|
||||
obj.status = "active"
|
||||
log ResolutionEntry(operation="promote_object",
|
||||
affected_refs=[obj.object_id],
|
||||
result="resolved")
|
||||
|
||||
world_state.stage = "resolved"
|
||||
```
|
||||
Reference in New Issue
Block a user