Files
ucwm/docs/implementation_constraints.md
bill b758d7ea60 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>
2026-05-01 16:09:55 -07:00

8.8 KiB
Raw Blame History

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 (13 objects)
  → 12 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?)