klbr v2: a persistent agent with a Rust core and Python policy hooks #
Date: September 6, 2026
Revision: 2 — deliberate Rust foundation; first-class Python lifecycle/policy hooks.
Status: proposed architecture and ordered implementation plan; not an implemented or tested replacement.
Basis: the supplied klbr.tar, supplied Lucid pack, the prior Prime source audit, and primary-source documentation listed at the end. Existing source findings and proposed changes are distinguished throughout. This revision incorporates the operator’s explicit preference for Rust reliability/performance and broad Python hooks. The hook contract is specified in hooks-and-policy.md; the delta and reading order are in revision-notes.md. This supersedes the earlier recommendation to defer a policy boundary. Earlier source findings are retained, not claimed to have been newly runtime-tested.
1. Architecture decision #
Keep one continuous agent that participates through local chat and Discord, thinks in an explicit scratchpad, chooses when to speak, and can work or wait without a connected client. Replace the fragile orchestration underneath that experience.
Use a Rust daemon for durable state, provider execution, scheduling mechanisms, delivery, accounting and process supervision. Give each active agent session a separate persistent CPython workbench. Expose core behavioral decisions and lifecycle boundaries as typed Python hooks executed by a supervised hook worker using the same runtime package. Make policies, capabilities, reusable procedures, prompts and ordinary settings editable without rebuilding Rust. Use an LCM-inspired history layer, not the current reflection/note-garden pipeline, as the foundation of continuity.
The central distinctions are durable agent identity and commitments versus disposable live computation, and Rust mechanisms/invariants versus Python policies/skills. Rust is a deliberate long-term choice for typed resource ownership, memory/data-race safety and efficient always-on execution, not merely a way to salvage existing code. These strengths do not prove recovery correctness or eliminate general race conditions; those require explicit contracts and tests. No comparative performance benchmark has been run. The Python heap is useful but is not the agent’s only memory. [W10, W11]
The root agent is one session spanning the operator and admitted Discord conversations. A Discord channel is a provenance/routing identity, not automatically a new agent. Explicitly spawned children get their own sessions, histories, kernels, budgets, and working directories. This preserves the current continuous-participant model rather than quietly replacing it with a chatbot per channel.
At deployment there is one daemon, one SQLite database, an artifact directory, a versioned behavior directory, the existing web client, and supervised Python processes. No broker, distributed workflow engine, service mesh or plugin marketplace is required. A small typed hook registry and dispatcher is part of the core, not a second agent orchestrator.
What this keeps #
Scratchpad-versus-speech separation; the operator's scratchpad diagnostics; explicit local and Discord sends; optional participation in ambient chatter; attention to DMs and mentions; Discord backreading, reactions, pending-item decisions and conversational reply threading; operator preemption; interruptible waiting; an editable soul; client independence; structured coding operations; and explicit self-upgrades.
What this changes #
Accepted inputs become durable before scheduling. Session persistence is actually session-keyed. Sends become recorded effects with honest completion states. Context compaction becomes an isolated archival operation. Python routines become normal versioned source files. Lifecycle operations are typed host commands rather than string matches on tool names. Hook decisions are explicit typed proposals; post-commit observers request follow-up work through the same durable operations. Default social/context/model policies are editable Python rather than a hidden Rust policy layer.
What this deliberately does not include initially #
A second general-purpose agent loop in Python; automatic reflection after every turn; autonomous mutation of archival originals; arbitrary Python-heap checkpoint/replay; whole-runtime hot reload; semantic memory graphs; mandatory embeddings; a scheduler based on arbitrary pickled continuations; or an extensible Rust tool registry for each new Python ability.
2. Preserve the experience, not incidental mechanics #
Source IDs below refer to behavior-contracts.md. That companion contains exact source paths, line ranges, archive hashes, and excerpts.
2.1 Scratchpad and speech #
Existing: ordinary assistant output is treated as scratchpad. local_send and discord_send are explicit delivery actions. The source instructions call the scratchpad secret, but the implementation emits ScratchToken, and the web client renders a scratchpad block. Thus “not a sent message” is accurate; “invisible to the operator” is not. [K01–K03]
Contract: retain three separate things: model-authored scratchpad, provider-supplied reasoning data where available, and externally delivered messages. Do not collapse them into an assistant-text string. Scratchpad remains visible in authenticated operator diagnostics, never automatically sent to Discord or inserted into the local conversational-output lane.
Persist completed scratchpad records, including text emitted alongside tool calls. Preserve partial-stream records as partial when interrupted; do not manufacture a completed message. During context reconstruction and summarization, label scratchpad as the agent's draft/thought, not evidence that anyone was told anything. External delivery is supported by an effect receipt, not the wording of a scratchpad.
Do not turn this feature into a second mutable scratchpad.md memory system. Its primary representation is a typed part of the session transcript. Ordinary notebooks or files may still be used when useful.
print(...), expression display, stdout, and stderr are workbench output. They are not local speech either. An absent send is not repaired by forwarding text automatically.
Keep at most one delivery reminder per unresolved addressed input/attention frame, not merely one global reminder for a whole mixed-channel turn. The reminder should offer an explicit send or an intentional disposition. It must not force a reply to every ambient batch. Exhausting reminders ends in an explicit idle/pause state rather than a repeated nudge loop.
2.2 Discord as participation #
Existing: messages carry DM, mention, or guild context; the instructions explicitly encourage participation rather than helpdesk behavior. The integration has send, backread, react, list-conversations, and mark operations. Successful sends mark the associated pending item acted. Ambient guild messages are not pending items. [K01, K04–K09]
Preserve those behaviors through Python objects. A DM or mention should demand attention, not mechanically mandate a particular generated response. The agent may respond, react and explicitly record a decision, or deliberately dismiss an inappropriate/irrelevant item. It need not ask the operator for permission to exercise ordinary social judgment.
Preserve the current configuration defaults for the first cutover: ingest mode all, ambient waking enabled, bot messages excluded, ambient batching interval 8 seconds, and pending aging threshold 600 seconds. Preserve configured channel filters. These values are compatibility defaults, not claims of optimal social policy. [K04]
Preserve the short conversation labels, but persist their mapping to canonical source/account/channel identifiers. Today's in-memory d1, d2, ... map is not an adequate lifetime identity across restarts. Store canonical IDs; treat short IDs as stable display aliases, never infer a routing identity from their spelling. [K08]
Keep the smart reply behavior: ordinarily send a plain message when the channel has not advanced since the attended message; attach a reply reference when subsequent messages make the target ambiguous. Resolve and reserve the anchor for the specific effect, and consume it only on confirmed delivery—not before a network attempt. [K08]
The current default-channel behavior must become frame-local. Omitting a destination is permitted only when the current immutable attention frame has exactly one Discord conversation. A later incoming batch must not change where code from the earlier frame sends. Explicit conversation handles remain usable for proactive messages. Validate that a supplied pending-item ID belongs to the destination.
Persist accepted source messages before batching. Batches are scheduling/presentation views over those records, not the only surviving copy. A newly arriving directed message should flush a waiting ambient batch promptly; do not preserve the current accidental delay when a DM arrives during an ambient sleep. Apply rate limits and per-source fairness without dropping stored messages.
Preserve pending/seen/acted/ignored and explicit reopening. Keep automatic aging compatible at first, but record decision_origin=aging, not a fictitious claim that the model read the item. Aging, explicit dismissal, successful delivery, and deliberate manual marking are different evidence. A reaction does not silently imply that an item was replied to; combine it with an explicit decision when desired.
Backread is a bounded query with source IDs and before/after pagination. Reading history is not accepting new work or marking items acted. Preserve Unicode/custom reaction handling. Record attachment metadata immediately; perform bounded media fetching/enrichment separately so one slow attachment cannot prevent reception of following events. A persisted URL is not a promise that its bytes will remain available indefinitely.
The existing send tool description incorrectly says plain assistant text goes to local chat. Remove that stale wording along with the old tool schema. The new docs should be generated from the actual speech/scratchpad contract. [K09]
2.3 Preemption, waiting, and autonomous work #
Existing: operator messages and management commands preempt execution; external events queue behind the active turn. Waiting is interrupted by input and supports a bounded timeout. The existing outer loop can continue autonomously without a new user request. [K10–K12]
Preserve those priorities. Persist an operator event before requesting cancellation. A cancellation ends an attempt; it does not erase the interrupted task, input, or previously committed effects. External Discord input normally waits for the next boundary, but is received and stored promptly.
Preserve the ability to do sustained autonomous work. Do not impose “one user message, one final answer, stop.” The host continues while a turn is productive and within configured budgets; explicit waiting suspends it. Budget exhaustion, repeated empty completions, repeated errors, or repeated nudges produce a visible pause/backoff instead of a hidden infinite inference loop. Resumption of the interrupted objective is a recorded session concern, not a promise that a Python stack survives.
Expose await runtime.wait(seconds=60, reason="...") as a terminal yield from the current cell and agent attempt, not asyncio.sleep. The host records the wait and closes the tool execution with a yielded outcome. A new event or deadline later produces a new model step with its wake reason. Code after runtime.wait is not run. The runner and SDK implement this using an explicit control outcome; the host rejects subsequent effect requests from the yielded execution.
Retain the 600-second cap for that compatibility wait. Add seconds=None for indefinite event-driven waiting. Reject negative/non-finite durations; reject a zero-duration self-wake loop rather than retaining clamping as an accidental behavior. Long-term reminders belong to durable schedules, not a sleeping kernel. Ordinary asyncio.sleep remains available for ephemeral computations.
Model a wakeup and pending-input check in the same transactional scheduling decision. If input arrives before the wait commits, the host sees the pending inbox; if it arrives after, ingress wakes the waiting session. This prevents a lost-wakeup gap. Timeout delivery is deduplicated. Timers use persisted deadlines; the live process uses monotonic time for elapsed measurements.
2.4 Soul, reset, manual compaction, restart #
Existing: soul text is editable, while general instructions are compiled through include_str!. The old restart operation replaces the daemon process image after compilation. [K13]
Keep the soul editor and move evolving instructions out of the Rust binary. Preserve one authoritative version of each document; do not create a bidirectionally synchronized SQLite/file garden. Drafts live in files. Published immutable revisions are activated through one host record. The UI edits through the same revision path as the agent.
Separate operations that were previously conflated: reset the active context, restart a kernel, activate a behavior revision, and upgrade/restart the host. Reset should start a new context epoch without deleting archival evidence or silently cancelling durable jobs. A destructive purge must be a separate explicit administrative action. Manual compaction uses the same LCM implementation as automatic compaction.
A normal Python capability improvement should not reconnect Discord or restart the daemon. A host upgrade still requires a build and an explicit deployment path; Python cannot eliminate the compilation requirement for an actual Rust change.
3. Ownership and boundaries #
3.1 Rust host #
The host owns admitted ingress; session/attempt state; provider requests and credentials; the single model-visible python executor; cell admission and cancellation; durable inbox/outbox transitions; LCM source relationships and context assembly; wakeups and job records; active behavior revision selection; hook invocation/validation and observer delivery state; and the client event stream.
Retain suitable provider adapters, HTTP/WebSocket infrastructure, configuration parsers, Discord/Twilight integration, and existing web UI components after characterization tests. Do not rewrite a working Discord transport into Python merely to satisfy language uniformity: the agent-facing Discord vocabulary can be Python while the durable transport stays Rust.
Keep modules centered on real domains. Initially klbr-core can contain runtime, store, history, kernel, hooks, delivery, jobs, behavior, and the existing provider boundary. There is no requirement to make each module a separate crate. Make klbr-ipc independent of klbr-core storage/provider implementation types.
The host has an explicit lifecycle machine and validated transition contracts. Python decision hooks choose legal next actions, transforms contribute to context/output proposals, and observers react to committed outcomes. Batching, attention, model selection, compaction preferences and turn-continuation policy are editable Python plus settings. Persistence laws, authority checks, hard resource ceilings and effect accounting remain host-owned. Rust applies a narrow documented fallback if a policy is unavailable; do not duplicate every ordinary Python policy in Rust.
3.2 Python package and behavior checkout #
Use two Python responsibilities, not two orchestrators:
klbr_runtime: the kernel/hook-worker runners, shared transport, and typed host SDK. It is versioned with the host protocol and updated deliberately. Its distribution provides the publicklbrimport used in the examples; this is a facade over the same SDK, not a second implementation. Runner internals are not the everyday skill API.- The behavior checkout: normal importable
klbr_skillsroutines/source tooling andklbr_hookspolicy/observer modules, including the editable defaults. Projects may have their own modules and instructions. These are normal Python packages/modules, not additional schedulers.
A behavior checkout also holds the soul, operational/social guidance, summarization prompt, child profiles, and settings. Static skill descriptions/docstrings provide discovery; do not import every skill for discovery, since imports may have effects. Keep only a bounded index in the model context. Load full skill instructions explicitly when needed, and expose import paths and useful examples.
Keep data/results in artifacts or scoped durable application state. A useful one-off function may begin in a live cell; making it an enduring capability means saving its source, tests, and dependencies. Notebook globals do not become a second source of deployment truth.
No hook registration is needed to add klbr_skills.my_project.run_checks(); automatic lifecycle invocation, unlike an ordinary function call, requires a named hook binding in the published manifest. The addition is an ordinary module. New persistent external integrations may be supervised Python source jobs with checkpointed cursors and a narrow ingress API; they need not add a bespoke Rust model tool.
3.3 Kernel protocol #
Launch CPython as a subprocess, not embedded through PyO3. Use a persistent namespace and top-level await. One foreground cell executes at a time per kernel. Background asyncio tasks are supported but explicitly ephemeral. Interrupt suspended awaits cooperatively; escalate an unresponsive computation to process termination and record a kernel-reset event.
Use an inherited dedicated bidirectional control socket/pipe, separate from stdout/stderr. Frame typed messages with bounded lengths; include protocol version, session, kernel generation, execution ID, request ID, and message kind. Handshake before admitting work. Reject old-generation or completed-execution effect requests. Do not deserialize pickles from the worker.
Representative commands are execute, interrupt, shutdown; representative events are output, host request, execution completion, and kernel exit. Host requests expose a small closed vocabulary of durable domain operations. Ordinary file parsing, HTTP reads, and computation do not each need a host method.
Bound previews and spool larger output. Record which execution/background task produced output; do not accidentally attribute late background output to the next cell. A kernel protocol cannot make arbitrary native output perfectly attributable; retain an explicit unassigned/background category when necessary.
Host-owned credentials should not be casually copied into the kernel environment. However, a same-user unrestricted subprocess is not a sandbox. This plan's first implementation is a trusted personal-agent deployment; real isolation requires operating-system restrictions beyond an SDK. Do not promise that typed handles alone prevent arbitrary filesystem/network access or cross-channel disclosure.
3.4 First-class Python hooks #
The detailed, normative proposed contract is in hooks-and-policy.md. Distinguish one-owner decision slots, explicitly ordered representation transforms, and asynchronous post-commit observers. All receive typed immutable inputs or scoped references; Rust validates outputs before changing authoritative state. Durable registration is an importable entrypoint pinned to a published revision, not a closure living in the REPL.
The workbench can discover, edit, test and activate hooks. Required core callbacks run in a separate persistent hook-worker process using the same runtime package and framed protocol. This avoids making core progress depend on a busy workbench or calling back into a kernel waiting for its own host request. It is a bounded callback evaluator, not a second orchestration service. Workbench-local initialization/interactive subscriptions are explicitly different lifetimes. [W12]
Never await a callback while holding a database transaction or authoritative mutation lock. Enforce deadlines and generation checks from Rust; keep ingress, cancellation and repair controls independent. An optional enrichment failure can omit enrichment; a required send/execution guard failure holds the operation. Recorded observer state/commands/acknowledgement commit together. Long callbacks request tracked jobs instead of becoming new unbounded loops.
Ship editable defaults for ordinary policies. Hooks cover attention, turns, context, model planning, cells/results, delivery, LCM, jobs/children and revision/runtime events as each domain lands. They do not expose database commits or raw host state for arbitrary mutation. Preserve scratchpad-versus-speech and routing authority across every transform. Do not put mandatory Python calls on each streamed token.
4. Durable data and recovery model #
Use SQLite with explicit migrations, foreign keys, transactional state transitions, a bounded writer path, configured busy handling, and an intentional durability mode. WAL with synchronous FULL is an appropriate starting candidate on local storage, subject to integration testing and actual filesystem guarantees. Do not perform network calls or run a summarizer inside a database transaction. [W08]
Pin and check the actual SQLite runtime, not only the Rust crate version. The WAL documentation records a WAL-reset corruption fix in SQLite 3.51.3 and later, with listed backports to 3.44.6 and 3.50.7. Require a verified fixed build for deployment, record sqlite_version() and build provenance in diagnostics, and test the selected writer/checkpoint policy. Do not infer the bundled SQLite version from an unverified dependency name. [W08]
Use an append-only journal for historical facts and ordinary relational tables for current operational state. This is not a requirement to build a generic event-sourcing framework or reconstruct every table by replaying all history. Store source content once and reference it from presentation/projection records.
Minimum logical families #
| Family | Responsibility |
|---|---|
| events + session_items | Canonical typed source/model/cell/delivery records; ordered session membership points to records instead of duplicating content. Preserve occurred-at and received-at times separately. |
| sessions + attempts + executions | Parentage, context epoch/version, state, model request identity, kernel generation, behavior revision, attempt outcome, execution/receipt linkage. |
| inbox | Durable ingress scheduling state: pending, claimed, consumed, cancelled or paused. Claiming is not successful completion. |
| conversations + attention_items | Canonical destinations and display aliases; DM/mention attention decisions independent of inbox scheduling. |
| outbox | A specific requested effect and its pending/attempting/confirmed/failed/unknown outcome, destination, receipt and idempotency key. |
| summaries + summary_children + context_items | Derived summary text, ordered coverage, and the current chronological representation. |
| jobs + wakeups | Child/command/routine/source jobs, deadlines, cancellation, checkpoints and notifications. |
| artifacts | Immutable content references, hashes, media metadata, storage state and provenance. |
| behavior_releases + activation state | Candidate/previous/active component revisions, one authoritative activation manifest, test evidence, compatibility and pinning. |
| hook_invocations + observer_deliveries + hook_state | Decision input/version/revision, accepted result or fallback, observer checkpoints/dispositions, bounded versioned namespaced state, and atomic proposed-command acknowledgement. |
| search projections | Rebuildable lexical indexes over appropriately scoped originals and summaries. |
Table names are suggested; the listed ownership and transitions are the contract. Avoid one generic JSON blob that erases distinctions between waiting, running, failed, and unknown. JSON is appropriate for versioned external payloads and bounded routine-owned data.
Admission and execution #
- Authenticate/classify an input and store its canonical event, source dedupe key, and inbox state atomically. Acknowledge a local submission only after commit. For Discord, this guarantee begins at local durable admission; Gateway delivery is not a transactional acknowledgment protocol with the local database.
- Claim work for a session/attempt transactionally. Channels notify the scheduler; they are not the only copy of work. Use one active model request/foreground cell per session.
- Freeze an attention frame containing the admitted event IDs, routing handles and the behavior/context versions used for the model request.
- Persist a complete validated model response/tool call before execution. Preserve provider-specific content blocks or opaque metadata where required for valid continuation; do not assume every provider can be flattened to role/content strings.
- Journal cell acceptance and execute under a specific generation. Persist tool completion before submitting the next model request.
- On an interrupted tool sequence, render an explicit interrupted/unknown result under the existing tool-call ID when the adapter supports that reconstruction. Do not execute partially streamed tool arguments, silently drop the group, or claim success.
- Resume from committed boundaries. No automatic replay of arbitrary cells. Model inference may be retried as a new attempt, with any known actions and uncertainty included in the context.
Effects and honest receipts #
A request is deduplicated by its persisted operation identity, not by identical message text. Reconnecting the SDK with the same request ID returns the existing result. Intentionally sending identical text twice remains possible through two distinct actions.
local.send succeeds when the visible local delivery is durably published to the operator stream. It does not prove that a browser was connected or a human read it. The UI can replay it later.
discord.send must not report delivery merely because it enqueued an outbox row. It returns a confirmed receipt, an explicitly pending/unknown result, or a typed failure. Store network receipt and automatic pending-item transition together. If the response was received but bookkeeping fails, keep the delivery evidence and repair the bookkeeping rather than encouraging another send.
Use a stable Discord nonce with enforce_nonce when supported by the selected adapter/API. Discord documents uniqueness checking only over the past few minutes; it is not indefinite exactly-once delivery. After a crash in an ambiguous window, use available receipt/backread evidence and otherwise surface unknown, rather than blindly retrying indefinitely. Verify whether the pinned Twilight request builder supports the needed fields; extend that one adapter request if necessary, not the whole integration. [W05]
Hook-generated tracked effects use this same outbox and retain their causative event/handler identity. Committing an observer acknowledgement, scoped state and proposed command records is one local transaction; a worker retry does not mint new logical commands. Hook revisions alone do not cause historical replay. Tracked effects do not cover arbitrary Python side effects. A cell can change files or talk to another service directly; after process failure those outcomes may be unknown. Useful durable services should route important mutations through an outbox-aware routine/host capability, but the plan does not claim universal exactly-once execution.
Client stream #
Expose durable event cursors for input acceptance, delivered speech, completed scratchpad/cells, job transitions, and recovery notices. Reconnect with a cursor plus a bounded snapshot. Streaming token deltas are best-effort; completed records are durable. Deduplicate by event ID. A lost token animation does not imply a lost message. Authenticate the operator diagnostics stream; never expose its raw contents to Discord participants.
5. LCM-inspired history, with scratchpad semantics intact #
The borrowed idea is immutable originals plus a compact active representation and navigable source relationships. The proposed implementation is narrower than the full LCM paper: a chronological summary tree, bounded direct reads into Python, and no required map operator or embedding model. This is LCM-inspired, not a claim to reproduce its implementation or benchmark results. The paper itself distinguishes retained originals from guaranteed successful model recall. [W03]
Representation and invariants #
Keep recent original session items and older summary references in order. A summary covers a contiguous run of complete message/tool groups or earlier summaries. The host stores the ordered source relationships; the model writes only the summary text. Do not let generated text invent coverage IDs.
Every session item within the active epoch's historical coverage is represented exactly once, directly or through one ancestor. A context reset deliberately starts a new epoch; the excluded older epoch remains archived, not magically covered. The selected input to a summarizer and the context version are fixed. Generate outside a transaction; validate and atomically commit the new summary plus the replacement range. If the version/range changed, discard or retry against the current state. Never mutate original records to achieve compaction. Python can contribute retrieval/context patches, compaction preferences and summary prompts through the domain hooks; Rust still enforces chronological coverage, legal ranges, source authority, provider validity and the final input budget.
Summary prompt requirements include: distinguish operator requests from external observations; label scratchpad as tentative internal text; distinguish a proposed send from a confirmed delivery; retain corrections, unresolved questions, useful paths, artifact identities and uncertainties; and preserve source/channel/author attribution. Operational truth such as pending jobs and outbox status is still read from explicit records, not from this summary.
Budget and convergence #
Account for the complete provider input, loaded guidance and tool schema, reserved output, and a safety margin. Use adapter-aware estimates and recorded usage; do not assume byte length is an exact token count.
Begin with synchronous between-step compaction. Summarize the oldest eligible complete block using a tool-free call. Make at most two bounded model attempts; if they fail, exceed their allotted output, or fail to shrink the representation, insert an explicit degraded reference stub. The host guarantees a measured strict reduction or returns a context-budget error. If only fixed instructions/current mandatory input remain and do not fit, fail honestly rather than looping.
An optional Python compaction policy can request earlier compaction or select among eligible ranges. It cannot suppress the hard-budget pressure-relief path or extend retry limits indefinitely. Under the threshold, do not run a summarizer or reflection as a routine tax. Add background soft-threshold compaction only after the synchronous representation and commit semantics are proven. Reserve model capacity for actual addressed input so compaction/children cannot starve it.
Retrieval and artifacts #
Expose bounded history.search, history.describe, history.read, and history.children. Search returns IDs, short previews, scope and continuations. An exact reference remains resolvable without semantic search. Python can load a larger range under explicit byte limits, reduce it programmatically, or give references to a child. Merely assigning text to a variable does not insert all of it into the model's next prompt. Display uses separate preview limits.
Begin with lexical search plus hierarchy navigation. Add embeddings only after task-based evidence shows a retrieval gap. Do not require embeddings for admission, compaction, startup, or recovering a known source ID.
Enforce visibility scope before returning search results or summaries to restricted children. A mixed-source summary is not safe for a narrower grant merely because its metadata has several labels. Return it only to a principal authorized for all its covered scopes, or return narrower source material. The unified root can know multiple conversations, but this is not a guarantee against the model accidentally disclosing cross-channel information.
Spool large tool output into immutable artifacts with bounded previews. Preserve a referenced file version through an artifact or stable revision when historical bytes matter; label ordinary workspace paths as live references. Retention and disk quotas are explicit. A delete/purge policy changes the advertised lossless-retention boundary and must leave a tombstone/reason rather than a fake retrievable reference. Logs, exports and backups must treat scratchpad and credentials as sensitive.
6. The Python language presented to the agent #
These are proposed API contracts, not currently installed functions. Use typed result objects with useful repr, docstrings and inspectable signatures. Keep ordinary Python objects and upstream APIs available where appropriate; do not wrap everything into JSON dictionaries.
from klbr import local, discord, runtime, history, code, agents, jobs, behavior
# A local response; print() would remain internal workbench output.
receipt = await local.send("i found the issue; testing the fix now")
# Current-frame defaulting is allowed only for one unambiguous conversation.
conversation = discord.current()
recent = await conversation.history(limit=20)
receipt = await conversation.send("oh, that makes sense")
# Silence is a real action; a reaction need not manufacture a written reply.
await conversation.react(message_id, "❤️")
await discord.mark(item_id, status="ignored", note="nothing useful to add")
# Search data without dumping the entire archive into the prompt.
hits = await history.search("previous build failure", scope=history.project("klbr"), limit=8)
source = await history.read(hits[0].ref, max_bytes=24_000)
print(source.text[:2_000])
# Persistent delegation: spawn means admitted, not finished.
child = await agents.spawn(
task="investigate the cancellation path",
refs=[source.ref],
profile="code-review",
workspace="read_only",
)
print(child.id)
# Terminal yield: later events cause a new model step, not continuation below here.
await runtime.wait(seconds=60, reason="waiting for results or messages")
The example is a vocabulary sketch, not one recommended combined cell; actual work should use separate cells and validate query results before indexing them. When omitted, the send source item may be inferred only from the immutable attention frame under the compatibility rules. An explicit source_item must identify an existing attention item in the same conversation. Backread messages are not all automatically pending items.
Calling agents.get(child_id) or jobs.get(job_id) reconstructs a handle after a kernel restart. child.result() may await the host's durable result while a kernel is alive, but the suspended Python continuation itself is not persisted. Long waits should usually yield the agent and receive a completion event. Children report to the parent; external speech requires an explicit delegated capability rather than being silently enabled.
Hook discovery and editing #
Expose hooks.list, hooks.describe, hooks.bind on a draft, hooks.test with fake effects, and hooks.trace for recorded invocations. Publish/activate through the existing behavior workflow. Full proposed shapes, slot names and failure rules are in hooks-and-policy.md. Ordinary skills remain importable functions; only code the host invokes automatically needs a hook binding.
Structured coding operations #
Use Python tree-sitter bindings and provisioned grammars. Compare the current language-pack surface with direct grammar packages; choose on the required query coverage, offline installation and native-runtime compatibility, not dependency count. Verify Rust, Python, TypeScript/TSX and the repository languages actually used. No runtime grammar download should be required for the shipped coding skill. [W06]
source = code.open("src/agent.rs")
symbol = source.symbols.exact("run_turn").one()
print(symbol.text)
patch = symbol.replace_declaration(new_declaration)
print(patch.diff)
patch.apply()
source binds bytes, language detection/provenance and a content hash. symbol binds byte ranges, kind, query provenance, and diagnostics to that snapshot. patch carries the expected base hash and explicit edits. Ambiguous matches, missing queries, changed source, and overlapping edits are separate errors. Explore fuzzily; mutate exactly.
Preserve raw queries and best-effort reference search without marketing it as compiler-backed resolution. Distinguish replacing a complete declaration from replacing its body; the existing replace_symbol_body path replaces the tagged range and should not be ported under a misleading name. [K14]
Support UTF-8 byte ranges, multiline definitions, partially invalid source, and a diff-before-apply workflow. Use checked single-file replacement; preserve permissions and choose symlink behavior explicitly. Ordinary content-hash checks are not an atomic cross-process CAS against arbitrary editors. Separate modifying agents into worktrees and serialize changes within a shared workspace. Multi-file patch application must either expose partial progress or implement a real bounded transaction/recovery mechanism; do not imply filesystem-wide atomicity.
For commands, expose structured argv/cwd/env and bounded output. Keep direct subprocess access for disposable work, and host-managed jobs for work that must be tracked. An optional shell helper must use explicitly selected Nushell rather than silently falling back to Bash. The repository's existing Bash build scripts are separate compatibility dependencies, not the agent's assumed interactive shell.
7. Self-editability without self-corrupting recovery #
The kernel alone is sufficient for live experimentation. Persistent self-editability additionally needs a place for source, discovery, dependency identity, activation, and recovery. Build those as a small release workflow, not a model-driven refinement subsystem.
Editable layers #
| Change | Activation | Survives restart? |
|---|---|---|
| Function/object created in a cell | Immediately in that kernel | No, unless saved explicitly |
| Ordinary skill/routine source | Stage and test; load in a new/pinned kernel revision | Yes |
| Durable hook source/bindings | Stage and test; activate a candidate hook worker at a defined epoch boundary | Yes; a compatible hook-only change does not reset the workbench |
| Soul or instruction document | New published document revision at next model boundary | Yes |
| Batching/wake/model-profile settings | Validate and activate at a defined safe boundary | Yes |
| New persistent event source | Registered supervised Python entrypoint plus scoped state | Yes, according to the source's checkpoint/restart contract |
| Kernel runner / SDK / host binary | Compatibility-tested runtime deployment | Yes; Rust changes still need compilation |
Make routine workspace and skill editing autonomous by default in this personal deployment. Do not require the operator to approve normal Discord participation or every useful helper. Keep active recovery binaries, access policy, credentials and destructive migrations outside the routine behavior-edit path. This separates reversible everyday changes from changing the machinery that makes rollback possible; it does not claim that an unrestricted same-user agent is technically unable to edit protected paths.
Revision layout and authority #
Use a normal Git checkout for drafts and reviews. A published revision contains immutable source/document content, a dependency/interpreter fingerprint, host/SDK protocol compatibility, and test results. Source-only revisions may share an already-built compatible environment. Dependency changes stage a different environment; they never pip install into the live kernel's environment.
The database activation record is the only authority for the active behavior manifest. It references immutable document/settings, skill/environment and hook component revisions. Compute the corresponding immutable paths from that record; do not introduce independently mutable activation authorities. Record component/import dependency fingerprints so a genuinely hook-only change can be distinguished from a shared-dependency change. A convenience symlink may exist for humans, but it is not another independently mutable source of truth. Every attempt, kernel and job records its pinned relevant revisions; each hook invocation records its selected handler revision and behavior epoch.
Use Nix to pin the host, interpreter and native dependencies on the NixOS deployment. Validate any uv-managed Python environment under that pinned interpreter/native closure; do not assume every downloaded wheel works on NixOS merely because it works on another Linux distribution. uv's lock/sync workflow is suitable for Python-only dependency pinning where verified, but production launches must not re-resolve or mutate dependencies. [W07]
Change workflow #
- Create a draft from the currently active revision, recording the parent revision.
- Edit normal source files or documents. Make the diff and intended behavior change available to the operator.
- Validate schema, imports, syntax, targeted tests and SDK compatibility in a candidate process. Run the trusted smoke tests with fake host effects; no test should send real Discord messages by accident. The candidate's own assertion that it passed tests is not authoritative evidence.
- Publish immutable content and prepare its environment. Reject stale-parent activation unless it is explicitly rebased/merged; two children must not silently overwrite each other's behavior changes.
- For prompt-only changes, switch the document revision at the next provider request. For hook-only source/binding changes with unchanged compatible skill/environment dependencies, validate/start a candidate hook worker and switch future invocations at the declared epoch boundary without resetting the workbench. For skill/environment or shared-dependency changes, replace all affected process roles at safe boundaries. Do not tear down working processes before replacements can initialize. Emit an activation record, plus a kernel-reset record only when the namespace was actually replaced. Pin the old epoch through in-flight attempts/effects; observer cutover preserves assigned pending deliveries and uses a declared cursor for new deliveries.
- Keep existing jobs/children on their pinned revision unless explicitly migrated. New jobs inherit the creating session's revision; a session may explicitly adopt the active revision. Do not move existing job code underneath a running process.
- On startup/import/handshake failure, retain or restore the last known-good revision without requiring the agent to reason its way out. On later behavioral regression, expose explicit rollback and an optional bounded crash-loop fallback; passing startup tests does not prove semantic improvement.
- Rollback changes future behavior. It does not erase events, reverse sent messages, roll back source repositories edited by a skill, or resurrect a lost Python heap.
Namespaced hook state needs an explicit schema and compatibility/migration policy. Keep it backward-compatible while older revisions still have work, or explicitly drain/pause affected consumers. Operator recovery/disable/rollback never requires invoking candidate-controlled hooks.
Do not use importlib.reload as the correctness basis. Python documents that references and existing instances can retain old definitions after a module reload. Allow it as an explicit interactive experiment, but use fresh kernels/processes for published revisions. [W04]
The common case is a new Python skill, improved instructions or a changed routine—not a Rust rewrite. Keep the rare host-upgrade route documented and separate: build a candidate artifact, test compatibility, quiesce and checkpoint the running host, restart under the service manager, and recover. Initially prefer migrations that remain backward-readable during the rollback window. Restoring an old database backup after new deliveries is not a safe generic downgrade strategy.
8. Children, durable jobs, and new event sources #
Use one job model with distinct kinds where their semantics differ: child agent, external command, scheduled Python routine, and persistent input source. Do not pretend that an arbitrary subprocess can resume at an instruction boundary after a crash.
For a child, atomically create the job and child session before returning the spawn handle. Pin its behavior, model profile, budget and input references. Completion stores a result/artifact and a deduplicated parent notification together. The parent discovers the result even if its kernel is replaced. Cancellation propagates explicitly; shared/dependent jobs have stated ownership instead of being killed accidentally when one caller disconnects.
Bound concurrency, depth, total descendants, tokens, wall-clock execution and output. Reserve capacity for the root. All provider calls from children pass through the host so accounting and limits apply across the whole tree. A sentence explaining why a subtask is smaller is not a termination proof.
A modifying child receives its own worktree. A read-only child must have its actual access level stated; a prompt alone does not enforce read-only OS access. Default children receive only supplied evidence/capabilities, not the entire root scratchpad or unrestricted external-send authority.
A durable routine is an importable entrypoint plus serializable input/checkpoint state, not a pickled closure. Store its revision and explicit recovery policy: retry-safe, resume-from-checkpoint, or manual/unknown after interruption. Schedules use persisted UTC deadlines and explicit local-timezone metadata when requested. Define missed-deadline behavior; default one-shot jobs fire once after recovery rather than producing an unbounded catch-up burst.
A new Python source runs as a supervised job and calls a bounded ingress API with a stable source key. Where supported, commit admitted events and the source checkpoint together. Separate receipt/ingress authority from arbitrary operator authority: an external adapter cannot make a trusted operator instruction by putting <system> in its payload. This permits a new notification/feed integration without rewriting the daemon.
Jobs and sources are optional extensions after the single-session foundation, not an excuse to build a generic workflow DSL. Start with spawn/get/cancel, a scheduled entrypoint, and emit/checkpoint. Add a host-managed map primitive only after repeated batch workloads justify it.
9. Ordered implementation slices #
Each slice is intended to be reviewable and to leave one coherent contract. Dependencies are explicit. Use fake providers, clocks and transports until a slice's acceptance gate calls for an integration test. These are implementation steps, not claims that the tests have already been run.
0 — Freeze valuable behavior before replacing internals #
Touch: klbr-core/src/instructions.md, agent.rs, agent/turn.rs, interrupt.rs, local/wait/restart tools; klbr-discord/src/{lib,inbox,formatting,tool_defs}.rs; klbr-web/src/App.svelte; klbr-ipc/src/lib.rs.
Extract the contracts in section 2 into fixtures and focused assertions. Cover scratch-only output, scratch plus a tool call, explicit local sends, single- and mixed-conversation Discord frames, delayed replies, ignored items, operator preemption, waits, soul updates and reconnect behavior. Capture realistic redacted provider-response fixtures. Label accidental behavior separately: lost interrupts, singleton snapshots, hidden latest-channel routing, stale tool wording and tool-name-driven lifecycle branches are not invariants to preserve.
Include hook fallback/hold behavior, stable ordering and decision fixtures for social attention/context/model planning; record source/version provenance so replay tests can use fake reads/effects.
Gate: the fixture suite describes the desired experience, including deliberate deviations. A failing existing test that encodes a documented bug is not a reason to preserve the bug.
1 — Establish durable identities and a new store boundary #
Depends on: 0.
Add: klbr-core/src/store/, versioned SQL migrations, typed IDs/state variants, a storage test harness.
Implement events, sessions, attempts, inbox, executions, conversations and outbox first; add other domain tables in their owning slices. Store durable input before scheduler notification. Give source admission a uniqueness key and preserve both source and receipt timestamps. Move away from context_snapshots.id = 1.
Keep the old production database untouched while developing the new format. Use a new database and deterministic fixture importer for early tests, not concurrent dual writes. Define a single writer/transaction boundary and reconnect behavior after disk or database errors.
Gate: the runtime SQLite build has the required fixes; duplicate ingress returns one accepted identity; a crash after commit but before notification does not lose work; a burst of events during an active attempt is preserved; two sessions cannot overwrite each other's context.
2 — Define the protocol and explicit lifecycle machine #
Depends on: 1.
Touch/add: klbr-ipc/src/lib.rs, klbr-core/src/runtime/, klbr-core/src/kernel/protocol.rs, web protocol types/fixtures.
Remove IPC reexports of core storage/provider types. Define typed client commands, source events, attention frames, host effects, kernel generations and completed outcomes. Keep wire-format evolution versioned. Use one schema/fixture source to check Python and TypeScript consumers; choose code generation only where it removes actual duplicated contracts.
Implement session transitions such as ready, running, waiting, paused and stopped, with attempt/execution outcomes represented separately. Model cancellation as a request/acknowledged outcome, not a magical guarantee that all external effects were undone.
Define typed hook slots, decisions/transforms/observers, invocation/schema IDs, declared SDK reads, deadlines, failure contracts and behavior epochs alongside the protocol. Add the minimal invocation/observer-state persistence required by the next slice. Authoritative state transitions await decisions outside their lock/transaction and validate source versions on return.
Gate: invalid state transitions and stale-generation host requests are rejected; two ingress sources cannot race the active destination; management commands do not mutate the store behind the runtime's transaction boundary.
3 — Build the supervised persistent Python workbench #
Depends on: 2.
Add: python/klbr-runtime/ with runner, framing, display/output handling and SDK transport; klbr-core/src/kernel/ supervision.
Implement handshake, persistent globals, top-level await, final-expression display, output spooling, errors, cancellation, background task labeling and shutdown. Start with a small useful environment, then provision the actual libraries/grammars needed. Port independently useful Prime evaluator ideas only after reviewing their license and coupling; do not transplant the surrounding AgentSession implementation.
Expose one model tool python(code=...). Record full validated tool calls and completion records. Reuse provider adapters behind an explicit interface rather than rebuilding the whole provider stack.
Gate: variables survive across cells; await/background work functions; stdout cannot forge control frames; output stays bounded; interrupting a blocked kernel yields an explicit reset; one crashed kernel leaves the daemon and other sessions alive.
3a — Build the hook worker and editable policy boundary #
Depends on: 2–3.
Add: klbr-core/src/hooks/{spec,dispatch,observations}.rs, the hook-worker mode under python/klbr-runtime/, typed SDK/discovery fixtures, and initial klbr_hooks modules in the behavior checkout. Names describe responsibilities, not a required file count.
Implement importable entrypoints, single-owner policy slots, explicitly ordered transforms, observer deliveries, bounded invocation queues, host-enforced deadlines, source-version validation, generation invalidation and independent repair controls. Start with a static published manifest; live editing arrives in slice 9. Share transport/supervision with the workbench rather than introducing a service framework. An ordinary optional callback does not need a model call.
Ship the first minimal editable defaults and fake-host fixture runner. Decision/transform handlers return plans rather than directly sending messages. Durable observer acknowledgement/state/proposed commands commit atomically; expensive work is a tracked job once that domain is available. Define unavailable-policy fallbacks before integrating each slot.
Gate: a busy workbench cannot block a send-review hook; a blocked hook cannot block durable ingress or operator cancellation; bad/stale replies cannot partially mutate state; a required guard failure holds its operation; already committed observer commands are not duplicated on retry. Record overhead rather than claiming a performance improvement.
4 — Rebuild the turn loop around explicit effects and yield #
Depends on: 1–3a.
Replace: relevant agent/turn.rs and agent.rs flow; all_tools() registration in the new runtime.
Assemble a raw-history context initially. Persist model text as scratchpad, not speech. Execute only the Python tool. Route local sends, waits and control operations through the SDK/host command path. Implement terminal wait semantics, durable wakeups, preemption and bounded no-progress behavior.
Integrate turn.prepare, turn.next, context.prepare, model.plan, execution.review and completion hooks into the real turn loop now, using the defaults shipped in Python. Do not hide all ordinary policy in Rust and postpone the extension boundary. Preserve immutable attempt epochs, kernel-request identity and context/source versions.
Retain autonomous work across multiple model steps. Resolve delivery reminders against typed attention/effect records instead of variables like discord_send_called. Finish complete tool groups for provider replay; journal aborted attempts honestly.
Gate: a model that only emits prose cannot accidentally send a message; a wait does not occupy a Python sleep; an input arriving during the wait transition is not stranded; interrupted work retains its source and existing receipts; no empty-response loop consumes unlimited inference.
5 — Reconnect the local UI to durable, distinct output lanes #
Depends on: 4.
Touch: klbr-daemon/src/daemon.rs, klbr-web/src/{App.svelte,lib/protocol.ts,lib/ui-types.ts}, relevant CSS/components.
Preserve the visible distinction among scratchpad, provider reasoning, tool execution and delivered text. Add session/revision/kernel indicators and explicit pending/failed/unknown delivery cards. Replace broadcast-only reconnect behavior with cursored durable replay plus optional token deltas. Route reset, soul edits and compaction commands through the host.
Gate: disconnect during generation, reconnect after completion, and observe one completed scratchpad/cell/delivery record; a hidden/incomplete stream does not masquerade as sent speech; the operator can stop a malfunctioning agent without model cooperation.
6 — Move existing Discord semantics onto the new inbox/outbox #
Hook integration: Wire input.accepted, attention.plan, delivery.prepare, delivery.review and receipt observers. Initial Python defaults match the frozen social fixtures. All destination, pending-item and reply-anchor mutations remain validated Rust operations; a failed guard never turns scratchpad into speech.
Depends on: 1, 2, 4; use 5 for operator diagnostics.
Touch: klbr-discord/src/{lib,inbox,formatting,tool_defs}.rs; add Python Discord facade.
Retain Twilight transport and existing parsing/formatting/reaction knowledge. Replace direct memory-tool registration with typed host commands. Persist conversation aliases, source admission, frame membership, reply anchors, attention decisions and effects. Fix default-destination races, consume reply anchors after delivery, and atomically record receipts plus auto-marking. Preserve configured defaults and expose them outside compiled code.
Store raw incoming metadata before batching/enrichment. Implement bounded backread and artifact-backed media handling. Add the nonce behavior only after verifying the pinned adapter; distinguish ambiguity from a definitive failure. Include Gateway reconnect/resume handling and a recorded resynchronization gap when the remote service cannot provide every missed event. Do not promise full offline capture of all Discord activity. [W09]
Gate: mixed conversations cannot be confused; a DM wakes an ambient batch promptly; repeated source messages are not duplicated; ignored items stay ignored; a successful send marks only its associated item; retries preserve identity; process failure after remote acceptance does not trigger an unqualified second send. Test against a fake transport first, then a controlled bot/channel with explicit test messages.
7 — Add LCM history and remove compaction's learning dependencies #
Hook integration: Wire context/ranking contributions and compaction.plan, summary.prepare and post-compaction observation. Hooks may alter policy and representations, not authoritative source relationships, hard-budget convergence or compaction commit semantics.
Depends on: 4; use 6 fixtures for mixed-source history.
Add: klbr-core/src/history/{store,compact,assemble,search}.rs or equivalent modules; Python history facade.
Replace: agent/compaction.rs and live reflection/recollection injection on this path.
Implement the chronological hierarchy and active-context coverage invariant. Preserve structured tool groups, scratchpad visibility and effect evidence. Build tool-free bounded summarization with compare-and-swap commit, degraded fallback and a real budget error. Provide exact-reference reads and lexical search. Add large-output artifact references and privacy-aware traversal.
Do not port note-garden reconciliation, embedding requirements, reranking, or automatic reflection into this layer. Keep old memory available through a read-only import/archive path later.
Gate: property tests recover every covered source item exactly once; a failed/crashed summarizer leaves a valid context; compaction never reports a draft send as confirmed; a restricted child cannot receive a mixed-scope summary; repeated compaction can still recover a correction, an exact command and its receipt. Test actual retrieval quality with recorded tasks separately from structural correctness.
8 — Build the Python coding vocabulary and ordinary skills #
Hook integration: Wire result.present and outcome observers where useful. Keep complete original code/results and bounded derived previews separately. Do not add hooks around every tree-sitter internal function.
Depends on: 3, 4; 7 supplies historical references.
Add: python/klbr-skills/, coding fixtures, provisioned grammar environment; move useful query definitions from the old tree-sitter code.
Implement source snapshots, structural queries, symbol selection, diagnostics, checked patches, structured command results and skill discovery. Preserve useful filesystem operations without preserving each tool's JSON schema. Reuse upstream Python libraries directly where they provide the right language. Add concise import examples to the operational instructions.
Gate: a realistic Rust/TypeScript/Python edit can be inspected, patched and tested through Python; Unicode and ambiguous definitions behave correctly; a stale patch fails; unavailable syntax queries are not mistaken for empty results; a new helper becomes discoverable without a Rust code change.
9 — Ship everyday self-editing and version activation #
Hook integration: Extend revision management for component fingerprints, durable hook bindings, dry-run traces, state compatibility and observer activation cursors. Prove that a compatible hook-only edit replaces only the worker, and that old attempts/pending observer deliveries keep their recorded epoch. Never let user hooks veto repair/rollback.
Depends on: 5 and 8; 7 supplies source evidence for changes.
Add: klbr-core/src/behavior/, behavior checkout/release format, trusted candidate validator, UI revision view; Python behavior facade.
Externalize the soul and evolving operational instructions. Add draft/publish/validate/activate/rollback. Pin revisions per attempt/kernel; handle prompt-only activation without needless kernel reset. Prepare candidate kernels before switching Python revisions. Keep environment resolution outside live execution. Build source-only edit tests that explicitly do not invoke Cargo.
Retain a host-admin path for stopping, rolling back and upgrading without the model. Keep private profiles, tokens and test recordings out of public Git commits.
Gate: the agent edits a skill, runs checks, publishes it, adopts it and uses it after restart without rebuilding Rust; a syntax error cannot strand the daemon; a stale candidate cannot overwrite a newer revision; startup failure restores the previous revision; existing jobs remain pinned; rollback does not alter the archival transcript.
10 — Add durable jobs, schedules and resumable Python sources #
Hook integration: Use the same jobs/outbox for hook-generated follow-up actions; add job.plan and completion/failure observers. Enforce causal loop limits and command quotas. Observers request jobs rather than maintaining another scheduler.
Depends on: 1, 2, 9.
Add: klbr-core/src/jobs/, scoped routine state/checkpoint operations; Python jobs/source facades.
Implement tracked commands and importable routines, durable one-shot deadlines, explicit retry policy, checkpoints, cancellation and deduplicated completion events. Introduce source jobs that admit external events with checkpointed cursors. A host restart may restart/reconcile a job; it does not guarantee an arbitrary child process continued running.
Gate: a scheduled routine still fires once after a host restart; an expired deadline does not cause a catch-up storm; non-retry-safe interrupted work becomes unknown/manual rather than silently rerun; a Python source resumes from its saved cursor and deduplicates repeated input.
11 — Add RLM-style child sessions using the same primitives #
Hook integration: Expose child planning and result/failure hooks through the job family. Validate suggested budgets and scopes in Rust; reserve root capacity regardless of hook preferences.
Depends on: 7, 9, 10.
Add: klbr-core/src/runtime/children.rs or equivalent; Python agents facade and child profiles.
Implement spawn/get/result/cancel, parent notification, evidence grants, per-child kernel/history and worktree assignment. Enforce global and subtree budgets at admission and before provider requests. Make detached versus parent-owned work explicit. Reserve root responsiveness. Child defaults inherit the parent's pinned behavior, not a moving global environment.
Gate: child results survive parent kernel replacement; children do not overwrite root context; cancellation and budget limits terminate further admission; two modifying children never share an unintended workspace; restricted children cannot send on the root's external channels without delegation.
12 — Import existing data, prove recovery, and cut over #
Depends on: all required preceding slices.
Add: deterministic importer, consistency checker, backup/restore and service deployment procedures; port only meaningful old benchmarks.
Back up the old SQLite database consistently and retain the archive. Import soul, configuration, source history, actual delivered messages, artifacts and intentional guidance where identifiable. Treat recollections and experimental memory cards as derived/imported material, not equivalent to raw evidence. Preserve stable old reference aliases where unambiguous. Keep ambiguous legacy Discord aliases session/import-scoped rather than guessing one global mapping.
Run the new daemon against fixtures and then a side-effect-disabled replay. Do not shadow it against live sources with real sends enabled. At cutover, stop the old ingress/agent owner, take the final consistent snapshot, import/verify, start the new daemon, reconcile source cursors/pending deliveries and verify counts. Never run two active senders for the same agent during this transition.
Prove failure windows: commit-before-notify, remote-send-before-receipt, summary-before-context-swap, wait-before-wakeup, candidate-start-before-activation, kernel failure mid-cell, disk-full while spooling, and websocket lag/reconnect. Run a soak test under a synthetic mixture of idle time, coding tasks, ambient chatter, directed messages and child completions. A proposed minimum 24-hour soak is a release test, not proof of perpetual reliability.
Remove the superseded live tool registry, tool-name lifecycle branches, old reflection-compaction linkage and deprecated memory write paths after successful cutover. Retain read-only migration fixtures/format support as needed. Do not keep two permanent dialects for send, wait, history or behavior activation.
Gate: restored backups are readable; every locally acknowledged input is accounted for; all effects have honest outcomes; the old and new processes cannot both deliver; the agent's scratchpad/social behavior matches the compatibility fixtures; routine self-editing works on the target NixOS deployment.
Deployment and diagnostics included in cutover #
Extend the existing flake.nix for a pinned host/interpreter/native closure; add a new NixOS service module rather than relying on an interactive development shell for production. Keep daemon state, artifacts, published behavior revisions and writable drafts in explicit directories. The module must provide service-manager restart, bounded shutdown, credential loading, and resource controls for kernels/jobs. This is a new deliverable, not a claim that the current flake already implements it. A Mac development launch should use the same protocol/behavior fixtures without pretending Linux-specific confinement is portable.
Expose hook diagnostics as part of the same status surface: selected handler/epoch, invocation latency and fallbacks, held operations, observer backlog/dead-letter records, worker generation/restarts, pinned revisions and bounded state size.
Expose an operator status view and machine-readable diagnostics: active and last-good revisions; session/kernel state; oldest queued input; directed attention backlog; pending/unknown effects; current jobs and wakeups; context budget and compaction failures; model usage; kernel restarts; database and artifact usage. Correlate records with session/attempt/execution/effect IDs, not unstructured log text alone. Redact credentials and avoid duplicating raw private conversation content into operational logs. Pause new work explicitly on storage failure; never silently acknowledge input that could not be retained.
Use a consistent SQLite backup method and test restoration with artifacts and referenced release manifests. Copying only the live .db file in WAL mode is not a backup contract. Garbage collection must understand retained history, active/pinned jobs and rollback revisions before deleting blobs or environments. Establish a storage budget and make any retention/purge rule explicit; ordinary context compaction must never be an implicit archival deletion policy. [W08]
10. Acceptance scenarios that define success #
“Think, speak, and wait” #
An operator message arrives. The model writes scratchpad, runs two cells, explicitly sends one local message, and yields. The operator sees the scratchpad diagnostics and one delivered message. A disconnected client later reconstructs the completed records. The agent is idle without repeated empty model requests.
“Ambient room, directed DM” #
Ambient messages arrive in two Discord channels; the agent is allowed to stay quiet. A DM arrives during batching. It is admitted immediately and presented with its own destination. The agent replies to that DM, not the most recently active guild channel. Only the correct pending item becomes acted. A separate ignored mention stays ignored across restart.
“Self-improvement survives amnesia” #
The agent finds a repeated workflow problem, edits a Python skill and its tests, publishes a revision and starts a new kernel. Its old globals are gone. Its skill, LCM history, soul, pending work and effect receipts survive. The new import works without Cargo. A deliberately broken subsequent candidate fails and does not replace it.
“Not said versus said” #
A scratchpad contains a draft saying a message was sent, but the outbox receipt says failure or unknown. After several compactions, the agent can recover the source and still distinguishes intention from actual delivery. It does not tell the operator a fictional conversation happened.
“Killed at the worst instant” #
The host dies just after a Discord request may have succeeded. Recovery does not blindly replay the Python cell. It uses the persisted effect identity and available reconciliation evidence. If the delivery cannot be established, the system shows uncertainty. A missing guarantee is represented rather than hidden.
“Long-lived work without a long-lived stack” #
A child and a scheduled routine outlive the root kernel. Their identities, revisions and outcomes remain queryable. The root wakes to a deduplicated completion event and continues from artifacts and history, without needing its former Python coroutine.
“Self-edit the policy, retain the workbench” #
The agent edits attention/model/context policy in Python, validates it against captured fixtures with fake effects, and activates a compatible hook-only revision. Existing variables remain in the workbench; an in-flight attempt keeps its old policy epoch and future attempts use the new one. A broken candidate does not replace working hooks or disable operator repair.
“No callback circular wait” #
A workbench cell awaits a host send that requires Python review. Review runs in the independent hook worker without holding the host state lock. Its valid answer allows the tracked operation to proceed. When review hangs, its deadline produces a held operation, while new inputs and operator controls remain available.
“Observer crash and revision replay” #
Crash an observer before and after its atomic state/command/acknowledgement commit. Local follow-up commands are not duplicated; unresolved external effects remain honestly uncertain. A new handler revision does not silently redeliver already completed history. An explicit replay is identified and uses fake effects by default.
11. Tradeoffs and the cases that would change this recommendation #
Rust is now an explicit architectural requirement, not a provisional salvage decision. Its type/resource model and efficient always-on execution are valuable, while logical recovery and service behavior still need tests. Python handles everyday policy as well as skills. New durable primitives or changes to host invariants still require normal Rust development; the hook boundary is not a claim that all future core work disappears. [W10, W11]
The supervised hook worker is included from slice 3a. It adds a process boundary and bounded dispatch/activation state, but has no agent loop, independent database authority or scheduler. Its concrete benefit is that core callbacks do not depend on the interactive kernel being available. Reuse the runtime protocol/supervisor and measure the overhead. Do not grow it into a second orchestrator. [W12]
Fresh kernels for affected skill/environment revisions sacrifice live handles and caches. Hook-only compatible revisions replace their worker instead; prompt-only changes need no Python restart. Use component fingerprints, cheap reconstruction helpers and long-lived host jobs to minimize disruption. Transparent heap migration is not a hidden prerequisite for self-editing.
One unified root session preserves the current agent experience but permits information from multiple conversations to share its working context. Scoped child access and explicit destinations reduce accidental mixing; they do not constitute mathematical noninterference for the root model. Stricter isolation would require separate principals/sessions and is an explicit product change.
SQLite, a persistent Python environment and a library-rich coding skill may not minimize repository size or dependency closure. Evaluate them by the boundaries they simplify, real installation footprint, startup behavior, memory growth and maintenance. Do not claim a line-count reduction is a reliability result. [W01, K15]
12. Completion and scope notes #
This plan is implementable in ordered slices, but none of the replacement runtime, migrations, protocol or recovery tests described here were executed in preparing it. The attached source was inspected; excerpts are in the companion. The prior Prime audit applies to the saved snapshot identified there, not verified current upstream HEAD. No new claim of Prime test coverage is made here.
Public API shapes shown in section 6 and hooks-and-policy.md are proposed. The hook addendum and integrated revision are design work only; document-consistency checks are not runtime validation. Package capabilities and lifecycle caveats were checked against primary documentation; exact versions and NixOS compatibility must be pinned and tested during the relevant slice. The archive includes existing code and implementation-status documents, not a production database; import outcomes cannot be validated from it alone.
The design's intended result is the same continuous, socially autonomous agent, with inspectable thought, deliberate speech, durable history and commitments, and a Python working vocabulary it can improve itself. The part it improves most often should be easy to change; the part responsible for surviving those changes should stay understandable and recoverable.
Sources #
K01–K15: exact supplied-source excerpts and SHA-256 identities in behavior-contracts.md. The supplied Lucid pack's lucid-code, lucid-review, dependency-recon, and semantic-compression guide informed contract/accident separation, domain notation, dependency choice, and state ownership. The prior uploaded audits remain supplementary evidence, not substitute source for the scratchpad/Discord inspection.
W01 — jyn, “simple is not small”: https://jyn.dev/simple-is-not-the-same-as-small/
W02 — Ideonomy README, plain skill and methods catalog (Grace Kind / Patrick Gunkel framework; read as idea-space material rather than an implementation dependency): https://github.com/latentwill/ideonomy-skill
W03 — Ehrlich and Blackman, “LCM: Lossless Context Management,” architecture and limitations: https://arxiv.org/html/2605.04050v1
W04 — Python importlib.reload behavior and caveats: https://docs.python.org/3/library/importlib.html
W05 — Discord Message Resource, create-message, reply/allowed-mention fields and bounded nonce uniqueness: https://docs.discord.com/developers/resources/message
W06 — Primary Python tree-sitter bindings and language-pack documentation: https://github.com/tree-sitter/py-tree-sitter and https://github.com/xberg-io/tree-sitter-language-pack
W07 — uv project and locked synchronization semantics: https://docs.astral.sh/uv/guides/projects/ and https://docs.astral.sh/uv/concepts/projects/sync/
W08 — SQLite WAL, synchronous and backup guarantees: https://sqlite.org/wal.html ; https://sqlite.org/pragma.html#pragma_synchronous ; https://sqlite.org/backup.html
W09 — Discord Gateway connection and resume behavior: https://docs.discord.com/developers/events/gateway
W10 — Rustonomicon, data races versus other concurrency errors: https://doc.rust-lang.org/nomicon/races.html
W11 — Rust project, ownership/performance/reliability rationale: https://rust-lang.org/
W12 — Python asyncio, cooperative scheduling and blocking code: https://docs.python.org/3/library/asyncio-dev.html