Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
0eb2d00
feat(flows): advance tinyflows to v0.5.1 and gate unrunnable node kinds
senamakel Aug 2, 2026
f1d2cdf
feat(tinyagents): map OpenHuman config into crate session config
senamakel Aug 2, 2026
9f8ff9a
refactor(agent): repoint required-output enforcement onto crate config
senamakel Aug 2, 2026
3ea8995
refactor(agent): thread host config into the sub-agent runtime
senamakel Aug 2, 2026
b1dc263
docs(specs): record ambient-load threading outcome and the task_dispa…
senamakel Aug 2, 2026
3a9218e
chore(vendor): advance tinyagents to the host capability traits
senamakel Aug 2, 2026
8c7b0ed
feat(session): start the Phase 2 transcript parity soak
senamakel Aug 3, 2026
a8fa0be
feat(tinyagents): land the ten host capability adapters
senamakel Aug 3, 2026
14510db
Merge upstream/main into oh-kernel-tinyagents
senamakel Aug 3, 2026
5d9f8ba
Merge upstream/main into oh-kernel-tinyagents
senamakel Aug 4, 2026
9e15a1f
feat(session): promote the session config handle and expose host capa…
senamakel Aug 4, 2026
1032201
fix(tests): fix the tinyplace flake, harden archivist ingest isolation
senamakel Aug 4, 2026
e770b32
chore(vendor): advance tinyagents to main with the host capability tr…
senamakel Aug 4, 2026
c7688e6
fix(clippy): satisfy the pre-push lint on the threaded config sites
senamakel Aug 4, 2026
9ba475f
docs(specs): fix broken cross-links and drop the duplicated memory spec
senamakel Aug 4, 2026
4fddf93
fix(experience_store): import retrieve_across_stores for cross-store …
senamakel Aug 4, 2026
3aa4ff2
feat(experience_store): add shared recall store for profile sessions
senamakel Aug 4, 2026
cd22718
feat(experience_store): add shared recall memory store
senamakel Aug 4, 2026
f7f758c
chore: files changed src/openhuman/agent/tinyagents/host/experience_s…
senamakel Aug 4, 2026
61dbfbb
fix(session): align experience store read/write asymmetry with live t…
senamakel Aug 4, 2026
3b0e4ee
feat(config): add migration to enable session shadow reads
senamakel Aug 4, 2026
337d3cd
chore(config): register enable_session_shadow_reads migration
senamakel Aug 4, 2026
0277325
chore(migrations): bump current schema version to 9
senamakel Aug 4, 2026
0be48ac
feat(migrations): add migration to opt workspaces into session shadow…
senamakel Aug 4, 2026
6d9f1d1
test(migrations): use constant for schema version assertions
senamakel Aug 4, 2026
e32d343
test(migrations): add tests for v8 to v9 shadow reads migration
senamakel Aug 4, 2026
edf893c
test(experience_store): cover shared recall with profile-local writes
senamakel Aug 4, 2026
4a17af0
chore: files changed src/openhuman/agent/tinyagents/host/experience_s…
senamakel Aug 4, 2026
8a97da0
chore(agent): clarify session_shadow_reads default comment
senamakel Aug 4, 2026
60048ab
feat(agent): track registered tools for wildcard denylist support
senamakel Aug 4, 2026
54f7233
fix(agent): initialize registered_tools field in registry constructors
senamakel Aug 4, 2026
fbada9e
feat(definition_registry): add with_registered_tools builder method
senamakel Aug 4, 2026
ac35d8c
fix(agent): resolve wildcard tool scope against registered tools
senamakel Aug 4, 2026
2bd2721
fix(definition_registry): document explicit wildcard and empty-scope …
senamakel Aug 4, 2026
faf2bc0
test(definition_registry): clarify tool scope projection edge cases
senamakel Aug 4, 2026
d8c980a
chore(definition_registry): reformat assertion for readability
senamakel Aug 4, 2026
c355934
feat(tool-outcome-classifier): make timeout retry safety depend on ex…
senamakel Aug 4, 2026
9c98a48
fix(host): make timeout retryable only when retry_safe
senamakel Aug 4, 2026
64960f8
fix(host): classify timeouts as permanent when retry is unsafe
senamakel Aug 4, 2026
911487e
fix(host): mark timeout outcomes retry-safe when configured
senamakel Aug 4, 2026
3ec68f0
chore(tool_outcome_classifier): add imports for HashSet and Arc
senamakel Aug 4, 2026
539a535
test(tool_outcome_classifier): pass retry_safe flag in class mapping …
senamakel Aug 4, 2026
f573585
fix(host): treat timeouts as permanent for side-effecting tools
senamakel Aug 4, 2026
cf8971b
fix(tool-outcome-classifier): treat timeouts on external-effect tools…
senamakel Aug 4, 2026
2b6608a
test: verify dispatched name affects only timeout verdicts
senamakel Aug 4, 2026
79a72e6
chore: reformat test helper and assertion chains
senamakel Aug 4, 2026
d647733
feat(security_gate): track pending audit request ids
senamakel Aug 4, 2026
68dc8ad
feat(security_gate): add take_audit_request_id for pending approvals
senamakel Aug 4, 2026
9d45ebe
fix(security_gate): persist audit request ids for approved tool calls
senamakel Aug 4, 2026
90f1f8e
fix(security_gate): clarify approval ownership in module docs
senamakel Aug 4, 2026
c5f5459
fix(security_gate): add mutex-protected tool registry
senamakel Aug 4, 2026
178c84d
test(security_gate): cover audit id draining semantics
senamakel Aug 4, 2026
fbb2d3d
fix(agent_memory): honor cross-session flag in recall scoping
senamakel Aug 4, 2026
0a13e71
fix(agent_memory): pass cross_session flag to scope filter
senamakel Aug 4, 2026
a18d6a6
fix(agent_memory): document cross_session skip in defensive scope pass
senamakel Aug 4, 2026
038c474
test(agent_memory): cover cross-session recall scope
senamakel Aug 4, 2026
3491411
test(agent-memory): add regression test for cross-session recall with…
senamakel Aug 4, 2026
f62fcc8
chore: reformat scope_allows test assertions
senamakel Aug 4, 2026
2949fa6
fix(agent_memory): honor cross_session flag in test stub
senamakel Aug 4, 2026
34e82cd
fix(progress): bound lifecycle sends with a grace timeout
senamakel Aug 4, 2026
273a58e
refactor(progress_sink): use lifecycle forwarding for turn events
senamakel Aug 4, 2026
a3802fd
feat(progress_sink): add send grace for lifecycle events
senamakel Aug 4, 2026
fa63f02
fix(progress_sink): await lifecycle event forwarding
senamakel Aug 4, 2026
33459fa
fix(progress_sink): await sink flush on run end
senamakel Aug 4, 2026
9289e38
fix(progress_sink): preserve lifecycle events under transient backpre…
senamakel Aug 4, 2026
3a5afc4
fix(tests): make progress sink ordering test race-free
senamakel Aug 4, 2026
e49138e
fix(tests): await both progress events in order
senamakel Aug 4, 2026
9174f84
fix(host): rename retry-safe tool allowlist in outcome classifier
senamakel Aug 4, 2026
6103ccd
refactor(tool-outcome-classifier): invert retry-safety tool set seman…
senamakel Aug 4, 2026
cee6856
docs(host): clarify retry-safe tool classification as an allowlist
senamakel Aug 4, 2026
04bea4f
test: invert tool classifier test setup to retry-safe allowlist
senamakel Aug 4, 2026
15e91fe
feat(host): add regression test for arg-sensitive tool retry safety
senamakel Aug 4, 2026
c476285
fix(host): route only known model tiers through role resolver
senamakel Aug 4, 2026
fa4ee3d
feat(host): add is_known_model_tier helper for model resolver
senamakel Aug 4, 2026
904a7e2
fix(host): default lead role to chat
senamakel Aug 4, 2026
192c3a9
test: pin orchestrator default and tier-suffix edge cases
senamakel Aug 4, 2026
6443e38
chore: reformat assertion in model resolver test
senamakel Aug 4, 2026
bf3ed8e
refactor(agent): remove unused model tier check
senamakel Aug 4, 2026
1c6841b
feat(host): add known model tier check for role resolution
senamakel Aug 4, 2026
1730710
refactor(agent): remove redundant seam assertion in model resolver tests
senamakel Aug 4, 2026
16d2251
chore(agent): clarify test comment for orchestrator routing
senamakel Aug 4, 2026
3967c72
fix(security_gate): continue evaluation after channel approval
senamakel Aug 4, 2026
1f4bd2c
feat(security_gate): track channel approval for tool calls
senamakel Aug 4, 2026
a5be0a2
fix(security_gate): use settled decisions for approved channels
senamakel Aug 4, 2026
aa49e07
fix(security_gate): use settled approval channel for tool calls
senamakel Aug 4, 2026
ebf6252
feat(security_gate): avoid duplicate prompts for already approved calls
senamakel Aug 4, 2026
d5fbc67
chore(security_gate): document approval short-circuit behavior
senamakel Aug 4, 2026
f598ab1
test(security_gate): add tests for readonly tier overriding channel a…
senamakel Aug 4, 2026
a1acafd
fix(tests): format tool policy call in security gate test
senamakel Aug 4, 2026
1326ce7
feat(budget_gate): add background work flag to budget gate
senamakel Aug 4, 2026
60568e9
feat(budget_gate): add background work opt-in for model calls
senamakel Aug 4, 2026
97d1e19
fix(budget-gate): skip background scheduler for interactive turns
senamakel Aug 4, 2026
11fb8c7
fix(budget_gate): attach grant id to interactive permits
senamakel Aug 4, 2026
6c57e52
test(budget_gate): add test that interactive gates bypass background …
senamakel Aug 4, 2026
409d11b
fix(tests): use background gates in budget gate tests
senamakel Aug 4, 2026
f4afdbe
fix(experience_store): over-fetch candidates before agent filtering
senamakel Aug 4, 2026
7b0ffde
fix(experience_store): truncate recalled attempts after agent filtering
senamakel Aug 4, 2026
a988bed
feat(experience_store): add candidate pool multiplier for agent recall
senamakel Aug 4, 2026
68426a8
test(experience_store): cover agent recall when another agent dominates
senamakel Aug 4, 2026
d6d4a22
chore: format test code in experience_store
senamakel Aug 4, 2026
a06cc95
fix(experience_store): document why filtering precedes truncation
senamakel Aug 4, 2026
4277ce6
fix(host): honor per-agent profile and memory omissions in context co…
senamakel Aug 4, 2026
4b5c5e3
feat(host): add user-file omission policy to context composer
senamakel Aug 4, 2026
39bdf94
test(context_composer): add test for user file omission flags
senamakel Aug 4, 2026
c1caaa1
fix(progress_sink): track per-run progress state
senamakel Aug 4, 2026
843758b
fix(progress_sink): track iterations per run and batch tool calls
senamakel Aug 4, 2026
8fea25a
fix(progress): track per-run state for nested agent runs
senamakel Aug 4, 2026
6e2abea
feat(progress_sink): track run iteration state
senamakel Aug 4, 2026
ea9883d
docs(progress_sink): clarify iteration counting and multi-run behavior
senamakel Aug 4, 2026
0ec5002
fix(progress_sink): correct garbled text in doc comment
senamakel Aug 4, 2026
196904a
chore(progress_sink): reformat chained method calls
senamakel Aug 4, 2026
366bece
fix(progress_sink): count parallel tool calls as one iteration
senamakel Aug 4, 2026
a906637
fix(progress_sink): correct comment wording in test
senamakel Aug 4, 2026
7bf2450
test(progress_sink): cover sub-run lifecycle and iteration scoping
senamakel Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
496 changes: 496 additions & 0 deletions docs/specs/2026-07-28-agent-session-transcript-to-tinyagents-design.md

