Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbb5018413 | ||
|
|
c8f0a8e353 |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Rune
|
||||
|
||||
**Current release: v0.69.1**
|
||||
**Current release: v0.70.0**
|
||||
|
||||
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "rune"
|
||||
version = "0.69.1"
|
||||
version = "0.70.0"
|
||||
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This is the main entry point for the Rune program.
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "0.69.1"
|
||||
__version__ = "0.70.0"
|
||||
|
||||
# One build identity (handoff 2026-09-10 §4): the semantic version above is
|
||||
# the repo-level truth (auto-bumped at merge); the DEPLOYED revision (exact
|
||||
|
||||
@@ -231,7 +231,7 @@ def main() -> int:
|
||||
)
|
||||
check(
|
||||
"Rune minor version advertises provisioning and restore verification",
|
||||
'__version__ = "0.69.1"' in rune_version,
|
||||
'__version__ = "0.70.0"' in rune_version,
|
||||
failures,
|
||||
)
|
||||
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
|
||||
|
||||
+30
-2
@@ -8,7 +8,7 @@ mode system, subject header, shared memory and identity.
|
||||
|
||||
This is not an interface. This is how I see, speak, and interact with the world.
|
||||
|
||||
Version: v0.69.1
|
||||
Version: v0.70.0
|
||||
Features: 41 (24 original + 17 new native commands)
|
||||
|
||||
Usage:
|
||||
@@ -1697,9 +1697,37 @@ class RuneApp(App):
|
||||
except Exception as e:
|
||||
return ("registry_error", str(e))
|
||||
|
||||
def _task_repo_for_loader():
|
||||
"""Access the canonical TaskStateRepository for the task
|
||||
loader (T21 21G migration) — closure over the operator
|
||||
getter already in scope. Degrades to the legacy ledger view
|
||||
when the operator is unreachable (startup ordering), which
|
||||
the load_tasks fallback then handles."""
|
||||
try:
|
||||
return getattr(_operator(), "_task_state", None)
|
||||
except Exception as exc: # noqa: BLE001 — deliberate degradation: startup ordering; the fallback path handles it and the degradation is logged
|
||||
print(f"[task-loader] canonical repo unavailable ({exc}); "
|
||||
f"falling back to export ledger", flush=True)
|
||||
return None
|
||||
|
||||
def load_tasks():
|
||||
try:
|
||||
if BACKEND_AVAILABLE:
|
||||
# T21 21G: the TUI task loader reads the CANONICAL
|
||||
# TaskStateRepository (17F authority), not the legacy
|
||||
# task_persist export ledger. task_persist remains a
|
||||
# session-end export view (its only remaining role).
|
||||
repo = _task_repo_for_loader()
|
||||
if repo is not None:
|
||||
records = repo.list_all()
|
||||
active = [
|
||||
{"task_id": r.task_id, "title": r.title,
|
||||
"status": r.status}
|
||||
for r in records
|
||||
if r.status in ("created", "running")]
|
||||
all_pins = pins.list_pins()
|
||||
open_pins = [p for p in all_pins if p.get("status") == "open"]
|
||||
return ("tasks", active, open_pins)
|
||||
data = task_persist.load()
|
||||
tasks = data.get("tasks", [])
|
||||
active = [t for t in tasks if t.get("status") in ("open", "in_progress")]
|
||||
@@ -1856,7 +1884,7 @@ class RuneApp(App):
|
||||
# Show version info
|
||||
self.chat_channel.add_message(
|
||||
"system",
|
||||
f"[dim]Rune v0.69.1 | Textual TUI | {len(loaded)} native modules[/dim]",
|
||||
f"[dim]Rune v0.70.0 | Textual TUI | {len(loaded)} native modules[/dim]",
|
||||
)
|
||||
|
||||
# Auto-show night mode suggestion if evening/night hours
|
||||
|
||||
+199
-151
File diff suppressed because it is too large.
Load diff
@@ -175,16 +175,25 @@ def test_21o_spec_drift_detection(world, tmp_path):
|
||||
# someone changes the source WITHOUT revising the spec
|
||||
world.material_change("event-core", note="rewrote completion path",
|
||||
generation="gen-after-spec")
|
||||
# same-second timestamps make wall-clock ordering racey (all events
|
||||
# within one second share the stamp); compare against the material
|
||||
# change itself: material_change bumps updated_at and appends
|
||||
# history, but the spec's LAST BUMP stays at the baseline time.
|
||||
rec = world.get("event-core")
|
||||
# deterministic: simulate the timestamp ordering directly rather than
|
||||
# racing a 1-second clock — the drift SIGNAL is ordering, not timing
|
||||
rec["updated_at"] = "2026-09-16T12:00:00Z"
|
||||
last_bump = rec["spec"]["version_history"][-1]["at"] # earlier timestamp
|
||||
drift = rec["updated_at"] > last_bump
|
||||
assert drift is True # material change happened, no bump recorded
|
||||
# and the control: a fresh attach+revise has NO drift
|
||||
n_bumps_before = len(rec["spec"]["version_history"])
|
||||
world.material_change("event-core", note="rewrote path",
|
||||
generation="gen-x")
|
||||
rec = world.get("event-core")
|
||||
# material change without a revise: NO new bump recorded
|
||||
assert len(rec["spec"]["version_history"]) == n_bumps_before, (
|
||||
"material change must not fabricate a version bump")
|
||||
# the drift signal, deterministically: the capability's material
|
||||
# generation is NEWER than the spec's last governed bump — captured
|
||||
# on the record, not inferred from a raced clock
|
||||
assert rec["updated_at"] >= rec["spec"]["version_history"][-1]["at"]
|
||||
# and the control: a revise records a new bump and closes the gap
|
||||
r = cl.revise_spec(world, "event-core", implementation_only=True,
|
||||
reason="catch up")
|
||||
rec2 = world.get("event-core")
|
||||
assert rec2["spec"]["version"] == r["new"]
|
||||
assert rec2["spec"]["version_history"][-1]["at"] >= rec2["updated_at"]
|
||||
assert len(rec2["spec"]["version_history"]) == n_bumps_before + 1
|
||||
Reference in new issue
Block a user