Branch: 001-lattice-core | Date: 2026-05-24 | Spec: spec.md
Input: Feature specification from specs/001-lattice-core/spec.md
Build the Runcor Lattice — one autonomous cognitive entity that runs
continuously over long horizons, driving an LLM through the eight-phase cycle
(observe → ground → recall → decide → act → judge → write → pulse), governed
by an enforced substrate of eleven laws, supported by four distinct memory
systems, persisted as a single SQLite file per lattice, and operable via a
Vue 3 Bridge that lets a single local operator instantiate, observe, and
adjust running lattices.
The codebase is a TypeScript/Node 22 monorepo (pnpm workspaces + optional
turborepo). The lattice runtime is a library composed of ~17 cognitive
packages (substrate, memory, identity, goals, drives, temporal, jobs, two
deciders, watchdog, skills, trace, capabilities, snapshot, collaboration,
runtime, engine) plus the vendored rpp-parser. The runtime is driven by two
separate OS processes per lattice — apps/lattice (the fast clock) and
apps/slowclock (the slow clock) — sharing one SQLite file under a lock
file. The Bridge is two apps: bridge-api (Fastify) and bridge-ui (Vue 3
- Vite + Pinia). Every model call is built and validated as R++ via the parser; every cycle, correction, and decision is written to a JSONL trace plus an in-SQLite indexed store. Snapshot durability and model backend are both swappable behind small interfaces.
Build order follows the 15 vertical slices in intent spec §23 — each slice leaves the system runnable and is "done" only when its tests pass. Slice 1 is "one cycle end-to-end" with stubs everywhere; subsequent slices fill in real implementations.
Language/Version: TypeScript 5.x on Node 22 LTS. ESM modules across the
monorepo. Strict TypeScript mode (strict: true, noUncheckedIndexedAccess: true).
Primary Dependencies:
- pnpm workspaces (monorepo) + turborepo (task orchestration)
better-sqlite3^11 (entity store; synchronous, fast, prebuilt binaries)fastify^5 (Bridge HTTP API, schema-validated)zod^3 (runtime validation at every untrusted boundary)@modelcontextprotocol/sdk^1 (MCP perception/action + self-exposure + registry queries)@anthropic-ai/sdk^0.x (direct-API model backend default)vitest^2 (test runner, TypeScript-native)pino^9 (operational logging — distinct from the cognitive trace)- Vue 3 ^3.5,
vite^5,pinia^2 (Bridge UI) - The
rpp-parser(vendored fromruncor-ai/rpp-parser; pure TS, zero deps; attributed inpackages/rpp-parser/ATTRIBUTION.md)
Storage: SQLite via better-sqlite3. One file per lattice IS the entity
(constitution Principle II). All persistent state — identity, plan, episodic
memory, semantic memory, skills, cycle counter, dial positions, deferred
items, the trace's indexed store — lives in the single file. WAL mode for
crash safety. Snapshot durability is provided by a swappable module
(packages/snapshot) with default destination = local folder; a cloud-
bucket adapter ships as a v1 follow-up behind the same interface.
Testing: vitest with three layers:
- Unit tests live next to their source (
*.test.tscolocated). - Integration tests in
tests/integration/exercise multi-package flows (loop turns, resume parity, two-clock interaction, substrate enforcement). - E2E tests in
tests/e2e/drive the full stack via the Bridge HTTP API and a headless Vue runner.
Each build-order step is done when its tests pass (constitution Testing Discipline section). The resume-parity test is the single most important test in the suite (per intent spec §24 + constitution Principle II).
Target Platform: Cross-platform Node 22 (Windows 11, macOS 13+,
Ubuntu 22.04+). Bridge UI runs in any modern browser served locally by the
Bridge HTTP server on 127.0.0.1:<port>. No remote deployment in v1.
Project Type: TypeScript monorepo with packages + apps. The lattice
runtime is a composed library (~17 packages). The Bridge is two apps
(API + UI). Two CLI apps drive a lattice (lattice, slowclock).
Performance Goals (from spec Success Criteria):
- SC-001: ≥1,000 cycles unattended.
- SC-002: Resume within 5 seconds with logical-state equality.
- SC-003: Slow-clock cadence within ±10% of target (baseline ~100 cycles).
- SC-005: Single-lattice instantiation visible on roster in <10s.
- SC-006: Company of N lattices cycling within 5 minutes.
- SC-007: Deferred item live within 1 cycle of unblock detection.
- SC-008: Dial adjustment in effect within 2 cycles.
Constraints:
- Self-contained per intent §17: no external database service required to run a lattice; everything is in the SQLite file.
- MIT licensed (FR-057).
LICENSEfile at repo root. - Single-tenant local-only Bridge (FR-055): binds to 127.0.0.1.
- Per-lattice-lifetime budget enforcement (FR-056).
- All eight cycle phases execute in pinned order (FR-001).
- Logical state equality on resume (FR-007, clarified 2026-05-24).
- Every model call wrapped by substrate + validated as R++ (FR-018, FR-024).
- No shared memory between lattices (FR-044/045; constitution XIV).
- Two clocks as separate processes sharing one SQLite under a lock file (constitution VII).
Scale/Scope:
- 14 constitution principles (4 NON-NEGOTIABLE).
- 57 functional requirements; 13 user stories at three priority levels; 12 success criteria; 11 enumerated edge cases.
- 15 vertical slices per intent spec §23.
- ~17 packages + 4 apps + 2 test suites.
- Single-machine deployment is the primary target. Multi-lattice on one machine is supported (each owns its own SQLite file) but not the primary v1 ergonomics focus.
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
| Principle | Plan Compliance | Notes |
|---|---|---|
| I. Entity is the container | ✅ | The fast-clock loop in packages/runtime contains no internal exit condition. apps/lattice exits only on operator stop or unrecoverable substrate fault. |
| II. Database IS the entity (NON-NEGOTIABLE) | ✅ | One better-sqlite3 file per lattice. No package keeps side-databases. Snapshot module is the only thing that copies the file outward. |
| III. Lattice steers itself by judgement | ✅ | The runtime composes goals/drives/temporal/identity inputs into the ground and recall phases; decide is pure LLM judgement. No baked-in scheduler. |
| IV. Memory is drift control | ✅ | packages/memory exposes four genuinely separate stores (identity/plan/episodic/semantic) with distinct survival rules; episodic uses the exact decay formula. Recall = index-plus-selector by default. |
| V. Two tools for two problems (NON-NEGOTIABLE) | ✅ | See "Logic Classification" below. Discernment gate is code-first; the LLM fallback only fires where code is inconclusive. |
| VI. Eight cycle phases in order (PINNED) | ✅ | packages/runtime/src/cycle.ts exports a single runCycle() whose phase order is enforced by a type-level state machine (you can't call decide before recall). Tests assert order in trace entries. |
| VII. Two clocks, cycle-count cadence (PINNED) | ✅ | apps/lattice runs the fast clock; apps/slowclock is a separate process. They share one SQLite file. A packages/runtime/src/lockfile.ts primitive prevents two lattice processes on the same file. |
| VIII. Substrate is enforced physics (NON-NEGOTIABLE) | ✅ | packages/substrate exports wrapCall(prompt) → guardedPrompt and discern(output) → outcome. The lattice runtime calls model backends ONLY via the substrate-wrapped path. No read API. Skills cannot import the substrate package internals. |
| IX. R++ for every model call (NON-NEGOTIABLE) | ✅ | The model backend interface accepts a RppPrompt type built and validated by packages/rpp-parser. A string will not type-check. |
| X. Trace is mandatory | ✅ | packages/trace provides a Trace handle injected into every phase. JSONL writer + in-SQLite indexed store (one table). The Bridge reads the indexed store. |
| XI. Modular and swappable | ✅ | Interfaces declared in contracts/: Decider, ModelBackend, SnapshotDestination, Capability, Perception. Each has a default impl in its package. |
| XII. Admission rule for memory (PINNED) | ✅ | packages/memory/src/admission.ts is a gate function every write passes through. Tests assert re-perceivable facts are rejected. |
| XIII. Job completion is a layer with deferral | ✅ | packages/jobs models the checklist, completion checks, deferral with reason + unblock condition, partial completion. Sign-off path branches on the autonomy dial. |
| XIV. No shared memory between lattices (NON-NEGOTIABLE) | ✅ | Lattice instances are file-scoped; no global module state. Cross-lattice communication is via MCP only (packages/collaboration). |
| Technology Stack | ✅ | All listed dependencies match the constitution's pinned set. No substitutions in v1. |
| Development Workflow | ✅ | This very plan is the Spec Kit output. Tests land alongside code per Testing Discipline. |
Per Principle V, every component must be classified as deterministic code OR LLM judgement. The plan does this up front:
Deterministic code (the subconscious + flat checks):
- The discernment gate's per-law code checks (Reality, Translation, Constraint, Feedback, Memory, Compounding, Cost-Value, Simplicity → all code-first).
- The subconscious sweep in
writephase. - Completion-check deterministic hooks for job items.
- The admission rule (
packages/memory/src/admission.ts). - The episodic decay formula and threshold-based forget/promote.
- The lockfile primitive and snapshot transaction boundaries.
- R++ structural validation.
- The slow clock's cycle-count tick (not a wall-clock timer).
- Tool-discovery substrate-constraint matching (rule-based filter).
LLM judgement (work layer + sleep + targeted gate fallback):
- The
decidephase (single-model or dialectic). - The slow-clock memory consolidation pass ("the dream").
- The slow-clock drift review (and the watchdog's gap-detection it feeds).
- The discernment gate's LLM fallback when code is inconclusive.
- The completion-check judgement pass for irreducible items.
- Identity's reflective update.
- Goal proposal.
- Skill synthesis (specific + generic extractions).
- Recall's "which N memories are relevant" cheap selector pass.
Anything new added later must pick a side and justify it. Mixing is the forbidden case per Principle V.
✅ PASS — no violations identified at plan time. Complexity Tracking table is empty.
A re-check is scheduled after Phase 1 design (data-model.md, contracts/); the result is recorded at the bottom of this plan.
specs/001-lattice-core/
├── plan.md # This file
├── spec.md # Functional spec (already written)
├── research.md # Phase 0 output (written by this command)
├── data-model.md # Phase 1 output
├── quickstart.md # Phase 1 output
├── contracts/ # Phase 1 output (interface contracts)
│ ├── decider.md
│ ├── model-backend.md
│ ├── snapshot-destination.md
│ ├── capability.md
│ ├── perception.md
│ ├── bridge-http-api.md
│ └── mcp-self-exposure.md
├── checklists/
│ └── requirements.md # From /speckit-specify
└── tasks.md # /speckit-tasks output (next)
runcor-lattice/
├── LICENSE # MIT
├── README.md
├── package.json # Workspace root
├── pnpm-workspace.yaml
├── turbo.json
├── tsconfig.base.json
│
├── packages/
│ ├── rpp-parser/ # Vendored from runcor-ai/rpp-parser (MIT, attribution)
│ ├── substrate/ # 11 laws + discernment gate (Principle VIII)
│ ├── memory/ # Four memory systems + decay formula + admission rule
│ ├── identity/ # Identity memory + reflective update (uses decider)
│ ├── goals/ # Discovered intention stack
│ ├── drives/ # Motivational pulse (resource pressure, curiosity, reactivity, coherence)
│ ├── temporal/ # Cycles-based deadlines and commitments
│ ├── decider/ # Decider interface + single-model decider (default)
│ ├── dialectic/ # Multi-model decider (Player / Coach / Judge)
│ ├── watchdog/ # Slow-clock-only outside observer (needs-vs-tools gap)
│ ├── skills/ # Skill library, SKILL.md format, mint + recall
│ ├── trace/ # JSONL transcript + in-SQLite indexed store
│ ├── engine/ # Routing, retries, MCP plumbing, cost tracking,
│ │ # swappable model backends (direct API + host-CLI)
│ ├── capabilities/ # MCP/API connectors, sense/action wiring, tool discovery,
│ │ # MCP Registry client
│ ├── jobs/ # Checklist, completion checks, deferral, partial completion
│ ├── collaboration/ # MCP self-exposure, peer registry, conversation/delegation,
│ │ # read-only shared-source-of-truth client
│ ├── snapshot/ # Swappable snapshot module (local folder default,
│ │ # cloud-bucket adapter behind same interface)
│ ├── runtime/ # The lattice runtime: composes the above, runs the fast clock,
│ │ # owns cycle.ts (the eight-phase state machine),
│ │ # owns lockfile.ts, owns graceful-shutdown registry
│ ├── slowclock/ # The slow-clock worker logic (consolidation + drift review)
│ └── bridge-shared/ # Types shared between bridge-api and bridge-ui (Pinia stores,
│ # zod schemas, instantiation form types, dial types)
│
├── apps/
│ ├── lattice/ # CLI: `lattice start <config.json>`
│ │ # Runs the fast clock for one lattice. Long-lived process.
│ ├── slowclock/ # CLI: `slowclock attach <sqlite-path>`
│ │ # Runs the slow clock for one lattice. Separate process.
│ ├── bridge-api/ # Fastify server. Owns instantiation, roster, inspect, adjust.
│ │ # Spawns `lattice` and `slowclock` child processes.
│ └── bridge-ui/ # Vue 3 + Vite + Pinia. Single-page app. Served by bridge-api.
│
├── prebuilt/ # Slice-15 deliverable: prebuilt role lattices
│ ├── ceo/ # Each role = seed-prompt.rpp + starting-knowledge.json +
│ ├── cfo/ # defaults.json (dial defaults + tool manifest)
│ ├── marketing/
│ └── sales/
│
├── tests/
│ ├── integration/ # Cross-package: loop turns, resume parity, two clocks,
│ │ # substrate enforcement, memory decay, jobs + deferral,
│ │ # skill mint + recall, collaboration
│ └── e2e/ # Full stack: Bridge instantiates → cycles → operator adjusts
│
├── .specify/ # Spec Kit scaffold (already in place)
├── .claude/ # Operator skill (Anthropic skill format)
└── runcor-lattice-intent-spec.md # Source of truth
Structure Decision: Monorepo with packages/ (libraries) + apps/
(executables) + prebuilt/ (data) + tests/ (integration & e2e),
selected for the following reasons:
- Principle XI (modular/swappable) is satisfied at the package boundary: each major piece is a workspace package with its own interface, default impl, and tests. Swapping the decider, model backend, or snapshot destination touches one package, not the runtime.
- Principle II (single SQLite file) is satisfied: every package
takes a
Dbhandle in its constructor; no package opens its own database connection at module-load time. The runtime opens the file once and passes the handle in. - Principle VII (two processes) is satisfied:
apps/latticeandapps/slowclockare siblings sharing the samepackages/*dependencies but running as separate Node processes, coordinated via the SQLite file + lockfile. - Bridge ↔ runtime isolation: the Bridge spawns
latticeandslowclockas child processes; it doesn't import the runtime in- process. This keeps Principle XI honest at the runtime↔Bridge boundary too — a different Bridge could drive the same runtime by spawning the same CLI. bridge-sharedlets the Vue UI and the Fastify API agree on payload shapes via shared zod schemas without the UI bundling Node- only code.
Constitution Check passed with no unjustified violations. Table empty.
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| (none) |
Phase 0 — Research: see research.md. Reuse patterns and
attribution table for the runcor reference repos.
Phase 1 — Design & Contracts:
data-model.md— SQLite schema, JSONL trace shape, SKILL.md frontmatter, dial registry.contracts/— small interfaces for decider, model backend, snapshot destination, capability, perception, Bridge HTTP API, and MCP self-exposure.quickstart.md— "how an operator instantiates a lattice in 60 seconds".
Re-evaluated after Phase 1 artifacts were written:
| Concern | Status |
|---|---|
The contracts expose RppPrompt (not string) on every model-bearing interface |
✅ |
The contracts give Decider, ModelBackend, SnapshotDestination, Capability, Perception each a single small interface with a default impl |
✅ |
| The data model puts every persistent surface (memory, plan, identity, skills, dials, trace index) inside one SQLite file per lattice | ✅ |
| The data model omits any cross-lattice table or shared schema | ✅ |
| The data model identifies the deterministic vs LLM logic at the table level (which rows are written by code, which by reasoning) | ✅ |
| The Bridge HTTP API surface contains only the four pinned operations (instantiate, roster, inspect, adjust) | ✅ |
✅ PASS post-design. Ready for /speckit-tasks.
Run /speckit-tasks to generate the dependency-ordered task breakdown
mapped to the intent spec §23 build-order slices.