Skip to content

Selvedge v0.3.10 — Config + delivery

Latest

Choose a tag to compare

@github-actions github-actions released this 06 Aug 03:07

A deliberate two-theme release: the memory comes to the agent (SessionStart and PreCompact delivery hooks, a committable markdown export), and the store gets its dials (first-class .selvedge/config.toml, retention, size bounds, secret-shape warnings). The version number is itself a fix: the official MCP Registry's latest-resolution is semver-shaped and could not rank four-segment PEP 440 releases above 0.3.9, so ?version=latest served 0.3.9 — the one release that dies at import. Three-segment 0.3.10 is the durable correction, and the registry now serves it as latest.

Added

  • SessionStart delivery hook. Injects a compact, relevance-gated digest as a session begins: decisions due for revisit, entities whose standing verdict is reverted, recent changesets. Quiet when there is nothing to say, size-capped via digest_max_bytes, read-only, fail-open, templated.
  • PreCompact delivery hook. Fires immediately before context compaction destroys the session's reasoning and names watched entities edited this session with no log_change recorded, subtracting what is already stored. Advisory only — the hook API allows a veto, and Selvedge deliberately uses neither mechanism: blocking compaction doesn't inconvenience a tool call, it wedges the session. Asserted in tests.
  • selvedge export --format markdown. A deterministic, stably-anchored digest of the store, grouped by entity with reverted decisions first, designed to be committed next to .selvedge/ so captured intent is reviewable in a diff. Regenerating with no new events produces a zero-line diff. Zero-LLM, no new MCP tool.
  • First-class .selvedge/config.toml with the full key set — retention_days_events (default: never), retention_days_tool_calls, backup_keep_last, diff_bytes, reasoning_bytes, db_size_warn_mb, stale_days, digest_max_bytes, redaction_patterns — and a canonical precedence chain: CLI flag > env var > project config > global ~/.selvedge/config.toml > default. SELVEDGE_DB remains the one exception and always wins for database resolution. selvedge doctor prints the effective value and the precedence step for every setting.
  • selvedge prune --include-events. The first path that can delete captured reasoning, so it requires both an interactive confirmation and SELVEDGE_DESTRUCTIVE=1, plus an audit line in .selvedge/prune.log. Neither gate alone is enough: --yes in a cron entry defeats a prompt, and a shell profile defeats an env var. Events retention defaults to infinity.
  • Event-size bounds at log time. diff_bytes / reasoning_bytes truncate with a …[truncated NNKB] marker, a warning in the existing warnings list, and a count in selvedge stats. Loud on purpose — silently clipping the reasoning behind a decision is the loss this tool exists to prevent.
  • Secret-shape warnings at log_change. A conservative built-in set (vendor-prefixed keys, PEM headers, bearer tokens, SECRET=<opaque> assignments, credentialed connection strings), extendable via redaction_patterns. Warn, never reject. A doctor row scans what is already stored, since the write-time check cannot see it.

Changed

  • One canonical description, everywhere, enforced. The repo carried five different one-liners and none was canonical — including pyproject.toml's, which becomes the PyPI summary that downstream directory cards echo verbatim. All short-description surfaces now carry an identical line derived from docs/positioning.md, with a description-sync test beside the version-sync test.
  • README positioning. Rejected paths lead; determinism is stated as shared ground rather than a separator, with append-only testimony as the actual wedge. The comparison table gains a "Rejected paths" column, corrects Git AI's mechanism (agent-invoked checkpoint → Git notes, not git hooks), and links every entry.
  • The README CI-snippet action pin joins the version-bump surfaces, with a hygiene test grepping it against the current version. It sat at v0.3.9 through three point releases: stale, and post-mcp-2.0 non-installable.
  • .selvedge/config.toml settings are read through one shared layer rather than per-command, and both log_change write paths share one limits check.

Fixed

  • The PreToolUse hook's allow path is ~40% faster — p50 33.6 ms → 20.1 ms per gated tool call against a 14 ms interpreter floor, measured n=60 interleaved. The hook's own logic was always 0.58 ms; the rest was import cost for work the common path never does. SELVEDGE_HOOK_DISABLE=1 now actually short-circuits — it was checked after every import had run, so it measured the same as not setting it. Verified byte-identical across 13 payloads. (#20)
  • log_change no longer discards revisit_after, constraint and stale_when on the rename and supersede branches, or diff on supersede. They were accepted, validated, then dropped, with {"status": "logged", "warnings": []} returned — so a decision recorded at a rename never surfaced in selvedge stale. Fixed in storage so both surfaces inherit it. (#21)
  • The Docker image no longer ships the maintainer's database. .selvedge/ is git-tracked by design, and COPY . /app put it where the entrypoint's walk-up resolution selected it — the container served someone else's change history as its default state. .dockerignore is now a fail-closed allowlist, SELVEDGE_DB is pinned in the image, and a hygiene test asserts the resulting file set. (#22)
  • The CLI and the MCP server return identical structures. metadata was a dict on blame and a JSON string on five other read surfaces, so the documented event["metadata"]["renamed_from"] idiom raised TypeError everywhere but blame. A shared presenter layer now backs both. (#26)
  • ~12 deliberate guards were executed but unasserted — each deletable with the suite green. Mutation score on the sampled set: 64% → 100%. Adds the repo's first tests/conftest.py, pinning SELVEDGE_DB to a temp path so no test can reach the real store. (#23)

Notes

  • Test suite 826 → 984. The soft budget for this phase is ≤45 new tests and this release exceeds it, which the budget discipline requires calling out: the overrun is the cost of the two-theme combine plus the five review issues folded in, of which #23 is a pure test-coverage issue. Coverage 88.3% → 89.0%.
  • Schema tax unchanged at 3705/3800 tokens — no MCP tool signatures were touched, and no tools were added.
  • The config and delivery halves landed as separate commit series so either can be reverted without dragging the other.

Install: pip install selvedge==0.3.10
PyPI: https://pypi.org/project/selvedge/
Docs: https://github.com/masondelan/selvedge/blob/main/docs/getting-started.md