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_changerecorded, 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.tomlwith 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_DBremains the one exception and always wins for database resolution.selvedge doctorprints 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 andSELVEDGE_DESTRUCTIVE=1, plus an audit line in.selvedge/prune.log. Neither gate alone is enough:--yesin 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_bytestruncate with a…[truncated NNKB]marker, a warning in the existing warnings list, and a count inselvedge 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 viaredaction_patterns. Warn, never reject. Adoctorrow 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 fromdocs/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.9through three point releases: stale, and post-mcp-2.0 non-installable. .selvedge/config.tomlsettings are read through one shared layer rather than per-command, and bothlog_changewrite 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=1now 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_changeno longer discardsrevisit_after,constraintandstale_whenon the rename and supersede branches, ordiffon supersede. They were accepted, validated, then dropped, with{"status": "logged", "warnings": []}returned — so a decision recorded at a rename never surfaced inselvedge 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, andCOPY . /appput it where the entrypoint's walk-up resolution selected it — the container served someone else's change history as its default state..dockerignoreis now a fail-closed allowlist,SELVEDGE_DBis pinned in the image, and a hygiene test asserts the resulting file set. (#22) - The CLI and the MCP server return identical structures.
metadatawas a dict onblameand a JSON string on five other read surfaces, so the documentedevent["metadata"]["renamed_from"]idiom raisedTypeErroreverywhere butblame. 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, pinningSELVEDGE_DBto 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