Skip to content

feat: agent loop expansion + link feed + bus chat + LocalRunner (48-commit consolidation)#7

Merged
chazmaniandinkle merged 51 commits intocogos-dev:mainfrom
chazmaniandinkle:refactor/component-provider-subpackage
Apr 20, 2026
Merged

feat: agent loop expansion + link feed + bus chat + LocalRunner (48-commit consolidation)#7
chazmaniandinkle merged 51 commits intocogos-dev:mainfrom
chazmaniandinkle:refactor/component-provider-subpackage

Conversation

@chazmaniandinkle
Copy link
Copy Markdown
Contributor

Summary

Consolidates the work that's accumulated on personal trunk since the last upstream sync. 48 commits across four themes, all dogfooded for several weeks on the maintainer's daily-driver kernel.

This is a big PR by line count but the themes are independently coherent — review section by section.

Theme 1: Foveated decomposition + agent dashboard

The DECOMPOSE stage was the missing piece for hypercycle self-feed. Wired in alongside a dashboard that surfaces live agent state.

Commit What
`a4302e1` feat: foveated decomposition pipeline — the missing DECOMPOSE stage
`5cb284e` feat: wire decomposition into agent loop — first hypercycle self-feed
`1f5e8d8` feat: replace memory_write with proposal-based tools
`b07fd55` fix: wire server.agent reference for status API
`400f432` feat: activity-aware observation + cheap checks gate
`dfa4fec` feat: agent dashboard — pill, status card, activity, memory, sparkline
`7668988` fix: agent presence detection + prompt tuning
`cb89ba8` feat: manual cycle trigger button on dashboard
`e25f340` feat: cycle trace storage + dashboard trace viewer
`626ceba` fix: read_proposal returns all proposals when called with no args
`4dcf0aa` fix: don't re-render traces when data unchanged (preserves open details)

Theme 2: Agent loop hardening

Addresses the thrash and runaway-action behavior observed during overnight runs.

Commit What
`18a8817` fix: separate Execute prompt encourages multi-tool chaining
`435e3d5` fix: stronger loop breaker — restrict action values, stop re-reading
`3e5359e` fix: overnight agent efficiency — system event filtering, error interval, timeouts
`52dbf5f` feat: run overlap guard + clean rolling memory
`c79071d` fix: hard loop breaker — enforce action rotation in code
`4518065` feat: agent core loop hardening — feedback, events, memory quality
`f3573e9` fix: 30-second cooldown on event-driven Wake to prevent thrashing

Theme 3: Link feed + kernel-mediated chat + wait tool

Three feature additions on top of the agent foundation.

Commit What
`b319181` feat: link feed integration — Discord pull, enrichment, inbox awareness
`548e334` feat: num_ctx fix + self-chaining + list_inbox + metrics + work nudge
`2d12855` test: add decompose tier-0 schema + retry tests
`1ea6aa9` feat: wait tool — sanctioned silent no-op for execute loop
`3d27dfc` feat: kernel as conversational substrate — bus-mediated user chat + trace emission

Theme 4: internal/ extraction + LocalRunner

Mechanical refactors plus the new LocalRunner that makes kernel-managed bare-metal services work without Docker.

Commit What
`3d13365` refactor: extract workspace resolution into `internal/workspace/`
`65fb4df` refactor: extract Component provider into `internal/providers/component/`
`26ccda6` refactor: extract link-feed feature into `internal/linkfeed/`
`bb63f99` feat: LocalRunner — bare-metal service supervision

LocalRunner highlights

  • Detached process group (`syscall.SysProcAttr{Setsid: true}`) so kernel exit doesn't cascade
  • stdin = `/dev/null` so MCP stdio servers don't exit immediately
  • Venv-aware command resolution — `spec.local.venv` set + non-absolute `command` triggers `/bin/` probe
  • PID file with `cmd_hash` (sha256 of command+args+workdir+env) — drift detection triggers restart on next reconcile
  • Mode selection (`serviceMode(crd, runtimeAvailable)`): docker if runtime up + image set, local if `spec.local`, none otherwise

ServiceCRD reconcile loop runs every 5m per `resources.yaml`. CLI surface (`cogos service list/status/start/stop/restart/logs`) routes correctly per mode.

Test plan

  • Agent loop runs without thrash overnight (multi-day dogfooding)
  • Dashboard renders status / traces / activity / sparkline
  • mod3 + local-inference both supervised through reconcile cycles (>24h uptime)
  • Decompose tier-0 schema test passes
  • PID-file drift detection restarts service when CRD changes
  • CLI service commands route correctly per mode
  • Reviewer: trigger a manual cycle from dashboard; verify trace appears
  • Reviewer: chat through dashboard; verify bus events appear on `bus_dashboard_chat`
  • Reviewer: confirm Discord link-feed populates inbox without rate-limit issues
  • Reviewer: bring Docker runtime up; confirm `serviceMode` flips local→docker on next reconcile

