ADR 0018 — Canonical Haskell Module Tree
Cortex and the downstream Logos repository organize Haskell modules by canonical substrate and reasoning-library layers, not implementation-era roots such as Agent, Task, Run, Provider, Json, or Document.
On this page
- Status
- Context
- Decision
- Canonical Module Ownership
- Cortex.Algebra
- Cortex.Wire
- Cortex.Pulse
- Cortex.Capability
- Cortex.Artifact
- Logos
- Executor Boundary
- Migration Map
- Import Direction
- Cabal Exposure Policy
- Migration Slices
- Consequences
- Alternatives Considered
- Keep current roots and document them as staging
- Move everything in one large rename commit
- Preserve all old roots indefinitely as public compatibility modules
- Put all reasoning support under Capability
- Keep Document as a canonical root
- Open Questions
- Related
ADR 0018 — Canonical Haskell Module Tree
Status
Proposed - records the concrete Haskell module tree and migration policy for the final src/Cortex
root refactor. ADR 0040 narrows this ADR after implementation: the current public Cortex Haskell
roots are substrate roots only, and concrete reasoning/provider/report surfaces live in Logos.
Context
The current src/Cortex tree still exposes several implementation-era roots:
Cortex.AgentCortex.ProviderCortex.RunCortex.TaskCortex.ResearchCortex.Json- root
Cortex.Document - root
Cortex.Memory Cortex.MemoryCompaction- root
Cortex.Graph - root
Cortex.Circuit Cortex.EventandCortex.Events
Some of these names describe mechanisms at the wrong architectural layer. Agent, Task, Run,
and Research mostly describe model-mediated reasoning behavior, which belongs above the substrate
in Logos. Provider is external authority, which belongs under Cortex.Capability. Document
mixes generic artifact substrate with report/research semantics. Memory mixes cognitive context
construction with durable Pulse state. Json and Text are generic support utilities rather than
Cortex concepts. Graph is the law-bearing algebra, and Circuit is Wire’s compiled form.
ADR 0016 already decides the canonical public root taxonomy:
src/Cortex
|-- Algebra
|-- Wire
|-- Pulse
`-- Capability
Digimuoto/logos:src/Logos
|-- Archetypes
|-- Thought
|-- Memory
`-- Patterns
The remaining decision is how strict to make the Haskell tree and how to migrate current consumers without preserving the old roots as first-class vocabulary.
Decision
The canonical Cortex Haskell tree under src/Cortex is organized by substrate layer:
Cortex
|-- Algebra -- law-bearing graph/relation algebra
|-- Wire -- source language, contracts, ports, compiled circuit form
|-- Pulse -- durable execution runtime, run state, events, persistence
`-- Capability -- executor registration and native pure-executor capability surfaces
The canonical Logos Haskell tree under the downstream Digimuoto/logos repository’s src/Logos
tree is organized by reasoning-library layer:
Logos
|-- Archetypes -- epistemological modes and activation bundles
|-- Thought -- one model-mediated node evaluation
|-- Memory -- cognitive context construction
`-- Patterns -- reusable reasoning programs
Only the Cortex roots should be treated as canonical public Cortex roots. Logos is public through
the separate downstream logos package, not through a Cortex Cabal component. A root module such as
Cortex.Wire or Logos may be an umbrella module for its own component. Other root-level modules
are either temporary compatibility shims, test fixtures, or private implementation details that
should move under the nearest canonical root.
Compatibility shims may remain only when there is a concrete migration need. A shim must be shallow: it re-exports the canonical module, contains no independent logic, and is documented in the migration table or module header as temporary. New code must import the canonical path.
Generic support code that is not a Cortex concept belongs outside the public Cortex root. JSON,
text, HTTP, database, retry, observability, and durable-task support should live under Platform.*
when reusable, or under a canonical Cortex.<Layer>.Internal.* module when it is layer-specific.
Canonical Module Ownership
Cortex.Algebra
Cortex.Algebra owns pure graph and relation algebra:
- graph construction and validation
- relation operations
- Mokhov algebra laws
- rewrite laws and validation helpers that do not depend on Wire syntax or Pulse runtime state
Target examples:
Cortex.Algebra.GraphCortex.Algebra.Graph.MokhovCortex.Algebra.Relation
Root Cortex.Graph and Cortex.Graph.* are migration shims once the canonical modules exist.
Cortex.Wire
Cortex.Wire owns the authoring and compiled-program layer:
- Wire syntax, parser, compiler, and reference grammar
- contracts, ports, payload kinds, and runtime envelopes
- executor references and compile-time executor projections
- compiled circuit form produced from Wire
- Wire rewrite/lowering rules that are language semantics rather than durable runtime behavior
The compiled circuit form moves under Wire because it is the executable shape of a Wire program, not a separate architectural root.
The active Wire syntax implementation is canonical Wire syntax, not a versioned Haskell namespace.
Version labels may appear in reference grammar documents for spec history, but Haskell modules
should expose the accepted syntax through Cortex.Wire.Syntax, Cortex.Wire.Parser, and
Cortex.Wire.Compile rather than Cortex.Wire.V1.*.
Target examples:
Cortex.Wire.SyntaxCortex.Wire.ParserCortex.Wire.CompileCortex.Wire.ContractCortex.Wire.ExecutorCortex.Wire.PortCortex.Wire.ValueCortex.Wire.Circuit.IRCortex.Wire.Circuit.Lower
Root Cortex.Circuit and Cortex.Circuit.* are migration shims once the canonical modules exist.
Cortex.Pulse
Cortex.Pulse owns durable execution:
- run ids, attempts, checkpoints, replay, resume, scheduler, frontier, and persistence
- execution events and operator-visible runtime state
- durable graph-state ownership and materialization
- Pulse-specific memory state when it is persisted execution state
Target examples:
Cortex.Pulse.RunCortex.Pulse.EventCortex.Pulse.Executor.*Cortex.Pulse.PersistenceCortex.Pulse.Memory.*for durable run-state queries only
Cortex.Event, Cortex.Events, and the durable parts of Cortex.Run.Types move or shim into
Pulse. Thought lifecycle events do not belong in Pulse unless they are generic execution events;
model-mediated thought events belong in Logos.
Cortex.Pulse.Executor is the durable execution engine, not the canonical home for Wire executor
identity or host executor registration. It runs already-bound StageAction values, persists state,
resumes runs, retries stages, and records execution events. It should not know that an action came
from @native.deep_report, @pure, or any future reasoning pattern except through generic stage
metadata needed for replay and operator visibility.
Cortex.Capability
Cortex.Capability owns substrate authority surfaces:
- executor registration records
- native pure-executor configuration
- authority checks that are required before a bound action can enter Pulse
Target examples:
Cortex.Capability.ExecutorCortex.Capability.Executor.Pure
Model clients, provider adapters, tool-call records, and structured-output fallback policy are not part of the Cortex public Haskell surface after ADR 0040. They live in Logos or in host bindings.
Capability does not own prompts, reasoning memory, archetype activations, or pattern-level evaluation. Those are Logos concerns that consume capabilities.
Cortex.Artifact
Cortex.Artifact is not a current public Haskell root. Artifact and provenance remain Cortex
architecture concepts through Wire contracts, runtime envelopes, and Pulse provenance. The concrete
report/document IR and rendering surface moved to Logos.Patterns.DeepReport.Artifact.* in
ADR 0040.
A future substrate-shaped artifact API requires a separate ADR and a concrete reusable need. Until
then, do not add an empty Cortex.Artifact marker root.
Logos
Logos owns the structured reasoning library above the substrate:
- canonical archetype taxonomy and activation slots
- one bounded model-mediated cognitive evaluation, named a thought
- cognitive memory: retrieval, ranking, packing, compaction, source selection, and topological context construction
- reusable reasoning patterns such as DeepReport
- prompt families, memory presets, evaluation rules, contract-entry conventions, and Wire templates that are interpreted by substrate mechanisms
Target examples:
Logos.Archetypes.*Logos.Thought.FrameLogos.Thought.PolicyLogos.Thought.HostLogos.Thought.ToolHostLogos.Thought.ToolLoopLogos.Thought.RuntimeLogos.Thought.StructuredOutputLogos.Thought.EventLogos.Memory.*Logos.Patterns.DeepReport.*Logos.Patterns.DeepReport.Executorsfor inert profiles or adapters, if a later executor-definition ADR introduces themLogos.Patterns.PlanReview
Agent is not a canonical Cortex word. Pulse evaluates graph nodes as bounded stage actions;
durable personas and product agents remain downstream. Logos may interpret selected stages as
model-mediated thoughts. Task is likewise not a root. It either becomes thought runtime machinery
or a named Logos pattern. Research and report-generation code become DeepReport or other Logos
patterns when they are LLM-shaped reasoning semantics.
Executor Boundary
Executor is a boundary term used at several layers. It is not another canonical root beside the
Cortex substrate roots or the downstream Logos component.
The layers own different parts of executor meaning:
| Context | Owns | Does not own |
|---|---|---|
Cortex.Wire.Executor | Source-level executor references such as @native.deep_report, lowered native evaluator ids such as pure, compile-time projections, port constraints, contract obligations, and inert purity/effect metadata needed by Wire. | Host authority, Haskell application codecs, provider keys, DB access, prompts, or Pulse scheduling. |
Cortex.Capability.Executor | Authority-bearing registration records: config decoders, native pure-executor configuration, application codecs, host interpreters, and runnable binding into substrate actions. | Model provider policy, reasoning-pattern semantics, or durable scheduling. |
Cortex.Pulse.Executor | Durable execution of already-bound stage actions: scheduling, persistence, replay, resume, retry, cancellation, and execution events. | Wire executor registry, source-level executor ids, application semantics, or provider/tool admission. |
Logos.* | Reasoning-shaped executor profiles, model/provider adapters, prompts, tool-call records, pattern templates, report artifact IR, and reusable catalog values that consumers may opt into. | Host authority, provider credentials, product tools, DB loading, or artifact destinations. |
This makes @native.deep_report a Wire executor reference admitted by a host binding. The reusable
DeepReport pieces may live under Logos.Patterns.DeepReport.*, but the binding that grants runnable
authority belongs to the consumer or to a Capability-level registration surface. Pulse only sees the
resulting bound StageAction and its generic replay/runtime metadata.
CorePure follows the same split. Authors write pure output equations without @; the compiler
lowers them to a host-registered native evaluator id whose config is checked through Wire-level
syntax and contract/port rules, whose deterministic evaluator is admitted through the executor
registration surface, and whose resulting action is run by Pulse. A pure evaluator may be provided
upstream by Cortex, but it is not a special Pulse executor and it does not install new Pulse framing
codecs. It operates over Wire values and closed, deterministic functions rather than downstream
Haskell application types.
This ADR decides the namespace boundary: no top-level Cortex.Executor root, and no expansion of
Cortex.Pulse.Executor into the generic executor registry. A separate ADR is required before Cortex
introduces any of the following public semantics:
- a concrete
ExecutorSpecor executor-registration API - a revised
WireCompileEnvshape for executor projections - a pure-expression language or pure evaluator registry
- executor purity/effect/replay metadata with runtime consequences
- standard upstream executor packs such as DeepReport executor profiles or structural primitives
ADR 0017 and ADR 0020 already set constraints for those future decisions: Wire gets compile-time
projections, host binding grants authority, Pulse persists closed WireValue framing, and pure
output equations lower to the native evaluator rather than introducing a third node executor kind.
Migration Map
The migration proceeds by adding canonical modules first, then converting imports, then retiring old exposed roots once no internal or known external consumer still requires them.
| Current surface | Canonical target | Policy |
|---|---|---|
Cortex.Graph, Cortex.Graph.* | Cortex.Algebra.Graph.* | Move implementation under Algebra; keep shallow root shims during migration. |
Cortex.Circuit, Cortex.Circuit.* | Cortex.Wire.Circuit.* | Move compiled-form modules under Wire; keep shallow root shims during migration. |
Cortex.Event, Cortex.Events | Cortex.Pulse.Event or Logos.Thought.Event | Split execution events from thought lifecycle events. |
Cortex.Provider.OpenRouter.* | Logos.Provider.OpenRouter.* | Provider adapters are Logos surfaces after ADR 0040. |
Cortex.Agent.Config | Logos.Thought.Frame | Model/budget/prompt appendix are thought-frame concerns. |
Cortex.Agent.Policy | Logos.Thought.Policy | Tool scope and approval policy belong to thought orchestration. |
Cortex.Agent.Definition | Logos.Thought.Frame or removed | Avoid durable persona semantics in Cortex. |
Cortex.Task.Host | Logos.Thought.Host | Host for one model-mediated thought. |
Cortex.Task.ToolHost | Logos.Thought.ToolHost | Host-side tool execution for a thought. |
Cortex.Task.ToolLoop | Logos.Thought.ToolLoop | Model/tool loop is thought orchestration over capabilities. |
Cortex.Task.Runtime | Logos.Thought.Runtime | Thin thought runner and lifecycle helpers. |
Cortex.Task.StructuredOutput | Logos.Thought.StructuredOutput | Structured-output fallback and model-output parsing are Logos concerns. |
Cortex.Task.Plan | Logos.Patterns.PlanReview | Planner/reviewer shape is a reusable reasoning pattern. |
Cortex.Task.Gather | Logos.Patterns.DeepReport.Gather | Evidence gathering is DeepReport pattern semantics. |
Cortex.Task.Report | Logos.Patterns.DeepReport.LegacyReportTask initially | Decompose after contracts and ports stabilize. |
Cortex.Research.Section | Logos.Patterns.DeepReport.Section | Section schema is report-pattern semantics. |
Cortex.Research.Runtime | Logos.Patterns.DeepReport.Section.Runtime | Section-output parsing belongs with the pattern. |
Cortex.Run.Types | Cortex.Pulse.Run and Logos.Thought.Stage | Durable run ids are Pulse; thought stage descriptors are Logos. |
Cortex.Run.Engine | Logos.Thought.Event or Logos.Thought.Runtime | Current event emission is thought lifecycle machinery. |
Cortex.Document.IR, Metadata, Host | Logos.Patterns.DeepReport.Artifact.* | ADR 0040 moves the concrete report/document IR to Logos. |
Cortex.Document.Report, Section | Logos.Patterns.DeepReport.* | Report/research semantics are Logos pattern semantics. |
Cortex.Memory.* | Logos.Memory.*, Cortex.Pulse.*, Cortex.Capability.*, or internal helpers | Classify per module; cognitive context moves to Logos, persistence stays substrate. |
Cortex.MemoryCompaction | Logos.Memory.Compact | Keep root shim only if concrete consumers require it. |
Cortex.Pulse.Memory.* | Cortex.Pulse.Memory.* or Logos.Memory.Topological.* | Durable execution state stays Pulse; context construction moves to Logos. |
Cortex.Json.* | Platform.Serde.Json.* or layer-internal helpers | Generic serde helpers are not Cortex roots. |
Cortex.Text | Platform.Text or layer-internal helpers | Generic text helpers are not Cortex roots. |
Logos.<Archetype> | Logos.Archetypes.<Archetype> | Archetypes live under the Archetypes catalog. |
Logos.<Archetype>.Capability | Logos.Archetypes.<Archetype>.Activation | Avoid collision with external-authority Capability. |
Cortex.Logos.* | Logos.* | Cortex.Logos is superseded staging vocabulary and should not remain public. |
Import Direction
The source tree enforces the vertical split:
Logosmay import substrate modules.- Substrate modules must not import
Logos. Cortex.Algebramust not import Wire, Pulse, Capability, or Logos.Cortex.Wiremay import Algebra, but not Pulse or Logos.Cortex.Pulsemay import Algebra, Wire, and Capability where runtime execution requires those substrate surfaces, but not Logos.Cortex.Capabilitymay import platform support and shared substrate value types when necessary, but it must not encode reasoning-pattern semantics.Platform.*must not importCortex.*.
When a cycle appears, the type belongs in the lowest layer that can own the concept without importing its consumers. Do not solve cycles by adding a new public root.
Cabal Exposure Policy
cortex.cabal should expose canonical modules and temporary compatibility shims only. The main
cortex library exposes substrate modules. The downstream logos package exposes Logos.* modules
from its own repository. A compatibility shim should be removed from exposed-modules when Cortex,
Logos, and known consumers have migrated.
New public modules must be added under a canonical root. New internal helpers should prefer
other-modules or a .Internal subtree under the owning root.
Migration Slices
The refactor should land in small buildable slices:
- Add this ADR and update architecture/index links.
- Add canonical namespace spines and shallow compatibility shims where missing.
- Move provider adapters to Logos and retire Cortex provider imports.
- Move graph algebra to
Cortex.Algebra.Graph.*. - Move compiled circuit modules to
Cortex.Wire.Circuit.*. - Move report/document artifact IR to Logos.
- Move thought runtime surfaces from
Agent,Task, andRunintoLogos.Thought.*. - Move report/research surfaces into
Logos.Patterns.DeepReport.*. - Classify memory modules and move cognitive-context modules to
Logos.Memory.*. - Move generic JSON/text helpers to
Platform.*or internal helpers. - Remove obsolete exposed roots once compatibility windows close.
Each slice must keep the library buildable. A moved module and its tests should land together. Generated or Cabal materialization changes must stay with the source change that requires them.
Consequences
Positive consequences:
- The Haskell tree matches the architecture docs and ADR 0016.
- LLM-shaped and reasoning-layer code becomes visibly separate from the runtime substrate.
- Provider authority, tool authority, artifact substrate, and reasoning patterns get distinct homes.
- New code has a clear import target and fewer ambiguous root names.
Costs and risks:
- Many imports and test module names will churn.
- Compatibility shims may temporarily make the tree look duplicated.
- Moving memory and structured-output modules requires judgement because some functions are generic mechanics and others are reasoning policy.
- External consumers may need coordinated migration if they import old roots.
Mitigations:
- Keep shims shallow and temporary.
- Move one layer at a time.
- Add import-boundary checks once the canonical tree exists.
- Prefer deletion of unused compatibility roots over preserving them for hypothetical consumers.
Alternatives Considered
Keep current roots and document them as staging
Rejected. The current roots encode outdated concepts and continue to invite new code into the wrong layer.
Move everything in one large rename commit
Rejected. A single tree-wide rename would be difficult to review and would make it harder to distinguish semantic boundary decisions from mechanical import updates.
Preserve all old roots indefinitely as public compatibility modules
Rejected. Indefinite shims make the old vocabulary permanent. Compatibility must be justified by concrete consumers and removed once migration is complete.
Put all reasoning support under Capability
Rejected. Capability means external authority. Prompts, thought frames, cognitive memory, archetype activations, and reasoning patterns consume authority but do not grant it.
Keep Document as a canonical root
Rejected. Document currently mixes generic artifact substrate with report and research semantics.
Artifact is the substrate concept; report/research shape is Logos pattern semantics.
Open Questions
- Which old root modules still have concrete external consumers that require temporary exposed shims?
- Should any provider-neutral model contract ever become substrate API, or should all model/provider surfaces stay in Logos and host bindings?
- Which
Cortex.Memory.*modules are purely cognitive context construction, and which are generic enough to remain substrate/internal support? - Does a future
Cortex.Artifactroot need a generic contract catalog for artifact references, or should artifact contracts remain in Wire until schemas mature?