Project

reanimator

0.0
The project is in a healthy, maintained state
Reanimator reads a repository, infers what "healthy" means for it, and proves that health without changing a single line of code. It classifies lifecycle intent, discovers a vitality contract from evidence, resolves an effective policy, and produces a read-only proof-of-life baseline - before any coding agent or write permission is ever involved.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 0
~> 0.8
>= 5.0
~> 1.3
~> 0.7
~> 0.12
~> 2.6
 Project Readme

Reanimator

Turn repository intent into a verified, low-friction stewardship contract.

An Autogenetica system for self-maintaining software.

The first successful Reanimator run should prove something useful without changing a single line of code.

Reanimator reads a repository, infers what healthy means for it, and proves that health read-only — before any coding agent or write permission is ever involved. It classifies lifecycle intent, discovers a vitality contract from evidence, resolves an effective policy against a non-bypassable safety floor, and produces a proof-of-life baseline.

This gem implements the read-only foundation of the Onboarding & Configuration Specification: delivery slices 1–2 (inventory & intent, discovery & baseline) plus the canonical schema and effective-policy resolver. It is the "trustworthy heartbeat" the spec says to ship before building an autonomous surgeon.

Installation

# Gemfile
gem "reanimator"
$ bundle install
# or, standalone:
$ gem install reanimator

Requires Ruby >= 3.3.

The onboarding path, on the command line

Every command is read-only by default and maps to an onboarding stage.

1. Classify before acting — inventory

