Skip to content

Argus roadmap — additive work on the closed foundation

The core invariants are LOCKED (2026-06-11): immutable raw_items as the one source of truth; every view regenerable from it; every decision audited and queryable (boundary_reason/score, segment_checks, project_method/confidence, gate_rejections, dedup_log, model/model_real). Nothing below reshapes them.

Milestones (the spine — feature lists per overnight run live in harness/)

  • M0-M1 — foundation (SHIPPED 2026-06-10, overnight run 1, 20 features; list archived at harness/feature_list.m1.json): capture/recall/distill pipeline, episodes, dedup, registry, hub skeleton, hooks, compose stack.
  • M2 — hardening (SHIPPED 2026-06-10..12 as audit cycles 1-2 + fix PRs #6-#20): dedup verdict enforcement, capture idempotency, embed batching, Go 1.25.11, TRUNCATE guard, dead-letter, routine-prompt skip, 9 findings closed; suite 20 -> 97 tests.
  • M3 — run-reporting + watchdog (SHIPPED 2026-06-12, PR #21): enforced two-phase task reporting via run.py, schedule-aware no-show watchdog from config.ini, noshow ledger rows; closes spec F3/F4 + observability-ops-2.
  • M4 — refinements + PWA UI + Telegram (SHIPPED 2026-06-12, PR #22, overnight run 2, 33 features; list at harness/feature_list.json): 5 memory-core fixes, 10 audit-run-2 fix features (32 findings incl. 1 high), review queue w/ hold-until-approved, episode continuity, global project, mid-session recall, llm handler + taskkit seed, scheduled Obsidian export, installable PWA (runs/episodes/notes/review), golangci in CI, Telegram relay (sender + alerts + /alert webhook + CI red alerts); suite 97 -> 223. Post-merge same day: DNS identity (argus/argus-mem.aaronbrazier.com), hooks v0.3.0 everywhere, mid-session recall live, YACT+track memory curation (2,473 notes retired, global decommission facts minted).
  • M4.5 — vault enrichment (SHIPPED 2026-06-12/13, PRs #24-#28): the Obsidian vault becomes navigable instead of a flat note dump. Per-note kNN "Related" links (calibrated cosine ceiling 0.35) + mutual-kNN clusters (tighter ceiling 0.22 — the giant-component guard, caught on the first real run when 0.35 chained 2,400/2,564 notes into one blob); idempotent export (compare-before-write, skips byte-identical files — kills the full-vault write burst that crashed livesync-bridge and reverted the vault on 2026-06-12); note titles at distill time + a one-time backfill (2,873 live notes titled, real graph labels); named cluster hubs (LLM name + one-para digest per cluster of >=3, cached by membership hash, pruned on dissolve, scheduled 04:15 pre-export — 126 hubs). Vault published to every device via livesync-bridge -> CouchDB (LXC 157). spec docs/plans/2026-06-12-*.md; suite 223 -> 238.
  • M5 — recall precision + PWA surfaces + Telegram digest + feed (SHIPPED 2026-06-13, PR #29, overnight run 3, 9 features; list at harness/feature_list.json): short-prompt substance gate, project-scoped recall (cwd -> deriveProject -> same-project score boost, a multiplier not a filter), injection systemMessage breadcrumb, injections PWA page (/ui/injections over the audit log), registry list + edit PWA pages (/ui/projects[/{slug}/edit] writing through to PUT /projects), Telegram digest (pure builder + scheduler armed @every 24h, silent when no news), feed view (feed_state + /feed + /ui/feed over the existing resurface engine). suite 238 -> 277. Loop lesson (folded into harness/PROMPT.md): a one-shot headless agent must verify SYNCHRONOUSLY and never end its turn waiting on a background job — one feature was finished-but-uncommitted and recovered by the operator, then the prompt was hardened and the remaining 7 features each landed in a single clean iteration.
  • M5.5 — distill resilience + parked-units operability (SHIPPED 2026-06-13/14, PRs #32-#33): transient-aware parking — a distill failure is classified infra (timeout / conn-refused / 5xx / 429 / EOF / no-such-host) vs genuine content, and infra failures no longer advance the dead-letter streak. The M2 systemic guard already held streaks when EVERY unit in a run failed (>=2 attempted); this closes the two gaps it can't see — a single-unit tick and partial degradation (small episodes succeed while a slow one times out) — which is how an LLM-infra restart parked 5 healthy episodes on 2026-06-13. Genuine poison still parks. Parked units are now VISIBLE + ACTIONABLE: a "Parked distill units" section in the Telegram digest (GET /process/parked + scheduler SetParkedSource), and a /ui/parked PWA page listing each dead-lettered unit (kind/id, fail count, one-line error) with a Retry button (POST /process/parked/{kind}/{id}/retry -> Store.UnparkUnit -> re-distills) — retiring the by-hand DELETE FROM distill_failures. ProcessReport gains TransientFailures. New tests: classifier (16 cases), transient-never-parks (vs permanent-does), ParkedUnits/UnparkUnit store, digest + /ui/parked UI. Also CLOSED this window: audit cycle 3 (PR #31 — 6 low findings fixed + 1 accepted, against merged main with the run-3 harness tweaks).
  • M6 — evidence-derived task system (SHIPPED 2026-06-17, PRs #39-#47, 9 features; list at harness/feature_list.json): the work-tracking capability YACT + track lost on decommission, rebuilt trust-first. An epic/feature/task hierarchy (task_items) with TWO-TRACK status — lifecycle is agent-writable (backlog/ready/in_progress/blocked/cancelled, deliberately NO "done"), verified is checker-only. Status is DERIVED, never asserted: a deterministic VerifySweep re-derives verified from tamper-resistant evidence (task_evidence kinds ci_run/pr/deploy_log/artifact/approval) via a Forgejo CISource (PR merged && references-item && green CI) — AND across all bound evidence, and re-derivable so a reverted PR un-verifies. The tamper boundary is the API itself: strict JSON decode rejects any body carrying a verified field (400), and there is NO verified-write route. A recursive task_rollup view rolls done-ness bottom-up (task done = verified; parent done iff >=1 non-optional child AND all non-optional children done). Append-only task_events audit every transition. Surfaces: REST /tasks (+ /{id}/evidence, /{id}/lifecycle, /verify)
  • public GET /tasks.json, a read-only /ui/tasks tree with a human Approve action (HX-fenced), and a verify-evidence scheduled task (@every 10m). Migrations 0027/0028. Design docs/specs/2026-06-15-task-system-design.md; research docs/research/2026-06-14-task-system-research.md; plan docs/plans/2026-06-17-task-system-m6-impl.md. suite 277 -> 315. DEPLOYED to the live argus-build stack 2026-06-17 (migrations applied, verify-evidence running) — but the tree is still EMPTY; not yet dogfooded with real items.

  • M7 — SHIPPED (2026-06-17..22): recall quality + the dogfooded task loop. M6's task system went from empty to the spine of all M7/M8 work — every epic below was filed as epic/feature/task and verified ONLY by the checker (merged PR + green CI), the loop M6 was built to enable.

  • Recall relevance floor — observable/calibratable/tunable (ARGUS-18): slog-on-drop, note_scores on the injections row (migration), and an ARGUS_RECALL_FLOOR env knob — the verifiability half the M5 floor lacked.
  • Floor calibration UX (ARGUS-20): injection score-distribution histogram on /ui/injections, counting ONLY prompt-mode recalls.
  • Recall hygiene (ARGUS-23): skip prompt-recall on system-injected turns (<task-notification> / <system-reminder>).
  • Memory linter (ARGUS-25): deterministic lint checks + lint_findings + /ui/lint, a contradiction-suspect LLM check over the sub-dedup-floor band, the noisy attribution_mismatch check dropped, and an HNSW vector index on distilled_notes.embedding (fixed the O(N^2) lint kNN timeout). Migration 0030/0031.
  • Recall grounding (ARGUS-28): precision grading (sample injections, LLM- judge relevance), a floor=0 miss-probe for relevant-but-dropped notes, and dedicated response-format judge clients (each pinned to its own contract). Migrations 0032/0033.
  • Recall quality tuning (ARGUS-36): injected-note cap 5->8 + ARGUS_RECALL_MAX_NOTES, and MMR diversity re-rank in HybridRecall — measured precision 0.40 -> 0.55 post-deploy.
  • Fast dockertest suite (ARGUS-32): one shared Postgres container + per-test template-DB clone — suite ~10min -> ~30s.
  • Docs: docs/MEMORY-PIPELINE.md, docs/CLAUDE-CODE-HOOKS.md, docs/research/2026-memory-best-practices.md.

  • M8 — SHIPPED (2026-06-22): reliability + first-class observability.

  • Memory job reliability (ARGUS-39): lint + recall-grade crons were both @every and fired together every 2h, serializing on llama-server's single --parallel 1 GPU slot until one blew its 300s wrapper timeout (the source of overnight false [fail] alerts) — staggered to fixed-minute cron (lint :10, grade :40) + 600s wrapper timeout, kept local-only; plus jobs-registry descriptions for the /ui/jobs page (TestAllJobsHaveDescriptions guards them).
  • Prometheus + Grafana observability (ARGUS-42): stateless /metrics exporters on hub (job runs / last-success-age / no-shows / task rollup) and memory (recall precision, lint backlog, parked, dedup queue, gate rejections, pipeline volume), over a shared dependency-free text renderer; scraped by pve-prometheus as argus-hub/argus-memory (fallback_scrape_protocol for Prom 3.x); a Grafana "Argus" dashboard + four alert rules (no-show, job-failing, precision-low, parked). Plus alert CONSOLIDATION — the hub's own [noshow]/[fail]/[resolved] Telegram sends retired behind ARGUS_SELF_ALERTS=false (Grafana now alerts via the hub's /alert relay -> Telegram); no-show kept dual-pathed (Grafana + Gatus) by choice. Detection (runs ledger) is untouched.
  • Cancelled-task correctness (ARGUS-51): a cancelled child no longer blocks its parent epic/feature from rolling up (migration 0034 redefines task_item_done to exclude cancelled, like optional), and the /ui/tasks status label reads "closed" not "open".

  • M9 — IN PROGRESS (2026-06-22..23): core completion — universal ingest, resilience, observability. First milestone driven both through the dogfooded task loop AND by live production feedback (real ingest + a real GPU-contention incident). Shipped so far:

  • Embed-tier resilience — detection (ARGUS-57): a deep /healthz/embed probe (real round-trip + 768-dim assert), argus_embed_pending_total, a Gatus check + Grafana alert. Finding: a different embed model can never be a fallback (wrong dims / different vector space silently corrupts recall) — so make an outage VISIBLE, don't swap models; a self-hosted same-space redundant nomic is the only valid fallback (deferred, ARGUS-67).
  • Universal ingest (ARGUS-58): the hub feeds memory beyond Claude Code. Telegram multi-channel (ARGUS-60/68/69) — a channel registry as the inbound allow-list + per-channel project/label tagging, visible discovery (new chats land pending), long-poll not webhook; Karakeep bookmarks (ARGUS-61) — cursor-paged sync, dedup-idempotent, 543 captured. (Codex ARGUS-59 parked — backup account, unused.) Downstream distill/embed/dedup/ recall were already source-agnostic, so each connector is a thin feeder.
  • Ingest-goroutine observability (ARGUS-71): the Telegram poller + Karakeep sync run as hub goroutines OUTSIDE the no-show watchdog — gave them an ingest_heartbeat + argus_ingest_last_success_timestamp_seconds + Grafana staleness alerts so a silently-dead poller surfaces.
  • Distill hardening (ARGUS-72/77/78/80): a pg_advisory_lock makes /process self-exclusive (no concurrent-run distill_state PK collisions); the batch + the WithoutCancel detach ceiling are bounded (8m) so a wedged run self-releases the lock instead of silently starving the cron; and a distill queue-depth metric + panel (argus_distill_pending_total) makes the backlog visible — closing the exact blind spot a Tdarr GPU-starvation incident exposed (distill froze at ~380 while every run reported "ok").
  • rtk fleet metrics (ARGUS-65/66): per-host rtk gain rolled up via a fail-open rtk-hook -> hub exporter -> Grafana.
  • Recall ranking eval (ARGUS-74): /recall/eval-k (precision@K + recall@K over a labeled window) reframed recall as a RANKING problem — precision ~0.46 AND recall ~0.38 (~5 relevant notes dropped per injection), so raising the floor would HURT recall; the miss-probe caught it.
  • Jobs-UI failed-run status now renders red (ARGUS-79).
  • Recall precision push (SHIPPED 2026-06-24/25): tuned K=12 (ARGUS-75) + importance signal (ARGUS-89) + boost-before-floor gate (ARGUS-90) + windowed hit/noise KPIs (ARGUS-94); per-stage LLM routing (ARGUS-98: grade=Sonnet, distill/rest=gpt-oss-120b after Groq deprecated llama-3.3-70b, ARGUS-99); feedback-driven self-cleaning hygiene sweep (ARGUS-100) + distill salience prompt to stop minting operational chatter (ARGUS-101). Manual 49-note sweep validated the loop (counterfactual precision 0.469->0.596). Adaptive-K (ARGUS-91) + cross-encoder rerank (ARGUS-80) measured as washes -> cancelled. Still open: structural co-retrieval signals (ARGUS-92), project-derivation audit (ARGUS-93), HITL review queue (ARGUS-95/96/97).
  • Open in M9: contention-free grade data source (ARGUS-82), resurfacing routing (ARGUS-62/63/64), self-hosted redundant embedder (ARGUS-67), Codex ingest (ARGUS-59), distill not-falling alert (ARGUS-81), task-tracker UX (ARGUS-53/54/70).

  • Next candidates (beyond M9):

  • Tdarr monitor as the first agent-type Argus task (retire the in-session HEVC tick / host cron; needs claude CLI + ssh/keys in the task-runner image). NOTE 2026-06-23: the host cron is currently PAUSED + Tdarr itself paused (see In flight) — that pause is the moment to reconsider this.
  • Gatus postgres reachability check (the embed-tier DEEP check shipped in M9 / ARGUS-57).
  • opencode local-model coding tier — still a proposal (see Dev tooling).
  • GPU contention is the standing constraint behind the M9 distill incident: a single 5070 Ti slot shared by llama-server + Tdarr; a bulk-ingest backlog starves the LLM judges. The deferred fix is a P2000 to move Tdarr off the GPU.
  • Headroom eval (ARGUS-83): multi-day Tier-2 running; decide adopt/drop + teardown. Tier-1 verdict: complementary to rtk, not a replacement.
  • Backlog: auto_update across the remaining GitOps stacks (HOMELAB-APPS-2).

In flight

  • M9 in progress (above) — universal ingest + resilience + observability; every task filed + verified through the dogfooded loop.
  • Distill backlog / Tdarr contention — RESOLVED (2026-06-25): the backlog drained, and distill moved off the local 14B entirely (now gpt-oss-120b on Groq via the fast tier — see the recall precision push above), so Argus no longer contends for the GPU. Tdarr monitor host cron re-enabled on .96; the 5070 Ti is free for Tdarr + the planned Telegram chat model.
  • Headroom multi-day eval running on .96 (proxy + ANTHROPIC_BASE_URL routing via ~/.zshrc; see memory headroom-eval-setup, decision tracked as ARGUS-83).
  • Deploy lessons (current): an argus-build deploy that changes tasks/config.ini must docker compose restart ofelia hub; ALSO restart ofelia after killing a distill run mid-flight, or its no-overlap thinks distill is still running and silently stops scheduling it. Ofelia mounts the whole tasks/ DIR (so a distill.py edit is live once .96 is on merged main); the HUB bind-mounts config.ini as a SINGLE FILE whose inode detaches on edit. Code/migration changes bake into the image (rebuild). Hook releases via a v* tag -> CI cross-compile -> ~/.argus/bin/{recall,capture,rtk}-hook; current v0.7.0.
  • Off the Argus core but live: the metamcp idle-session leak is mitigated (no upstream TTL exists) by a nightly Komodo restart procedure + image pin 2.4.22, and the metamcp stack now has auto_update=true so it can't drift again.

Deferred (explicit)

  • pve-argus cutover: docs/plans/2026-06-11-pve-argus-cutover.md — deferred 2026-06-12 until more feature-complete (tasks module + minimal UI first); secrets rebuild (10 env keys incl. ARGUS_ROUTINE_PREFIXES) stays step one. NOTE 2026-06-14: pve-argus is now PROVISIONED (registered as a Komodo server, region dumbledore) but the live stack still runs on the argus-build dev box (LXC 136); the cutover itself stays deferred. Plan doc status reconciled to Deferred. (The metamcp/komodo-mcp stateless fix also restored reliable Komodo deploys, removing one blocker for an eventual Komodo-managed cutover.) UPDATE 2026-06-17: the stated feature-completeness gate (tasks module + UI) is now MET by M6 — the cutover is no longer blocked on features, only on the operator's call to do the secrets rebuild + move. Still deferred by choice.

Shipped — audit cycle 1 (2026-06-10/11)

  • Dedup verdict enforcement: separate judge client, contract errors, backfill endpoint (PR #6); corpus backfill consolidated 5,258 -> ~4,990 live notes
  • Capture idempotency both layers: raw dedup_key + duplicate flag (server), per-line offset checkpoints + 15m deadline (hook) (PR #8)
  • EmbedNotes bounded 64-note batches, per-batch commits (PR #9 — fixed the embed death-spiral under fire)
  • Go 1.25.11 + patch-pinned golang images everywhere (PR #11; golang images ship GOTOOLCHAIN=local, floating tags cannot satisfy the directive)
  • Routine-prompt episodes skip distillation (PR #13; deployed, verified live); 139 historical tick notes tombstoned, 50 episodes backfilled routine=true
  • v0.2.0 released: hook binaries on the patched toolchain; linux box upgraded
  • Audit waivers + docs (PRs #5, #7, #10, #12); full regen complete (1,643 episodes, 22.9k turns, 0 unembedded), Ofelia autonomous

Audit follow-ups (run 2026-06-10/11) — CLOSED OUT 2026-06-12 (PRs #16-#20)

All app-code findings fixed: correctness-2 (timestamp unify), correctness-4 (distill dead-letter), correctness-5/6 (project active + torn-line guard), sql-schema-1/2 (TRUNCATE guard + pending-scan index), test-quality-1/2/3 (limit budget, partial failure, handler matrix — suite 55 -> 86 tests). The MCP merge tool was found to silently no-op during this batch; merges went in as merge commits (CI verified green via the Forgejo DB).

Remaining: none — Ofelia job-failure visibility (observability-ops-2) closed with the M3 run-reporting + watchdog (run.py reports every task's exit; the no-show watchdog catches silent death; failures surface in /runs.json and Telegram).

Memory-core refinements (each an independent PR)

  • Prediction-error distillation filter ("predictable implies redundant", Nemori) — NEEDS A SPEC before any build: the prior-prediction mechanism and what counts as "residual" are undecided (docs/research notes it as a future upgrade only). Do not loop-build it unspeced.
  • Links/synthesis pass: cross-note network + summary-of-summaries. FIRST SLICE SHIPPED in M4.5 (named cluster hubs: LLM digest per cluster, member wikilinks, body hub-links). STILL OPEN: typed note-to-note links written to the distilled_notes.links column (supersedes / elaborates / contradicts) — the column exists and is intentionally unpopulated, reserved for this pass.

Views still to build

  • Feed view: feed_state side table + the resurface engine — SHIPPED in M5 (feed_state, GET /feed, /ui/feed). STILL OPEN: content-source ROUTING (podcasts/bookmarks/monitor-tick episodes) into the feed — needs its own spec (see M7).
  • Task / directed-synthesis engine (podcast cron + "review this link" unify; outputs are new raw_items)
  • Wiki/entity rollups (later)

Registry / UX

  • Seed live Mac-side projects (track/turftrack/fpp-stream/personal-site etc.) via PUT /projects with their CLAUDE.mds (NOT yact — decommissioned 2026-06-12)
  • "Looks like a new project?" suggestion from persistent cwd/none-method episodes

Infra / surface

  • Gatus: argus-memory/hub/noshows/route monitors LIVE 2026-06-12; still missing: postgres reachability and the embed-tier DEEP check — LiteLLM /health/liveliness exists but an embed-tier DEEP check (actual embedding round-trip with a latency condition) would have caught both nomic storms (hermes flood 2026-06-10, zombie-retry spiral 2026-06-11) before the pipeline felt them; the embed tier has no LiteLLM fallback, making it the system's single point of fragility
  • Registry editing page on the PWA — SHIPPED in M5 (/ui/projects + /ui/projects/{slug}/edit). PWA dashboard surfaces now: runs, episodes, notes, review, injections, projects(+edit), feed.
  • Rebuild LUP podcast summarizer as an Argus task (tighter output); Coder Radio cron retired with hermes
  • Reconcile old hermes USER.md into the registry/global profile

Dev tooling (homelab-wide, not Argus-specific)

opencode (SST) — local-model coding tier

  • What: open-source terminal AI coding agent by SST/terminal.shop; MIT, TypeScript/Bun, launched mid-2025 (~174K GitHub stars, June 2026). Client/ server split: HTTP+SSE backend, TUI / VS Code / web clients connect to it.
  • Decision: ADOPT for the local-model coding tier; keep Claude Code for hard/multi-repo work (it stays faster + more capable at scale).
  • Integration: point at the LiteLLM proxy (192.168.1.28:4000); config in ~/.config/opencode/opencode.json, provider @ai-sdk/openai-compatible. Default model = the planned qwen2.5-coder-32b coder slot (llama-swap); Claude smart as the explicit escape-hatch for complex work.
  • Verified gotcha: opencode sends reasoningSummary; non-Anthropic APIs reject it. Add additional_drop_params: ["reasoningSummary"] to each affected model in litellm-config.yaml (LiteLLM opencode tutorial).
  • MCP + LSP: both supported (per-agent MCP config; built-in LSP for 25+ langs).
  • Anthropic OAuth: Claude Pro/Max OAuth was removed from opencode (Anthropic blocked it 2026-01-09, code removed 2026-03-19); API-key-via-LiteLLM is the correct path here anyway, so no impact.
  • Honest caveat: a 32B coder (Qwen2.5-Coder-32B) handles boilerplate, tests, and isolated refactors well but is NOT Sonnet-level for complex multi-file agentic sessions, especially as context grows past ~30K tokens — a cost-free fast lane, not a Claude Code replacement. (Claims fact-checked 2026-06-13.)