Large diffs are not rendered by default.

263 changes: 263 additions & 0 deletions docs/specs/kernel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
# OpenHuman as a Kernel — Subsystem & Driver Model

**Status:** proposed · **Date:** 2026-07-28 · **Scope:** `src/` (core crate), all `src/openhuman/*` domains
**Companion spec:** [`plan-memory.md`](plan-memory.md) — memory is the first subsystem cut to this model.

---

## 1. Thesis

Linux is a good kernel because it does not implement filesystems, network cards, or
schedulersuites — it defines **narrow contracts** (VFS, netdev, block layer), owns **policy and
mechanism** (permissions, namespaces, scheduling, lifecycle), and lets independently-developed
**drivers** implement the actual behaviour behind those contracts. A driver can be built in, a
module, or absent; userspace never learns which.

`openhuman-core` should be that kernel for a personal AI runtime. Today it is closer to a
monolith with one very good in-tree implementation per capability: memory *is* TinyCortex, agents
*are* TinyAgents, channels *are* TinyChannels. Each already has a seam (`src/openhuman/tinycortex/`,
`src/openhuman/tinyagents/`), which proves the shape works — but the seams are **bespoke per
domain**, the contracts are **not versioned**, and there is **no way for a third implementation to
be bound at runtime**.

This spec defines the general model. It deliberately generalises patterns the repo already has
rather than inventing new ones.

**Non-goal:** a plugin marketplace, dynamic `.so` loading, or an ABI. Drivers are Rust crates
compiled in, or out-of-process services reached over a documented wire contract. Nothing here
requires unsafe dynamic linking.

---

## 2. What already exists (the raw material)

| Kernel concern | Existing mechanism | Gap for the kernel model |
| --- | --- | --- |
| Syscall surface | Controller registry (`src/core/all.rs`), JSON-RPC `/rpc`, `/schema` | Method set is fixed at compile time; cannot vary by bound driver |
| Runtime composition | `DomainSet` / `DomainGroup` on `CoreBuilder` | Selects *whether* a domain runs, not *which implementation* |
| Build composition | Per-domain Cargo `[features]` (`voice`, `web3`, `mcp`, `channels`, …) | Gate = on/off, not a choice between implementations |
| Service lifecycle | `ServiceSet`, `src/core/runtime/services.rs` | No per-subsystem health/degraded state |
| IPC | `event_bus/` broadcast + native request/response | Fine as-is; becomes the kernel's internal bus |
| Policy | `SecurityPolicy`, approval gate, `MemoryTaint`, `source_scope`, redaction | Enforced *inside* domains, so a swapped implementation could bypass it |
| Trust metadata | `CapabilityProviderConfig` (`config/schema/capability_providers.rs`) | Already the right shape; unused by domains |
| Seams | `src/openhuman/tinycortex/`, `src/openhuman/tinyagents/` | Adapter to *one* crate, not to a trait a second crate could also satisfy |

The kernel model is mostly **naming and enforcing** the above, plus one genuinely new piece: the
**subsystem registry with a bound driver per slot**.

---

## 3. Model

### 3.1 Definitions

- **Kernel** — `src/core/` plus the always-on platform domains. Owns: RPC transport and the
controller registry, the event bus, config load/validation, `SecurityPolicy` and the approval
gate, scheduling/cron, the workspace and path roots, observability, and the subsystem registry.
The kernel contains **no capability implementation**.
- **Subsystem** — a named capability slot: `memory`, `inference`, `channels`, `skills`, `flows`,
`sandbox`, `voice`. Each subsystem owns a **contract** (a set of Rust traits + value types), a
**config section**, a **stable RPC namespace**, and an **agent-tool family**.
- **Driver** — an implementation of a subsystem contract. Three classes:
- **`embedded`** — an in-tree/vendored Rust crate (`tinycortex`, `tinyagents`, `tinychannels`).
The default; no network, no extra process.
- **`external`** — an out-of-process backend reached through a transport adapter over a
documented wire contract (HTTP/JSON, or MCP). This is how a third party ships a driver
without touching this repo.
Comment on lines +64 to +66

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
file="docs/specs/kernel.md"
printf '%s\n' '--- relevant specification sections ---'
sed -n '45,85p;135,185p;240,263p' "$file"
printf '%s\n' '--- transport, credential, fallback, and guard references ---'
rg -n -C 3 'credential_ref|SecretString|transport|HTTP/JSON|redirect|origin|HTTPS|fallback|trust_state|Guard|driver|egress' "$file"

Repository: tinyhumansai/openhuman

Length of output: 17942


Security Misconfiguration (CWE-16)

Make credential-bearing external transport fail closed.

The example uses transport = "http" with an HTTPS endpoint, but the specification does not define adapter security. Require destination authorization before resolving credential_ref, certificate- and hostname-validated HTTPS, origin-bound credentials, and rejection of downgrades or cross-origin redirects. Define SecretString as the usable secret with redacted display output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/specs/kernel.md` around lines 64 - 66, Update the external backend
transport specification to require destination authorization before resolving
credential_ref, certificate- and hostname-validated HTTPS, origin-bound
credentials, and rejection of protocol downgrades or cross-origin redirects.
Define SecretString as the usable credential value while requiring its display
representation to be redacted, and ensure the existing HTTP/HTTPS transport
example reflects these fail-closed rules.

- **`null`** — a stub advertising zero capabilities. What a compiled-out or unconfigured
subsystem binds to. Replaces today's hand-written `stub.rs` files with one generic answer.
- **Binding** — exactly **one** driver is bound per subsystem per process, chosen by config at
boot. Same rule OpenClaw uses for `plugins.slots.memory`: installing a second memory backend
disables the first with a warning, because two live memory backends means two truths. Fan-out
across several backends is expressed as a **composite driver** (§3.5), not as a second binding.

### 3.2 The contract shape (normative)

Every subsystem contract is defined in an **API module with no engine dependencies** and follows
the same five-part shape:

```rust
// 1. Identity + lifecycle — every driver implements this.
#[async_trait]
pub trait Driver: Send + Sync + 'static {
fn id(&self) -> &str; // "tinycortex", "supermemory", "null"
fn class(&self) -> DriverClass; // Embedded | External | Null
fn capabilities(&self) -> Capabilities; // 2.
async fn health(&self) -> DriverHealth; // Ready | Degraded { reason } | Down { reason }
async fn shutdown(&self) -> Result<()>;
}

// 2. Capability descriptor — a bitset/struct of optional trait families, not a version number.
// 3. Capability traits — one per family; a driver implements only what it advertises.
// 4. Value types — serde-only, dependency-free, shared by every driver.
// 5. Errors — a typed enum with a mandatory `Unsupported { capability }` variant.
```

**Rules:**

1. **Capabilities are negotiated, not assumed.** The kernel asks `capabilities()` once at bind
time and caches it. Calling an unadvertised capability is a kernel bug, not a driver error.
2. **Value types are inert.** Serde/std only — no SQLite, no tokio-specific types, no engine
types. This is the same carve-out rule the `skills` and `mcp` Cargo gates already follow
(`AGENTS.md`: *"put a domain's inert types in a dep-free submodule and leave it ungated"*), now
applied across crate boundaries so an external driver can depend on the API without pulling
the embedded engine.
3. **Drivers never see kernel concerns.** No RPC schemas, no `SecurityPolicy`, no keychain, no
event bus, no `Config`. Everything a driver needs is passed in its constructor or per call.
4. **Contracts are versioned.** Each API module carries `pub const CONTRACT_VERSION: (u16, u16)`.
Minor bump = capability added; major bump = existing signature changed. External drivers
report the version they speak in their handshake; a major mismatch fails the bind.

### 3.3 Degradation is a first-class outcome

When a bound driver does not advertise a capability, the kernel does **not** register a handler
that returns "not implemented". It behaves exactly like today's compile-time gates:

- the corresponding **RPC methods are unregistered** — unknown-method over `/rpc`, absent from
`/schema`;
- the corresponding **agent tools are absent** from the tool list, not present-and-failing;
- the **UI** reads the capability set from `<subsystem>_status` and hides the surface.

Absence beats a stub that errors. A registered-but-failing method teaches the model that the
capability exists and makes it retry (the exact reasoning already recorded for the `flows` gate).
The one exception is the **CLI**, which keeps its subcommand arm and reports a *build/config fact*
("memory driver `supermemory` does not support tree summarisation") — same reasoning as the
retained `mcp` and `tui` CLI arms.

### 3.4 Policy is kernel-side and non-bypassable

Every subsystem call from product code goes through a kernel-owned **guard decorator**, never to
the driver directly:
Comment on lines +129 to +130

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

cd /dev/null 2>&1; find . -name "kernel.md" -type f | head -5

Repository: tinyhumansai/openhuman

Length of output: 228


🏁 Script executed:

cat -n docs/specs/kernel.md | head -150

Repository: tinyhumansai/openhuman

Length of output: 9945


🏁 Script executed:

cat -n docs/specs/kernel.md | sed -n '120,160p'

Repository: tinyhumansai/openhuman

Length of output: 2458


🏁 Script executed:

cat -n docs/specs/kernel.md | sed -n '135,160p'

Repository: tinyhumansai/openhuman

Length of output: 1726


🏁 Script executed:

cat -n docs/specs/kernel.md | sed -n '235,270p'

Repository: tinyhumansai/openhuman

Length of output: 2076


🏁 Script executed:

cat -n docs/specs/kernel.md | sed -n '240,250p'

Repository: tinyhumansai/openhuman

Length of output: 1015


🏁 Script executed:

cat -n docs/specs/kernel.md | sed -n '240,265p'

Repository: tinyhumansai/openhuman

Length of output: 1835


🏁 Script executed:

rg -n "Guard" docs/specs/kernel.md | head -20

Repository: tinyhumansai/openhuman

Length of output: 615


Authorization Bypass (CWE-693)

Definition of Done item 5 requires type-system enforcement, not text-based checks.

The spec lists "Guard bypass" as a known risk (lines 257-258) and proposes a mitigation: drivers remain private to the registry module, with a grep-based lint test for constructor calls. However, grep-based checks catch only new D() syntax, not all paths that escape a raw driver instance.

A function defined in the registry module can return raw D, store D in a public type, or place D in a field visible to product code. Grep cannot distinguish safe returns (Guard-wrapped) from unsafe ones (raw D).

To enforce Definition of Done item 5, require:

  • Driver types and constructors fully sealed to the registry module; not exported.
  • Compile-fail test that verifies product code cannot obtain raw D by any means (direct construction, return type, public field, type inference).
  • Type boundary check that Guard<D> is the only public surface for each driver contract.

Replace the grep-based lint check with type-system enforcement. This prevents SecurityPolicy, taint stamping, redaction, and approval gates from being bypassed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/specs/kernel.md` around lines 129 - 130, Update the Definition of Done
and guard-bypass mitigation in the kernel specification to require type-system
enforcement instead of grep-based constructor checks. Require driver types and
constructors to remain unexported within the registry module, add compile-fail
coverage proving product code cannot obtain raw driver instances through
construction, return types, public fields, or inference, and require each driver
contract’s public surface to expose only Guard<D>.


