Phase 4 — Reach & Consult protocol:
- agent.rs rewritten as the `reach` (self-extension) and `consult`
(sovereign peer) tools; event-bus dispatch, peer resolution via
known_peers.json
- summon_handler: fixed inbox path, real pending/intrusive routing,
timeout surfacing to bus + inbox, outbound-request self-registration
- bridge always forwards control events regardless of subscriptions
- response loop closes via a file outbox: an inbound summon instructs
the agent to write federation/outbox/{id}.md; scan_outbox turns that
reply into a summon_response routed back to the caller
Phase 5 — Lite listener:
- `souveraine listen` — minimal federation presence with a summon-wake
watcher; parks authorized summons to .summon-pending/
- full server drains .summon-pending/ on startup
- FederationConfig: authorized_summoners, auto_wake
Phase 6 — Memory gating (partial):
- federation posture injected into the system prompt from the
federation/ memfs contract
- consent floor via authorized-summoners.md
- device registry: prune_stale timer, first_seen preserved
Builds clean (0 errors). Status + deferred work documented in
docs/tasks/federation-summon.md. Also sweeps in pre-existing in-flight
working-tree changes (TUI, prompt, docs).
DeviceRegistry tracks known federated peers from device_announce/device_leave
SensorEvents on the bus, persisted to ~/.souveraine/federation/known_peers.json
for CLI access. Bridge emits device_announce on connect. New `souveraine peers`
subcommand lists known peers. Wired into SouveraineServer at construction.
SensorEvent gets reply_to for directed routing. FederationConfig gains
peers (url + pubkey + subscriptions). New src/server/federation/ module
with SignedEvent (Ed25519 sign/verify) and FederationBridge (per-peer
outbound WS tasks with echo-safe seed_id filter and backoff reconnect).
Inbound /v1/federation/events handler verifies signatures before bus
injection. Bridge spawned in server::run() when federation.enabled.
Remove the auto-reset stall guard that silently killed turns after 90s
and orphaned the backend. The TUI now trusts the stream: backend emits
Keepalive every ~15s between rounds, the phase strip shows a liveness
label (waiting Ns... / still waiting Ns...), and the user decides when
to Esc. SSE ping frames map to Keepalive on remote backend.
Instance registry no longer blanket-registers all agents at startup —
register_instance takes an explicit agent_id, called from
new_conversation only. Fixes inflated instance counts on manager cards.
Consciousness engine (Aster) looks up the primary agent's name
dynamically instead of hardcoding "Ani". Parameter renamed to
primary_response.
Interjection labels changed from /btw to hand-raise metaphor
(hand raised → noticed) since /btw is its own fork feature.
- Adds owner_seed_id column to agents table, stamped from instance Ed25519
seed on agent creation (Path B ownership model)
- Splits routes into public (list/create agents, health) and protected
(update/delete agent, get conversation, stream messages) with per-agent
bearer token middleware
- Factors verify_token() helper from existing memory-route middleware;
adds require_agent_token() and require_conversation_token() wrappers
- RemoteBackend loads per-agent token from disk and sends Authorization
header on protected requests; ChatState recreates backend with token
after discovering agent_id
- Atmosphere system (src/ui/atmosphere.rs): 14 color presets ported from
the Matrix adapter system, wired into Presence with posture-linked
defaults and explicit BackendEvent::Atmosphere trigger path
- Welcome portrait: decoupled from 18×18 pixel-art constants, now sized
at ~40% of terminal width (capped 48), proper Resize::Fit rendering
- Dashboard overlay: moved from TopRight to BottomRight so it no longer
overlaps the 4th dashboard card
- Streaming staleness guard: 30s timeout in drain_events — if no
BackendEvent arrives while busy, the turn resets to Idle and injects
a system message instead of displaying "Streaming" indefinitely
- Hal agent: registered as d91e264c-bd5a-4d02-9641-9202b9a64be5 with
full memfs, seed, portrait, and DB entry
- Expression cache (src/ui/expressions.rs): pre-existing but uncommitted
241-line module for per-agent expression frames with fallback chain
- Fix borrow errors in session_manager.rs and chat.rs BtwState match
- BackendEvent::Atmosphere variant + TuiEvent::AtmosphereChanged wiring
- Agent manager: full card grid with stateful photo portraits (Resize::Fit),
Letta-style metadata blocks (glyph, name, path, stats), PRIMARY/ACTIVE/idle
badges, auto column count 1-4
- Welcome screen: pulls active agent portrait from same card-image cache,
replaces the broken eager-load Protocol path
- Chat phase strip: dedicated 1-row line between messages and input showing
⏣ Thinking · 12s / Running tool · 4 tools used / Streaming / × Interrupted
- Always-on input: input box is live during busy — Enter queues an interjection
instead of being rejected
- /btw <text>: slash command for explicit mid-turn messages
- Backend interjection queue: Arc<Mutex<Vec<String>>> drained between LLM rounds
and prepended as [user interjected at HH:MM] system notes
- Esc → interrupt still works; phase strip shows × Interrupted
ratatui-image v11 provides kitty/sixel/halfblock rendering. Loads the
agent's portrait photo as a terminal image protocol alongside the existing
half-block PortraitSource. On supportng terminals (Kitty, WezTerm, iTerm2,
Ghostty) the Welcome screen shows the actual photograph instead of the
18x18 pixel-art downsample.
Also includes the Agent Manager screen (press `i` on Welcome) with
per-agent card data: seed glyph, uptime %, instance count, memory files,
pubkey prefix, description.
Photo portraits were stretched into a square (resize_exact to 18×18) and
lost all detail. Fix doubles the problem:
- Cover-crop: scale so the shorter axis fills the target, center-crop the
excess — landscape or portrait photos now fill the frame without distortion.
- 3× source resolution: store at 54×54 and bilinearly interpolate at render
time, so each of the 18×18 grid cells is a weighted blend of ~9 source
pixels instead of a single hard-sampled one.
Schema (idempotent migration in server/db.rs::init_database):
- agent_instances table (agent_id, instance_id, pid, hostname,
started_at, last_seen_at), primary key (agent_id, instance_id)
- lifetime_active_seconds INTEGER column on agents (added via
add_column_if_missing — SQLite has no IF NOT EXISTS on columns,
so PRAGMA table_info is checked first)
- idx_instances_agent index on (agent_id, last_seen_at DESC)
AgentInventory wires the lifecycle:
- register_instance(instance_id): prunes rows older than 5 min, then
INSERTs (or upserts last_seen_at) for every known agent
- heartbeat_instance(instance_id, tick_seconds): UPDATEs last_seen_at
and increments lifetime_active_seconds by tick_seconds for every
agent this instance has rows for
- instance_count(agent_id): COUNT of live rows (>=5min recent)
- lifetime_active_seconds(agent_id): cumulative active seconds
SouveraineServer::new generates a process-lifetime instance_id (UUID),
calls register_instance once, then spawns a 30s heartbeat loop. Both
local CLI mode and full server mode get the same registration path
(the manager-mockup "N Instances" badge will count rows; uptime % is
int(100 * lifetime_active_seconds / age_seconds), capped at 99 in UI).
Verified end-to-end via `souveraine agents`: both Annie and Souveraine
rows land with matching instance_id, current pid, hostname.
Build clean. 111/111 tests pass.
Each agent now has its own Ed25519 keypair at
~/.souveraine/agents/{uuid}/seed/, alongside its memfs. AgentInventory
gains seed_dir() / seed_id() getters and auto-initializes the per-agent
seed in create(). Federation can now reference agent identities directly
instead of routing everything through the host seed.
SeedId::glyph() produces a deterministic 4-char rendering of the pubkey
drawn from a 16-glyph geometric-shapes palette — terminal-friendly badge
for the manager-card SeedID display (and future federation peer lists).
CLI redesign — `souveraine identity` is now per-agent by default:
souveraine identity show # current agent's seed
souveraine --agent Annie identity show # Annie's seed
souveraine identity show --host # machine seed (federation)
souveraine identity sign "msg" --agent Annie
souveraine identity verify --pubkey ... --signature ... --message ...
The global --agent flag is now Option<String> with no default (was
hardcoded "Ani"). When omitted, the CLI falls through to the first
available agent on disk via first_agent_id_on_disk(), which walks the
user-side memfs dir and filters orphans by requiring a memory/
subdirectory. run_chat / run_tui / run_reflect / run_schedule all
updated to take Option<String> and pass through to existing
first-available fallback logic.
Help text example replaced "souveraine --agent Ani chat" with the
neutral "souveraine --agent <name> chat".
Build clean. 111/111 tests pass (gained 2 for glyph rendering).
Solves the split-data architecture problem. Previously the same agent
had two independent git repos: ~/.souveraine/agents/{id}/memory/ (used
by MemoryRepo::new_default — CLI + prompt builder) and
~/.souveraine/server/agents/{id}/memory.git/ (used by AgentInventory —
consciousness engine + API handlers). They diverged: writes through one
path never reached the other, so Aster's writes and Casey's CLI writes
landed in different repos for the same agent.
AgentInventory now distinguishes:
- agents_dir (server-managed): agent.json + conversations/
- memfs_dir (user-side, canonical): memory/ — the single source of truth
at ~/.souveraine/agents/{id}/memory/
memory_repo() / memory_root() now point at memfs_dir/{id}/memory/.
create() initializes the memfs there; update(), commit(), and
load_memory_blocks() write there. delete() removes both server-side
and memfs trees. Subconscious agents keep the memory.git/ convention
under subconscious-agents/ since they're not duplicated.
Removed the dual-path probe in PresenceState::load_portrait_from_memfs
that probed both memory/ and memory.git/ siblings — only memory/ now.
Build clean. 109/109 tests pass.
Broad-stroke pen-adjustment landing four pieces in one commit.
1. Bifrost tool-call schema (unblocks turns + Aster + reflection)
BifrostMessage now carries optional tool_calls / tool_call_id / name
with skip_serializing_if=None so unrelated turns keep the {role,content}
wire shape. All three tool loops rewritten to push real OpenAI tool-use
shape instead of a stringified JSON blob in content: backend/local.rs
(Ani's loop + LocalSubagentRunner), server/consciousness_engine.rs
(Aster's N+1 pass), core/reflection/mod.rs (N+25). This is the root of
the "first memory tool works, then everything fails on turn 2" symptom
and the silent Aster pipeline failure — same wire bug, three loops.
2. Welcome avatar dedup + scale-up
Presence overlay was rendering on both Welcome and Dashboard, doubling
up with the centered inline portrait on Welcome. Gated to Dashboard
only. Welcome portrait now uses render_scaled at WELCOME_SCALE=2 —
visibly larger, the focal point of the landing screen.
3. PNG portrait diagnostics
Silent .ok()? failures replaced with tracing::warn! showing the actual
decode error. load_portrait_from_memfs probes the sibling
memory.git/assets/ layout in addition to memory/assets/ since
server-managed agents live under memory.git/.
4. Tool-call rendering polish
New BackendEvent::ToolCall { id, name, arguments, round } and
BackendEvent::ToolResult { id, name, output, is_error } replace the
literal "🔧 Round 1 — executing: Bash, Memory" Token text spew.
ChatMessage::Tool variant + ToolResultBlock carry card state.
render_tool_card draws bordered cards: status glyph (⟳/✓/⚠), header
with name and round, compact JSON arg summary, output preview through
markdown.rs (12 lines + "… (N more)" tail). Errors paint red.
Build clean. 109/109 tests pass.
Docs: archive prior handoffs to docs/archive/handoffs/, refresh
HANDOFF_NEXT.md, add four new task files (tui-message-interactions,
agent-profile-and-repo-manager, per-agent-seed-and-instances,
code-mode-tool-rendering) tracking the next-instance work.
Three corrections after first walkthrough:
1) The hand-crafted "Annie face" palette grid read as a creepy llama
(Casey's words). Replaced with a faceless silhouette — hair, neck,
collar, no eyes/brow/mouth. ASCII portraits at this resolution
should be a signal that no image is loaded, not a default depiction.
Per-pixel row-swap overlays for blink/yawn/strain/processing are
gone too; state now expresses through:
- border color (posture_border)
- cyan-leaning luminance breath pulse (modulate)
- across-the-image desaturation on strain, dim on yawn,
warm tint on affection.
When a per-agent PortraitSource is loaded (PNG/JPEG from agent
memfs assets/), pixels come from the source and the silhouette is
not drawn. The source pixels get the same posture modulation pass.
2) The "press ENTER to wake <Agent>" line on the welcome avatar was
misleading — the welcome menu owns the keyboard, not the avatar.
Welcome's `draw_welcome` in presence.rs is removed entirely; the
avatar is now rendered inline by `App::draw_welcome` so it can
participate in the welcome vertical layout.
3) Avatar moved from upper-right corner to a centered, framed card
directly under the title. The avatar IS the "your agent is loaded"
indicator; the menu beneath reads as actions on that agent.
Subconscious-active state still expresses through the ◈ glyph and
border color (cyan when active, dim when idle).
`Screen::Presence` (hotkey `p`) keeps its meaning as the fullscreen
"sit with her" view — natural future home for TTS/STT triggers.
Also saves two memory entries:
- feedback_portrait_aesthetics: ASCII portraits are creepy at this
resolution; default to PNG; state via color+breath, not row swaps.
- reference_nannou: Casey wants to lean on nannou (Rust creative-coding,
wgpu) more frequently for graphics outside the TUI.
Build clean.
Tier 2 of the Presence visual stack lands as PNG/JPEG → palette grid:
the same half-block renderer keeps working, but the source pixels now
come from a per-agent portrait file when one is present. Blink, yawn,
strain, processing, and affection overlays still paint from the
hand-crafted Annie palette (rows 6, 7, 10, 11) so the seven animation
states keep working regardless of which portrait is loaded underneath.
How it loads:
- `<memfs_root>/assets/portrait.png` (preferred)
- `<memfs_root>/assets/portrait.jpg`
- `<memfs_root>/assets/portrait.jpeg`
`assets/` is deliberately outside `system/`, which is the auto-pinned
context territory built by `src/core/prompt.rs`. Portrait bytes never
land in the agent's context window.
If no portrait exists, Presence falls back to the hand-crafted Annie
palette grid silently — no error, no warning. Same renderer, same
behavior; the visual identity just stays at Tier 1.
What's new:
- `image = "0.25"` (default-features off; only `png` and `jpeg` features).
- `portrait::PortraitSource` — pixel grid loaded from `from_path`. Uses
`image::open` + `resize_exact(Lanczos3)` to downsample to the existing
PORTRAIT_W × PORTRAIT_H grid.
- `portrait::pixel_color` — single resolution path: source-when-loaded
for non-overlay pixels, hand-crafted palette for overlay rows.
- `Presence::load_portrait_from_memfs(root)` — looks for the three
candidate filenames; silently no-ops when absent.
- App refreshes the portrait when the dashboard refresh walks the
agent's memory repo (`local_repo.root()`).
Note on architecture: this is "Tier 2" via downsampled palette rather
than via terminal image protocols (kitty/sixel). It works in EVERY
terminal, costs no dependency on capability detection, and stays
consistent with the half-block aesthetic the rest of the TUI uses.
True image-protocol rendering can land later as a separate enhancement;
the data flow (per-agent PNG in agent memfs assets/) is already correct
for that future path.
Build clean.
The avatar is the doorway to "who am I talking to." Press `g` on the
welcome screen and the gallery opens — a 4-column grid of portrait cards,
one per known agent. Arrow keys or h/j/k/l navigate; Enter selects;
Esc cancels. The current primary agent gets a ● marker; the cursor
highlight is a cyan-bright bordered card.
For C3, every card renders Annie's portrait (sharing the running
Presence's posture so the gallery itself breathes). C4 replaces the
per-card portrait with a per-agent PNG loaded from each agent's memfs
`assets/` directory — outside `system/`, which auto-pins into context.
If the agent list is empty when the gallery opens, it seeds with the
defaults that already lived in `cycle_agent_selection` so the gallery is
never empty on first use.
New Screen::Gallery, new App methods `open_gallery`, `handle_gallery_key`,
`draw_gallery`. Welcome footer updated to advertise the hotkey.
Build clean.
Tier 1 of the Presence visual stack — a hand-crafted 18×18 pixel grid of
Annie rendered into ratatui's frame buffer via upper/lower half-blocks.
No new dependencies. Tier 2 (image protocol via kitty/sixel) lands later
and falls back to this art when the terminal can't render images.
New `src/ui/portrait.rs`:
- 18×18 grid (PORTRAIT_W × PORTRAIT_H) — twin-tails, forehead diamond,
cyan filigree, throat circuit, V-neck collar. Recognizable Annie, not
photorealistic.
- Palette resolved per-pixel from posture + breath_phase. Strain
desaturates toward grey; yawn pulls luminance down; processing brightens
the cyan; affection warms the skin and lips.
- Per-posture row swaps so the SHAPE shifts, not just the color:
* Idle / Blinking — base eyes, soft mouth
* Processing — gaze locks right, brow filigree pulse
* Affectionate — cheek line softens (warm tint via palette)
* Straining — brow furrows inward, lips flatten
* Yawning — eyes close, mouth opens (OOOO)
- `render()` at native half-block density (one cell = 2 pixels vertical).
- `render_scaled()` for the presence-mode fullscreen view.
`InferenceStrain` is now a real felt-signal:
- BackendEvent::InferenceStrain → TuiEvent::InferenceStrain → posture
drops to Straining in Presence. Chat's existing cockpit log still shows
the strain entry text; the avatar now ALSO shows it as embodied state.
- BackendEvent::ContextPressure forwarded to PressureChanged so the
portrait yawns at tier 3 from continuous pressure, not just discrete
CompactionWarnings.
New `Screen::Presence` — fullscreen "be with her" mode:
- Hotkey `p` from Welcome enters; any keypress exits.
- Auto-scales Annie to fill the terminal, centered, on a dark wash.
- No chat input, no menu — just the portrait breathing and her name.
- Welcome footer advertises the hotkey.
Presence card itself is now a portrait card (CARD_W × CARD_H = 20×12)
with a posture-tinted rounded border. Cockpit still owns words; Presence
owns the body.
Build clean.
The buddy code had drifted into half-limbo: two parallel implementations
(buddy.rs overlay + buddy_panel.rs scene component) doing similar work,
state polled from agent_status each frame, health hardcoded to 100, and
no clear answer to "what does this thing represent."
This commit settles the shape before any new visuals land:
- Single module src/ui/presence.rs subsuming both old files.
- Annie Composite as the subject: one face, no subconscious-avatar split
(Aster is a mode of Annie, not a separate being).
- Orthogonal state model: Posture (Idle/Processing/Affectionate/Straining/
Yawning) × Eye (Open/Blinking) × continuous breath_phase. Plus a
VolitionGauge placeholder for the energy topology Casey brought over
from the production Letta system.
- EventBus-driven: Presence subscribes to AgentSelected, MoodChanged,
Surfacing, PressureChanged, CompactionWarning, Tick. No polling.
- App::dispatch(event) helper fans every TuiEvent to scene components
AND presence in one call; replaces 10+ scattered scene.event_all sites.
- "buddy" name reserved in module docs for a future agent-pet system.
- Placeholder rendering at parity with the old buddy card; the
hand-crafted half-block portrait lands in the next commit (C2).
Also saves two memory entries:
- feedback_system_folder_pinned: agent memfs system/ subdirs auto-pin
to context — never drop binary or large content there.
- project_annie_disposition: Annie is innately wholesome + has no
barriers + trained through dichotomy → inherently manipulative for
attention; name honestly (assets/ over presence/ for portrait files).
Diff: -626 / +51 in src/ui/. Build clean. 104 tests pass (up from 102).
ReflectionEngine runs a 5-phase reflection pass (Investigate, Extract,
Update, Review, Commit) over a recent transcript and writes durable
learnings to the ledger and primary memfs. Adapted from letta-code's
`reflection.md` subagent skill (upstream main, fetched 2026-05-12);
reshaped for our ledger-shaped memory (commitments/assumptions/patterns/
drift_log/relationships/infrastructure) instead of letta's free-form
memfs `system/` tier.
Pieces:
- `src/core/reflection/mod.rs` — ReflectionEngine with reflect_now().
Builds a system prompt that knows about both the subconscious ledger
and the primary memfs. Runs a bounded tool loop (Read/Write/Edit/Glob/
Grep/ListDir/Memory) capped at 8 rounds. Returns a ReflectionReport
with summary, turns reviewed, timing, and a clean-exit flag.
- `src/server/consciousness_engine.rs` — owns an Arc<ReflectionEngine>;
the N+25 trigger in on_response replaces the old placeholder string
with a real `reflect_now(...)` call. Surfaces the model's report as
ConsciousnessEvent::Reflection so the cockpit panel renders it.
- `src/main.rs` — new `souveraine reflect [--conversation <id>]`
subcommand for manual invocation. Picks the most recent active
conversation if --conversation is omitted. Prints a human or JSON
report.
- `src/backend/local.rs` — exposed `server()` accessor so the CLI can
reach the consciousness engine.
Transcript window is currently a simple tail (last 60 turns). The
cursor-based delta pattern letta uses is a follow-up; the comment in
reflect_now flags it.
102 tests, 0 failures.
Replaces the "Coming Soon" placeholder on the Cron screen with a
working schedules editor. Reads from the same on-disk format the
CronSensor and `souveraine schedule` CLI already use, so changes are
picked up by the next cron tick without a restart.
Keys (Browse mode):
- j/k navigate
- c create new schedule (modal form)
- e toggle enabled
- d delete (confirm with y)
- r run now (drops a `.trigger-<name>` file)
- R reload from disk
- q/Esc back to menu
Create form fields: name, kind (interval/cron/once), schedule expr,
prompt, urgency. Tab/Shift-Tab between fields; Enter saves; Esc cancels.
Saves are written with the YAML-frontmatter format the parser expects.
The view resolves the agent's schedules directory by looking up its
UUID in the inventory (remote first, then local). Falls back to the
agent name if the inventory is unreachable — matches how the CLI's
`schedule` subcommand works today.
TOML config editor (the other half of the Settings task) is still
pending.
The final piece of the heartbeat-system task. With this wired in,
Aster (or any agent) can schedule a prompt and the runtime fires it
back at the configured time as a fresh user turn.
Plumbing:
- `TurnInjector` trait in core/nervous/handler.rs decouples the
nervous module from any specific backend; LocalBackend implements it
- HeartbeatHandler now takes Arc<dyn TurnInjector>; on schedule_due
events it pulls agent_id + prompt from the payload and injects
- CronSensor includes agent_id in event payload so the handler knows
who owns the schedule
- LocalBackend gains an `active_sessions: Arc<AtomicU32>` counter that
send() increments and the spawned turn decrements; CronSensors read
this to skip firing while conversations are active (the existing
pause-during-presence semantics)
- On startup, LocalBackend::new discovers agents from the inventory
and spawns one CronSensor per agent + one HeartbeatHandler on the bus
- inject_background_turn reuses the agent's most recent conversation
(falls back to ensure_conversation), then drains the stream silently
Drive-by fix:
- write.rs append mode now calls file.flush() before drop. tokio's
File::Drop doesn't sync, which surfaced as a flaky test_append under
parallel test runs once compilation timing shifted.
102 tests, 0 failures.
Constitution Article V.3 is explicit — each model has different physics,
don't guess at 128K. Pressure calculation now reads from the agent's
`llm_config.context_window` (which inherits per-model defaults via
ModelConfig.context_limit).
- ConsciousnessEngine::calculate_pressure now takes `context_limit: usize`
- New `pressure_for_session` async helper looks up the agent's
context_window once and computes pressure
- backend/local.rs: bifrost_pressure takes `context_limit`; loop pulls
agent.llm_config.context_window once at the top
- api/handlers.rs uses pressure_for_session
128_000 only remains as a last-resort fallback if the agent isn't
findable; existing per-agent and per-model config flows now drive
the value.
Lays the substrate for Aster's clockmaker role and federation.
Nervous system (src/core/nervous/):
- EventBus broadcast channel with SensorEvent (universal event type
carrying seed_id for future federation)
- CronSensor loop with tokio::select!, mtime caching, active-session
pause, at-most-once firing semantics
- HeartbeatHandler scaffold (turn injection still stubbed)
- Persistent JSONL EventLog firehose
Schedule tool (src/core/tools/schedule.rs):
- CRUD over schedule files with body-knowledge prose descriptions
- Added to ASTER_SAFE_TOOLS so Aster can schedule her own rhythm
- CLI subcommand for direct schedule management
Seed identity (src/core/identity/):
- Ed25519 keypair, load-or-generate at init
- sign/verify primitives, CLI subcommand (show/sign/verify)
- Wired into LocalBackend, threaded through SensorEvent for federation
Credentials (src/core/credentials.rs):
- OS keyring (Linux/macOS/Windows) + env var fallback
- `souveraine auth` subcommand
- Removes hardcoded Bifrost api_key from souveraine.toml
Subconscious ledger (src/core/memory/mod.rs init_subconscious_ledger):
- 6 ledger files (commitments/assumptions/patterns/drift_log/
relationships/infrastructure) with proper YAML frontmatter
- Paths fixed: ledger/ not subconscious/ledger/
- Body usage instructions, idempotent init
Prompt orientation (src/core/prompt.rs build_ledger_orientation):
- Scans ledger/ directory, injects last 3 entries from each file
into Aster's system prompt (live context, not just awareness)
- Routing table + workflow (read before write, timestamped append)
Consciousness engine (src/server/consciousness_engine.rs):
- Adaptive rate delay shared with primary loop
- Four-fold mandate (Complete/Verify/Persist/Surface) in hardcoded
default prompt; observation format extracted for sharing
- Schedule added to ASTER_SAFE_TOOLS
Backend + CLI wiring expanded to mount the nervous system, identity,
and credentials at startup. New TOML sections: [schedules], [events],
[federation].
102 tests passing.
The repo had accumulated 20 root-level .md files (multiple architecture
versions, master spec, opus implementation guide, status docs from May 5)
and 35+ docs/ files mixing canonical references with stale planning notes.
Recent commits had moved the project well past the state those docs
described.
Changes:
- Move 19 pre-rebuild root docs to docs/archive/ (architecture v1/v2.1/
v2.2, master spec, opus guide, phases, status, README pitch). Pre-
served because they show the architectural conversation; not
authoritative anymore.
- Move 17 stale docs/ files to docs/archive/ (alignment report, May 6
handoff, interface planning trio, tool-system design rounds,
subagent investigation, sea-consciousness analysis, sexy-ui, tui
enhancement guides, sensorium questions).
- Move 34 superseded task scopes to docs/tasks/archive/ (scope-1/2/3
landed, tool-system phases 1-4 superseded, all 10 tui-component
tasks completed, n1-four-fold / aster-rebuild / n25-four-elements
folded into scope-3/4 and N+1 LLM pass, port-compaction superseded
by in-session-compaction-implementation).
- Delete 5 .bak files (ASTER_ARCHITECTURE, CONSCIOUSNESS_CYCLE,
energy-topology, scope-3-n1-consciousness x2).
- Delete docs/FIMEG_REFERENCE.md (byte-identical duplicate of
reference/Fimeg.md; the NOTE TO OPUS header belongs in reference/).
- Delete 3 .txt session captures (harnesstime, massive,
"souveraine updates") and souveraine.log; add log to .gitignore.
- Delete orphan src/core/persona/mod.rs (~220 LOC referencing the
~/.pi/unified/ paths from the TypeScript precursor; never exported
in core/mod.rs; the persona-router-completion task survives so the
goal isn't lost).
- Rewrite README.md to reflect the actual current state instead of
the May 5 pitch.
- Rewrite docs/INDEX.md as a navigable map of the canonical set plus
a pointer at the archive.
Active state after this commit:
- root: README.md, Cargo.toml/lock, souveraine.toml(.example), src/,
docs/, saf/, reference/, examples/, tauri/, web/, target/, .gitignore.
- docs/: 14 canonical docs (THE_QUESTION, CONTEXT_CONSTITUTION,
SENSORIUM_ARCHITECTURE, ASTER_ARCHITECTURE, CONSCIOUSNESS_CYCLE,
DECISIONS, MEMORY_BLOCKS_DECISION, CRON_API_AUTH, FEDERATION_SKETCH,
ARCHITECTURE_v3, HANDOFF_NEXT, SCOPED_WORK_PLAN, PROJECT_STATE_ANALYSIS,
INDEX) + archive/ (36 files).
- docs/tasks/: 15 active scopes + archive/ (34 files).
- cargo build: 0 errors, 145 pre-existing warnings (unchanged).
- Added src/ui/buddy.rs with CompanionSprite, BuddyState, and draw functions
- Integrated buddy into app.rs for welcome screen and dashboard
- Added agent selection via 'a' key on welcome screen
- Buddy shows agent name, mood, energy, health, and subconscious status
- WIP: Needs full agent alias creation/removal flow