$ reanimator inventory ~/src/*
Portfolio inventory — 12 repositories
Recommended for onboarding: agentic, claude_memory, eliza

Active original repositories → suggest maintain:
  - agentic — Active original repository with recognizable structure.
Forks → suggest upstream:
  - langchainrb — The repository is a fork; defaulting to upstream observation.

Reanimator never preselects every repository — it recommends a small, high-confidence set that can reach a useful baseline quickly.

2. Find proof before asking for policy — discover

$ reanimator discover
Suggested profile: ruby-gem (high) — A gemspec indicates a published gem.

ecosystem:
  [high] Packaged and released as a Ruby gem.  ← widget.gemspec (file_present)
commands:
  [high] Tests run with RSpec.  ← .rspec (command_inference): bundle exec rspec
support matrix:
  [medium] Declared Ruby support: >= 3.1.0.  ← widget.gemspec (version_pin): >= 3.1.0

Every discovered fact carries its provenance and confidence, so the tool can always answer "Why does Reanimator believe this?"

3. Review the effective contract — contract

$ reanimator contract
Effective contract — codenamev/widget
Intent:   maintain (provisional)
Profile:  ruby-gem (inferred) — A published Ruby gem: resolves, tests, lints, and builds.
Autonomy: observe=automatic  diagnose=automatic  repair=disabled  submit=disabled  merge=disabled

Security posture: balanced
  Approved package-registry network, hosted agents allowed, patch submission permitted, no major automatic repair.
  Required: none
  Advisory: dependency review, secret scanning, security policy

Criteria (* = required):
  * tests: Test suite passes. — bundle exec rspec
    security-dependency-review: Dependency risk is reviewed automatically. — security_evidence
  ...
Protected paths (elevated review to change):
  - .reanimator.yml
  - .github/workflows/**

Export a minimal, round-trippable .reanimator.yml:

$ reanimator contract --export
version: 1
intent: maintain
profile: ruby-gem

4. Measure a proof-of-life baseline — baseline

Read-only by default (native checks run; commands are planned):

$ reanimator baseline
State:   UNPROVEN — A claim exists but sufficient executable evidence is absent or stale.
Mode:    read-only (planned)

Opt in to bounded execution to actually prove life:

$ reanimator baseline --execute
State:   ALIVE — All due obligations have fresh passing evidence and no unapproved blocking risk.
Mode:    executed (bounded, read-only; network not restricted; the balanced posture's package-registry allowlist is advisory)
Verified Vitality Rate: 100% (3/3 required)
  ✓* tests: Test suite passes. — bundle exec rspec (1.69s)
  ✓* build: Gem builds. — gem build widget.gemspec (0.32s)

NO SOURCE CHANGES WERE MADE

Execution is held to the security posture's network rule. Under strict or local, which deny egress, every command runs inside an empty network namespace (unprivileged unshare on Linux). Where that cannot be enforced, Reanimator refuses to execute rather than running unprotected:

$ reanimator baseline --execute   # strict posture, on macOS
State:   UNPROVEN — A claim exists but sufficient executable evidence is absent or stale.
Mode:    not executed — the strict posture requires denied network egress and this runner cannot enforce it (no network sandbox on arm64-darwin23)
  ◦* suite: The suite passes. — Not run: the strict posture requires denied network egress and this runner cannot enforce it (no network sandbox on arm64-darwin23).

Other formats: --format check (the GitHub Check surface) and --format json.

5. Prove an agent is ready — agents and commission

The agent is a replaceable life-force adapter: a repair provider is commissioned against a disposable fixture and graded independently — without any repository write.

$ reanimator agents
Coding agents:
  ✓ local (available) [plan, patch, repository_edit, sandbox_exec]
  ◦ codex (unavailable) [...] — No credentials found (set OPENAI_ACCESS_TOKEN); connect the codex provider first.

$ reanimator commission
Commissioning — local
Readiness: READY
Objective: arithmetic (at risk → alive)
Cost: $0.00  Latency: 0.001s  Tools: repository_edit, patch  Network: none
A connected profile is proven ready. NO REPOSITORY WRITES WERE REQUIRED.

The agent proposes a patch as data; the harness applies it to a separate, clean copy and grades it with Reanimator's own evaluators. A patch that reaches for a protected or verifier path is refused — the anti-gaming rule, enforced.

6. Bounded repair — repair

A repair is found, applied in an isolated workspace, and re-verified independently before anything is proposed. The result is a draft PR artifact — never a write, never a submission.

$ reanimator repair            # on a healthy repo
Outcome:  NO-OP (healthy)

$ reanimator repair            # repair not yet enabled in the contract
Outcome:  ESCALATION
Reason:   Repair is not enabled for patch_dependency (max autonomy diagnose); enable repair to proceed.

$ reanimator repair            # repair enabled, a fixable failure present
Outcome:  DRAFT PR (proposed, not submitted)
Evidence: at_risk → alive
Proposed draft PR:
  branch: reanimator/repair-arithmetic-901cec8a
  title:  Fix: add(2, 2) equals 4.
NO SOURCE WAS WRITTEN TO THE REPOSITORY.

Every outcome is recorded as an immutable run-ledger entry (no_op, report, draft_pr, escalation, safe_stop). Submitting a proposed change to GitHub is a deliberate, separate step — not something repair does on its own.

Repository families — families and gates

Related projects are a compatibility graph, not isolated targets. Families are inferred from portfolio structure (a project and its -examples suite, -example-* deployment samples, -docs, or a <host>-<project> plugin):

$ reanimator families ~/src/*
Repository families — 2 families (4 repositories that must stay alive together)

agentic:
  - agentic (library)
  - agentic-examples (compatibility suite)
  edge: agentic -> agentic-examples (compatibility suite)
  gate: agentic requires its own proof command in agentic-examples

gates proves each edge by running the downstream member's own check — read-only, bounded, in a disposable copy, so an upstream change triggers downstream checks without opening anything downstream. A failure names the exact edge:

$ reanimator gates ~/src/* --execute
Family gates — slimer
  ✗ slimer -> slimer-example-docker (deployment example) — bin/smoke failed.
BROKEN EDGE: slimer -> slimer-example-docker (deployment example)

A repository can also declare its membership (family: {id:, role:}), and a full section 10 family policy with explicit members: and gates: is parsed as-is. Declaration always wins over inference.

Configuration drift — drift

A contract is only trustworthy while it still describes the repository. Drift detection compares what a repository resolved to when you recorded it against what its evidence resolves to now, and sorts every difference by one question: is less being proven than before?

$ reanimator drift                       # what am I required to prove right now?
Contract snapshot — codenamev/widget
Intent:   maintain   Profile: ruby-gem   Security: balanced posture
Proves:   4 criteria (2 required)
Evidence: runtimes 3.3, 3.4 · security gates dependabot

Nothing was written. Record this with `reanimator drift --record` so later drift has something to be measured against.

$ reanimator drift --record
Recorded contract snapshot for codenamev/widget → .reanimator-snapshot.json

$ reanimator drift                       # now there is something to compare against
Configuration drift — codenamev/widget

Policy changes (review required) — 2
  ! supported runtime removed: 3.2
    The runtime matrix narrowed: a version that used to be exercised no longer is.
  ! security control removed: dependabot
    A security gate was removed; it is no longer part of the proof.

Benign changes — 1
  · criterion tests command: bundle exec rspec → bin/rspec
    The same obligation is proven by a differently spelled command.

REVIEW REQUIRED: 2 policy changes — less is being proven than before.

Intended? Re-record with: reanimator drift --record

A renamed test command is benign — the obligation did not move, only its spelling. A narrowed matrix or a removed gate is a policy change nobody necessarily decided on, so it is never buried in the benign list. The command exits non-zero on policy drift, so CI can gate on it (and says how to accept drift you meant). The snapshot captures discovered evidence too — drift that never touches .reanimator.yml is still caught.

The snapshot lives in the repository, committed as .reanimator-snapshot.json beside .reanimator.yml. That is deliberate: the contract and the record of what it last required are the same kind of thing, and keeping both under review means a policy change arrives as a diff in a pull request rather than as state hidden inside a tool.

Across a portfolio

Pass several paths and drift becomes a triage list. Each repository is compared against its own snapshot, and one unreadable member never stops the rest:

$ reanimator drift ~/src/*
REANIMATOR DRIFT
Portfolio: portfolio
Mode: read-only

REVIEW REQUIRED — 1 repository
  codenamev/widget
    1 policy change
      ! security control removed: dependabot
        A security gate was removed; it is no longer part of the proof.

CHANGED, BENIGN — 1 repository
  codenamev/doodad
    · criterion tests command: bundle exec rake spec → bundle exec rspec

UNCHANGED — 3 repositories
  codenamev/gadget
  codenamev/gizmo
  codenamev/thing

NOT RECORDED — 1 repository
  codenamev/fresh
    nothing to compare against yet

REVIEW REQUIRED: 1 policy change across 1 repository — less is being proven than before.
Intended? Re-record with: reanimator drift --record

NO FILES WERE WRITTEN. NO SOURCE CHANGES WERE MADE.

For repositories you cannot commit a file into, --snapshots DIR keeps them side by side keyed by repository instead:

$ reanimator drift ~/src/* --snapshots ~/.reanimator/snapshots --record

Three outcomes are deliberately distinct. A repository with nothing recorded is named, not counted as fine, and does not fail the gate — a first run has nothing to compare against. A repository whose snapshot cannot be read does fail it: "we could not tell" is not "nothing changed", and corrupting a snapshot must never be a way to pass. Benign change never fails it at all.

Security posture — what it actually binds

The posture is a named decision with consequences, not a label. It decides how much security evidence a repository has to show; the project's shape does not.

# .reanimator.yml
version: 1
intent: maintain
profile: ruby-gem
security:
  posture: strict      # strict | balanced | local | custom
Posture Blocks without evidence Reported, not blocking
strict (default for private repositories) security policy, dependency review, secret scanning static analysis, ownership
balanced (default for public repositories) — dependency review, secret scanning, security policy
local secret scanning dependency review
custom — —

Controls are named by obligation, not by tool: "dependency risk is reviewed" is satisfied by Dependabot, bundler-audit, or actions/dependency-review-action alike, so a repository that already solved the problem its own way is credited rather than asked to adopt ours. A control with no evidence is reported as missing evidence with the remedy attached — never as a silent pass:

  •  security-secret-scanning: Committed secrets are scanned for.
     — No secret scanning found. Add gitleaks or trufflehog to CI.
  ✓  security-policy: A security policy says how to report a vulnerability.
     — A security policy is published (SECURITY.md).

Under strict and local those gaps are blocking, so a repository with no security evidence is Unproven, not Alive. When the gap is a deliberate, tracked decision, accept it with a waiver rather than weakening the posture.

custom implies nothing in either direction: "no hidden relaxation from a preset" means Reanimator adds no control you did not ask for, either.

Accepted risk — waivers

Sometimes a known failure is a deliberate, tracked decision, not a gap. A waiver records it as accepted risk — visible, attributable, and kept out of the vitality rate — without pretending it passed:

# .reanimator.yml
version: 1
intent: maintain
profile: ruby-gem
waivers:
  - target: boot                       # the acceptance-criterion id
    reason: "hardware-only smoke; tracked in OPS-7"
    expires: 2030-01-01                # optional; past this it fails closed

The baseline then reports boot under "Accepted risk (waived)" instead of as a blocker, and the repository can be Alive with the risk shown separately. An expired waiver stops applying and the obligation is blocking again.

Resolved autonomy — permissions

$ reanimator permissions
Permissions — codenamev/widget (autonomy: observe=automatic diagnose=automatic repair=disabled …)
  Patch dependency update: max=diagnose | observe=auto diagnose=auto repair=off submit=off merge=off …
  CI or verifier modification: max=diagnose | … [elevated review]

Task-class defaults (section 11) are intersected with the repository's enabled autonomy and the safety floor: with repair/submit opt-in and off, everything clamps to diagnose; merge is always off in v1.

The portfolio baseline (Appendix A)

$ reanimator portfolio ~/src/* --name codenamev
REANIMATOR BASELINE
Portfolio: codenamev
Mode: read-only

ALIVE
  agentic
UNPROVEN
  ups
    - Boot smoke against /up needs a running service; not established in a read-only baseline.

NO SOURCE CHANGES WERE MADE
No coding agent was required.

Library API

The CLI is a thin shell over a plain Ruby API — the same "80% path" in one call:

require "reanimator"

Reanimator.inventory(paths)          #=> Reanimator::Portfolio
Reanimator.discover(path)            #=> Reanimator::DiscoveryResult
Reanimator.contract(path)            #=> Reanimator::EffectiveContract
Reanimator.baseline(path, execute: true) #=> Reanimator::Baseline
Reanimator.snapshot(path)            #=> Reanimator::ContractSnapshot
Reanimator.drift(path, since: file)  #=> Reanimator::DriftReport
Reanimator.portfolio_drift(paths)    #=> Reanimator::PortfolioDrift

baseline = Reanimator.baseline(".")
baseline.state.to_sym                  #=> :unproven
baseline.verified_vitality_rate        #=> 0.0
baseline.to_h                          # fully serializable

Core model

The nouns that stay stable while implementations evolve (spec section 02):

Concept Class
Lifecycle intent LifecycleIntent (maintain, monitor, preserve, incubate, retire, upstream, ignore)
Vitality profile VitalityProfile / Profiles (ruby-gem, ruby-cli, rails-app, rails-legacy, …)
Discovered fact DiscoveredFact (+ Provenance, Confidence)
Effective contract EffectiveContract (+ EffectivePolicyResolver, SafetyFloor)
Baseline Baseline (+ BaselineRunner, Evaluators, ProofOfLife)
Repository state RepositoryState (alive, at risk, needs you, recovering, unproven, sleeping, diverged, retired)

Two rules are enforced, not just documented:

  • An owner-declared contract wins over inference.
  • Neither a declaration nor discovery may loosen the safety floor — merge stays off in v1, verifier and contract paths stay protected, and a task that modifies the rules of proof cannot use them to approve itself.

Specification coverage

Spec area Status
01–02 Model, goals, constraints ✅ Implemented
04–05 Portfolio intake, lifecycle intent ✅ Portfolio, Classifier, Cohort
06 Discovery by confirmation & drift ✅ Detectors::*, provenance, confidence, DriftDetector, PortfolioDrift
07 Configuration & precedence ✅ EffectivePolicyResolver, SafetyFloor
09 Acceptance criteria, verifier isolation & waivers ✅ AcceptanceCriterion, Evaluators, Waiver (accepted risk)
14–15 Baseline & proof-of-life ✅ BaselineRunner, ProofOfLife, Reports
08 Agent connections, commissioning, routing ✅ Agents::*, Commissioning, Routing::Router
11 Autonomy ladder & permission model ✅ AutonomyLevel, TaskClass, PermissionModel
13 / 17 Bounded repair & run ledger ✅ RepairRun, Obligation, ProposedChange, Run (no writes)
18 MVP slices 1–4 ✅ Inventory→baseline→commissioning→bounded repair
10 Repository families & compatibility graphs ✅ Relationship, Family, FamilyInference, Family::GateRunner
12 Security controls ✅ SecurityControl, SecurityPosture#obligations, Evaluators::SecurityEvidence, EgressControl (denied egress enforced on Linux, refused elsewhere; the balanced registry allowlist is advisory)
Draft-PR submission to GitHub ⏳ Deliberate opt-in; artifact is ready, the write is not wired
GitHub App, web UI ⏳ Explicitly later (spec sections 03, 18)

See .architecture/decisions for the reasoning behind key choices.

Development

$ bin/setup          # install dependencies
$ bundle exec rake   # run specs and StandardRB
$ bin/console        # an IRB session with the gem loaded

Releases are manual and credential-free — see RELEASING.md.

License

Released under the MIT License.