```
agent tool / RPC handler
Guard<D> ── SecurityPolicy · taint stamping · scope allowlist · redaction ·
│ egress budget · approval gate · audit event · tracing span
bound driver D
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

`Guard` implements the same contract traits as `D`, so it is transparent to callers and
impossible to skip by construction. This closes the single largest risk of a driver model: today
`MemoryTaint`, `source_scope`, and redaction are enforced *inside* the memory domain, so a
replacement implementation would silently drop them. After this change a driver **cannot** see
un-redacted content it was not granted, and cannot stamp its own provenance.

**External drivers additionally require:** a `CapabilityProviderConfig` entry with an explicit
`trust_state` (fail-closed `untrusted`), a recorded egress decision, and per-call budget
accounting. Sending user memory to a hosted backend is an egress event and is treated as one.

### 3.5 Composition instead of kernel special-cases

Multi-backend behaviour is expressed as drivers that wrap drivers:

- **`composite`** — fan out reads across N drivers, merge/rank, write to a designated primary.
- **`mirror`** — write to both, read from primary; the migration path between backends.
- **`cache`** — embedded driver in front of an external one.

Each is just another `Driver`, so the kernel keeps exactly one bind and zero special cases.

### 3.6 Config shape (uniform across subsystems)

```toml
[subsystems.memory]
driver = "tinycortex" # the bound slot; "null" disables

[subsystems.memory.drivers.tinycortex]
# embedded driver options

[subsystems.memory.drivers.supermemory]
class = "external"
transport = "http"
endpoint = "https://…"
credential_ref = "keychain:supermemory" # never an inline secret
```

Secrets are **references**, resolved kernel-side through the existing keychain, and passed to the
driver as a redacted `SecretString` — the pattern already pinned for Composio credentials.

### 3.7 Runtime axes (unchanged, now three)

| Axis | Question | Mechanism |
| --- | --- | --- |
| Compile-time | Is this code in the binary? | Cargo `[features]` |
| Runtime composition | Does this domain run this process? | `DomainSet` / `ServiceSet` |
| **Binding (new)** | **Which implementation answers?** | **subsystem registry + config** |

They compose: a subsystem compiled out binds `null`; a subsystem gated off by `DomainSet` is not
bound at all; a subsystem present and enabled binds the configured driver, falling back to the
embedded default if that driver fails to construct (logged loudly, surfaced in status, never
silent).
Comment on lines +189 to +192

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Fail closed when an explicitly selected driver cannot bind.

The proposed fallback to the embedded driver changes the configured source of truth and storage location. For memory, reads can return different data and writes can persist data in an unintended backend.

Allow fallback only when configuration explicitly opts in. Otherwise, surface the bind failure before serving subsystem calls.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/specs/kernel.md` around lines 189 - 192, Update the subsystem binding
behavior described in the composition rules so an explicitly configured driver
failure surfaces as a bind failure and prevents subsystem calls from being
served. Only fall back to the embedded default when configuration explicitly
opts into fallback; preserve the existing null and DomainSet gating semantics.


