# 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.
