- 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>
280 lines
8.8 KiB
Markdown
280 lines
8.8 KiB
Markdown
# Implementation Constraints (v1.1)
|
||
|
||
These constraints apply to every module in the system without exception. They are architectural rules, not guidelines. A module that violates any of them is not a valid UCWM module, regardless of whether it produces correct answers.
|
||
|
||
---
|
||
|
||
## 1. No hidden state between modules
|
||
|
||
Every module must:
|
||
- Accept only typed inputs defined by its contract
|
||
- Produce only typed outputs defined by its contract
|
||
- Not rely on hidden global context
|
||
- Not rely on implicit latent carryover from prior calls
|
||
|
||
If a module needs information, it must be passed explicitly through the WorldState or arguments. A module that reads from a global variable or internal cache that is not visible in WorldState is invalid.
|
||
|
||
---
|
||
|
||
## 2. WorldState is the source of truth
|
||
|
||
All state lives in WorldState. Modules must not:
|
||
- Store their own internally evolving state across calls
|
||
- Cache interpretations across calls unless that cache is explicitly registered as a field in WorldState
|
||
- Mutate shared objects directly — all changes must be returned as typed outputs and applied by the receiving layer
|
||
|
||
All changes take the form:
|
||
```
|
||
Operation {
|
||
type: "propose" | "merge" | "refine" | "link" | "suppress"
|
||
target_refs: string[]
|
||
payload: typed payload per operation type
|
||
confidence_delta: float?
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Every operation must be logged
|
||
|
||
All module outputs must include provenance. The provenance record must be sufficient to reconstruct:
|
||
|
||
```
|
||
final_answer → constraints → facets → objects → source text
|
||
```
|
||
|
||
Every Constraint, Facet, and Object created or modified must have a corresponding ProvenanceRecord with:
|
||
```
|
||
ProvenanceRecord {
|
||
source_module: string
|
||
input_refs: string[]
|
||
output_refs: string[]
|
||
reasoning_tag: string -- short label for what operation was performed
|
||
}
|
||
```
|
||
|
||
See `schemas/provenance.json` for the normative schema.
|
||
|
||
---
|
||
|
||
## 4. Bounded output enforcement
|
||
|
||
Every module must declare and enforce explicit output caps in its manifest. No module may produce unbounded output.
|
||
|
||
Default caps (may be overridden per module manifest):
|
||
```
|
||
max_objects_proposed: 8
|
||
max_constraints_emitted: 16
|
||
max_facets_emitted: 8
|
||
max_merges: 4
|
||
max_splits: 2
|
||
```
|
||
|
||
If a module reaches a cap, it must stop and return what it has, not raise an error and discard all output. Partial output is valid. Unbounded output is not.
|
||
|
||
Gates have separate caps: a gate emits exactly 1 GateSignal. No cap needed — it is structurally enforced.
|
||
|
||
---
|
||
|
||
## 5. No free-form text past the perception layer
|
||
|
||
After the perception / extraction stage:
|
||
- No module may output free-form natural language as a primary output
|
||
- All outputs must be structured, typed, and schema-compliant
|
||
- Diagnostic `notes` fields in logs may contain free text, but these are not part of the reasoning chain
|
||
|
||
Only the final synthesis layer may produce language as output.
|
||
|
||
This rule applies to constraint `expression` fields too — they are informational annotations, not authoritative outputs.
|
||
|
||
---
|
||
|
||
## 6. First-class error states
|
||
|
||
Every module must be able to return explicit error states. Forcing a resolution when insufficient information exists is a contract violation.
|
||
|
||
Valid first-class output states for any object, facet, or constraint:
|
||
|
||
| State | Meaning |
|
||
|---|---|
|
||
| `unknown` | The module could not determine a value |
|
||
| `ambiguous` | Multiple valid interpretations exist |
|
||
| `conflicting` | Two or more inputs contradict each other |
|
||
| `insufficient_data` | Not enough evidence to propose anything |
|
||
|
||
These states must be returned as typed fields, not inferred from missing values or nulls.
|
||
|
||
Example — SpecialistError type:
|
||
```
|
||
SpecialistError {
|
||
error_kind: "unknown" | "ambiguous" | "conflicting" | "insufficient_data"
|
||
affected_refs: string[]
|
||
description: string
|
||
}
|
||
```
|
||
|
||
The resolver preserves and propagates error states rather than collapsing them.
|
||
|
||
---
|
||
|
||
## 7. Constraint-first thinking
|
||
|
||
Specialists should prefer emitting constraints over conclusions.
|
||
|
||
Wrong:
|
||
```
|
||
Q_result.value = 3
|
||
```
|
||
|
||
Right:
|
||
```
|
||
quantity_difference(Q_result, Q1, Q2) -- status: unresolved
|
||
```
|
||
|
||
Resolution happens in the UCWM resolver, not inside specialists. A specialist that resolves arithmetic or logical conclusions itself before emitting constraints is bypassing the resolver contract.
|
||
|
||
The exception: arithmetic that is provably deterministic with all operands present may be resolved inline by the quantity specialist, with the resolution logged in provenance.
|
||
|
||
---
|
||
|
||
## 8. Test-driven module design
|
||
|
||
For every module, the following test classes must exist before the module is considered complete:
|
||
|
||
| Test class | Validates |
|
||
|---|---|
|
||
| Input → output correctness | Expected outputs for known inputs |
|
||
| Schema compliance | All outputs validate against declared schemas |
|
||
| Bounded output | Output never exceeds declared caps |
|
||
| Deterministic behavior | Same input → same output (where determinism is expected) |
|
||
| Failure modes | Module returns valid error states on ambiguous/missing data |
|
||
| Domain adherence | Module does not emit facet/constraint kinds outside its manifest |
|
||
|
||
See `tests/contracts/` for templates.
|
||
|
||
---
|
||
|
||
## 9. Minimal vertical slice priority
|
||
|
||
Do not build everything before validating anything.
|
||
|
||
The first working pipeline must support:
|
||
```
|
||
Input sentence
|
||
→ gate signals
|
||
→ object extraction (1–3 objects)
|
||
→ 1–2 specialists
|
||
→ constraints
|
||
→ resolution
|
||
→ correct answer
|
||
```
|
||
|
||
Suggested first domain: math word problems or simple transfer/state-change problems.
|
||
|
||
Expand coverage only after the vertical slice produces correct, inspectable, testable output.
|
||
|
||
---
|
||
|
||
## 10. Language binding stubs
|
||
|
||
Two interface stubs must exist from the start, even if naive:
|
||
|
||
```
|
||
to_ucwm(text_input: string) → WorldState
|
||
from_ucwm(world_state: WorldState) → string
|
||
```
|
||
|
||
See `synthesis/language_bindings.md` for the full interface specification.
|
||
|
||
These stubs enforce that translation and generation remain separable from reasoning. The reasoning core must never depend on how language enters or exits the system.
|
||
|
||
---
|
||
|
||
## 11. Composite routing must be multi-label
|
||
|
||
The router must activate multiple specialists simultaneously. Single-label routing is not valid for real-world inputs.
|
||
|
||
A valid RoutingDecision activates:
|
||
```
|
||
temporal: true
|
||
causal: true
|
||
quantity: true
|
||
```
|
||
|
||
Not:
|
||
```
|
||
category: "quantity" ← wrong; exactly one specialist selected
|
||
```
|
||
|
||
See `schemas/routing.json#/$defs/RoutingDecision` — the `assignments[]` array is the mechanism.
|
||
|
||
---
|
||
|
||
## 12. Facets must be independent
|
||
|
||
A facet describes only its own dimension. It must not modify or depend on another facet's fields.
|
||
|
||
A TemporalFacet must not modify ownership state. A QuantityFacet must not set truth status. Cross-facet interaction happens only through constraints, never through direct facet-to-facet references.
|
||
|
||
---
|
||
|
||
## 13. Resolver must be replaceable
|
||
|
||
The resolver is defined by an interface, not an implementation:
|
||
|
||
```
|
||
resolve(world_state: WorldState) → WorldState
|
||
```
|
||
|
||
The rest of the pipeline must not depend on resolver internals. Any implementation that satisfies `tests/contracts/resolver_contract_tests.md` is a valid resolver. This makes it possible to swap:
|
||
|
||
- Simple rule-based resolver
|
||
- Graph-based constraint solver
|
||
- Probabilistic constraint solver
|
||
- SAT-style system
|
||
|
||
---
|
||
|
||
## 14. Keep implementations simple first
|
||
|
||
Prefer clarity over performance. Prefer correctness over cleverness. This system will become complex naturally — do not add complexity early.
|
||
|
||
Guidelines:
|
||
- Use simple, flat data structures where possible
|
||
- Avoid premature abstraction
|
||
- An obvious implementation that is slow is better than a clever one that is wrong
|
||
- Performance optimization begins after contract tests pass
|
||
|
||
Language choice is not prescribed. Whatever language is chosen, it must support schema validation of WorldState at every module boundary.
|
||
|
||
---
|
||
|
||
## 15. Debug mode
|
||
|
||
Every implementation must support a debug mode that emits a step-by-step trace:
|
||
|
||
```
|
||
[Step 1] Gate outputs: {gate_id: activated, confidence}
|
||
[Step 2] Objects proposed: {object_id, kind, label, confidence}
|
||
[Step 3] Routing decision: {specialist_id, target_refs, budget}
|
||
[Step 4] Specialist outputs: {specialist_id, new_facets[], new_constraints[]}
|
||
[Step 5] Constraints: {constraint_id, type, args, strength, status}
|
||
[Step 6] Resolution log: {operation, affected_refs, result}
|
||
[Step 7] Final answer: {answer, confidence, derivation_trace}
|
||
```
|
||
|
||
Debug mode must write to a structured log (JSON or equivalent), not only to stdout. The log must be sufficient to replay and inspect any inference.
|
||
|
||
---
|
||
|
||
## Priority order
|
||
|
||
When any two implementation choices conflict, resolve by this priority:
|
||
|
||
1. Contracts (schema compliance, interface adherence)
|
||
2. Testability (can this behavior be verified independently?)
|
||
3. Inspectability (can intermediate state be read and debugged?)
|
||
4. Correctness (does it produce right answers?)
|
||
5. Performance (how fast?)
|
||
6. Scale (how big?)
|