> **Feature-forwarding gate applies.** Any new default-ON gate (e.g. `memory-embedded`) must be
> added to `app/src-tauri/Cargo.toml`'s explicit feature list — the shell sets
> `default-features = false`. `scripts/ci/check-feature-forwarding.mjs` enforces this; the `voice`
> and `tokenjuice-treesitter` incidents are why.

---

## 4. Kernel/driver split criterion

One question decides where any file lives:

> **Would a build whose only driver is a third-party external backend still need this file?**

- **Yes → kernel.** RPC schemas and ops, agent-tool definitions, `SecurityPolicy` and policy
guards, provenance/taint, scope and redaction, credentials and keychain, schedulers and cron,
the event bus, config mapping, the driver registry and transport adapters, export/import.
- **No → driver crate.** Storage engines, indexes, chunking, embeddings pipelines, retrieval and
ranking, summary trees, job engines, source readers and parsers, provider-specific
normalisation, on-disk formats and migrations.

This is a sharper rule than "is it product policy or engine logic", which is how the
2026-07-28 cutover evaluation landed on keeping several engine-shaped modules in the host. Under
the kernel criterion those modules are **implementation of the default driver** and belong to it —
see the memory spec §6 for the concrete re-disposition.

---

## 5. Subsystem roadmap

| Subsystem | Default driver | Contract status | Order |
| --- | --- | --- | --- |
| **memory** | `tinycortex` (embedded) | To be defined — companion spec | **1st (pilot)** |
| inference | `tinyagents` routing | Partly exists (`routing`, provider traits) | 2nd |
| channels | `tinychannels` | Trait exists (`channels::traits`, already an ungated carve-out) | 3rd |
| sandbox | local OS jail | Already trait-shaped (Docker / Landlock / Noop) | 4th — smallest, good validation |
| skills · flows | in-tree | Gated already; contract later | later |

Memory goes first: it has the most mature seam, a golden-workspace parity harness, and a real
external demand (pluggable backends such as Supermemory/mem0).

---

## 6. Definition of done (kernel layer)

1. `src/core/subsystem/` exists: `Driver`, `DriverClass`, `DriverHealth`, `Capabilities`,
`SubsystemRegistry`, `Guard`, and the `[subsystems.*]` config mapping.
2. Binding happens once at `CoreBuilder` time; a failed bind falls back to the embedded default,
emits a `DomainEvent`, and is visible in `<subsystem>_status`.
3. Controller registration in `src/core/all.rs` is filtered by the bound driver's capability set,
the same way it is filtered by `DomainSet` today.
4. Agent-tool assembly is filtered by the same set.
5. `Guard` is the only path from product code to a driver; a test asserts no direct driver call
site exists outside the registry module.
6. `openhuman subsystems` CLI + `subsystems_status` RPC list slot, bound driver, class, health,
contract version, and capabilities.
7. Docs: `gitbooks/developing/architecture/kernel.md` describes the model; `AGENTS.md` gains a
"adding a subsystem driver" checklist.

## 7. Risks

- **Capability sprawl.** Every optional trait is a branch in RPC registration and tool assembly.
Mitigation: capability families are coarse (≤10 per subsystem) and adding one requires a
contract minor bump plus a both-ways test.
- **Guard bypass.** Mitigation: drivers are private to the registry module; a lint test greps for
out-of-module construction.
- **Parity regressions.** Mitigation: the golden-workspace harness that already backs the
TinyCortex cutover is promoted to the general conformance suite (§ memory spec 7).
- **Over-abstraction.** Mitigation: memory ships end-to-end and a second real driver exists before
a second subsystem is cut over. One proven seam beats five speculative ones.

Loading
Loading