Skip to main content

Yasarda 0.4.0

Reviewed plans. Guarded file changes. Durable receipts.

A local tool with a Python SDK, CLI, and MCP stdio interface over one engine. No account, cloud service, model API, GitHub dependency, or project-specific code.

Install

pip install yasarda
yasarda --version

From the source project: python -m pip install .. Python 3.10+ is declared. The runtime dependency is jsonschema>=4.23,<5. MCP stdio is included in the base installation; yasarda[mcp] remains an empty compatibility extra. There is no separate transport dependency.

Guarded mutation currently requires supported POSIX local filesystems for both the target repository and journal state. Yasarda refuses known macOS cloud-synced locations (including iCloud Drive and ~/Library/CloudStorage) and known Linux remote/FUSE mounts by default. The host-only --allow-unsupported-filesystem override exists for deliberate testing or recovery, but does not make remote filesystem semantics safe. Linux and local macOS storage are exercised in the accompanying report; Windows mutation and power-loss behavior remain unsupported.

The workflow

Put reviewed manifests in <repository>/.yasarda/remediations/*.json, or use an external catalog with --catalog /absolute/catalog/path. Catalog creation is an explicit authoring step; Yasarda does not install a sample over an existing file.

# Inspection and planning do not change repository files.
yasarda --repo /path/to/repo doctor
yasarda --repo /path/to/repo list
yasarda --repo /path/to/repo plan RP-001 --out /outside/repo/RP-001.plan.json

# Read the diff and approve this specific digest independently.
yasarda --repo /path/to/repo --mode approved-mutation \
  execute /outside/repo/RP-001.plan.json \
  --approve REVIEWED_64_CHARACTER_PLAN_ID --yes

yasarda --repo /path/to/repo verify RP-001
yasarda --repo /path/to/repo receipt REVIEWED_64_CHARACTER_PLAN_ID

Global options go before the subcommand. --out never overwrites an existing artifact and must point outside the repository. An output directory must already exist. A plan contains exact input bytes, output bytes, modes, dependency manifests, Git identity, and a digest of its derived contents. Loading checks that the stored transformations, diff, and digest agree. Execution does not replan.

A plan digest is an integrity identifier, not a signature or proof of human approval. The caller/host must independently decide whether to authorize it.

For trusted, immediate local use, yasarda --repo /path apply RP-001 --yes plans and authorizes that plan in one invocation. It is not the deferred-review path. Without --yes, this command is inert. An explicit restrictive --mode wins even when --yes is present.

A changed input, changed branch/HEAD, unknown file state, unresolved prerequisite, unsupported link, or conflicting scope stops execution. The plan is bound to its absolute repository root. To use another checkout location, regenerate a plan from the manifests and review the new digest; do not edit saved plan JSON.

If a repository is stored in iCloud Drive, copy or clone it to local storage before executing a plan. Planning and structural verification remain available in the cloud-synced location because they do not publish repository changes.

Modes

Mode Repository behavior
read-only Inspect, plan, and structurally verify; no mutation.
proposal-only Same repository restrictions; proposals can be explicitly saved outside it.
new-files-only Create absent files or recognize exact existing results; no changes to existing files.
approved-mutation Execute explicitly approved plans within all remaining guards.

Read/plan CLI commands default to proposal-only. MCP defaults to read-only. The SDK retains a trusted-code convention: constructing Repository(path) and calling apply is explicit caller authorization. Set Policy(mode=...) when embedding Yasarda in a restricted worker; the documentation examples do so. A Python caller with direct filesystem access is not sandboxed by a Python class.

Dirty Git worktrees are refused by default. The host can permit them using --allow-dirty; exact input fingerprints remain mandatory. Git's index is never updated by Yasarda. Bare Git object stores are not mutation targets.

Manifest example

{
  "schema_version": 1,
  "id": "RP-001",
  "version": 1,
  "title": "Replace the legacy greeting",
  "changes": [
    {
      "type": "replace_text",
      "path": "hello.txt",
      "before": "hello old world\n",
      "after": "hello new world\n",
      "count": 1
    }
  ],
  "verify": [
    {"type": "contains", "path": "hello.txt", "text": "hello new world\n"}
  ]
}

yasarda schema emits the same JSON Schema enforced by the loader. Unknown fields, incorrect types, nonpositive counts, conflicting hashes, duplicate JSON keys, unsupported schema versions, and unsafe paths are rejected.

The four primitives are replace_text, replace_file, create_file, and delete_file. replace_file requires expected_old_sha256; the desired hash is computed from content. delete_file requires the old hash. create_file only creates an absent path or recognizes matching bytes/mode. New files default to 0644; an explicit integer mode is permitted.

Text replacements operate on UTF-8 token bytes, not newline-normalized file strings. Unrelated bytes, CRLF, BOMs, final-newline state, and permission bits are preserved. Counted text detection is conservative, not semantic code analysis. For stronger old/new identity, add both expected_old_sha256 and expected_new_sha256 to a text change. Empty replacement text requires both.

One transformation per path is deliberate. Duplicate paths, case-fold collisions, and parent/child collisions are rejected before publication. To make several edits to one file, author one exact final replace_file payload instead of a sequence of loosely overlapping edits.

Optional branches and depends_on are enforced through all orchestrating APIs, including catalog[id].apply(repo). Missing prerequisites and cycles fail closed. The staged result is also checked not to undo its own prerequisites.

Verification means what it says

Supported structural checks: exists, absent, contains, not_contains, and sha256. Every execution also checks the exact planned final bytes and modes. verify RP-001 checks the manifest's desired-state predicates and declared checks; a historical receipt is not a fresh observation of today's files.

Command checks do not run. A manifest containing a legacy command check fails staged verification before destination publication. Run builds, tests, Instruments, benchmarks, or other executable checks in a separately authorized environment. A successful structural check is not proof that an app is correct, fast, accessible, or secure.

Recovery and receipts

Before publication, Yasarda stores an operation journal outside the repository, stages the scoped output in a private directory, verifies it, and rechecks the live inputs. Cooperating Yasarda writers share an advisory lock. Publication is conditional and per-file; new-file publication does not clobber an existing path.

# Recover an interrupted operation to its original file state.
yasarda --repo /path/to/repo recover PLAN_ID --approve PLAN_ID --yes

# Explicitly undo a completed operation, provided its outputs have not diverged.
yasarda --repo /path/to/repo rollback PLAN_ID --approve PLAN_ID --yes

Repeating execution of the same saved plan returns its verified receipt only when the result still matches. It does not apply the transformation twice. A rolled-back or failed operation needs a fresh reviewed plan, not a forced retry. An interrupted operation blocks further publication until recovery is resolved. doctor reports pending operation IDs.

Recovery checks all affected files before restoring any. Unknown/newer content or a changed Git branch/HEAD causes refusal, not overwriting. Recoverable original bytes and modes remain in the journal. Empty directories created by an operation are removed on rollback; directories containing somebody else's additions remain. Receipts distinguish staged, published, and rollback verification.

Default state location: ~/.local/state/yasarda/<repository-root-digest>/<plan-id>.json. Use --state-dir or host environment YASARDA_STATE_DIR to choose another private location outside the repository. State directories are 0700; journals and exported plans are 0600. Journals and plans contain source bytes; treat them as sensitive, keep them while recovery may be needed, and manage retention yourself. Receipts returned through the API omit those embedded source payloads. Known cloud/remote filesystem refusal applies to this state path as well as the repository because its lock and journal durability are part of recovery safety.

Vinyasar supervisor boundary

Yasarda 0.3.0 adds two narrow read-only SDK methods for the separately packaged Vinyasar automation layer:

engine.pending_operations()             # exact IDs requiring recovery
repository.mutation_filesystem_issue()  # reason mutation storage is unsupported

Vinyasar calls these methods plus the existing Engine.recover() API. It does not read journal files or call transaction internals. Yasarda remains the only component that stages, publishes, restores, and verifies repository bytes.

Python SDK

from pathlib import Path
from yasarda import Catalog, Engine, Plan, Policy, Repository

root = Path("/path/to/repo")
repo = Repository(root, policy=Policy(mode="proposal-only"))
catalog = Catalog.load(root / ".yasarda/remediations")
plan = catalog.plan("RP-001", repo)
print(plan.diff)
plan.save("/outside/repo/reviewed.plan.json")

# Later, potentially in a new process, after independent review:
reviewed = Plan.load("/outside/repo/reviewed.plan.json")
executor = Repository(root, policy=Policy(mode="approved-mutation"))
receipt = Engine(executor).execute(reviewed, approval=reviewed.id)
assert receipt["state"] in {"verified", "already_satisfied"}

The approval in this example represents a trusted host decision. Do not copy that auto-approval pattern into an untrusted agent endpoint. Engine(..., catalog=live_catalog) additionally checks its supplied catalog snapshot against the plan. A self-contained saved plan without a live catalog deliberately uses its embedded, approved manifests and verifies dependencies against current files.

Errors have stable code, message, and details fields. CLI failures are JSON on stderr; codes are 0 success, 2 blocked/invalid/I/O, 3 missing immediate-apply confirmation, 4 verification failure, and 130 keyboard interruption.

MCP stdio

Read-only host configuration:

{
  "mcpServers": {
    "yasarda": {
      "command": "yasarda-mcp",
      "args": ["--repo", "/absolute/repository", "--mode", "read-only"]
    }
  }
}

To delegate execution, the host separately supplies the saved plan and approved ID at process startup:

yasarda-mcp --repo /absolute/repository --mode approved-mutation \
  --plan-file /outside/repo/reviewed.plan.json --approve REVIEWED_PLAN_ID

There are no tool arguments for changing repository/catalog roots, mode, dirty-worktree permission, command permission, or approvals. Read-only servers omit mutation tools. A write-enabled server still rejects plans absent from its host approval set. Completed-operation rollback additionally requires the host's --allow-rollback flag; interrupted-operation recovery uses its approved ID.

Tools expose status, catalogs, inspection, planning, structural verification, plan summaries, and receipts. Authorized hosts additionally expose execution and recovery. Plans and manifests are available as yasarda:// JSON resources.

The included transport is a bounded, synchronous tools/resources implementation of MCP stdio for protocol versions 2025-11-25 and 2025-06-18. It negotiates the version, validates input schemas, returns structured results/tool errors, and keeps stdout protocol-only. It does not provide HTTP, OAuth, prompts, sampling, tasks, subscriptions, or live mid-operation cancellation. Process interruption is handled through the journal. See the test report for what was actually exercised; this is not a claim of certification or testing in every MCP host application.

Important limits

  • This is not an OS security boundary, multi-file atomic filesystem, or semantic code-repair oracle. Keep noncooperating writers out of the destination while publishing. An agent with independent shell/filesystem access can bypass a tool.
  • Regular singly-linked files only. Symlinks, hardlinks, devices, special permission bits, and directory transformations are rejected. Content and POSIX mode are tracked; ownership, ACLs, xattrs, and timestamps are not preserved as a complete metadata snapshot. Use an appropriate filesystem copy tool when those attributes matter.
  • Protected paths include Git metadata, Yasarda configuration, credential directories, dotenv files, and root CI workflows. A trusted SDK host may supply a different Policy; MCP callers cannot.
  • Default limits: 8 MiB per file, 24 MiB input snapshot, 200 scoped files. Generated outputs have an 8 MiB hard cap; serialized plans have a 64 MiB cap. MCP sessions hold at most 100 plans and 64 MiB of serialized plans. Oversized jobs must be deliberately split, not silently truncated.
  • Local source artifacts only. No AST adapters, signed registries, network publication, sandboxed build runner, or hidden cloud dependencies.

Tests and handoff

python -m pip install '.[dev]'
python -m pytest

The source includes the unmodified seven original tests and eight original audit regressions, plus failure-injection, interruption, permission, saved-plan, and MCP wire tests. See docs/TEST_REPORT.md, docs/SAFETY.md, CHANGELOG.md, and docs/HANDOFF.md for evidence and precise limitations.

Release files for yasarda 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for yasarda 0.4.0
File Size Uploaded
yasarda-0.4.0.tar.gz 62.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for yasarda 0.4.0
File Interpreter ABI Platform
yasarda-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 103.6 kB

Release files / yasarda-0.4.0.tar.gz

Download URL yasarda-0.4.0.tar.gz
Size 62.0 kB
Tags Source
SHA-256 checksum
How to use checksums
d7b9fef52cb2cc67984c4a4cf80d0384bf8b94bd88166987384a362231abe645
BLAKE2b-256 checksum
How to use checksums
b4eb9e8119129433cc71bb9c81e855f7e49df990ed0fa71455d074181a9985a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.11

Release files / yasarda-0.4.0-py3-none-any.whl

Download URL yasarda-0.4.0-py3-none-any.whl
Size 41.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
caac9f4fd6f6d2024a0a086d9175e607b1b3b586f60f745ca0d24faff848f53d
BLAKE2b-256 checksum
How to use checksums
cb5f8ef07e51d3976fac0a6c010b4f262a15c0ee03fdac3ffd298d3c93620393
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.11

Release history Release notifications | RSS feed

0.4.2

2 release files

0.4.1

2 release files

This release

0.4.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page