Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ The `EmbeddingProvider` interface is defined in `spec/`. Swap in OpenAI, Cohere,
## Eternal storage

```bash
soul archive aria.soul --tiers local,ipfs
soul recover aria.soul --source ipfs
soul archive aria.soul -t ipfs -t arweave
soul recover QmRef123... --tier ipfs --output recovered.soul
soul eternal-status aria.soul
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1647,7 +1647,7 @@ Archive a `.soul` file to eternal storage tiers (IPFS, Arweave, Blockchain). Use

```bash
soul archive my-soul.soul
soul archive .soul/ --tiers ipfs arweave
soul archive .soul/ -t ipfs -t arweave
```

**Arguments:**
Expand Down
4 changes: 2 additions & 2 deletions rfc/RFC-005-ETERNAL-STORAGE-PROVIDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ itself. This provides:
The runtime supports archiving to multiple tiers simultaneously:

```bash
soul archive aria.soul --tiers local,ipfs
soul recover aria.soul --source ipfs
soul archive aria.soul -t ipfs -t arweave
soul recover QmRef123... --tier ipfs --output recovered.soul
soul eternal-status aria.soul
```

Expand Down
8 changes: 7 additions & 1 deletion src/soul_protocol/cli/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# cli/main.py — Click CLI for the Soul Protocol (org + user groups + runtime commands)
# Updated: 2026-07-17 (#286) — Catch unknown-tier ValueError in ``soul archive``;
# exit 1 so scripts can detect failure.
# Updated: 2026-07-18 (#284) — Replaced ~45 private attribute accesses
# (soul._memory, m._episodic, m._graph_entities.clear(), etc.) with public
# MemoryManager API methods. `repair --rebuild-graph` now calls
Expand Down Expand Up @@ -989,7 +991,11 @@ async def _archive():
manager.register(MockIPFSProvider())
manager.register(MockArweaveProvider())
manager.register(MockBlockchainProvider())
results = await manager.archive(soul_data, soul.did, tiers=tier_list)
try:
results = await manager.archive(soul_data, soul.did, tiers=tier_list)
except ValueError as exc:
console.print(f"[red]Archive failed:[/red] {exc}")
raise SystemExit(1)

# Persist archive results into the .soul manifest
_update_soul_manifest(path, results)
Expand Down
30 changes: 30 additions & 0 deletions tests/test_eternal/test_cli_eternal.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# test_eternal/test_cli_eternal.py — CLI tests for eternal storage commands.
# Created: 2026-03-06 — Tests archive, recover, and eternal-status commands.
# Updated: 2026-07-17 (#286) — Added test_archive_unknown_tier; verifies
# clean error message and exit code 1 for unregistered tiers.

from __future__ import annotations

Expand Down Expand Up @@ -61,3 +63,31 @@ def test_recover_missing_reference(tmp_path):

assert result.exit_code == 0
assert "failed" in result.output.lower() or "Recovery failed" in result.output


def test_archive_unknown_tier(tmp_path):
"""archive with an unknown tier prints a clean error, not a traceback."""
runner = CliRunner()
soul_path = str(tmp_path / "unknown-tier.soul")

runner.invoke(cli, ["birth", "TierBot", "-o", soul_path])
result = runner.invoke(cli, ["archive", soul_path, "-t", "local"])

assert result.exit_code == 1
assert "Archive failed" in result.output
assert "local" in result.output
# Must NOT contain a raw traceback
assert "Traceback" not in result.output


def test_archive_two_tier_flags(tmp_path):
"""archive with repeated -t flags archives to multiple tiers (#286 review)."""
runner = CliRunner()
soul_path = str(tmp_path / "multi-tier.soul")

runner.invoke(cli, ["birth", "MultiBot", "-o", soul_path])
result = runner.invoke(cli, ["archive", soul_path, "-t", "ipfs", "-t", "arweave"])

assert result.exit_code == 0
assert "ipfs" in result.output.lower()
assert "arweave" in result.output.lower()
Loading