Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15dbbc562a | ||
|
|
6eb5e06119 | ||
|
|
78e4750a77 | ||
|
|
c3948bae98 | ||
|
|
b8184a0f01 | ||
|
|
469b91f217 | ||
|
|
bb960b1656 |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Rune
|
||||
|
||||
**Current release: v0.96.1**
|
||||
**Current release: v0.98.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.96.1"
|
||||
version = "0.98.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.96.1"
|
||||
__version__ = "0.98.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
|
||||
|
||||
@@ -19,7 +19,8 @@ def has_idempotent_label_removal(workflow: str) -> bool:
|
||||
return bool(
|
||||
re.search(
|
||||
r"for \(const name of staleManaged\) \{\s*"
|
||||
r"try \{\s*await github\.rest\.issues\.removeLabel\(\{.*?"
|
||||
r"try \{.*?await github\.request\('DELETE /repos/\{owner\}/\{repo\}/issues/\{issue_number\}/labels/\{id\}', \{.*?"
|
||||
r"id: currentLabelIds\.get\(name\),\s*"
|
||||
r"\}\);\s*\} catch \(error\) \{\s*"
|
||||
r"if \(error\.status !== 404\) \{\s*throw error;\s*\}\s*"
|
||||
r"console\.log\(`Managed label already absent: \$\{name\}`\);\s*"
|
||||
@@ -64,7 +65,7 @@ def main() -> int:
|
||||
"infrastructure",
|
||||
"life",
|
||||
"custodian",
|
||||
"priority: high",
|
||||
"P0 — critical", "P1 — high", "P2 — medium", "P3 — low",
|
||||
"bug",
|
||||
"feature",
|
||||
)
|
||||
@@ -80,7 +81,8 @@ def main() -> int:
|
||||
)
|
||||
check(
|
||||
"stale managed labels are removed",
|
||||
"managedLabels.has" in auto_label and "!desiredLabels.has" in auto_label and "removeLabel" in auto_label,
|
||||
"managedLabels.has" in auto_label and "!desiredLabels.has" in auto_label
|
||||
and "currentLabelIds.get(name)" in auto_label,
|
||||
failures,
|
||||
)
|
||||
check(
|
||||
@@ -231,7 +233,7 @@ def main() -> int:
|
||||
)
|
||||
check(
|
||||
"Rune minor version advertises provisioning and restore verification",
|
||||
'__version__ = "0.96.1"' in rune_version,
|
||||
'__version__ = "0.98.0"' in rune_version,
|
||||
failures,
|
||||
)
|
||||
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
|
||||
|
||||
@@ -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.96.1
|
||||
Version: v0.98.0
|
||||
Features: 41 (24 original + 17 new native commands)
|
||||
|
||||
Usage:
|
||||
@@ -1895,7 +1895,7 @@ class RuneApp(App):
|
||||
# Show version info
|
||||
self.chat_channel.add_message(
|
||||
"system",
|
||||
f"[dim]Rune v0.96.1 | Textual TUI | {len(loaded)} native modules[/dim]",
|
||||
f"[dim]Rune v0.98.0 | Textual TUI | {len(loaded)} native modules[/dim]",
|
||||
)
|
||||
|
||||
# Auto-show night mode suggestion if evening/night hours
|
||||
|
||||
+6
-135
@@ -53,6 +53,12 @@ from rune.structural_store import ( # shared generic core (SCAFFOLD-TRANCHE-001
|
||||
reconcile_authorities, mark_conflict, resolve_status, authority_health,
|
||||
dedupe_observations as _ss_dedupe_observations,
|
||||
)
|
||||
from rune.spine_language import ( # noqa: F401 — re-exported (import-compat): the
|
||||
# #2465 doc-level transforms live here now (giant-file carve); tests and
|
||||
# the canary_spine_meta read them THROUGH spine, exactly as before.
|
||||
_classification_sides, _place_languages, _normalize_discovery_ids,
|
||||
_apply_contract_discoveries, _contract_discoveries,
|
||||
)
|
||||
|
||||
SCHEMA_VERSION = 1
|
||||
|
||||
@@ -337,141 +343,6 @@ def _rust_core():
|
||||
return None
|
||||
|
||||
|
||||
def _place_languages(doc: dict) -> int:
|
||||
"""Stamp a language verdict per entity (no-op for entities that carry
|
||||
one). Deterministic: substring classification over the entity id + the
|
||||
kind; the exact same rule construction_decision.language_placement
|
||||
implements (kept inline here to avoid a per-entity FFI crossing for
|
||||
the spine's own 218 entities; the FFI path is used in contracts)."""
|
||||
GO_HINTS = ("server", "transport", "dispatch", "entry", "bridge",
|
||||
"hook_engine", "provider", "watch", "notif", "voice",
|
||||
"gitea", "event_", "message_", "tool_exec", "work_manager")
|
||||
RUST_HINTS = ("memory", "drift", "warm", "capab", "token", "spine",
|
||||
"scaffold", "construct", "structural", "curator",
|
||||
"consolidat", "elastic", "cognit", "abstract", "contract",
|
||||
"ledger", "audit", "claim", "evidence", "behavio",
|
||||
"verif", "pins", "witness", "anchor", "growth", "goal",
|
||||
"salience", "store", "tui_", "rune_tui")
|
||||
placed = 0
|
||||
for e in doc.get("entities", []):
|
||||
eid = e.get("id", "")
|
||||
kind = e.get("kind", "")
|
||||
if eid.startswith("contract:"):
|
||||
lang = "n/a"
|
||||
elif kind == "rust-crate":
|
||||
lang = "rust"
|
||||
elif kind == "go-surface":
|
||||
lang = "go"
|
||||
elif kind == "workflow" or kind == "config":
|
||||
lang = "n/a"
|
||||
elif kind == "deployment_surface":
|
||||
lang = "go"
|
||||
elif "tui" in eid:
|
||||
lang = "python-tui (Wave 6)"
|
||||
elif any(h in eid for h in GO_HINTS):
|
||||
lang = "go"
|
||||
elif any(h in eid for h in RUST_HINTS):
|
||||
lang = "rust"
|
||||
elif kind == "module":
|
||||
lang = "rust"
|
||||
else:
|
||||
lang = "unassessed"
|
||||
e.setdefault("identities", {})["language"] = lang
|
||||
if lang != "unassessed":
|
||||
placed += 1
|
||||
return placed
|
||||
|
||||
|
||||
def _normalize_discovery_ids(doc: dict) -> int:
|
||||
"""Migrate the old unqualified contract references, preserving evidence.
|
||||
|
||||
A file path (or 'suite') is not a module ID. Only migrate the malformed
|
||||
module records produced by the earlier discovery projector; leave all
|
||||
other open-world entity classes alone.
|
||||
"""
|
||||
renamed = {}
|
||||
observed = {
|
||||
eid for _, entities in _contract_discoveries()
|
||||
for eid, _, provenance in entities if provenance == "observed"
|
||||
}
|
||||
for e in doc.get("entities", []):
|
||||
if (e.get("kind") == "module" and ":" not in e.get("id", "")
|
||||
and any(ref.startswith("contract-discovery:")
|
||||
for ref in e.get("evidence_refs", []))):
|
||||
old = e["id"]
|
||||
e["id"] = f"construction-surface:{old}"
|
||||
e["kind"] = "construction-surface"
|
||||
e.setdefault("identities", {})["source_ref"] = old
|
||||
renamed[old] = e["id"]
|
||||
if e.get("kind") == "construction-surface" and e.get("id", "").startswith("construction-surface:"):
|
||||
# Earlier code promoted every *candidate* to PRESENT. Undo only
|
||||
# that unsupported claim; an actually observed surface retains it.
|
||||
if e["id"].split(":", 1)[1] not in observed:
|
||||
e["maturity"] = "DECLARED"
|
||||
e["tags"] = [tag for tag in e.get("tags", []) if tag != "code"]
|
||||
for rel in doc.get("relationships", []):
|
||||
for endpoint in ("from", "to"):
|
||||
if rel.get(endpoint) in renamed:
|
||||
rel[endpoint] = renamed[rel[endpoint]]
|
||||
return len(renamed)
|
||||
|
||||
|
||||
def _apply_contract_discoveries(doc: dict) -> int:
|
||||
"""Contract-discovery write-back (issue #2465 addition 2): entities
|
||||
DISCOVERED by closed contracts become modeled spine entities — the
|
||||
spine learns what construction actually built. Doc-level transform,
|
||||
applied to BOTH cores' output. The CANDIDATE law holds: only CLOSED
|
||||
contracts write back."""
|
||||
placed = 0
|
||||
for cid, entities in _contract_discoveries():
|
||||
for eid, kind, provenance in entities:
|
||||
before = len(doc.get("entities", []))
|
||||
if kind == "construction-surface":
|
||||
eid = f"construction-surface:{eid}"
|
||||
add_entity(doc, id=eid, kind=kind,
|
||||
tags=["contract-discovery"],
|
||||
identities={"source_ref": eid.split(":", 1)[1]} if kind == "construction-surface" else None,
|
||||
evidence_refs=[f"contract-discovery:{cid}"],
|
||||
maturity="PRESENT" if provenance == "observed" else "DECLARED")
|
||||
if len(doc.get("entities", [])) > before:
|
||||
placed += 1
|
||||
else:
|
||||
placed += 0 # high-water merge — no new entity, still evidence
|
||||
return placed
|
||||
|
||||
|
||||
def _contract_discoveries():
|
||||
"""Closed contracts' DISCOVERED entities (issue #2465 addition 2):
|
||||
expected_new_observed + candidate corrections, gathered from the
|
||||
scaffold store. Returns [(contract_id, [(entity_id, kind), ...])]."""
|
||||
try:
|
||||
doc = json.loads((ROOT / "store" / "scaffold" / "current.json").read_text())
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return []
|
||||
out = []
|
||||
for e in doc.get("entities", []):
|
||||
cid = e.get("id", "")
|
||||
if not cid.startswith("contract:") or e.get("phase") != "closed":
|
||||
continue
|
||||
q = e.get("ext", {}).get("contract_quality", {})
|
||||
found = []
|
||||
# expected-new surfaces observed at close (REAL delivered entities)
|
||||
for eid in q.get("expected_new_observed", []):
|
||||
kind = eid.split(":", 1)[0] if ":" in eid else "construction-surface"
|
||||
if kind in ("rust-crate", "go-surface", "config", "workflow", "module"):
|
||||
found.append((eid, kind, "observed"))
|
||||
elif kind == "construction-surface":
|
||||
found.append((eid, kind, "observed"))
|
||||
# candidate corrections: the closure flagged them for modeling
|
||||
for cand in q.get("candidate_corrections", []):
|
||||
eid = cand.get("entity", "")
|
||||
kind = eid.split(":", 1)[0] if ":" in eid else "construction-surface"
|
||||
if eid and kind in ("rust-crate", "go-surface", "config", "workflow", "module", "construction-surface"):
|
||||
found.append((eid, kind, "candidate"))
|
||||
if found:
|
||||
out.append((cid[9:], found))
|
||||
return out
|
||||
|
||||
|
||||
def _gather_build_inputs(manifest_path: Path | None = None) -> dict:
|
||||
"""Every I/O read build() needs, in one place (zero-dep split law:
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
"""spine_language.py — the #2465 doc-level transforms (carved from spine.py
|
||||
to honor the giant-file henxel: keep it under 1200 lines).
|
||||
|
||||
Three transforms, applied to BOTH cores' output at build time:
|
||||
1. language placement — every entity gets a language verdict (kind
|
||||
defaults; subsystems derive from member-majority over the measured
|
||||
classification map);
|
||||
2. discovery-ID normalization — migrate legacy unqualified contract
|
||||
discoveries to construction-surface kind without losing evidence;
|
||||
3. contract-discovery write-back — closed contracts teach the spine
|
||||
what construction actually built (observed → PRESENT, candidate →
|
||||
DECLARED; the CANDIDATE law holds).
|
||||
|
||||
Same dependency laws as spine.py: structural_store primitives only, no
|
||||
I/O beyond the classification/scaffold store reads, deterministic.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from rune.structural_store import add_entity, now
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SRC_DIR = ROOT / "src" / "rune"
|
||||
|
||||
def _classification_sides() -> dict:
|
||||
"""The measured dichotomy map (W0): module stem → RUST|GO|TUI-LAST|...
|
||||
Loaded once per _place_languages call, best-effort — a missing or
|
||||
unreadable classification.json degrades to {}/unassessed verdicts,
|
||||
never to invented facts."""
|
||||
try:
|
||||
data = json.loads((ROOT / "store" / "rebase" / "classification.json").read_text())
|
||||
return {name: rec.get("side", "") for name, rec in data.items() if isinstance(rec, dict)}
|
||||
except (OSError, json.JSONDecodeError, AttributeError):
|
||||
return {}
|
||||
|
||||
|
||||
def _place_languages(doc: dict) -> int:
|
||||
"""Stamp a language verdict per entity (no-op for entities that carry
|
||||
one). Deterministic: substring classification over the entity id + the
|
||||
kind; the exact same rule construction_decision.language_placement
|
||||
implements (kept inline here to avoid a per-entity FFI crossing for
|
||||
the spine's own 218 entities; the FFI path is used in contracts)."""
|
||||
GO_HINTS = ("server", "transport", "dispatch", "entry", "bridge",
|
||||
"hook_engine", "provider", "watch", "notif", "voice",
|
||||
"gitea", "event_", "message_", "tool_exec", "work_manager")
|
||||
RUST_HINTS = ("memory", "drift", "warm", "capab", "token", "spine",
|
||||
"scaffold", "construct", "structural", "curator",
|
||||
"consolidat", "elastic", "cognit", "abstract", "contract",
|
||||
"ledger", "audit", "claim", "evidence", "behavio",
|
||||
"verif", "pins", "witness", "anchor", "growth", "goal",
|
||||
"salience", "store", "tui_", "rune_tui")
|
||||
placed = 0
|
||||
CLASSIFICATION_SIDES = _classification_sides()
|
||||
for e in doc.get("entities", []):
|
||||
eid = e.get("id", "")
|
||||
kind = e.get("kind", "")
|
||||
if eid.startswith("contract:"):
|
||||
lang = "n/a"
|
||||
elif kind == "rust-crate":
|
||||
lang = "rust"
|
||||
elif kind == "go-surface":
|
||||
lang = "go"
|
||||
elif kind == "workflow" or kind == "config":
|
||||
lang = "n/a"
|
||||
elif kind == "deployment_surface":
|
||||
lang = "go"
|
||||
elif "tui" in eid:
|
||||
lang = "python-tui (Wave 6)"
|
||||
elif any(h in eid for h in GO_HINTS):
|
||||
lang = "go"
|
||||
elif any(h in eid for h in RUST_HINTS):
|
||||
lang = "rust"
|
||||
elif kind == "module":
|
||||
lang = "rust"
|
||||
elif kind == "subsystem":
|
||||
# A subsystem is a CONTAINER — its language is its members'
|
||||
# majority side per the measured dichotomy. The evidence map is
|
||||
# classification.json (measured, 174 modules; honest note: newer
|
||||
# files may be unclassified, so the count of classified members
|
||||
# rides the verdict). Members arrive as belongs_to relationships
|
||||
# whose from-ids carry module:<name> — map through classification.
|
||||
members = [
|
||||
r.get("from", "").split(":", 1)[1]
|
||||
for r in doc.get("relationships", [])
|
||||
if r.get("type") == "belongs_to" and r.get("to") == eid
|
||||
and r.get("from", "").startswith("module:")
|
||||
]
|
||||
sides = [
|
||||
str(CLASSIFICATION_SIDES.get(m) or "").upper()
|
||||
for m in members
|
||||
]
|
||||
rust = sides.count("RUST")
|
||||
go = sides.count("GO")
|
||||
if rust and go:
|
||||
lang = f"mixed (rust {rust} / go {go} members)"
|
||||
elif rust:
|
||||
lang = "rust"
|
||||
elif go:
|
||||
lang = "go"
|
||||
else:
|
||||
lang = "unassessed"
|
||||
else:
|
||||
lang = "unassessed"
|
||||
e.setdefault("identities", {})["language"] = lang
|
||||
if lang != "unassessed":
|
||||
placed += 1
|
||||
return placed
|
||||
|
||||
|
||||
def _normalize_discovery_ids(doc: dict) -> int:
|
||||
"""Migrate the old unqualified contract references, preserving evidence.
|
||||
|
||||
A file path (or 'suite') is not a module ID. Only migrate the malformed
|
||||
module records produced by the earlier discovery projector; leave all
|
||||
other open-world entity classes alone.
|
||||
"""
|
||||
renamed = {}
|
||||
observed = {
|
||||
eid for _, entities in _contract_discoveries()
|
||||
for eid, _, provenance in entities if provenance == "observed"
|
||||
}
|
||||
for e in doc.get("entities", []):
|
||||
if (e.get("kind") == "module" and ":" not in e.get("id", "")
|
||||
and any(ref.startswith("contract-discovery:")
|
||||
for ref in e.get("evidence_refs", []))):
|
||||
old = e["id"]
|
||||
e["id"] = f"construction-surface:{old}"
|
||||
e["kind"] = "construction-surface"
|
||||
e.setdefault("identities", {})["source_ref"] = old
|
||||
renamed[old] = e["id"]
|
||||
if e.get("kind") == "construction-surface" and e.get("id", "").startswith("construction-surface:"):
|
||||
# Earlier code promoted every *candidate* to PRESENT. Undo only
|
||||
# that unsupported claim; an actually observed surface retains it.
|
||||
if e["id"].split(":", 1)[1] not in observed:
|
||||
e["maturity"] = "DECLARED"
|
||||
e["tags"] = [tag for tag in e.get("tags", []) if tag != "code"]
|
||||
for rel in doc.get("relationships", []):
|
||||
for endpoint in ("from", "to"):
|
||||
if rel.get(endpoint) in renamed:
|
||||
rel[endpoint] = renamed[rel[endpoint]]
|
||||
return len(renamed)
|
||||
|
||||
|
||||
def _apply_contract_discoveries(doc: dict) -> int:
|
||||
"""Contract-discovery write-back (issue #2465 addition 2): entities
|
||||
DISCOVERED by closed contracts become modeled spine entities — the
|
||||
spine learns what construction actually built. Doc-level transform,
|
||||
applied to BOTH cores' output. The CANDIDATE law holds: only CLOSED
|
||||
contracts write back."""
|
||||
placed = 0
|
||||
for cid, entities in _contract_discoveries():
|
||||
for eid, kind, provenance in entities:
|
||||
before = len(doc.get("entities", []))
|
||||
if kind == "construction-surface":
|
||||
eid = f"construction-surface:{eid}"
|
||||
add_entity(doc, id=eid, kind=kind,
|
||||
tags=["contract-discovery"],
|
||||
identities={"source_ref": eid.split(":", 1)[1]} if kind == "construction-surface" else None,
|
||||
evidence_refs=[f"contract-discovery:{cid}"],
|
||||
maturity="PRESENT" if provenance == "observed" else "DECLARED")
|
||||
if len(doc.get("entities", [])) > before:
|
||||
placed += 1
|
||||
else:
|
||||
placed += 0 # high-water merge — no new entity, still evidence
|
||||
return placed
|
||||
|
||||
|
||||
def _contract_discoveries():
|
||||
"""Closed contracts' DISCOVERED entities (issue #2465 addition 2):
|
||||
expected_new_observed + candidate corrections, gathered from the
|
||||
scaffold store. Returns [(contract_id, [(entity_id, kind), ...])]."""
|
||||
try:
|
||||
doc = json.loads((ROOT / "store" / "scaffold" / "current.json").read_text())
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return []
|
||||
out = []
|
||||
for e in doc.get("entities", []):
|
||||
cid = e.get("id", "")
|
||||
if not cid.startswith("contract:") or e.get("phase") != "closed":
|
||||
continue
|
||||
q = e.get("ext", {}).get("contract_quality", {})
|
||||
found = []
|
||||
# expected-new surfaces observed at close (REAL delivered entities)
|
||||
for eid in q.get("expected_new_observed", []):
|
||||
kind = eid.split(":", 1)[0] if ":" in eid else "construction-surface"
|
||||
if kind in ("rust-crate", "go-surface", "config", "workflow", "module"):
|
||||
found.append((eid, kind, "observed"))
|
||||
elif kind == "construction-surface":
|
||||
found.append((eid, kind, "observed"))
|
||||
# candidate corrections: the closure flagged them for modeling
|
||||
for cand in q.get("candidate_corrections", []):
|
||||
eid = cand.get("entity", "")
|
||||
kind = eid.split(":", 1)[0] if ":" in eid else "construction-surface"
|
||||
if eid and kind in ("rust-crate", "go-surface", "config", "workflow", "module", "construction-surface"):
|
||||
found.append((eid, kind, "candidate"))
|
||||
if found:
|
||||
out.append((cid[9:], found))
|
||||
return out
|
||||
@@ -20,6 +20,24 @@ HOT_THRESHOLD = 7 # accessed within 7 days = hot
|
||||
WARM_THRESHOLD = 30 # accessed within 30 days = warm
|
||||
# > 30 days = cold
|
||||
|
||||
|
||||
def _core():
|
||||
"""The Rust cognition_core when available (strangler: Python fallback is
|
||||
load-bearing). RUNE_COGNITION_CORE=python pins the fallback."""
|
||||
try:
|
||||
from rune import cc_loader
|
||||
except ImportError: # standalone script execution
|
||||
import cc_loader
|
||||
try:
|
||||
return cc_loader if cc_loader.available() else None
|
||||
except Exception: # availability probe must never crash the fallback
|
||||
return None
|
||||
|
||||
|
||||
def _py_core_now() -> str:
|
||||
"""The injected clock (iso Z) — module-level for the kernel boundary."""
|
||||
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Tier storage characteristics
|
||||
TIER_CONFIG = {
|
||||
"hot": {
|
||||
@@ -56,7 +74,24 @@ class TierManager:
|
||||
"""Determine the tier for an entry based on its access patterns.
|
||||
|
||||
Returns 'hot', 'warm', or 'cold'.
|
||||
Delegates to the Rust kernel when built; the Python body below is
|
||||
the parity oracle (W3.4: identical thresholds injected, naive
|
||||
timestamps refuse per the PARITY TRAP law).
|
||||
"""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
from rune import cc_loader as _cl
|
||||
|
||||
raw = _cl.tier_get(entry, _py_core_now(),
|
||||
float(HOT_THRESHOLD), float(WARM_THRESHOLD))
|
||||
return str(raw)
|
||||
except Exception: # noqa: BLE001 — kernel failure → oracle, never a broken call
|
||||
pass
|
||||
return self._py_get_tier(entry)
|
||||
|
||||
def _py_get_tier(self, entry: dict) -> str:
|
||||
"""Python parity oracle (W3.4)."""
|
||||
wash_count = entry.get("wash_count", 0) or 0
|
||||
last_accessed = entry.get("last_accessed")
|
||||
rock_status = entry.get("rock_status", "washable")
|
||||
@@ -88,7 +123,26 @@ class TierManager:
|
||||
return "warm"
|
||||
|
||||
def promote(self, entry: dict) -> str:
|
||||
"""Promote an entry to a higher tier. Returns new tier."""
|
||||
"""Promote an entry to a higher tier. Returns new tier.
|
||||
Delegates to the Rust kernel when built (Python oracle below)."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
from rune import cc_loader as _cl
|
||||
|
||||
# The kernel's promote/demote take the FULL ENTRY (plus
|
||||
# thresholds + clock) — not a tier string. They recompute
|
||||
# the current tier internally, so a tier-string call was
|
||||
# silently landing on defaults (the live probe caught it).
|
||||
raw = _cl.tier_promote(entry, _py_core_now(),
|
||||
float(HOT_THRESHOLD), float(WARM_THRESHOLD))
|
||||
return str(raw)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
return self._py_promote(entry)
|
||||
|
||||
def _py_promote(self, entry: dict) -> str:
|
||||
"""Python parity oracle."""
|
||||
current = self.get_tier(entry)
|
||||
if current == "cold":
|
||||
return "warm"
|
||||
@@ -97,7 +151,24 @@ class TierManager:
|
||||
return "hot" # already hot
|
||||
|
||||
def demote(self, entry: dict) -> str:
|
||||
"""Demote an entry to a lower tier. Returns new tier."""
|
||||
"""Demote an entry to a lower tier. Returns new tier.
|
||||
Delegates to the Rust kernel when built (Python oracle below)."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
from rune import cc_loader as _cl
|
||||
|
||||
# Same full-entry contract as promote; the kernel handles
|
||||
# rock_status internally (rock never demotes).
|
||||
raw = _cl.tier_demote(entry, _py_core_now(),
|
||||
float(HOT_THRESHOLD), float(WARM_THRESHOLD))
|
||||
return str(raw)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
return self._py_demote(entry)
|
||||
|
||||
def _py_demote(self, entry: dict) -> str:
|
||||
"""Python parity oracle."""
|
||||
current = self.get_tier(entry)
|
||||
rock_status = entry.get("rock_status", "washable")
|
||||
|
||||
|
||||
+391
-270
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user