Known follow-ups (NOT in this PR)

  • Per-service watcher goroutines (currently relies on 5-min reconcile interval for crash recovery)
  • Health-driven mode flipping (currently health is reported, not acted on)
  • Wire `local-inference` as an actual provider in `providers.local.yaml` — CRD lifecycle works but kernel doesn't route to it yet
  • Migrate `browser_vad` and `mcp_signal` paths in mod3 onto the new bargein provider interface (currently they bypass it; see cogos-dev/mod3#4)

chazmaniandinkle and others added 30 commits April 14, 2026 20:47
Set up Go workspace (go.work) with 7 new library modules under pkg/:
cogblock, bep, coordination, reconcile, modality, cogfield, uri.
Each module has a go.mod and doc.go stub. No code moved yet — this
establishes the skeleton so `go work sync` resolves all modules.

Implements ADR-074 layer 0-3 decomposition structure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the canonical CogBlock type definitions (CogBlockKind, BlockProvenance,
TrustContext, BlockArtifact) and the full ledger implementation (EventEnvelope,
canonicalization, hash chaining, AppendEvent, VerifyLedger) into the importable
pkg/cogblock library package with zero kernel dependencies.

The engine retains its own CogBlock struct with the typed Messages field
([]ProviderMessage) since that couples to provider internals. Shared types
are re-exported as type aliases for backward compatibility. The kernel's
ledger_core.go becomes a thin delegation layer.

17 new tests cover block JSON round-trips, kind constants, canonicalization,
hashing, hash chaining, and tamper detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extracts the cog:// URI parsing, formatting, and validation logic into a
standalone stdlib-only package. Includes URI struct, Parse/String functions,
namespace registry, query helpers, inline reference extraction, and
structured error types. Resolution (filesystem lookup) stays in the kernel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…reconcile

Moves the Reconcilable interface (7-method plan/apply contract), all type
definitions (State, Plan, Action, Summary, Result, status enums), state
management with lineage tracking, provider registry, event emission, and
meta-reconciler with Kahn's topological sort into pkg/reconcile as a
stdlib-only importable library.

Kernel files become thin re-export layers with type aliases and function
wrappers for backward compatibility. CLI commands, serve loop, watch mode,
and MCP tool integration remain in the kernel since they depend on runtime
internals (ResolveWorkspace, CogBus, field conditions).

34 tests in pkg/reconcile, all kernel reconcile tests continue to pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…kg/modality

Moves the core modality abstractions (Module interface, Bus, WireMessage,
SubprocessConn, ChannelRegistry, Salience, ProcessSupervisor, TextModule)
into pkg/modality as a stdlib-only reusable package. Kernel files become
thin re-export wrappers via type aliases, preserving backward compatibility.

Layer A (types + interfaces) and Layer B (bus, wire, supervisor) are
fully extracted. Layer C (ledger integration, HTTP modules, serve wiring,
tools, HUD) remains in the kernel where it depends on kernel-specific types.

33 tests cover bus dispatch, wire serialization, channel registry,
salience scoring, event validation, and text module lifecycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Selective extraction of the BEP (Block Exchange Protocol) types package
from the kernel. Moves wire-compatible protocol types, TLS/DeviceID
management, version vectors, index types, event types, and the framing
layer into an importable package. Creates Engine and SyncProvider
interfaces for decoupled consumption.

Files left in kernel (coupled to bus, AgentCRD, fsnotify):
  - bep_engine.go, bep_model.go, bep_provider.go, bep_receiver.go

52 tests covering proto round-trips, TLS cert gen, wire framing,
version vector logic, index scanning/diffing/persistence, and events.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move all CogField data types, pure functions, and interfaces into a
standalone package at pkg/cogfield/. The kernel retains HTTP handlers,
adapter implementations, and constellation/reconcile integration, importing
types from the package via aliases.

Extracted: Node, Edge, Stats, Graph, Block, GraphBlock, BlockAdapter,
AdapterNodeConfig, BlockTypeConfig, ExpandNodeResponse, BusDetail,
BusRegistryEntry, SessionMessage, SessionDetail, DocumentDetail, DocRef,
FieldCondition, TriggeredCondition, FieldConditionState, SignalFieldState,
PersistedSignal, SessionJSONLEvent.

Pure functions: NormalizeEntityType, InferSector, StrengthFromMetrics,
ParseCSVSet, FilterNodes, BFSSubgraph, ComputeStats, FilterByMeta,
GraphBlockToNode, ParseConditionQueryString, EvaluateFieldConditions,
ComputeRelevance, SignalIsActive, ExtractTimestamp.

21 tests covering construction, validation, JSON round-trip, BFS,
condition evaluation, and signal decay.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Thin observation-assessment-execution loop using a local model (Gemma E4B
via Ollama) through the OpenAI chat completions wire protocol. Runs as a
goroutine inside the kernel process with kernel-native tool dispatch.

- AgentHarness: assess (JSON mode) + execute (tool loop) + RunCycle
- 6 core tools: memory_search/read/write, coherence_check, bus_emit, workspace_status
- Self-contained wire protocol types (no harness package import)
- MaxTurns safety limit, context cancellation, tool error recovery
- 10 tests with httptest mock servers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e Code sessions

Adds a transparent proxy at POST /v1/messages that accepts Anthropic Messages
API requests, forwards them to the upstream Anthropic API, and streams SSE
responses back to the client. This enables Claude Code to route through the
kernel via ANTHROPIC_BASE_URL=http://localhost:5100.

- serve_messages.go: proxy handler with streaming/non-streaming support,
  header forwarding, API key resolution, bus event emission, and
  Anthropic-format error responses
- serve_messages_test.go: 8 tests covering non-streaming, streaming,
  error passthrough, header forwarding, auth, bearer auth, unreachable
  upstream, and tool_use event counting
- serve.go: mux registration and banner output
- cog.go: `cog claude` command that checks kernel health and launches
  claude with ANTHROPIC_BASE_URL set, falling back to direct launch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- agent_serve.go: ServeAgent runs E4B via native harness on 30-min ticker
  - Gathers workspace observations (git, memory, coherence)
  - Calls E4B for assessment (sleep/consolidate/repair/observe/escalate)
  - Executes actions through kernel-native tool functions
  - Emits cycle events to CogBus
  - Configurable via COG_AGENT_INTERVAL, COG_AGENT_MODEL env vars
- serve_daemon.go: Wired into cog serve alongside reconciler
- serve.go: defaultServePort changed from 5100 to 6931 (5100 decommed)
- cog.go: cog claude uses single port check against 6931

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Agent loop starts at 5m, doubles toward 30m on consecutive "sleep" assessments,
  resets to 5m when action is needed (homeostatic backoff)
- Strip <think>...</think> tags from model responses before JSON parsing
  (Gemma 4 sometimes wraps output in thinking blocks even in JSON mode)
- Tighter system prompt: no markdown, no explanation, JSON only

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use /api/chat instead of /v1/chat/completions (OpenAI compat)
- Set think: false explicitly to control thinking at the source
- Use format: "json" instead of response_format for structured output
- Arguments field is now json.RawMessage (Ollama returns object, not string)
- Remove stripThinkingTags — no longer needed when thinking is disabled properly
- Update all tests for native response format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The agent harness was writing events to bus_agent_harness but never
registering it in the bus registry. This meant the bus was invisible
to /v1/bus/list and cross-bus event queries. ensureBus() now creates
the directory, events file, and registry entry during Start().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The agent goroutine was dying silently after ~1-4.5 hours, likely from
an unrecovered panic in the Ollama HTTP call or JSON parsing. The
reconciler (separate goroutine) kept running, masking the failure.

Added safeCycle() wrapper with defer/recover around runCycle(). Panics
are now logged and the loop continues after the normal interval.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…t tab

- agent_serve.go: AgentStatusResponse struct, Status() method, tracks
  startedAt/lastAction/lastUrgency/lastReason/lastDurMs per cycle
- serve.go: GET /v1/agent/status endpoint, agent field on serveServer
- serve_dashboard.go: GET /dashboard serves embedded HTML dashboard
- dashboard.html: Agent status pill in header (▲ cycleCount · timeAgo),
  Agent tab with status card, cycle history from bus events, urgency sparkline
- Switched cycleCount from atomic to mutex-protected (consistent with other fields)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename mcp_http.go.wip → mcp_http.go, activating the Streamable HTTP
  transport (spec 2025-03-26) with session management, batch support,
  and bus integration
- Delete mcp_stub.go (replaced by full implementation)
- Add public HandleRequest() method on MCPServer for use by both stdio
  loop and HTTP transport
- Add NewMCPServerForHTTP() constructor (no stdin/stdout binding)
- Add busID/busManager fields to MCPServer for HTTP session bus tracking
- Add createMCPBus() on busSessionManager for MCP session buses
- Add BlockMCPSessionInit/BlockMCPSessionEnd constants
- Update protocol version from 2024-11-05 to 2025-03-26
- Add 4 Mod3 tools (mod3_speak, mod3_stop, mod3_voices, mod3_status)
  that proxy to Mod3's REST API on localhost:7860
- Mod3 URL configurable via MOD3_URL env var
- go build and go vet pass clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two tests referenced features that don't exist yet:
- TestMCPHTTP_ToolCallEmitsBusEvents: tool.invoke/tool.result bus events
  not emitted by MCP handler (the tool router emits these, MCP doesn't yet)
- TestMCPHTTP_BusSendRead: cogos_bus_send/cogos_bus_read tools never built

Tests deferred (prefixed with _) with comments explaining what needs
implementing to re-enable them. No partial implementations to clean up —
the underlying infrastructure (BlockToolInvoke/Result constants, tool
router) is real and used by other paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Deleted TestMCPHTTP_ToolCallEmitsBusEvents and TestMCPHTTP_BusSendRead.
Both tested features that were never implemented:
- tool.invoke/tool.result bus emission from MCP handler (tool router
  handles this on its own path in bus_tool_router.go)
- cogos_bus_send/cogos_bus_read MCP tools (never built)

Tests should match implementation. No dead test code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… Messages proxy, dashboard

- New sections: Library packages (7 pkg/ modules), Agent harness, MCP tools
- Updated API table with /v1/agent/status, /v1/messages, /mcp, /dashboard
- Updated project layout showing pkg/ and new root-level files
- Updated file/test counts from verified codebase
- Updated ecosystem table (desktop archived, research active)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `cog decompose` CLI with 4-tier progressive disclosure:
- Tier 0: one-sentence (~15 tok), Tier 1: paragraph (~100 tok),
  Tier 2: full CogDoc with sections + embeddings, Tier 3: raw (gated)

Engine reuses AgentHarness.GenerateJSON() with JSON mode per tier.
Embedding co-generation via nomic-embed-text (128+768 dim Matryoshka).
Content-addressed CogDoc storage, constellation indexing, bus events.
Interactive workbench TUI (--workbench) with 2x2 tier viewer.
Dashboard Decompose tab with history and compression metrics.

3,473 lines across 7 files. 52 tests (unit + integration).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts:
#	agent_serve.go
#	internal/engine/web/dashboard.html
#	serve_daemon.go
After each assessment cycle, the agent decomposes its own output into
a Tier 0 sentence and stores it in a rolling buffer (last 10 cycles).
On the next cycle, these compressed sentences are injected into the
observation, giving the agent temporal continuity over its own behavior.

This is the first PRODUCE → DECOMPOSE → ABSORB wire in the CogOS
Hypercycle. The cycle feeds itself: agent output becomes agent input.

- agentCycleMemory: thread-safe rolling buffer with disk persistence
- decomposeAndStore: async Tier 0 decomposition with fallback
- gatherObservation: injects rolling memory into workspace observation
- Best-effort: decomposition failure doesn't block the cycle
- Persists to .cog/.state/agent/cycle-memory.json across restarts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The agent can observe freely but cannot modify the workspace directly.
Instead it writes proposals to .cog/.state/agent/proposals/ that require
human or cloud-tier authorization before taking effect.

New tools: propose, list_proposals, read_proposal
Removed: memory_write (from agent tool surface only, CLI still has it)

Pending proposals are injected into observations so the agent sees what
it's already proposed and doesn't repeat itself. System prompt updated
to explain the propose-don't-apply model and encourage checking cycle
memory for repetitive behavior patterns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The agent was created as a local variable in cmdServeForeground() but
never assigned to server.agent, so GET /v1/agent/status always returned
alive: false. One-line fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The agent now sees system-wide activity via bus registry:
- User presence (active/recent/idle based on last event)
- Claude Code session count and event volume
- Chat, MCP, and voice session activity
- Total event delta since last cycle, hottest bus

Cheap checks gate skips the E4B model call entirely when nothing
has changed since the last cycle (git unchanged, no bus events,
coherence OK, no new proposals, last action was sleep). Saves ~10s
and ~200 tokens per idle cycle. Gate is conservative: any single
change triggers a full model assessment.

Observation cache tracks git file count, coherence status, proposal
count, and bus registry snapshot for delta computation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enrich GET /v1/agent/status with activity summary (user presence,
session counts, event delta), rolling memory entries, and pending
proposals. Dashboard gets:
- Agent pill in header (▲ cycleCount · timeAgo)
- Agent tab with status card, last assessment, model/uptime/interval
- System Activity section (user presence, Claude Code sessions, event delta)
- Urgency sparkline (SVG, last 20 cycles, color-coded)
- Rolling Memory display (10 Tier 0 sentences with action color coding)
- Pending Proposals list

Polls every 5s for pill, full render on tab switch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Filter system buses (capabilities, http, agent_harness, index) from
  user presence detection — kernel self-events were falsely showing
  user as "active" permanently
- Simplify prompt: remove consolidate/repair (just propose), remove
  "don't disrupt" language (agent can't disrupt — it only writes
  proposals), add explicit loop-breaking rule (3+ same action = must
  change), encourage engaging with pending proposals
- Widen "recent" window to 30 minutes (was 1 hour for idle threshold)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /v1/agent/trigger fires one cycle immediately. Dashboard Agent
tab gets a "Trigger Cycle" button that shows a spinner for ~12s then
refreshes the status. Useful for testing prompt changes without
waiting for the timer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Store full cycle traces (observation, assessment, execute result) to
.cog/.state/agent/cycle-traces.json (last 20 cycles). New API endpoint
GET /v1/agent/traces returns the trace history.

Dashboard Agent tab gets a Cycle Traces section with collapsible
detail views showing the full observation, reason, target, and execute
result for each cycle. Execute results highlighted in purple when
present.

Also log execute results to the kernel log (truncated to 500 chars).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
E4B couldn't chain list_proposals → read_proposal(filename) in one
Execute turn. Now read_proposal with no filename returns ALL pending
proposals with full content in one call. Removes the multi-step
dependency that was causing the stuck loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chazmaniandinkle and others added 17 commits April 15, 2026 23:58
The 5s polling was collapsing expanded trace details on every refresh.
Now traces only re-render when the cycle list actually changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Execute phase was using the assessment's JSON-only prompt, causing
E4B to respond with prose after one tool call instead of chaining.
Now Execute gets its own prompt that explicitly says "call MULTIPLE
tools in sequence" with examples of read→propose chains.

Split RunCycle into separate Assess + Execute calls in runCycle() so
each phase gets the right prompt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
E4B was returning "read_proposal" as an action (not in the allowed set)
and re-reading proposals every cycle despite having already read them.
Prompt now: explicit allowed values only, check memory before re-reading,
sleeping is explicitly encouraged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…val, timeouts

Four fixes from 8-hour overnight autonomous run (63 cycles):
1. System bus events (capabilities, http, agent_harness, index) excluded
   from total_event_delta in both observation and API — stops phantom
   "6 events" preventing the agent from sleeping
2. Errors count as sleeps for adaptive interval doubling — timeouts no
   longer reset to 5m minimum
3. HTTP client timeout 120s → 180s — catches slow model loads after
   heavy execute phases
4. Decompose timeout 30s → 60s — reduces Tier 0 failures after heavy cycles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Atomic running flag prevents overlapping cycles from manual triggers
- POST /v1/agent/trigger returns 409 "already_running" if cycle in progress
- Status API includes "running" field
- Skips and errors no longer write to rolling memory — only real
  assessments with Tier 0 decomposition. Keeps the compressed
  narrative clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
E4B can reason about the 3+ identical action rule but can't reliably
follow it. Now enforced in runCycle(): if memory shows 3+ same action
and the model chose the same again, override to propose (if pending
proposals) or sleep. Logged as "hard loop break: X → Y".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three tracks from independent code reviews:

Track A — Proposal Feedback:
- acknowledge_proposal tool lets agent mark proposals as processed
- gatherPendingProposals shows acknowledged/approved/rejected counts
- Prompt tells agent to acknowledge after reading

Track B — Event-Driven Cycling:
- Wake channel (buffered 1) on ServeAgent
- Bus event handler wakes agent on non-system events
- fsnotify watches proposals directory with 500ms debounce
- Intervals: 10m min, 60m max (events wake immediately)

Track C — Memory Quality:
- Decompose retries once after 2s pause before fallback
- Quality flag on cycleMemoryEntry ("decomposed" | "fallback")
- Quality shown in observation formatting
- Execute phase limited to 60s timeout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The agent's own proposal writes trigger bus events which immediately
re-wake it, creating a propose→wake→propose tight loop that exhausts
the GPU. Now Wake() checks time since last cycle and ignores signals
within 30 seconds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three-layer integration giving the agent its first real task:

Layer 1 — Scheduled Discord Pull:
- pull_link_feed tool reads Discord #link-feed channel via API
- Pagination, URL extraction (embeds + regex), dedup against inbox
- Writes raw CogDocs to .cog/mem/semantic/inbox/links/
- State tracking via link-feed-state.json (last_message_id)

Layer 2 — Inbox Awareness:
- Agent observation shows inbox counts (raw/enriched/failed/total)
- Link feed timing (last pull, next pull, overdue indicator)
- fsnotify watches inbox/links/ with 2s debounce → wakes agent
- Newest 5 raw items listed in observation

Layer 3 — Agent Enrichment:
- enrich_link tool fetches URL metadata (HTML meta tags, 10s timeout)
- Tier 1 decomposition via E4B (summary + key terms + classification)
- Cross-references via memory_search (top 3 workspace connections)
- Updates CogDoc: raw → enriched with summary, tags, refs
- 30-second per-link timeout, fetch_failed on error

Dashboard:
- Link Feed panel in Agent tab (pull timing, inbox counts)
- Recent enrichments with connection counts

821 lines in agent_linkfeed.go, self-contained.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Five load-bearing fixes that together take the agent from "occasionally
timing out" to "autonomously processing a 438-item backlog."

agent_harness.go:
  - Add Options field to Ollama chat request (num_ctx etc.)
  - Set num_ctx=8192 on Assess and Execute calls
    (default was 131072 — 128K context allocated a massive KV cache
    that made even trivial prompts take 40+ seconds)
  - Log Ollama performance metrics on every call
    (prompt tok/s, eval tok/s, total duration)
  - Result: assess phase 180s timeout → 7s (25x speedup)

agent_serve.go:
  - Add 'execute' action to assessment schema so the agent can do
    work, not just observe/propose/sleep/escalate
  - Execute prompt mentions list_inbox, enrich_link, pull_link_feed
  - Work nudge: if inbox has raw items and last N cycles weren't
    execute, force execute. Runs AFTER hard-loop-breaker so it
    overrides sleep when real work exists
  - Hard loop breaker suppression: execute×3 → sleep is wrong when
    inbox has raw items. Suppress and let the chain continue
  - Self-chaining: after a cycle completes with execute and inbox
    still has work, immediately re-cycle (up to 5 chains, 10s
    cooldown between, then relax)
  - Base interval 10m → 3m (now that cycles are fast)
  - Coherence check truncation in observation (600KB drift reports
    are no longer dumped wholesale into the assess prompt)

agent_tools.go:
  - Truncate coherence_check tool output to 4KB (same 600KB issue)

agent_linkfeed.go:
  - Register list_inbox tool so the agent can enumerate raw files
    (was trying to enrich without knowing the filenames)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds TestDecompTier0Unmarshal and companion coverage for the four-tier
decomposition pipeline's tier-0 schema parsing and retry-on-timeout path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Addresses the justification-loop pathology observed in the agent's
morning cycle (09:06–10:32 local): 14 "let me plan the backlog"
proposals in ~90 minutes with no execution, each a paraphrased
refinement of the prior. The agent had no sanctioned way to say
"nothing warrants action this cycle" other than fabricating a plan.

Inspired by Poke's wait primitive. Identified as Gap 1 in the
corpus-comparison analysis and pre-anticipated in the node-kernel-
metabolic-cycle architecture doc ("combined with Poke's wait
primitive, the problem dissolves structurally").

Implementation:
- agent_tools_wait.go: wait tool def + RegisterWaitTool + helper
- agent_tools.go: registers wait in RegisterCoreTools
- agent_harness.go: Execute() detects wait call after dispatch and
  returns cleanly with "waited: <reason>" rather than requesting
  another turn
- agent_harness_test.go: TestAgentHarness_Execute_WaitTerminates
  asserts the mock server is hit exactly once (structural proof
  that the loop terminates on wait)
- agent_serve.go: advertises wait in both Assess prompt (as an
  execute-phase escape hatch the model factors into its action
  choice) and Execute prompt (as a tool to prefer over fabrication)

All 11 harness tests pass; go vet clean; go build clean.

Deployment: ./cogos rebuilt at 23:21. Running ~/.cogos/bin/cogos-v3
(pid 46357) is still on the 10:49 AM binary — needs copy + restart
to pick up the change. Not executed in this session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…race emission

The metabolic cycle is now the single agent behind dashboard voice and text
chat, observed and responding via bus channels. Lands the full kernel-side
wiring for bus-mediated user messages:

  bus_dashboard_chat   (inbound)  user messages → cycle observation
  bus_dashboard_response (outbound) agent replies → dashboard
  bus_cycle_trace     (outbound)  state transitions + tool dispatches + assessments

Kernel wiring:
- agent_bus_inlet.go: subscribes to bus_dashboard_chat, bounded FIFO queue
  (pendingUserMessages, cap 100), drains into the harness observation. Handler
  registered on every workspace bus manager (not just server-default) so HTTP
  POSTs resolving to a named workspace actually dispatch.
- agent_tools_respond.go: `respond` tool publishes agent_response to the
  outbound bus. Returns byte-count on success.
- agent_serve.go: runCycle drains pending messages, enriches observation with
  high-salience user-message records, auto-fallback publishes execute_result
  to the response bus when pending messages were drained but the model
  narrated a reply in prose instead of invoking respond (Gemma E4B tool-call
  reliability insurance).
- agent_observation.go: typed ObservationRecord stream (kind + salience +
  provenance) replacing the string-concat observation. Kills the PRIORITY
  prose-prepend hack; salience=1.0 on user_message puts priority in the
  substrate, not in English.
- serve_daemon.go: inlet dedup-registered on every workspace busChat manager;
  first-install-wins semantics for the publish manager so responses land on
  the disk layout SSE consumers read from.
- trace/events.go + trace_emit.go: ADR-083 CycleEvent schema + non-blocking
  publish to bus_cycle_trace. state_transition, tool_dispatch, assessment.
- internal/engine/transition_hooks.go: ADR-072 state-transition handlers +
  YAML registry loader + shell-form hook dispatch. Phase-1 implementation;
  agent-form hooks pending Phase 3.
- agent_tools.go: RegisterRespondTool wired into RegisterCoreTools.
- process.go: SubmitExternal non-blocking inlet, cycle-ID plumbing.

Proven end-to-end: dashboard POST → bus → cycle observes → respond tool (or
auto-fallback) → SSE → Mod³ bridge → dashboard render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wave 0 of apps/cogos subpackage refactor (ADR-085).

Inlines the existing ResolveWorkspace + root discovery logic into
internal/workspace/ with dependency injection for global config and git
root. Main-package ResolveWorkspace becomes a var alias so existing
call sites (~100) compile unchanged while providers can now import the
workspace package directly.
Wave 1a of apps/cogos subpackage refactor (ADR-085).

The provider reaches the component registry/indexer types and helpers
that still live at the apps/cogos root through function-variable DI
seams (LoadRegistry, IndexComponentPaths, LoadBlob, EncodePath, NowISO),
mirroring the pattern already used by internal/workspace/. The main
package wires them in an init() hook (component_wiring.go).

joinReasons/truncHash were copied into the component package; the
joinReasons copy kept at the root (reconcile_helpers.go) is still used
by service_provider.go. This matches ADR-085 rule 7: no new exports
beyond what the move requires — the helpers are duplicated, not
exported across the package boundary.
Wave 1a of apps/cogos subpackage refactor (ADR-085).
Previously misfiled under agent_ prefix.

Decouples from main via a Harness interface (RegisterTool +
GenerateJSON) so linkfeed stays a leaf package. A linkfeedHarnessAdapter
in main bridges *AgentHarness to the interface. Four small helpers
(extractFMField, sanitizeFilename, runCogCommand, formatAgo) are
duplicated locally rather than exported from main — ADR rule 7
("no new exported types beyond what the move requires") prefers
duplication over growing main's public surface for four leaf helpers.

Exports capitalized where callers in main reach in: ScanInbox,
LinkFeedLastPull, LinkFeedCheckInterval, InboxLinksRelPath,
BuildInboxSummaryForAPI, ReadDiscordAuth.
Adds spec.local block to ServiceCRD and a process supervisor under
.cog/run/services/<name>.{pid,log}. ServiceProvider routes per-CRD:
Docker if runtime up + image declared; else local if spec.local defined.
Supervision uses the reconcile loop cadence — no per-service watcher
goroutines. CLI (list/start/stop/logs) is local-mode aware.

Recovered from backup/stash-parallel-wip after submodule → sibling
migration.
Addresses Codex review on cogos-dev#7. Two BLOCKERs and two CONCERNs.

BLOCKER: LocalRunner trusted stale PID files. After PID reuse (post-crash,
post-reboot) LocalStop would SIGTERM/SIGKILL the recorded PID and its
process group — killing arbitrary user processes. Now records the resolved
argv at start time and verifies the live process matches via ps before
signaling. Mismatch logs a warning and clears the stale PID file without
sending any signal.

CONCERN: localCmdHash omitted spec.local.venv. Swapping the venv left
reconcile reporting "in sync" while running the wrong interpreter. Venv is
now part of the hash inputs.

BLOCKER: Dashboard chat turns silently dropped. The inlet drains the queue
eagerly, so cycle errors / sleep-action / empty execute results consumed
the user message without any reply being published. New ensureUserTurnReply
runs before the err short-circuit and always emits something — error
message, execute result, or a synthesized "(no reply: <action>)" — when
pendingMsgs is non-empty and the respond tool didn't already publish.

CONCERN: Replies lacked session_id. publishDashboardResponse now accepts
the originating session_id; the respond tool reads it from ctx (new
WithSessionID/sessionIDFromContext). The cycle stamps ctx with the first
pending user message's session_id. Mod³-side filtering is a separate task.

Tests: TestVerifyOwnership (table — match, mismatch, dead, legacy, pid<=0),
TestLocalCmdHashIncludesVenv, TestEnsureUserTurnReply_* (cycle error, sleep,
execute result, no-turn no-publish, respond-already-invoked dedup).
@chazmaniandinkle
Copy link
Copy Markdown
Contributor Author

Codex review fixes — sub-PR open

Pushed chazmaniandinkle/cogos#3 (commit `29d48df`) against this PR's branch. When merged, the changes flow into this PR automatically.

What landed

# Severity Fix
1 BLOCKER LocalRunner verifies live process argv via `ps` before signaling — prevents PID-reuse from killing arbitrary user processes
2 CONCERN `localCmdHash` now includes `spec.local.venv`
3 BLOCKER New `ensureUserTurnReply` always emits a reply when a user turn was consumed (errors, sleep, timeouts no longer silently drop messages)
4 CONCERN Reply payload includes originating `session_id`; respond tool reads it from ctx (`WithSessionID`)

Tests

  • `TestVerifyOwnership` — table over match / mismatched-cmd / mismatched-args / dead / empty-ps / pid<=0 / legacy
  • `TestLocalCmdHashIncludesVenv` — distinct venvs → distinct hashes
  • `TestEnsureUserTurnReply_*` — cycle error / execute result / sleep+empty / no-turn / respond-dedup

`go test ./...` green.

Out of scope (deliberately deferred)

  • Mod³-side `session_id` filter — separate task is handling the broadcast suppression there. Cogos side just makes the field available.

Issue 1 — Session routing collapses on multi-message cycles:
  Previously the cycle stamped every reply with firstUserMessageSessionID,
  so when drainPendingUserMessages returned messages from multiple tabs,
  only the first session saw the response. Added uniqueUserMessageSessionIDs
  + WithSessionIDs context helper; both the respond tool and ensureUserTurnReply
  now fan out one reply per unique session_id in the queue. respondInvokeCount
  still bumps once per tool invocation so auto-fallback dedup stays correct.

Issue 2 — Legacy PID files cleared on upgrade:
  LocalStatusWithCRD now re-derives the expected argv from the current CRD
  when the PID file has no recorded Cmd and adopts the live process if it
  matches — preventing duplicate-spawn on in-place kernel upgrades. Strict
  refusal preserved when the CRD is missing or argv doesn't match.
  ListLocalProcessesWithCRDs + FetchLive CRD-map plumbing drives adoption
  during reconcile.
fix: PR #7 review — PID ownership + reply guarantee + venv hash + session_id
@chazmaniandinkle chazmaniandinkle merged commit c80e22f into cogos-dev:main Apr 20, 2026
3 of 4 checks passed
chazmaniandinkle added a commit that referenced this pull request Apr 21, 2026
… + HTTP

Closes Agent F's gap #7 — reframed per Agent Q's Survey Q design
(2026-04-21) as trace observability rather than diagnostic logs.
The content in `.cog/run/*.jsonl` is client-originated semantic
metabolite material (turn_metrics, attention signals, TRM
prediction-vs-reality, router traces), not kernel slog text.
Agent Q's naming reflects that: traces, not logs.

Additive only:
- New `QueryTraces(root, TraceQuery) (*TraceQueryResult, error)` in
  internal/engine/traces_query.go. Per-source scan + normalize,
  filter, merge with order-by-timestamp desc/asc, limit + truncated
  flag. Mirrors Agent L's QueryLedger algorithmic shape.
- New MCP tool `cog_search_traces` registered in registerTools().
  Handler delegates to QueryTraces; shares semantics with the HTTP
  surface via buildTraceQueryFromInput.
- New HTTP route `GET /v1/traces` + handleTraces. `/v1/proprioceptive`
  stays byte-for-byte identical — web/dashboard.html:1265 and
  web/canvas.html:1706 still consume `{entries, light_cone}` unchanged.

Unified result shape: `{source, timestamp, session_id?, level?, line}`
hides per-source schema drift. The normalization table owns translation:

  turn_metrics    → timestamp  + session_id
  attention       → occurred_at (no session)
  proprioceptive  → timestamp  + event-as-level
  internal_requests → timestamp (float unix seconds — drifts from spec's
                    'observed RFC3339'; parseTimestamp accepts both forms)

Filters: source, level, session_id, case-insensitive substring (byte
check before JSON parse), since/until (RFC3339 or Go duration), limit
(default 100, max 1000), order (desc|asc).

Diagnostics: sources_checked[] reports per-file {scanned, matched,
file_exists}. Missing file is not an error; callers can distinguish
"file absent" from "empty match".

Out of scope (follow-ups):
- Kernel slog stderr (cli.go:117). Couples to Windows service-manager
  stderr capture (Agent K territory) — gets its own PR.
- Live tail of trace files. Belongs on Agent N's ledger-backed event bus,
  not in a file-watcher bolted to this surface.
- Rotation semantics for turn_metrics.jsonl when it crosses 100 MB.
  Writer-side concern; spec is stable across that boundary.

Tests (18 total):
- 14 unit tests on QueryTraces: empty workspace, single-source, merged
  multi-source, session_id filter (incl. graceful 0 from no-session source),
  since duration + RFC3339, until bound, substring (case-insensitive),
  level on proprioceptive, truncation, malformed-line skipped, order=asc,
  bad source rejected, normalization correctness across
  turn_metrics/attention/internal_requests (including float unix drift),
  substring-too-long rejected.
- HTTP roundtrip + 400-on-unknown-source.
- MCP roundtrip with session_id filter.
- Regression guard: /v1/proprioceptive top-level shape stays exactly
  {entries, light_cone} — guards dashboard.html:1265 and canvas.html:1706
  from silent breakage.
- ParseTraceDurationOrTime: RFC3339, duration, rejection of garbage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant