# evolving
`ev` is **git for decisions**. It records human-authored decisions and the grounds they
rest on as an immutable, content-addressed *tick chain*, binds either a test-check or a
human re-check to each ground, and resurfaces a decision when a bound check goes red. It
deals in **facts, not verdicts** — there are no scores, no ranks, no auto-judgements;
just an honest record of what was decided, why, who is on the hook, and whether the check
guarding each reason is still alive.
## Status
`0.0.1` — an early, honest cut on the way to the **`0.1.0` honest-resurface slice**. A
single self-contained Rust binary, no network, no daemon; the store lives in a local
`.evolving/` directory.
**Shipped today:** recording decisions and their grounds (`ev decide`), binding a test or
human re-check after the fact (`ev guard`), reading a decision (`ev show`), and auditing the
chain and its refusals (`ev verify`). **Still landing toward `0.1.0`:** evaluating a bound
check's liveness and resurfacing a decision when it goes red (`ev check`), plus the
`reopen` / `list` / `log` surface. So today `ev` *freezes the contract* — it records what
must stay true and how it would be checked — but does not yet run the checks for you.
## Install
```sh
cargo install evolving
```
This installs an `ev` binary on your `PATH` (the package is named `evolving`; the command
is `ev`).
Build from source:
```sh
git clone https://github.com/wan9yu/evolving
cd evolving
cargo build --release
# binary at target/release/ev
```
## Quickstart
Create the store:
```sh
ev init
```
Record a decision with a chosen ground (re-checked by a human at a named time) and a
road-not-taken:
```sh
ev decide "build our own retrieval; reject pgvector" \
--observe "evaluating retrieval backend for v2" \
--assume "team has bandwidth to maintain it long-term" \
--revisit "Q3" \
--reject "pgvector: would lock our schema" \
--blame "You"
```
Record a decision whose chosen ground is guarded by a **test** rather than a human. A test
binding must carry a counter-test (the test that should flip red if the claim breaks), at
least one platform / trigger / surface for liveness, and the commit it was last verified at:
```sh
ev decide "restore-safety counter DB-backed; reject Redis" \
--observe "multi-pod restore-safety counter" \
--assume "no Redis; multi-pod coordination via the existing DB" \
--assume-test "pytest tests/test_redis_absent.py" \
--counter-test "pytest tests/test_redis_absent.py::test_redis_injection_flips_red" \
--on-platform linux-ci \
--triggered-by pyproject.toml \
--surface pyproject-deps \
--verified-at-sha d308afac1b2c3d4e5f60718293a4b5c6d7e8f901 \
--reject "Redis: a new infra dependency" \
--blame "You"
```
Attach a test to an unbound ground of the current HEAD decision *after the fact* with
`ev guard`. Because the check is part of the hashed payload, this writes a **new child**
rather than mutating the existing tick:
```sh
ev guard "pytest tests/test_schema_frozen.py" <HEAD-id> "schema stays frozen" \
--counter-test "pytest tests/test_schema_frozen.py::test_schema_change_flips_red" \
--on-platform linux-ci \
--triggered-by schema.sql \
--surface schema-ddl
```
`<HEAD-id>` is the id printed by the most recent `ev decide`/`ev guard`. The third
positional argument names which ground to bind (by claim text or by index); it is required
only when more than one ground is still unbound.
Audit the chain and the refusals, then read a decision in full:
```sh
ev verify
ev show <id>
```
`ev verify` confirms every id equals the hash of its payload, that lineage is
forward-only, and that every tick validates against the closed schema and check shape.
## The model
- **Tick** — one decision in the chain. Its hashed payload is `{decision, observe,
grounds, parent_id}`; `id`, `status`, `held_since`, and `blame` are bookkeeping kept
outside the hash.
- **Ground** — a reason a decision rests on. A ground is either **chosen** (a reason for
the decision taken) or a **road-not-taken** (`rejected:<option>`, a reason an
alternative was declined).
- **Check** — what keeps a chosen ground honest over time. Either a **Test** (a test
selector plus its counter-test, the platforms/triggers/surfaces that keep it live, and
the `verified_at_sha` it last passed at) or a human **Person** re-check (a reference to
when/where a person re-affirms the ground).
- **Identity** — `id = first 12 hex of SHA-256` over the canonical-JSON of `{decision,
observe, grounds, parent_id}`.
- **Append-only** — the chain is never edited in place. A change is a **new child** whose
`parent_id` points at its predecessor.
## The refusals it enforces (the red lines)
- **Closed schema.** A tick with any field outside the fixed schema is rejected.
- **A human re-check stays human.** A ground re-checked by a person can never be
force-bound to a test.
- **A rejected road carries no check.** A road-not-taken cannot take a check in `0.1.0`
(reserved for a future rejection-rationale liveness feature).
- **The system is never the subject of self-evolve language.** Self-evolve / self-improve
verbs must take a human subject, not the system (best-effort lexical lint).
- **Every mutating op names a human.** A decision or a guard must carry a `--blame` (or a
resolvable `git config user.name`).
- **No auto-close.** Nothing closes, prunes, or stops a decision on its own; a human
authors every change.
## Honesty / trust boundary
`ev` completes one specific picture: *does a human-vetted decision stay live, and is the
check guarding it itself alive?* It does that by content-addressing the decision record and
by demanding that every test binding name a counter-test and the surfaces that keep it
live, so a check that has quietly died is visible.
It does **not** claim tamper-resistance of offline test outcomes — `ev` records that a
test was bound and the commit it was verified at, but it cannot prove an offline test
result was honest. That is a documented boundary, not a guarantee.
## License
Apache-2.0.