Compare commits

...
11 Commits
Author SHA1 Message Date
conductor 5882cd46fa chore(release): auto-bump version to 0.255.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / rust-surface (push) Failing after 17s
CI Pipeline / go-surface (push) Failing after 20s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 16s
CI Pipeline / canary (push) Canceled after 31s
CI Pipeline / gitea-offline (push) Canceled after 16s
Auto-version and Release / release (push) Canceled after 36s
CI Pipeline / lint (push) Canceled after 35s
CI Pipeline / unit-tests (push) Canceled after 35s
Promotion record: (unknown)
2026-10-13 04:39:54 +01:00
Nick 6344dfb45d refactor(pane_authority): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 16s
CI Pipeline / go-surface (push) Failing after 20s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 27s
Auto-version and Release / release (push) Canceled after 45s
CI Pipeline / unit-tests (push) Canceled after 46s
deploy / deploy (push) Canceled after 46s
CI Pipeline / canary (push) Canceled after 45s
CI Pipeline / lint (push) Canceled after 46s
Typed UtteranceKind verdict (Control(&'static str) | Amendment |
Discussion) matched EXHAUSTIVELY downstream — the stringly kind chain
is gone; adding a class becomes a compiler error instead of a silent
fallthrough. Battery 2/2 replays frozen truth verbatim; live parity
826/826; pytest 19 passed both ways; clippy -D warnings clean.
2026-10-13 04:39:11 +01:00
Nick acafb72178 feat(pane_authority): port to FULL Rust — dichotomy wave
Language verdict: RUST per docs/language-dichotomy-ledger.md (Law 4 —
pure deterministic classification/apply law; no I/O, no clock, the
whole module crosses FFI).

- pa_classify_utterance/pa_apply_to_task doc-envelope FFI (pa_* symbols;
  free via capability_scoring cs_free_string — no free re-export);
  pa_loader mirroring broker_loader.py.
- Facade delegation with RUNE_PA_CORE=python pin; kernel floor on any
  kernel error.
- Python laws replicated exactly: (text or "") null law, control
  first-word OR '{verb} the task'/'{verb} everything' substring law,
  phrase-match verb="stop" law (even when the phrase matched a
  different control word), status = verb+"ped" unless pause (the
  oracle's 'cancelped' quirk frozen verbatim, not corrected), CHAT
  amendment promotes visibly (needs_confirm=True), WORK amendment sets
  directive, dict(task) shallow-copy semantics.
- 26-case frozen oracle truth (RUNE_PA_CORE=python pinned, deep-copied
  inputs, every public function + branch) embedded in the crate and
  replayed VERBATIM by the branch battery; live parity 826/826 (26
  frozen + 800 fuzz, seed 2479); pytest 19 passed BOTH ways
  (RUNE_PA_CORE=python and auto); clippy -D warnings clean.
2026-10-13 04:39:11 +01:00
conductor 47d3931071 chore(release): auto-bump version to 0.254.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 16s
CI Pipeline / gitea-offline (push) Successful in 18s
Auto-version and Release / release (push) Successful in 37s
CI Pipeline / type-check (push) Failing after 44s
CI Pipeline / lint (push) Failing after 1m7s
CI Pipeline / canary (push) Failing after 2m13s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 9m11s
Promotion record: (unknown)
2026-10-13 04:29:57 +01:00
Nick 9618cf7a50 chore(store): close outcome_utility dichotomy contract BUILDER-2026-10-13T00:04:22Z-4156e1-factory-batch-h (parity bfb87486, phase B 36ee24c1)
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 18s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / canary (push) Canceled after 20s
Auto-version and Release / release (push) Canceled after 34s
deploy / deploy (push) Canceled after 5s
CI Pipeline / type-check (push) Canceled after 16s
CI Pipeline / lint (push) Canceled after 26s
CI Pipeline / unit-tests (push) Canceled after 26s
CI Pipeline / gitea-offline (push) Canceled after 17s
2026-10-13 04:28:58 +01:00
Nick 097916411d refactor(outcome_utility): Phase B language-advantage pass — dichotomy wave (batch-h)
Rust-advantage pass on src/rust/outcome_utility:
- Outcome classified ONCE into an exhaustive enum (Helpful/Ignored/
  Unknown) with From<&str> — the unknown bucket can never silently
  grow a new arm; apply_outcome matches on it.
- shared clamp_utility helper (floor/cap in one place).
- in-crate phaseb unit tests: cap/floor saturation walks (1000x),
  negative-turn clamping, retrieval identity — compiled into Rust's package manager

Usage: cargo [+toolchain] [OPTIONS] [COMMAND]
       cargo [+toolchain] [OPTIONS] -Zscript <MANIFEST_RS> [ARGS]...

Options:
  -V, --version                  Print version info and exit
      --list                     List installed commands
      --explain <CODE>           Provide a detailed explanation of a rustc error message
  -v, --verbose...               Use verbose output (-vv very verbose/build.rs output)
  -q, --quiet                    Do not print cargo log messages
      --color <WHEN>             Coloring [possible values: auto, always, never]
  -C <DIRECTORY>                 Change to DIRECTORY before doing anything (nightly-only)
      --locked                   Assert that `Cargo.lock` will remain unchanged
      --offline                  Run without accessing the network
      --frozen                   Equivalent to specifying both --locked and --offline
      --config <KEY=VALUE|PATH>  Override a configuration value
  -Z <FLAG>                      Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
                                 details
  -h, --help                     Print help

Commands:
    build, b    Compile the current package
    check, c    Analyze the current package and report errors, but don't build object files
    clean       Remove the target directory
    doc, d      Build this package's and its dependencies' documentation
    new         Create a new cargo package
    init        Create a new cargo package in an existing directory
    add         Add dependencies to a manifest file
    remove      Remove dependencies from a manifest file
    run, r      Run a binary or example of the local package
    test, t     Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this package to the registry
    install     Install a Rust binary
    uninstall   Uninstall a Rust binary
    ...         See all commands with --list

See 'cargo help <command>' for more information on a specific command. so the language advantage (native tests, no FFI hop) is real.

Parity re-verified after the pass: frozen battery 97/97 verbatim,
clippy -D warnings 0, pytest 6/6 (both pins), live parity 100%.
Contract BUILDER-2026-10-13T00:04:22Z-4156e1-factory-batch-h.
2026-10-13 04:28:57 +01:00
Nick 14805261d2 feat(outcome_utility): port to FULL Rust — dichotomy wave (batch-h)
Language verdict (docs/language-dichotomy-ledger.md): RUST — Law 4/W6:
deterministic scoring/clamping compute (utility caps, floors, bounded
decay); no I/O, pure decision math.

- src/rust/outcome_utility: doc-envelope FFI (ou_apply_outcome,
  ou_apply_stale, ou_retrieval_alone, ou_record_verified_outcome,
  ou_free_string); frozen-truth battery embedded (battery.rs) — 97
  frozen-oracle cases replayed VERBATIM, bit-exact.
- src/rune/ou_loader.py: ctypes loader mirroring broker_loader.py;
  RUNE_OU_CORE=python pin honored inside the loader.
- src/rune/outcome_utility.py: facade delegation; the utility TABLE
  stays Python (storage in the caller's language); RUNE_OU_CORE=python
  pins the oracle floor.
- scripts/gen_outcome_utility_truth.py: frozen corpus generator
  (poisoned-truth law: oracle-only, deep-copied inputs, all branches).
- tests/test_outcome_utility_port.py: frozen replay VERBATIM, pin
  probe, facade delegation, oracle sanity, seeded live-parity sweep
  (500 rounds, seed 2479) — parity 100%.

Verification: cargo test green (97/97 frozen), cargo clippy -D
warnings 0, pytest 6/6 both pins, ruff clean.
Contract BUILDER-2026-10-13T00:04:22Z-4156e1-factory-batch-h
(builder=factory-batch-h) — building.
2026-10-13 04:28:57 +01:00
Nick 678b390cb9 chore(store): close persona_gate dichotomy contract BUILDER-2026-10-13T02:46:21Z-e4c445-factory-gap-pg (parity 0b77dce2, phase B bddc981c)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 7s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 17s
CI Pipeline / gitea-offline (push) Successful in 21s
Auto-version and Release / release (push) Successful in 37s
CI Pipeline / type-check (push) Failing after 51s
CI Pipeline / lint (push) Failing after 1m10s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / canary (push) Canceled after 2m6s
CI Pipeline / unit-tests (push) Canceled after 2m11s
deploy / deploy (push) Canceled after 1m55s
2026-10-13 04:27:14 +01:00
Nick d57ad585b5 chore(factory): claims — project_registry, execution_trace, persona_gate verified through extended ladder (7-8 PASS each); audit regenerated
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 16s
CI Pipeline / gitea-offline (push) Successful in 17s
Auto-version and Release / release (push) Successful in 37s
CI Pipeline / type-check (push) Failing after 45s
CI Pipeline / lint (push) Failing after 1m7s
CI Pipeline / canary (push) Failing after 2m14s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 13m1s
deploy / deploy (push) Canceled after 13m5s
2026-10-13 04:14:15 +01:00
conductor 17fa6cd933 chore(release): auto-bump version to 0.253.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Canceled after 0s
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 18s
CI Pipeline / gitea-offline (push) Successful in 16s
Auto-version and Release / release (push) Successful in 40s
CI Pipeline / type-check (push) Failing after 43s
CI Pipeline / lint (push) Failing after 1m7s
CI Pipeline / canary (push) Failing after 2m13s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 2m31s
Promotion record: (unknown)
2026-10-13 04:11:40 +01:00
Nick 2b87a88aa2 feat(factory): mechanical layer-7 connection checks in verify script
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 18s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
deploy / deploy (push) Canceled after 26s
CI Pipeline / type-check (push) Canceled after 31s
CI Pipeline / canary (push) Canceled after 44s
Auto-version and Release / release (push) Canceled after 45s
CI Pipeline / unit-tests (push) Canceled after 45s
CI Pipeline / lint (push) Canceled after 45s
- 7a/7b language-dependent: Rust = ctypes loader exists per facade import
  (abbreviated stems: subsystem_health -> sh_loader); Go = go build +
  exec/service connection (Go kernels are services by verdict, no .so).
- 7c: battery must reference the truth FILE (catches self-generated truth).
- 7d: CI gate presence check.
- WARN is now neutral (was incrementing FAIL — a pre-existing bug).
Verified green on both a known-good Rust landing (subsystem_health 8 PASS)
and Go landing (forge 7 PASS).
2026-10-13 04:11:00 +01:00
31 changed files with 22556 additions and 21 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# Rune
**Current release: v0.252.0**
**Current release: v0.255.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.**
+9 -9
View File
@@ -2,14 +2,14 @@
_Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL only when 80%+ of its Python functions have name-preserving counterparts in the Go/Rust implementation — directories without the functions are facades and count as NOT TOUCHED._
**Function-level completion: 46%** (1083/2366 runtime functions matched)
**Function-level completion: 47%** (1116/2369 runtime functions matched)
| Status | Modules | Lines | Meaning |
|---|---|---|---|
| FULL | 40 | 13895 | 80%+ of functions ported |
| PARTIAL | 98 | 46825 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 54 | 12187 | no name-preserving implementation |
| Total runtime | 192 | 72907 | |
| FULL | 42 | 14824 | 80%+ of functions ported |
| PARTIAL | 99 | 46362 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 51 | 11859 | no name-preserving implementation |
| Total runtime | 192 | 73045 | |
## NOT TOUCHED — the port queue, in priority order (largest first)
@@ -37,11 +37,9 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| canary_tui_drift | 170 | 2 | TEST-INFRA |
| canary_gitea_pipeline | 159 | 2 | TEST-INFRA |
| rune_graphic | 145 | 5 | TUI-LAST |
| project_registry | 139 | 11 | RUST |
| canary_rune_entry | 137 | 3 | TEST-INFRA |
| tui_backlog | 125 | 5 | TUI-LAST |
| state_checkpoint | 123 | 8 | unclassified |
| drift_resume | 118 | 3 | RUST |
| canary_conversation_state | 115 | 2 | TEST-INFRA |
| canary_query_memory | 111 | 2 | TEST-INFRA |
| tui_transport | 108 | 10 | TUI-LAST |
@@ -49,7 +47,6 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| message_widgets | 104 | 6 | TUI-LAST |
| inference_paths | 95 | 2 | unclassified |
| canary_schema_migration | 86 | 1 | TEST-INFRA |
| execution_trace | 71 | 4 | unclassified |
| pane_authority | 67 | 2 | unclassified |
| skill_compiler | 64 | 4 | unclassified |
| meaningful_events | 61 | 3 | unclassified |
@@ -80,7 +77,6 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| local_tool_runtime | 809 | 8/30 | _find_capability_executor, _extract_context_length, context_length, reset_context_length_cache, supports_tools, supports_thinking |
| construction_interface | 1193 | 6/27 | _to_stage_check, _guard_specs_from_obligations, t_layout, _obligations_projection, _obligation_scope_files, main |
| structural_store | 690 | 13/34 | _ss_core, _sorted, new_doc, _redact_secrets, save, canonical_digest |
| persona_gate | 724 | 7/26 | _now, _cc_pg_core, _py_lex, _lex_input, _py_overlap, _embed_raw |
| sleep_time | 984 | 8/26 | _ctx_budget, _ensure_dirs, db_connect, fetch_sessions, retain_to_store, reflect_local |
| provider_registry | 679 | 11/28 | _ctx_budget, _format_tools_openai, _execute_tool_calls, _execute_tool_calls_anthropic, get_registry, connect_provider |
| warm_router | 1518 | 19/36 | _ensure_use_history_table, record_outcome, record_neuron_fire, _py_base_level_from_uses, base_level_activation, _db |
@@ -160,6 +156,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| warm_neuron_store | 122 | 3/6 | _db, neuron_slugs, handle_hook |
| cognitive_budgeter | 221 | 1/3 | test_budgeter, to_dict |
| event_bus | 244 | 5/7 | _store_path, handle_hook |
| execution_trace | 108 | 3/5 | _kernel, _journal_path |
| experience_encoding | 113 | 1/3 | _ee_core, _py_evaluate |
| inference_trace | 204 | 4/6 | record_trace, read_traces |
| iteration | 261 | 7/9 | _kernel, _now |
@@ -171,6 +168,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| voice_injector | 138 | 3/5 | test_injection, to_dict |
| abstraction_ladder | 102 | 3/4 | _core |
| construction_depth | 172 | 2/3 | _core |
| drift_resume | 153 | 3/4 | _dr_core |
| recall_router | 165 | 2/3 | handle_hook |
## FULL — ported, parity-gated
@@ -178,6 +176,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| Module | Lines | Fns | Side |
|---|---|---|---|
| curator | 1838 | 43/43 | GO |
| persona_gate | 724 | 26/26 | GO |
| abstraction | 656 | 17/17 | GO |
| system_registry | 603 | 14/16 | RUST |
| construction_intent | 580 | 13/16 | RUST |
@@ -204,6 +203,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| construction_patterns_builtin | 266 | 1/1 | RUST |
| openai_compat_transport | 227 | 9/9 | GO |
| evidence_probe | 213 | 9/9 | GO |
| project_registry | 205 | 10/12 | RUST |
| forge_client | 203 | 18/18 | GO |
| behaviour_ledger | 195 | 11/13 | RUST |
| self_modify | 149 | 7/8 | unclassified |
+3 -3
View File
@@ -15,6 +15,7 @@
"drift_ledger",
"event_wait",
"evidence_probe",
"execution_trace",
"failure_recovery",
"forge",
"forge_client",
@@ -25,8 +26,10 @@
"model_routing",
"openai_compat_transport",
"outcome_ledger",
"persona_gate",
"programme_continuation",
"programme_gates",
"project_registry",
"provider_context",
"release_notes",
"route_risk_flags",
@@ -42,11 +45,9 @@
"worthiness"
],
"in_flight": {
"project_registry": "rune-wave-ba",
"state_checkpoint": "rune-wave-ba",
"drift_resume": "rune-wave-bb",
"inference_paths": "rune-wave-bb",
"execution_trace": "rune-wave-bc",
"pane_authority": "rune-wave-bc",
"skill_compiler": "rune-wave-bc",
"meaningful_events": "rune-wave-bc",
@@ -62,7 +63,6 @@
"context_broker": "rune-wave-bf",
"local_tool_runtime": "rune-wave-bg",
"structural_store": "rune-wave-bh",
"persona_gate": "rune-wave-bi",
"sleep_time": "rune-wave-bj",
"work_manager": "rune-wave-bk",
"growth_delta": "rune-wave-bl",
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "rune"
version = "0.252.0"
version = "0.255.0"
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
requires-python = ">=3.10"
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Live parity: pane_authority Rust kernel vs Python oracle (dichotomy
wave). Layers:
1. FROZEN — the 26 frozen-oracle cases replayed through the facade in
BOTH core modes (RUNE_PA_CORE=python floor vs auto kernel); exact
compare, inputs deep-copied.
2. FUZZ — 400 seeded random utterances/panes/tasks (seed 2479)
driven through both modes; exact compare.
"""
import copy
import json
import os
import random
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
sys.path.insert(0, str(REPO / "src" / "rune"))
import pane_authority as pa # noqa: E402
from rune import pa_loader # noqa: E402
if not pa_loader.available():
print("FATAL: Rust pane_authority core not built")
sys.exit(2)
TRUTH = json.loads(
(REPO / "src" / "rust" / "pane_authority" / "frozen_truth.json")
.read_text())
fails = []
counts = {"frozen": 0, "fuzz": 0}
def fail(layer, name, detail):
fails.append((layer, name, detail))
print(f"FAIL[{layer}] {name}: {detail}")
def drive(mode, fn, *args):
os.environ["RUNE_PA_CORE"] = mode
try:
return fn(*copy.deepcopy(args))
finally:
os.environ["RUNE_PA_CORE"] = "auto"
def frozen():
for c in TRUTH["classify_cases"]:
counts["frozen"] += 1
py = drive("python", pa.classify_utterance, c["args"]["text"])
rs = drive("auto", pa.classify_utterance, c["args"]["text"])
if py != rs:
fail("frozen", f"classify/{c['name']}", f"{py!r} != {rs!r}")
for c in TRUTH["apply_cases"]:
counts["frozen"] += 1
args = (c["args"]["pane"], c["args"]["task"], c["args"]["utterance"])
py = drive("python", pa.apply_to_task, *args)
rs = drive("auto", pa.apply_to_task, *args)
if py != rs:
fail("frozen", f"apply/{c['name']}", f"{py!r} != {rs!r}")
FRAGMENTS = [
"stop", "pause", "cancel", "stop the task", "pause everything",
"cancel the task", "change the task", "amend", "update the task",
"instead do", "actually do", "hello", "what about y?", "",
" STOP ", "Mixed Case AMEND here", "the task", "everything",
"instead", "actually", "stop", "\tpause\n", "cancel everything",
]
PANES = ["work", "chat", "review", "", None]
TASKS = [
{}, {"status": "running"}, {"status": "running", "directive": "do x"},
{"directive": "keep"}, {"status": "", "extra": [1, 2]},
]
def fuzz():
rng = random.Random(2479)
for i in range(400):
text = " ".join(rng.choice(FRAGMENTS)
for _ in range(rng.randint(0, 4)))
if rng.random() < 0.05:
text = None
pane = rng.choice(PANES)
task = rng.choice(TASKS)
py = drive("python", pa.classify_utterance, text)
rs = drive("auto", pa.classify_utterance, text)
counts["fuzz"] += 1
if py != rs:
fail("fuzz", f"classify-{i}", f"{text!r}: {py!r} != {rs!r}")
continue
py = drive("python", pa.apply_to_task, pane, task, text)
rs = drive("auto", pa.apply_to_task, pane, task, text)
counts["fuzz"] += 1
if py != rs:
fail("fuzz", f"apply-{i}", f"{pane!r}/{text!r}: {py!r} != {rs!r}")
frozen()
fuzz()
total = counts["frozen"] + counts["fuzz"]
print(f"parity: {total - len(fails)}/{total} "
f"(frozen {counts['frozen']}, fuzz {counts['fuzz']})")
sys.exit(1 if fails else 0)
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Close the #2479 dichotomy-wave persona_gate contract with SHA evidence.
Run on top of CURRENT master (a worktree store close never reaches master
by itself — build_basis/message_queue precedent)."""
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from rune.construction_interface import ConstructionInterface
CID = sys.argv[1] if len(sys.argv) > 1 else ""
PARITY_SHA = sys.argv[2] if len(sys.argv) > 2 else ""
PHASEB_SHA = sys.argv[3] if len(sys.argv) > 3 else ""
if not (CID and PARITY_SHA and PHASEB_SHA):
sys.exit("usage: close_contract_persona_gate.py <contract_id> <parity_sha> <phaseb_sha>")
iface = ConstructionInterface(builder="factory-gap-pg")
c = iface.close(CID, observed_evidence=[{
"kind": "port_evidence",
"detail": (
f"persona_gate GO gap-closing port — parity commit {PARITY_SHA}, "
f"Phase B commit {PHASEB_SHA}; audit fn coverage 7/26 → 26/26 "
"(19 audit-missing functions ported name-preserving); frozen-truth "
"battery 92/92 rows vs live oracle (scripts/gen_persona_gate_truth.py, "
"RUNE_COGNITION_CORE=python pinned, deep-copied inputs), live parity "
"31/31, go test -race green, gofmt/vet clean, Python seam tests 13 "
"passed."
),
}], scope_files=[
"src/rune/persona_gate.py",
"src/go/persona-gate/gaps.go",
"src/go/persona-gate/main.go",
"src/go/persona-gate/phaseb.go",
"src/go/persona-gate/truth_test.go",
"src/go/persona-gate/phaseb_test.go",
"src/go/persona-gate/truth.json",
"scripts/gen_persona_gate_truth.py",
"scripts/parity_persona_gate.py",
"scripts/open_contract_persona_gate.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", CID, "closed" if c.get("success") else "CLOSE_FAILED")
+66 -1
View File
@@ -14,7 +14,14 @@ LANG_="${3:-rust}"
REPO=/mnt/workdrive/rune
PASS=0; FAIL=0
check() { echo " [$1] $2"; if [ "$1" = "PASS" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo " FAILED: $2"; fi; }
check() {
echo " [$1] $2"
case "$1" in
PASS) PASS=$((PASS+1));;
FAIL) FAIL=$((FAIL+1)); echo " FAILED: $2";;
WARN) ;; # neutral: logged, counted neither way
esac
}
echo "=== verifying $SHA ($CRATE, $LANG_) ==="
@@ -67,5 +74,63 @@ else
check "WARN" "no module-specific Python tests (may use consumer tests)"
fi
# Layer 7: connection completeness — MECHANICAL portion (2026-10-13).
# What grep can prove, grep proves — coordinator LLM review now only
# judges what remains (Phase B genuineness, layer 6).
MOD=$(basename "$CRATE" | tr '-' '_') # crate dir -> module stem
STEM="${MOD%_loader}"; STEM="${STEM%-loader}"
LOADER="src/rune/${STEM}_loader.py"
FACADE="src/rune/${STEM}.py"
# 7a/7b: connection completeness. Language-dependent law:
# RUST — facade delegates through a ctypes *_loader.py (doc-envelope FFI).
# GO — Go kernels are services/binaries by verdict; the facade keeps
# pure-Python logic and the Go side is exercised by its own
# battery. Connection proof = Go binary builds + battery truth.
CRATE_DIR="$CRATE"
if [ "$LANG_" = "rust" ]; then
# 7a: loader exists — discovered from the facade's own import (loader
# naming uses abbreviated stems: subsystem_health -> sh_loader).
LOADER_REF=$(grep -oE "[a-z]+_loader" "$FACADE" 2>/dev/null | head -1)
if [ -n "$LOADER_REF" ] && [ -f "src/rune/${LOADER_REF}.py" ]; then
check "PASS" "7a loader exists: src/rune/${LOADER_REF}.py (per facade import)"
elif [ -f "$FACADE" ] && ! grep -qE "import.*_loader|from.*_loader" "$FACADE"; then
check "FAIL" "7a facade imports no loader"
else
check "FAIL" "7a loader referenced as $LOADER_REF but file missing"
fi
# 7b: facade delegation wired (core-selection seam)
if [ -f "$FACADE" ] && grep -qE "RUNE_[A-Z_]+_CORE|_loader" "$FACADE" 2>/dev/null; then
check "PASS" "7b facade has core-selection seam"
else
check "FAIL" "7b facade not wired to implementation"
fi
else
# GO: connection = go build succeeds (main package) + truth-replay tests exist
if (cd "$CRATE_DIR" && go build ./... >/dev/null 2>&1); then
check "PASS" "7a go build succeeds (service/binary connection)"
else
check "FAIL" "7a go build fails"
fi
if grep -qE 'RUNE_GO|exec\.Command|go run|os/exec' "$FACADE" 2>/dev/null; then
check "PASS" "7b facade reaches Go via exec"
else
check "WARN" "7b facade does not exec Go (may be service-dispatched)"
fi
fi
# 7c: battery test references the truth FILE (not self-generated truth)
if grep -rqE 'include_str!.*truth|frozen_.*truth|truth\.json' "$CRATE_DIR" \
--include='*.rs' --include='*.go' 2>/dev/null; then
check "PASS" "7c battery reads frozen truth"
else
check "FAIL" "7c battery does not reference a truth file"
fi
# 7d: CI gate exists for the crate (polyglot lint or module test in CI config)
if grep -rqE "$(basename $CRATE)" .gitea/workflows/ .github/workflows/ 2>/dev/null \
|| grep -rq 'polyglot_lint\|dichotomy' .gitea/workflows/ 2>/dev/null; then
check "PASS" "7d CI gate present"
else
check "WARN" "7d no explicit CI gate (repo-level lint may cover)"
fi
echo "=== result: $PASS PASS / $FAIL FAIL ==="
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""Freeze the dichotomy-wave truth corpus for src/rune/outcome_utility.py.
LAW (poisoned-truth): RUNE_OU_CORE=python is pinned at the top — truth is
generated by the PYTHON ORACLE ONLY, never by the kernel under test.
Every public function is exercised across ALL branches (helpful/ignored/
unknown × cap/floor boundaries; stale 0/positive/negative/clamping;
retrieval passthrough; record on new/existing/partial entries with
driven cap and floor saturation). Inputs are DEEP-COPIED before each
oracle call — the frozen corpus is byte-stable and carries no live
state. Output: src/rust/outcome_utility/src/frozen_outcome_utility_truth.json
"""
from __future__ import annotations
import copy
import json
import os
import sys
from pathlib import Path
os.environ["RUNE_OU_CORE"] = "python" # poisoned-truth law
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
import rune.outcome_utility as ou # noqa: E402
OUT = REPO / "src" / "rust" / "outcome_utility" / "src" / "frozen_outcome_utility_truth.json"
cases: list[dict] = []
def case(fn: str, name: str, args: dict):
"""Call the oracle on a DEEP COPY of the args; freeze the verdict."""
frozen_args = copy.deepcopy(args)
live_args = copy.deepcopy(args)
if fn == "apply_outcome":
expect = ou.apply_outcome(**live_args)
elif fn == "apply_stale":
expect = ou.apply_stale(**live_args)
elif fn == "retrieval_alone":
expect = ou.retrieval_alone(**live_args)
elif fn == "record_verified_outcome":
# store=None ≡ a fresh dict in the oracle; freeze the fresh dict so
# the mutation is captured (the FFI returns the table it built).
store_arg = live_args.get("store")
if store_arg is None:
store_arg = {}
summary = ou.record_verified_outcome(
live_args["entry_id"], live_args["outcome"], store_arg)
expect = {"summary": summary, "store": store_arg}
else: # pragma: no cover — generator typo guard
raise ValueError(fn)
cases.append({"fn": fn, "name": name, "args": frozen_args, "expect": expect})
# ── apply_outcome: all three branches × cap/floor boundaries ────────────────
for u in (0.0, 0.5, 0.95, 1.0, 1.0, -0.5, -1.0, 0.9499999, -1.0):
case("apply_outcome", f"helpful u={u!r}", {"utility": u, "outcome": "helpful"})
for u in (0.0, 0.5, -0.95, -1.0, -1.0, 1.0, -0.9499999):
case("apply_outcome", f"ignored u={u!r}", {"utility": u, "outcome": "ignored"})
for o in ("unknown", "", "HELPFUL", "helpful ", "partial", "verified"):
case("apply_outcome", f"unknown o={o!r}", {"utility": 0.42, "outcome": o})
# ── apply_stale: zero/negative/positive turns, clamp boundaries ─────────────
for u, t in ((0.5, 0), (0.5, 1), (0.5, 2), (0.5, 5), (1.0, 10), (-0.5, 3),
(-0.5, -3), (0.0, 60), (0.0, -60), (1.0, 200), (-1.0, 0),
(0.99, 100), (-0.02, 1000), (0.42, 1000000)):
case("apply_stale", f"stale u={u!r} t={t}", {"utility": u, "stale_turns": t})
# ── retrieval_alone: the anti-salience passthrough ──────────────────────────
for u in (0.0, 1.0, -1.0, 0.42, -0.77, 12345.678):
case("retrieval_alone", f"retrieval u={u!r}", {"utility": u})
# ── record_verified_outcome: new / existing / partial entries ───────────────
case("record_verified_outcome", "new entry default store",
{"entry_id": 1, "outcome": "helpful", "store": None})
case("record_verified_outcome", "new entry empty store",
{"entry_id": 7, "outcome": "ignored", "store": {}})
case("record_verified_outcome", "existing helpful",
{"entry_id": 1, "outcome": "helpful",
"store": {1: {"utility": 0.5, "confidence": 0.8}}})
case("record_verified_outcome", "existing ignored",
{"entry_id": 2, "outcome": "ignored",
"store": {2: {"utility": 0.5, "confidence": 0.8}}})
case("record_verified_outcome", "existing unknown outcome",
{"entry_id": 3, "outcome": "mystery",
"store": {3: {"utility": 0.25, "confidence": 0.9}}})
case("record_verified_outcome", "existing no confidence key",
{"entry_id": 4, "outcome": "helpful",
"store": {4: {"utility": 0.3}}})
case("record_verified_outcome", "existing no utility key",
{"entry_id": 5, "outcome": "ignored",
"store": {5: {"confidence": 0.1}}})
case("record_verified_outcome", "entry id zero",
{"entry_id": 0, "outcome": "helpful", "store": {}})
case("record_verified_outcome", "negative entry id",
{"entry_id": -3, "outcome": "ignored", "store": {}})
case("record_verified_outcome", "large entry id",
{"entry_id": 2**62, "outcome": "helpful", "store": {}})
# driven saturation: repeated outcomes walk utility to cap and floor
for i, outcome in enumerate(["helpful"] * 20):
case("record_verified_outcome", f"cap walk {i}",
{"entry_id": 10, "outcome": outcome,
"store": {10: {"utility": min(1.0, 0.1 * i), "confidence": 0.5}}})
for i, outcome in enumerate(["ignored"] * 25):
case("record_verified_outcome", f"floor walk {i}",
{"entry_id": 11, "outcome": outcome,
"store": {11: {"utility": max(-1.0, -0.05 * i), "confidence": 0.5}}})
doc = {
"module": "outcome_utility",
"frozen_ts": "2026-10-13T00:00:00Z",
"generator": "scripts/gen_outcome_utility_truth.py",
"pins": {"RUNE_OU_CORE": "python"},
"cases": cases,
}
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(json.dumps(doc, indent=1) + "\n")
print(f"frozen {len(cases)} cases -> {OUT}")
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Freeze the pane_authority oracle truth (dichotomy wave, #2479).
Generator law: RUNE_PA_CORE=python pinned, every input deep-copied, every
public function exercised across ALL branches:
* classify_utterance — control first-word (stop/pause/cancel), control
phrases mid-utterance (incl. the verb-"stop" law for phrase matches),
every amendment keyword, discussion, empty/None text, case+space
* apply_to_task — control from either pane (status mapping law),
CHAT amendment (needs_confirm=True, not applied), CHAT discussion,
WORK amendment (directive set, applied), WORK discussion
"""
import copy
import json
import os
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
sys.path.insert(0, str(REPO / "src" / "rune"))
os.environ["RUNE_PA_CORE"] = "python"
import pane_authority as pa # noqa: E402
cases_classify = []
cases_apply = []
def case_classify(name, text):
text = copy.deepcopy(text)
cases_classify.append({
"name": name,
"args": {"text": text},
"expect": pa.classify_utterance(copy.deepcopy(text)),
})
def case_apply(name, pane, task, utterance):
pane = copy.deepcopy(pane)
task = copy.deepcopy(task)
utterance = copy.deepcopy(utterance)
cases_apply.append({
"name": name,
"args": {"pane": pane, "task": task, "utterance": utterance},
"expect": pa.apply_to_task(copy.deepcopy(pane),
copy.deepcopy(task),
copy.deepcopy(utterance)),
})
# ── classify_utterance: every branch ────────────────────────────────────────
case_classify("control-first-stop", "stop everything")
case_classify("control-first-pause", "pause the pipeline")
case_classify("control-first-cancel", "cancel that")
case_classify("control-phrase-stop-the-task", "please stop the task now")
case_classify("control-phrase-cancel-everything", "actually, cancel everything for me")
case_classify("control-phrase-pause-everything", "we should pause everything here")
case_classify("control-case-and-spaces", " STOP ")
case_classify("amendment-change-the-task", "change the task to do w")
case_classify("amendment-amend", "amend the plan")
case_classify("amendment-update-the-task", "can you update the task status?")
case_classify("amendment-instead-do", "instead do the other thing")
case_classify("amendment-actually-do", "actually do z instead")
case_classify("discussion", "what if we tried y?")
case_classify("empty-text", "")
case_classify("none-text", None)
case_classify("whitespace-only", " ")
# ── apply_to_task: every branch ─────────────────────────────────────────────
TASK = {"status": "running", "directive": "do x"}
case_apply("work-control-stop", "work", TASK, "stop everything")
case_apply("chat-control-cancel", "chat", TASK, "cancel that")
case_apply("work-control-pause", "work", {"status": "running"}, "pause the run")
case_apply("chat-amendment-promotes-visibly", "chat", {"status": "running"},
"actually do z instead")
case_apply("chat-discussion-never-mutates", "chat", TASK, "what if we tried y?")
case_apply("work-amendment-applies-directly", "work", {"status": "running"},
"change the task to do w")
case_apply("work-discussion", "work", TASK, "thoughts on the approach?")
case_apply("unknown-pane-discussion", "review", TASK, "hmm")
case_apply("empty-task-chat-discussion", "chat", {}, "hello there")
case_apply("control-without-status-key", "chat", {}, "stop")
truth = {
"module": "pane_authority",
"core_pin": "RUNE_PA_CORE=python",
"classify_cases": cases_classify,
"apply_cases": cases_apply,
}
out = REPO / "src" / "rust" / "pane_authority" / "frozen_truth.json"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(truth, indent=1, sort_keys=True) + "\n",
encoding="utf-8")
n = len(cases_classify) + len(cases_apply)
print(f"frozen {n} cases -> {out}")
+1 -1
View File
@@ -6,7 +6,7 @@ This is the main entry point for the Rune program.
import json
from pathlib import Path
__version__ = "0.252.0"
__version__ = "0.255.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
+1 -1
View File
@@ -233,7 +233,7 @@ def main() -> int:
)
check(
"Rune minor version advertises provisioning and restore verification",
'__version__ = "0.252.0"' in rune_version,
'__version__ = "0.255.0"' in rune_version,
failures,
)
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust outcome_utility core (dichotomy wave,
batch-h). Mirrors broker_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* ou_apply_outcome({utility, outcome}) -> number
* ou_apply_stale({utility, stale_turns}) -> number
* ou_retrieval_alone({utility}) -> number
* ou_record_verified_outcome({entry_id, outcome, store})
-> {"summary": {...}, "store": {...}}
Free: ou_free_string (same crate). Override the .so with OU_RUST_LIB (a
FILE path). RUNE_OU_CORE=python pins the Python oracle — the pin is
honored HERE so every consumer gets it (strangler law: the fallback is
load-bearing until deletion is proven).
"""
from __future__ import annotations
import ctypes
import json
import os
import sys
from pathlib import Path
_REPO = Path(__file__).resolve().parents[2]
_CRATE_DIR = _REPO / "src" / "rust" / "outcome_utility"
_LIB_PATH = Path(os.environ.get("OU_RUST_LIB", _CRATE_DIR / "target" / "release" / "liboutcome_utility.so"))
_lib = None
class OutcomeUtilityUnavailable(FileNotFoundError):
"""The Rust utility core is not built. Callers fall back to Python."""
def available() -> bool:
if os.environ.get("RUNE_OU_CORE") == "python":
return False
return _LIB_PATH.is_file()
def core():
"""The live Rust core probe — the loader MODULE itself (never the raw
CDLL), or None when the Python pin is set or the crate is not built."""
if not available():
return None
try:
_load()
except (OSError, OutcomeUtilityUnavailable):
return None
return sys.modules[__name__]
def _load() -> ctypes.CDLL:
global _lib
if _lib is not None:
return _lib
if not _LIB_PATH.is_file():
raise OutcomeUtilityUnavailable(
f"Rust outcome_utility core not built: {_LIB_PATH} missing — run "
f"`cargo build --release` in {_CRATE_DIR}"
)
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in ("ou_apply_outcome", "ou_apply_stale", "ou_retrieval_alone",
"ou_record_verified_outcome"):
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p
lib.ou_free_string.argtypes = [ctypes.c_void_p]
lib.ou_free_string.restype = None
_lib = lib
return lib
def _call(sym: str, payload: dict):
lib = _load()
data = json.dumps(payload).encode()
buf = ctypes.create_string_buffer(data)
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
if not ptr:
raise RuntimeError(f"{sym} returned null")
out = ctypes.string_at(ptr).decode()
lib.ou_free_string(ptr)
doc = json.loads(out)
if not doc.get("ok"):
raise RuntimeError(doc.get("err", f"{sym} failed"))
return doc["value"]
def apply_outcome(utility: float, outcome: str) -> float:
return _call("ou_apply_outcome", {"utility": utility, "outcome": outcome})
def apply_stale(utility: float, stale_turns: int) -> float:
return _call("ou_apply_stale", {"utility": utility, "stale_turns": stale_turns})
def retrieval_alone(utility: float) -> float:
return _call("ou_retrieval_alone", {"utility": utility})
def record_verified_outcome(entry_id: int, outcome: str, store: dict) -> dict:
return _call("ou_record_verified_outcome", {
"entry_id": entry_id, "outcome": outcome, "store": store,
})
+43 -2
View File
@@ -14,10 +14,25 @@ Utility rule (deterministic):
Confidence (epistemic) is tracked separately and never mixed into the
utility number.
Dichotomy wave (batch-h): the utility DECISION MATH is served by the
Rust `outcome_utility` cdylib through ou_loader.py when built; the
utility TABLE stays Python (storage stays in the caller's language —
the injected `store` dict is never shipped over the FFI boundary).
RUNE_OU_CORE=python pins the Python oracle below — the pin is honored
inside ou_loader so every consumer gets it.
"""
from __future__ import annotations
try:
from . import ou_loader
except ImportError: # direct script execution
try:
import ou_loader # type: ignore[no-redef]
except ImportError: # noqa: S110 — no loader on path → Python oracle below
ou_loader = None # type: ignore[assignment]
UTILITY_CAP = 1.0
UTILITY_FLOOR = -1.0
HELPFUL_DELTA = 0.1
@@ -26,8 +41,23 @@ STALE_DELTA = -0.02
DECAY_PER_TURN = 0.99
def _core():
"""The Rust outcome-utility probe (strangler law). RUNE_OU_CORE=
python pins the Python oracle — honored inside ou_loader. Returns
the loader MODULE surface (never the raw CDLL)."""
if ou_loader is None:
return None
try:
return ou_loader if ou_loader.core() is not None else None
except Exception: # noqa: BLE001 — kernel failure → oracle below
return None
def apply_outcome(utility: float, outcome: str) -> float:
"""Apply a verified outcome to a utility value."""
c = _core()
if c is not None:
return c.apply_outcome(utility, outcome)
if outcome == "helpful":
return min(UTILITY_CAP, utility + HELPFUL_DELTA)
if outcome == "ignored":
@@ -37,6 +67,9 @@ def apply_outcome(utility: float, outcome: str) -> float:
def apply_stale(utility: float, stale_turns: int) -> float:
"""Bounded decay for stale activation."""
c = _core()
if c is not None:
return c.apply_stale(utility, stale_turns)
decayed = utility * (DECAY_PER_TURN ** max(0, stale_turns))
decayed += STALE_DELTA * max(0, stale_turns)
return max(UTILITY_FLOOR, min(UTILITY_CAP, decayed))
@@ -44,16 +77,24 @@ def apply_stale(utility: float, stale_turns: int) -> float:
def retrieval_alone(utility: float) -> float:
"""Mere retrieval never increases utility (the anti-salience rule)."""
c = _core()
if c is not None:
return c.retrieval_alone(utility)
return utility
def record_verified_outcome(entry_id: int, outcome: str,
store: dict | None = None) -> dict:
"""Record a verified outcome for an entry's utility. `store` is the
injected utility table (production passes the DB-backed one)."""
injected utility table (production passes the DB-backed one). The
table stays Python; the outcome math delegates to the Rust core."""
store = store if store is not None else {}
c = _core()
current = store.get(entry_id, {"utility": 0.0, "confidence": 0.5})
current["utility"] = apply_outcome(current.get("utility", 0.0), outcome)
if c is not None:
current["utility"] = c.apply_outcome(current.get("utility", 0.0), outcome)
else:
current["utility"] = apply_outcome(current.get("utility", 0.0), outcome)
store[entry_id] = current
return {"entry_id": entry_id, "utility": current["utility"],
"confidence": current.get("confidence", 0.5)}
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust pane_authority core (dichotomy wave, #2479
tranche). Mirrors broker_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* pa_classify_utterance({text}) -> {kind, verb?, priority}
* pa_apply_to_task({pane, task, utterance}) -> {task, amendment, applied}
Free: capability_scoring's cs_free_string (shared process). Override the
.so with PA_RUST_LIB (a FILE path).
"""
from __future__ import annotations
import ctypes
import json
import os
from pathlib import Path
_REPO = Path(__file__).resolve().parents[2]
_CRATE_DIR = _REPO / "src" / "rust" / "pane_authority"
_LIB_PATH = Path(
os.environ.get("PA_RUST_LIB", _CRATE_DIR / "target" / "release" / "libpane_authority.so")
)
_ERR_TYPES = {
"ValueError": ValueError,
"KeyError": KeyError,
"AttributeError": AttributeError,
"TypeError": TypeError,
}
_lib = None
class PaneAuthorityCoreUnavailable(FileNotFoundError):
"""The Rust pane-authority core is not built. Callers fall back to
Python."""
def available() -> bool:
return _LIB_PATH.is_file()
def _load() -> ctypes.CDLL:
global _lib
if _lib is not None:
return _lib
if not _LIB_PATH.is_file():
raise PaneAuthorityCoreUnavailable(str(_LIB_PATH))
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in ("pa_classify_utterance", "pa_apply_to_task"):
getattr(lib, sym).argtypes = [ctypes.c_void_p]
getattr(lib, sym).restype = ctypes.c_void_p
try:
lib.cs_free_string
except AttributeError:
scoring = ctypes.CDLL(str(
_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so"
))
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
lib._free = scoring.cs_free_string
_lib = lib
return lib
def _call(sym: str, payload: dict):
lib = _load()
data = json.dumps(payload).encode()
buf = ctypes.create_string_buffer(data)
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
if not ptr:
raise RuntimeError(f"{sym} returned null")
out = ctypes.string_at(ptr).decode()
free = getattr(lib, "_free", None)
if free is not None:
free(ptr)
doc = json.loads(out)
if not doc.get("ok"):
etype = doc.get("type", "RuntimeError")
raise _ERR_TYPES.get(etype, RuntimeError)(doc.get("err", f"{sym} failed"))
return doc["value"]
def classify_utterance(text: str | None) -> dict:
return _call("pa_classify_utterance", {"text": text})
def apply_to_task(pane: str, task: dict, utterance: str | None) -> dict:
return _call("pa_apply_to_task", {"pane": pane, "task": task,
"utterance": utterance})
+31
View File
@@ -15,6 +15,8 @@ Authority rules:
from __future__ import annotations
import os
WORK = "work"
CHAT = "chat"
@@ -25,8 +27,31 @@ AUTHORITY = {WORK: 2, CHAT: 1}
CONTROL = {"stop", "pause", "cancel"}
def _kernel():
"""The Rust pane-authority core (pa_loader) when RUNE_PA_CORE
allows it, else None — the classification and apply laws delegate
there first and fall back to the Python body below (same
semantics). Pure deterministic law — the whole module crosses FFI."""
if os.environ.get("RUNE_PA_CORE", "auto") == "python":
return None
try:
from rune import pa_loader
if pa_loader.available():
return pa_loader
except Exception: # noqa: BLE001 — kernel failure → Python floor
return None
return None
def classify_utterance(text: str) -> dict:
"""Classify an utterance: control verb? amendment? discussion?"""
k = _kernel()
if k is not None:
try:
return k.classify_utterance(text)
except Exception: # noqa: BLE001 — kernel floor
pass
low = (text or "").lower().strip()
first = low.split()[0] if low.split() else ""
if first in CONTROL or any(f"{c} the task" in low or f"{c} everything" in low for c in CONTROL):
@@ -42,6 +67,12 @@ def apply_to_task(pane: str, task: dict, utterance: str) -> dict:
"""Apply an utterance from a pane to a task. Returns
{task, amendment, applied}. CHAT discussion never mutates; CHAT
amendment promotes visibly; WORK applies directly; control preempts."""
k = _kernel()
if k is not None:
try:
return k.apply_to_task(pane, task, utterance)
except Exception: # noqa: BLE001 — kernel floor
pass
cls = classify_utterance(utterance)
task = dict(task)
if cls["kind"] == "control":
+2 -2
View File
@@ -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.252.0
Version: v0.255.0
Features: 41 (24 original + 17 new native commands)
Usage:
@@ -2037,7 +2037,7 @@ class RuneApp(App):
# Show version info
self.chat_channel.add_message(
"system",
f"[dim]Rune v0.252.0 | Textual TUI | {len(loaded)} native modules[/dim]",
f"[dim]Rune v0.255.0 | Textual TUI | {len(loaded)} native modules[/dim]",
)
# Auto-show night mode suggestion if evening/night hours
+135
View File
@@ -0,0 +1,135 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "indexmap"
version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "outcome_utility"
version = "0.1.0"
dependencies = [
"serde_json",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
dependencies = [
"foldhash",
"indexmap",
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "outcome_utility"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["rlib", "cdylib", "staticlib"]
[dependencies]
serde_json = { version = "1", features = ["preserve_order", "float_roundtrip"] }
+76
View File
@@ -0,0 +1,76 @@
//! Frozen-truth battery — the frozen_outcome_utility_truth.json corpus
//! (generated against the LIVE Python oracle with RUNE_OU_CORE=python
//! pinned) replayed VERBATIM through the Rust core. Bit-exact float
//! comparison; the corpus is embedded at compile time.
use serde_json::Value;
const TRUTH: &str = include_str!("frozen_outcome_utility_truth.json");
fn f(d: &Value, k: &str) -> f64 {
d.get(k).expect(k).as_f64().expect(k)
}
#[test]
fn frozen_truth_replays_verbatim() {
let doc: Value = serde_json::from_str(TRUTH).expect("frozen truth parses");
assert_eq!(doc["module"], "outcome_utility");
assert_eq!(doc["pins"]["RUNE_OU_CORE"], "python");
let cases = doc["cases"].as_array().expect("cases array");
assert!(!cases.is_empty(), "corpus must not be empty");
let mut replayed = 0usize;
for c in cases {
let name = c["name"].as_str().unwrap_or("?").to_string();
let args = &c["args"];
let expect = &c["expect"];
match c["fn"].as_str().expect("fn tag") {
"apply_outcome" => {
let got = crate::apply_outcome(f(args, "utility"), args["outcome"].as_str().unwrap());
assert_eq!(Some(got), expect.as_f64(), "case {name}");
}
"apply_stale" => {
let got = crate::apply_stale(
f(args, "utility"),
args["stale_turns"].as_i64().expect("stale_turns"),
);
assert_eq!(Some(got), expect.as_f64(), "case {name}");
}
"retrieval_alone" => {
let got = crate::retrieval_alone(f(args, "utility"));
assert_eq!(Some(got), expect.as_f64(), "case {name}");
}
"record_verified_outcome" => {
let mut store = match args.get("store") {
None | Some(Value::Null) => serde_json::Map::new(),
Some(Value::Object(m)) => m.clone(),
Some(_) => panic!("case {name}: store must be object/null"),
};
let got = crate::record_verified_outcome(
args["entry_id"].as_i64().expect("entry_id"),
args["outcome"].as_str().expect("outcome"),
&mut store,
);
let want_summary = &expect["summary"];
assert_eq!(got["entry_id"], want_summary["entry_id"], "case {name}");
assert_eq!(
got["utility"].as_f64(),
want_summary["utility"].as_f64(),
"case {name}"
);
assert_eq!(
got["confidence"].as_f64(),
want_summary["confidence"].as_f64(),
"case {name}"
);
assert_eq!(
Value::Object(store.clone()),
expect["store"],
"case {name}: store mutation diverged"
);
}
other => panic!("unknown frozen fn {other}"),
}
replayed += 1;
}
assert_eq!(replayed, cases.len());
}
File diff suppressed because it is too large. Load diff
+263
View File
@@ -0,0 +1,263 @@
//! outcome_utility — #35: outcome-learned utility (not retrieval
//! reinforcement). PARITY port of src/rune/outcome_utility.py
//! (dichotomy wave, batch-h; Rust verdict per
//! docs/language-dichotomy-ledger.md Law 4 / W6: deterministic
//! scoring/clamping compute).
//!
//! A memory gains value ONLY after a VERIFIED helpful outcome; ignored
//! outcomes earn NEGATIVE utility; stale activation decays bounded;
//! mere retrieval NEVER changes utility. Epistemic confidence stays
//! separate from attentional utility.
//!
//! FFI contract (doc-envelope law — every symbol takes one null-terminated
//! JSON payload and returns one null-terminated JSON doc-envelope
//! {"ok": true, "value": ...} | {"ok": false, "err": "..."}):
//! * ou_apply_outcome({utility, outcome}) -> number
//! * ou_apply_stale({utility, stale_turns}) -> number
//! * ou_retrieval_alone({utility}) -> number
//! * ou_record_verified_outcome({entry_id, outcome, store})
//! -> {"summary": {...}, "store": {...}}
//! * ou_free_string(ptr)
use serde_json::{json, Map, Value};
pub const UTILITY_CAP: f64 = 1.0;
pub const UTILITY_FLOOR: f64 = -1.0;
pub const HELPFUL_DELTA: f64 = 0.1;
pub const IGNORED_DELTA: f64 = -0.05;
pub const STALE_DELTA: f64 = -0.02;
pub const DECAY_PER_TURN: f64 = 0.99;
/// A verified outcome, classified once and matched exhaustively — the
/// unknown bucket can never silently grow a new arm.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Outcome {
Helpful,
Ignored,
Unknown,
}
impl From<&str> for Outcome {
fn from(s: &str) -> Self {
match s {
"helpful" => Outcome::Helpful,
"ignored" => Outcome::Ignored,
_ => Outcome::Unknown,
}
}
}
fn clamp_utility(v: f64) -> f64 {
UTILITY_FLOOR.max(UTILITY_CAP.min(v))
}
/// Apply a verified outcome to a utility value.
pub fn apply_outcome(utility: f64, outcome: &str) -> f64 {
match Outcome::from(outcome) {
Outcome::Helpful => UTILITY_CAP.min(utility + HELPFUL_DELTA),
Outcome::Ignored => UTILITY_FLOOR.max(utility + IGNORED_DELTA),
Outcome::Unknown => utility, // no change
}
}
/// Bounded decay for stale activation.
pub fn apply_stale(utility: f64, stale_turns: i64) -> f64 {
let turns = stale_turns.max(0) as f64;
// CPython float ** int routes through C pow(); powf matches bit-for-bit.
let decayed = utility * DECAY_PER_TURN.powf(turns) + STALE_DELTA * turns;
clamp_utility(decayed)
}
/// Mere retrieval never increases utility (the anti-salience rule).
pub fn retrieval_alone(utility: f64) -> f64 {
utility
}
/// Record a verified outcome for an entry's utility. `store` is the
/// injected utility table keyed by the entry id's decimal form.
/// Byte-parity notes vs the Python oracle:
/// * a NEW entry is seeded {"utility": 0.0, "confidence": 0.5};
/// * an EXISTING entry keeps exactly the keys it had (a missing
/// "confidence" reports 0.5 in the summary but is NOT written back);
/// * "utility" defaults to 0.0 when absent.
pub fn record_verified_outcome(
entry_id: i64,
outcome: &str,
store: &mut Map<String, Value>,
) -> Value {
let key = entry_id.to_string();
let current = if let Some(v) = store.get_mut(&key) {
v
} else {
store.insert(key.clone(), json!({"utility": 0.0, "confidence": 0.5}));
store.get_mut(&key).unwrap()
};
let util = current.get("utility").and_then(Value::as_f64).unwrap_or(0.0);
let new_util = apply_outcome(util, outcome);
current["utility"] = json!(new_util);
let confidence = store[&key]
.get("confidence")
.and_then(Value::as_f64)
.unwrap_or(0.5);
json!({
"entry_id": entry_id,
"utility": new_util,
"confidence": confidence,
})
}
// ── FFI (doc-envelope) ──────────────────────────────────────────────────────
fn parse(arg: *const i8) -> Result<Value, String> {
if arg.is_null() {
return Err("null payload".into());
}
let cstr = unsafe { std::ffi::CStr::from_ptr(arg) };
let bytes = cstr.to_bytes();
serde_json::from_slice(bytes).map_err(|e| format!("bad payload: {e}"))
}
fn emit(value: Result<Value, String>) -> *mut i8 {
let doc = match value {
Ok(v) => json!({"ok": true, "value": v}),
Err(e) => json!({"ok": false, "err": e}),
};
let s = std::ffi::CString::new(doc.to_string()).unwrap();
s.into_raw()
}
fn num(doc: &Map<String, Value>, key: &str) -> Result<f64, String> {
doc.get(key)
.and_then(Value::as_f64)
.ok_or_else(|| format!("missing/invalid '{key}'"))
}
/// # Safety
/// The payload must be a valid null-terminated UTF-8 JSON string.
#[no_mangle]
pub unsafe extern "C" fn ou_apply_outcome(arg: *const i8) -> *mut i8 {
let out = parse(arg).and_then(|d| {
let o = d.as_object().ok_or("payload must be an object")?;
let u = num(o, "utility")?;
let outcome = o
.get("outcome")
.and_then(Value::as_str)
.ok_or("missing/invalid 'outcome'")?;
Ok(json!(apply_outcome(u, outcome)))
});
emit(out)
}
/// # Safety
/// The payload must be a valid null-terminated UTF-8 JSON string.
#[no_mangle]
pub unsafe extern "C" fn ou_apply_stale(arg: *const i8) -> *mut i8 {
let out = parse(arg).and_then(|d| {
let o = d.as_object().ok_or("payload must be an object")?;
let u = num(o, "utility")?;
let t = o
.get("stale_turns")
.and_then(Value::as_i64)
.ok_or("missing/invalid 'stale_turns'")?;
Ok(json!(apply_stale(u, t)))
});
emit(out)
}
/// # Safety
/// The payload must be a valid null-terminated UTF-8 JSON string.
#[no_mangle]
pub unsafe extern "C" fn ou_retrieval_alone(arg: *const i8) -> *mut i8 {
let out = parse(arg).and_then(|d| {
let o = d.as_object().ok_or("payload must be an object")?;
let u = num(o, "utility")?;
Ok(json!(retrieval_alone(u)))
});
emit(out)
}
/// # Safety
/// The payload must be a valid null-terminated UTF-8 JSON string.
#[no_mangle]
pub unsafe extern "C" fn ou_record_verified_outcome(arg: *const i8) -> *mut i8 {
let out = parse(arg).and_then(|d| {
let o = d.as_object().ok_or("payload must be an object")?;
let id = o
.get("entry_id")
.and_then(Value::as_i64)
.ok_or("missing/invalid 'entry_id'")?;
let outcome = o
.get("outcome")
.and_then(Value::as_str)
.ok_or("missing/invalid 'outcome'")?;
let mut store = match o.get("store") {
None | Some(Value::Null) => Map::new(),
Some(Value::Object(m)) => m.clone(),
Some(_) => return Err("'store' must be an object".into()),
};
let summary = record_verified_outcome(id, outcome, &mut store);
Ok(json!({
"summary": summary,
"store": Value::Object(store.clone()),
}))
});
emit(out)
}
/// # Safety
/// `ptr` must have been returned by one of the ou_* symbols above.
#[no_mangle]
pub unsafe extern "C" fn ou_free_string(ptr: *mut i8) {
if !ptr.is_null() {
drop(std::ffi::CString::from_raw(ptr));
}
}
#[cfg(test)]
mod battery;
#[cfg(test)]
mod phaseb_units {
use super::*;
#[test]
fn outcome_classification_is_exhaustive() {
assert_eq!(Outcome::from("helpful"), Outcome::Helpful);
assert_eq!(Outcome::from("ignored"), Outcome::Ignored);
for s in ["", "unknown", "HELPFUL", "partial", "verified"] {
assert_eq!(Outcome::from(s), Outcome::Unknown);
}
}
#[test]
fn helpful_never_exceeds_cap() {
let mut u = 0.0f64;
for _ in 0..1000 {
u = apply_outcome(u, "helpful");
}
assert_eq!(u, UTILITY_CAP);
}
#[test]
fn ignored_never_below_floor() {
let mut u = 0.0f64;
for _ in 0..1000 {
u = apply_outcome(u, "ignored");
}
assert_eq!(u, UTILITY_FLOOR);
}
#[test]
fn stale_decay_is_bounded_both_ends() {
assert_eq!(apply_stale(1.0, 0), 1.0);
assert_eq!(apply_stale(0.0, 1_000_000), UTILITY_FLOOR);
assert_eq!(apply_stale(0.0, -7), 0.0); // negative turns clamp to zero
}
#[test]
fn retrieval_is_identity() {
for u in [0.0, 1.0, -1.0, 0.42, -0.77] {
assert_eq!(retrieval_alone(u), u);
}
}
}
+14
View File
@@ -0,0 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "evidence_provenance"
version = "0.1.0"
[[package]]
name = "pane_authority"
version = "0.1.0"
dependencies = [
"evidence_provenance",
]
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "pane_authority"
version = "0.1.0"
edition = "2021"
description = "FULL parity port of src/rune/pane_authority.py (dichotomy wave, #2479 tranche)"
[lib]
crate-type = ["rlib", "cdylib", "staticlib"]
[dependencies]
evidence_provenance = { path = "../evidence_provenance" }
+376
View File
@@ -0,0 +1,376 @@
{
"apply_cases": [
{
"args": {
"pane": "work",
"task": {
"directive": "do x",
"status": "running"
},
"utterance": "stop everything"
},
"expect": {
"amendment": {
"from_pane": "work",
"kind": "control",
"verb": "stop"
},
"applied": true,
"task": {
"directive": "do x",
"status": "stopped"
}
},
"name": "work-control-stop"
},
{
"args": {
"pane": "chat",
"task": {
"directive": "do x",
"status": "running"
},
"utterance": "cancel that"
},
"expect": {
"amendment": {
"from_pane": "chat",
"kind": "control",
"verb": "cancel"
},
"applied": true,
"task": {
"directive": "do x",
"status": "cancelped"
}
},
"name": "chat-control-cancel"
},
{
"args": {
"pane": "work",
"task": {
"status": "running"
},
"utterance": "pause the run"
},
"expect": {
"amendment": {
"from_pane": "work",
"kind": "control",
"verb": "pause"
},
"applied": true,
"task": {
"status": "paused"
}
},
"name": "work-control-pause"
},
{
"args": {
"pane": "chat",
"task": {
"status": "running"
},
"utterance": "actually do z instead"
},
"expect": {
"amendment": {
"from_pane": "chat",
"kind": "amendment",
"needs_confirm": true,
"text": "actually do z instead"
},
"applied": false,
"task": {
"status": "running"
}
},
"name": "chat-amendment-promotes-visibly"
},
{
"args": {
"pane": "chat",
"task": {
"directive": "do x",
"status": "running"
},
"utterance": "what if we tried y?"
},
"expect": {
"amendment": null,
"applied": false,
"task": {
"directive": "do x",
"status": "running"
}
},
"name": "chat-discussion-never-mutates"
},
{
"args": {
"pane": "work",
"task": {
"status": "running"
},
"utterance": "change the task to do w"
},
"expect": {
"amendment": {
"from_pane": "work",
"kind": "amendment",
"needs_confirm": false,
"text": "change the task to do w"
},
"applied": true,
"task": {
"directive": "change the task to do w",
"status": "running"
}
},
"name": "work-amendment-applies-directly"
},
{
"args": {
"pane": "work",
"task": {
"directive": "do x",
"status": "running"
},
"utterance": "thoughts on the approach?"
},
"expect": {
"amendment": null,
"applied": false,
"task": {
"directive": "do x",
"status": "running"
}
},
"name": "work-discussion"
},
{
"args": {
"pane": "review",
"task": {
"directive": "do x",
"status": "running"
},
"utterance": "hmm"
},
"expect": {
"amendment": null,
"applied": false,
"task": {
"directive": "do x",
"status": "running"
}
},
"name": "unknown-pane-discussion"
},
{
"args": {
"pane": "chat",
"task": {},
"utterance": "hello there"
},
"expect": {
"amendment": null,
"applied": false,
"task": {}
},
"name": "empty-task-chat-discussion"
},
{
"args": {
"pane": "chat",
"task": {},
"utterance": "stop"
},
"expect": {
"amendment": {
"from_pane": "chat",
"kind": "control",
"verb": "stop"
},
"applied": true,
"task": {
"status": "stopped"
}
},
"name": "control-without-status-key"
}
],
"classify_cases": [
{
"args": {
"text": "stop everything"
},
"expect": {
"kind": "control",
"priority": 3,
"verb": "stop"
},
"name": "control-first-stop"
},
{
"args": {
"text": "pause the pipeline"
},
"expect": {
"kind": "control",
"priority": 3,
"verb": "pause"
},
"name": "control-first-pause"
},
{
"args": {
"text": "cancel that"
},
"expect": {
"kind": "control",
"priority": 3,
"verb": "cancel"
},
"name": "control-first-cancel"
},
{
"args": {
"text": "please stop the task now"
},
"expect": {
"kind": "control",
"priority": 3,
"verb": "stop"
},
"name": "control-phrase-stop-the-task"
},
{
"args": {
"text": "actually, cancel everything for me"
},
"expect": {
"kind": "control",
"priority": 3,
"verb": "stop"
},
"name": "control-phrase-cancel-everything"
},
{
"args": {
"text": "we should pause everything here"
},
"expect": {
"kind": "control",
"priority": 3,
"verb": "stop"
},
"name": "control-phrase-pause-everything"
},
{
"args": {
"text": " STOP "
},
"expect": {
"kind": "control",
"priority": 3,
"verb": "stop"
},
"name": "control-case-and-spaces"
},
{
"args": {
"text": "change the task to do w"
},
"expect": {
"kind": "amendment",
"priority": 2
},
"name": "amendment-change-the-task"
},
{
"args": {
"text": "amend the plan"
},
"expect": {
"kind": "amendment",
"priority": 2
},
"name": "amendment-amend"
},
{
"args": {
"text": "can you update the task status?"
},
"expect": {
"kind": "amendment",
"priority": 2
},
"name": "amendment-update-the-task"
},
{
"args": {
"text": "instead do the other thing"
},
"expect": {
"kind": "amendment",
"priority": 2
},
"name": "amendment-instead-do"
},
{
"args": {
"text": "actually do z instead"
},
"expect": {
"kind": "amendment",
"priority": 2
},
"name": "amendment-actually-do"
},
{
"args": {
"text": "what if we tried y?"
},
"expect": {
"kind": "discussion",
"priority": 1
},
"name": "discussion"
},
{
"args": {
"text": ""
},
"expect": {
"kind": "discussion",
"priority": 1
},
"name": "empty-text"
},
{
"args": {
"text": null
},
"expect": {
"kind": "discussion",
"priority": 1
},
"name": "none-text"
},
{
"args": {
"text": " "
},
"expect": {
"kind": "discussion",
"priority": 1
},
"name": "whitespace-only"
}
],
"core_pin": "RUNE_PA_CORE=python",
"module": "pane_authority"
}
@@ -0,0 +1,108 @@
//! branch_battery — replay the FROZEN oracle truth VERBATIM
//! (frozen_truth.json, generated by scripts/gen_pane_authority_truth.py
//! against the LIVE Python oracle with RUNE_PA_CORE=python pinned and
//! deep-copied inputs).
use crate::{apply_to_task, classify_utterance};
use evidence_provenance::pyjson::{self, Json};
const TRUTH: &str = include_str!("../frozen_truth.json");
fn truth() -> Json {
pyjson::parse(TRUTH).expect("frozen truth parses")
}
fn cases(group: &str) -> Vec<Json> {
match truth().get(group) {
Some(Json::Arr(items)) => items.clone(),
_ => panic!("truth group {group} missing"),
}
}
fn at<'a>(v: &'a Json, path: &[&str]) -> Option<&'a Json> {
let mut cur = v;
for k in path {
cur = match cur {
Json::Obj(pairs) => pairs
.iter()
.find(|(k2, _)| k2 == k)
.map(|(_, x)| x)?,
_ => return None,
};
}
Some(cur)
}
fn jstr(v: Option<&Json>) -> String {
match v {
Some(Json::Str(s)) => s.clone(),
_ => String::new(),
}
}
fn opt_text(v: Option<&Json>) -> Option<String> {
match v {
Some(Json::Str(t)) => Some(t.clone()),
_ => None, // JSON null → None (FFI opt_text law)
}
}
/// Python-value equality on Json (order-insensitive objects).
fn peq(a: &Json, b: &Json) -> bool {
match (a, b) {
(Json::Null, Json::Null) => true,
(Json::Bool(x), Json::Bool(y)) => x == y,
(Json::Num(x), Json::Num(y)) | (Json::Float(x), Json::Float(y)) => x == y,
(Json::Float(x), Json::Num(y)) | (Json::Num(x), Json::Float(y)) => x == y,
(Json::Str(x), Json::Str(y)) => x == y,
(Json::Arr(x), Json::Arr(y)) => {
x.len() == y.len() && x.iter().zip(y).all(|(a, b)| peq(a, b))
}
(Json::Obj(x), Json::Obj(y)) => {
x.len() == y.len()
&& x.iter().all(|(k, v)| {
y.iter()
.find(|(k2, _)| k2 == k)
.map(|(_, v2)| peq(v, v2))
.unwrap_or(false)
})
}
_ => false,
}
}
#[test]
fn replay_classify_utterance() {
for c in cases("classify_cases") {
let text = opt_text(at(&c, &["args", "text"]));
let got = classify_utterance(text.as_deref());
let want = at(&c, &["expect"]).expect("expect");
assert!(
peq(want, &got),
"case {} diverged:\n want={}\n got={}",
jstr(at(&c, &["name"])),
pyjson::dumps(want),
pyjson::dumps(&got),
);
}
}
#[test]
fn replay_apply_to_task() {
for c in cases("apply_cases") {
let utterance = opt_text(at(&c, &["args", "utterance"]));
let got = apply_to_task(
&jstr(at(&c, &["args", "pane"])),
at(&c, &["args", "task"]).expect("task"),
utterance.as_deref(),
);
let want = at(&c, &["expect"]).expect("expect");
assert!(
peq(want, &got),
"case {} diverged:\n want={}\n got={}",
jstr(at(&c, &["name"])),
pyjson::dumps(want),
pyjson::dumps(&got),
);
}
}
+257
View File
@@ -0,0 +1,257 @@
//! pane_authority — two-pane authority semantics (#49).
//!
//! FULL parity port of src/rune/pane_authority.py (dichotomy wave, #2479
//! tranche). Pure deterministic classification/apply law — no I/O, no
//! clock, the whole module crosses FFI.
//!
//! FFI (doc-envelope law): every call takes one JSON doc and returns
//! {"ok": true, "value": ...} | {"ok": false, "err": ..., "type": ...}.
//! * pa_classify_utterance({text}) -> {kind, verb?, priority}
//! * pa_apply_to_task({pane, task, utterance}) -> {task, amendment,
//! applied}
//!
//! Python laws replicated exactly:
//! * `(text or "")` — null text classifies as "" (discussion)
//! * control = first word in {stop,pause,cancel} OR any
//! "{stop|pause|cancel} the task" / "{...} everything" substring;
//! verb = first word when it is a control verb ELSE "stop" (even
//! when the PHRASE matched a different control word — oracle law)
//! * status mapping: verb + "ped" unless verb == "pause" → "paused"
//! (so "cancel" → "cancelped" — frozen verbatim, not corrected)
//! * CHAT amendment promotes visibly (needs_confirm=True, not
//! applied); WORK amendment sets directive; task is a SHALLOW copy
//! (dict(task)) in both floors
//!
//! NOTE: no pa_free_string export — callers free through capability_
//! scoring's cs_free_string (shared process allocator).
use evidence_provenance::pyjson::{self, Json};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
fn obj(pairs: Vec<(&str, Json)>) -> Json {
Json::Obj(pairs.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
}
fn s(v: &str) -> Json {
Json::Str(v.to_string())
}
const CONTROL: [&str; 3] = ["stop", "pause", "cancel"];
const AMENDMENT_KEYS: [&str; 5] = [
"change the task",
"amend",
"update the task",
"instead do",
"actually do",
];
/// Phase B (language advantage): the classification result is a typed
/// verdict matched EXHAUSTIVELY downstream, not a stringly `kind` —
/// adding a class becomes a compiler error instead of a silent fallthrough.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UtteranceKind {
/// control verb: the effective verb (oracle law: phrase matches
/// that are not a first-word control verb get "stop")
Control(&'static str),
Amendment,
Discussion,
}
/// classify_utterance law, typed core.
pub fn classify_typed(text: Option<&str>) -> UtteranceKind {
let low = text.unwrap_or("").to_lowercase();
let low = low.trim();
let first = low.split_whitespace().next().unwrap_or("");
if CONTROL.contains(&first) {
return UtteranceKind::Control(CONTROL.iter().find(|c| **c == first).unwrap());
}
for c in CONTROL {
if low.contains(&format!("{c} the task")) || low.contains(&format!("{c} everything")) {
return UtteranceKind::Control("stop");
}
}
if AMENDMENT_KEYS.iter().any(|k| low.contains(k)) {
return UtteranceKind::Amendment;
}
UtteranceKind::Discussion
}
/// classify_utterance law.
pub fn classify_utterance(text: Option<&str>) -> Json {
match classify_typed(text) {
UtteranceKind::Control(verb) => obj(vec![
("kind", s("control")),
("verb", s(verb)),
("priority", Json::Num(3.0)),
]),
UtteranceKind::Amendment => {
obj(vec![("kind", s("amendment")), ("priority", Json::Num(2.0))])
}
UtteranceKind::Discussion => {
obj(vec![("kind", s("discussion")), ("priority", Json::Num(1.0))])
}
}
}
/// apply_to_task law: {task, amendment, applied}.
pub fn apply_to_task(pane: &str, task: &Json, utterance: Option<&str>) -> Json {
let cls = classify_typed(utterance);
// dict(task) — shallow copy
let mut pairs: Vec<(String, Json)> = match task {
Json::Obj(pairs) => pairs.clone(),
_ => Vec::new(),
};
match cls {
UtteranceKind::Control(verb) => {
let status = if verb != "pause" {
format!("{verb}ped")
} else {
"paused".to_string()
};
set_key(&mut pairs, "status", s(&status));
obj(vec![
("task", Json::Obj(pairs)),
(
"amendment",
obj(vec![
("kind", s("control")),
("verb", s(verb)),
("from_pane", s(pane)),
]),
),
("applied", Json::Bool(true)),
])
}
kind if pane == "chat" && kind == UtteranceKind::Amendment => {
let text = utterance.unwrap_or("");
obj(vec![
("task", Json::Obj(pairs)),
(
"amendment",
obj(vec![
("kind", s("amendment")),
("text", s(text)),
("from_pane", s("chat")),
("needs_confirm", Json::Bool(true)),
]),
),
("applied", Json::Bool(false)),
])
}
_ if pane == "chat" => no_amendment(pairs),
UtteranceKind::Amendment => {
let text = utterance.unwrap_or("");
set_key(&mut pairs, "directive", s(text));
obj(vec![
("task", Json::Obj(pairs)),
(
"amendment",
obj(vec![
("kind", s("amendment")),
("text", s(text)),
("from_pane", s("work")),
("needs_confirm", Json::Bool(false)),
]),
),
("applied", Json::Bool(true)),
])
}
_ => no_amendment(pairs),
}
}
fn no_amendment(pairs: Vec<(String, Json)>) -> Json {
obj(vec![
("task", Json::Obj(pairs)),
("amendment", Json::Null),
("applied", Json::Bool(false)),
])
}
fn set_key(pairs: &mut Vec<(String, Json)>, key: &str, value: Json) {
if let Some(slot) = pairs.iter_mut().find(|(k, _)| k == key) {
slot.1 = value;
} else {
pairs.push((key.to_string(), value));
}
}
// ── FFI (doc-envelope) ───────────────────────────────────────────────────
fn envelope(result: Result<Json, (String, String)>) -> *mut c_char {
let body = match result {
Ok(value) => obj(vec![("ok", Json::Bool(true)), ("value", value)]),
Err((err, ty)) => obj(vec![
("ok", Json::Bool(false)),
("err", Json::Str(err)),
("type", Json::Str(ty)),
]),
};
let out = pyjson::dumps(&body);
match CString::new(out) {
Ok(c) => c.into_raw(),
Err(_) => CString::new(
r#"{"ok":false,"err":"nul in output","type":"RuntimeError"}"#,
)
.unwrap()
.into_raw(),
}
}
fn parse_doc(raw: &str) -> Result<Json, (String, String)> {
pyjson::parse(raw).map_err(|e| (e, "ValueError".to_string()))
}
fn read_arg(ptr: *const c_char) -> Result<String, (String, String)> {
if ptr.is_null() {
return Ok(String::new());
}
unsafe { CStr::from_ptr(ptr) }
.to_str()
.map(|s| s.to_string())
.map_err(|e| (e.to_string(), "ValueError".to_string()))
}
fn opt_text(doc: &Json, key: &str) -> Result<Option<String>, (String, String)> {
match doc.get(key) {
None => Err((format!("{key} is required"), "KeyError".into())),
Some(Json::Null) => Ok(None),
Some(Json::Str(t)) => Ok(Some(t.clone())),
Some(_) => Err((format!("{key} must be a string or null"), "TypeError".into())),
}
}
macro_rules! ffi {
($name:ident, $body:expr) => {
#[no_mangle]
pub extern "C" fn $name(arg: *const c_char) -> *mut c_char {
let out: Result<Json, (String, String)> =
read_arg(arg).and_then(|raw| $body(&raw));
envelope(out)
}
};
}
ffi!(pa_classify_utterance, |raw: &str| -> Result<Json, (String, String)> {
let doc = parse_doc(raw)?;
let text = opt_text(&doc, "text")?;
Ok(classify_utterance(text.as_deref()))
});
ffi!(pa_apply_to_task, |raw: &str| -> Result<Json, (String, String)> {
let doc = parse_doc(raw)?;
let pane = match doc.get("pane") {
Some(Json::Str(p)) => p.clone(),
_ => return Err(("pane must be a string".into(), "TypeError".into())),
};
let task = match doc.get("task") {
Some(t @ Json::Obj(_)) => t.clone(),
_ => return Err(("task must be an object".into(), "TypeError".into())),
};
let utterance = opt_text(&doc, "utterance")?;
Ok(apply_to_task(&pane, &task, utterance.as_deref()))
});
#[cfg(test)]
mod branch_battery;
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,26 @@
# Contract BUILDER-2026-10-13T00:04:22Z-4156e1-factory-batch-h
**Phase:** closed · **Mode:** n/a · **Depth:** n/a
## What was built
Port src/rune/outcome_utility.py to FULL Rust (dichotomy wave, batch-h): doc-envelope FFI crate src/rust/outcome_utility, ctypes loader ou_loader.py mirroring broker_loader.py, facade delegation with RUNE_OU_CORE=python pin, frozen-truth battery, live parity 100%, Phase B language-advantage pass.
## What it connects to
- `src/rune/ou_loader.py`
- `src/rune/outcome_utility.py`
- `src/rust/outcome_utility/`
## How to update those connections
- Open a NEW construction contract (scaffolding is absolute) scoped to the same surface.
- Evidence trail: 4 checkpoint(s) recorded on the contract entity.
- Quality at close: {"candidate_corrections": [{"entity": "commits", "note": "genuinely unexpected new surface \u2014 evidence required before store update", "status": "CANDIDATE"}, {"entity": "frozen-truth battery", "note": "genuinely unexpected new surface \u2014 evidence required before store update", "status": "CANDIDATE"}, {"entity": "live parity", "note": "genuinely unexpected new surface \u2014 evidence required before store update", "status": "CANDIDATE"}, {"entity": "python tests", "note": "genuinely unexpected new surface \u2014 evidence required before store update", "status": "CANDIDATE"}, {"entity": "rust gates", "note": "genuinely unexpected new surface \u2014 evidence required before store update", "status": "CANDIDATE"}], "captured_at": "2026-10-13T03:03:06Z", "contract": "BUILDER-2026-10-13T00:04:22Z-4156e1-factory-batch-h", "correctly_predicted": [], "evidence_sources": ["unattributed"], "expected_new_entities": [], "expected_new_missing": [], "expected_new_observed": [], "expected_runtime_effects": [], "expected_runtime_missing": [], "expected_runtime_observed": [], "genuinely_unexpected_new": ["commits", "frozen-truth battery", "live parity", "python tests", "rust gates"], "missed": ["commits", "frozen-truth battery", "live parity", "python tests", "rust gates"], "needless": ["module:deploy_qualify", "module:outcome_utility"], "observed": ["commits", "frozen-truth battery", "live parity", "python tests", "rust gates"], "observed_existing": [], "observed_new": ["commits", "frozen-truth battery", "live parity", "python tests", "rust gates"], "predicted_but_unaffected": ["module:deploy_qualify", "module:outcome_utility"], "predicted_existing_affected": ["module:deploy_qualify", "module:outcome_utility"], "scope_check": {"in_scope": ["src/rune/ou_loader.py", "src/rune/outcome_utility.py", "src/rust/outcome_utility/"], "out_of_scope": ["scripts/gen_outcome_utility_truth.py", "tests/test_outcome_utility_port.py"], "verdict": "red"}}.
## Evidence
- builder:factory-batch-h
- builder:factory-batch-h
- builder:factory-batch-h
- closure-record
@@ -0,0 +1,24 @@
# Contract BUILDER-2026-10-13T02:46:21Z-e4c445-factory-gap-pg
**Phase:** closed · **Mode:** n/a · **Depth:** n/a
## What was built
Close the persona_gate dichotomy PARTIAL gap (issue #2479 tranche, GO): extend the existing Go port src/go/persona-gate with the audit-missing functions (_now, _cc_pg_core, _py_lex, _lex_input, _py_overlap, _embed_raw, _embed_vec, _cosine, _semantic_engagement, _rule_json, _py_decide, _journal, decide_and_journal, _user_rule_authorizes, _find_user_authorizing, _invoke_model, build_gate_prompt, report, handle_hook) name-preserving with logic 1:1 to the Python oracle src/rune/persona_gate.py, embedding/HTTP/subprocess/journal IO behind injectable seams, frozen-truth battery generated against the live oracle (RUNE_COGNITION_CORE=python pinned, deep-copied inputs), live parity 100% on the new functions, Phase B language-advantage pass.
## What it connects to
(no affected surface recorded)
## How to update those connections
- Open a NEW construction contract (scaffolding is absolute) scoped to the same surface.
- Evidence trail: 4 checkpoint(s) recorded on the contract entity.
- Quality at close: {"candidate_corrections": [], "captured_at": "2026-10-13T03:10:03Z", "contract": "BUILDER-2026-10-13T02:46:21Z-e4c445-factory-gap-pg", "correctly_predicted": [], "evidence_sources": ["unattributed"], "expected_new_entities": [], "expected_new_missing": [], "expected_new_observed": [], "expected_runtime_effects": [], "expected_runtime_missing": [], "expected_runtime_observed": [], "genuinely_unexpected_new": [], "missed": [], "needless": ["module:deploy_qualify"], "observed": [], "observed_existing": [], "observed_new": [], "predicted_but_unaffected": ["module:deploy_qualify"], "predicted_existing_affected": ["module:deploy_qualify"], "scope_check": {"in_scope": [], "out_of_scope": ["scripts/gen_persona_gate_truth.py", "scripts/open_contract_persona_gate.py", "scripts/parity_persona_gate.py", "src/go/persona-gate/gaps.go", "src/go/persona-gate/main.go", "src/go/persona-gate/phaseb.go", "src/go/persona-gate/phaseb_test.go", "src/go/persona-gate/truth.json", "src/go/persona-gate/truth_test.go", "src/rune/persona_gate.py"], "reason": "contract has no approved scope", "verdict": "unknown"}}.
## Evidence
- builder:factory-gap-pg
- builder:factory-gap-pg
- builder:factory-gap-pg
- closure-record
+173
View File
@@ -0,0 +1,173 @@
"""Tests: outcome_utility.py Rust core port (dichotomy wave, batch-h).
Layers, all must be green for parity 100%:
1. FROZEN — the frozen-oracle corpus (RUNE_OU_CORE=python pinned at
generation) replayed VERBATIM through the Rust core.
2. PIN — RUNE_OU_CORE=python pins the Python oracle; unset uses
the Rust core when built (strangler law).
3. LIVE — oracle vs kernel on a seeded random sweep, bit-exact.
"""
from __future__ import annotations
import copy
import json
import random
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys_path = str(ROOT / "src")
if sys_path not in __import__("sys").path:
import sys
sys.path.insert(0, sys_path)
from rune import outcome_utility as ou # noqa: E402
from rune import ou_loader # noqa: E402
TRUTH = json.loads(
(ROOT / "src" / "rust" / "outcome_utility" / "src" / "frozen_outcome_utility_truth.json")
.read_text()
)
@pytest.fixture()
def core_built():
if not ou_loader.available():
pytest.skip("rust outcome_utility core not built")
return True
def _restore_keys(store):
"""JSON froze the oracle's int-keyed store dicts with string keys —
restore the original key types so replay is oracle-faithful."""
out = {}
for k, v in store.items():
try:
out[int(k)] = v
except ValueError:
out[k] = v
return out
def _replay_row(row, target):
"""Replay one frozen row through `target` ('rust' loader or python
oracle module) on a DEEP COPY of the frozen args."""
a = copy.deepcopy(row["args"])
if row["fn"] == "apply_outcome":
got = target.apply_outcome(a["utility"], a["outcome"])
want = row["expect"]
assert got == want, f"{row['name']}: {got!r} != {want!r}"
elif row["fn"] == "apply_stale":
got = target.apply_stale(a["utility"], a["stale_turns"])
want = row["expect"]
assert got == want, f"{row['name']}: {got!r} != {want!r}"
elif row["fn"] == "retrieval_alone":
got = target.retrieval_alone(a["utility"])
want = row["expect"]
assert got == want, f"{row['name']}: {got!r} != {want!r}"
elif row["fn"] == "record_verified_outcome":
store = a.get("store")
if store is None:
store = {}
store = _restore_keys(store)
summary = target.record_verified_outcome(a["entry_id"], a["outcome"], store)
want = row["expect"]
# JSON freezes int dict keys as strings — compare normalized.
norm = lambda d: {str(k): v for k, v in d.items()}
assert summary == want["summary"], f"{row['name']}: {summary!r} != {want['summary']!r}"
assert norm(store) == norm(want["store"]), f"{row['name']}: store mutation diverged"
else:
raise AssertionError(f"unknown frozen fn {row['fn']}")
class _RustSurface:
"""The Rust core shaped like the Python module (store stays Python)."""
@staticmethod
def apply_outcome(utility, outcome):
return ou_loader.apply_outcome(utility, outcome)
@staticmethod
def apply_stale(utility, stale_turns):
return ou_loader.apply_stale(utility, stale_turns)
@staticmethod
def retrieval_alone(utility):
return ou_loader.retrieval_alone(utility)
@staticmethod
def record_verified_outcome(entry_id, outcome, store=None):
store = store if store is not None else {}
current = store.get(entry_id, {"utility": 0.0, "confidence": 0.5})
current["utility"] = ou_loader.apply_outcome(
current.get("utility", 0.0), outcome)
store[entry_id] = current
return {"entry_id": entry_id, "utility": current["utility"],
"confidence": current.get("confidence", 0.5)}
RUST = _RustSurface()
def test_frozen_truth_replays_verbatim_rust(core_built):
for row in TRUTH["cases"]:
_replay_row(row, RUST)
def test_frozen_truth_replays_verbatim_python_oracle_sanity(core_built, monkeypatch):
monkeypatch.setenv("RUNE_OU_CORE", "python") # the oracle, not the kernel
import rune.outcome_utility as oracle
for row in TRUTH["cases"]:
_replay_row(row, oracle)
def test_pin_python_and_kernel_probe(core_built, monkeypatch):
monkeypatch.setenv("RUNE_OU_CORE", "python")
assert ou_loader.core() is None
assert ou._core() is None
monkeypatch.delenv("RUNE_OU_CORE", raising=False)
assert ou_loader.core() is not None
assert ou._core() is not None
def test_facade_delegates_to_rust(core_built, monkeypatch):
monkeypatch.delenv("RUNE_OU_CORE", raising=False)
assert ou.apply_outcome(0.5, "helpful") == 0.6
assert ou.apply_stale(0.5, 2) == ou_loader.apply_stale(0.5, 2)
assert ou.retrieval_alone(0.42) == 0.42
store = {1: {"utility": 0.5, "confidence": 0.8}}
r = ou.record_verified_outcome(1, "helpful", store)
assert r == {"entry_id": 1, "utility": 0.6, "confidence": 0.8}
assert store[1]["utility"] == 0.6
def test_facade_pin_uses_python_oracle(core_built, monkeypatch):
monkeypatch.setenv("RUNE_OU_CORE", "python")
assert ou.apply_outcome(0.5, "helpful") == 0.6
assert ou.apply_stale(-0.5, 3) == -0.5451495
store = {}
r = ou.record_verified_outcome(9, "ignored", store)
assert r == {"entry_id": 9, "utility": -0.05, "confidence": 0.5}
def test_live_parity_seeded_sweep(core_built, monkeypatch):
"""Oracle vs kernel, bit-exact, across a seeded random sweep."""
monkeypatch.delenv("RUNE_OU_CORE", raising=False)
rnd = random.Random(2479)
for _ in range(500):
u = rnd.uniform(-1.5, 1.5)
outcome = rnd.choice(["helpful", "ignored", "unknown", "", "verified"])
assert ou.apply_outcome(u, outcome) == ou_loader.apply_outcome(u, outcome)
t = rnd.randint(-5, 400)
assert ou.apply_stale(u, t) == ou_loader.apply_stale(u, t)
assert ou.retrieval_alone(u) == ou_loader.retrieval_alone(u)
store_o, store_r = {}, {}
entry = rnd.randint(0, 3)
out_o = ou.record_verified_outcome(entry, outcome, store_o)
out_r = RUST.record_verified_outcome(entry, outcome, store_r)
assert out_o == out_r
assert store_o == store_r
+83
View File
@@ -0,0 +1,83 @@
"""Tests: pane_authority.py Rust core port (dichotomy wave, #2479).
Frozen-truth battery: the 26 frozen-oracle cases replayed VERBATIM
through the facade in BOTH core modes (RUNE_PA_CORE=python floor vs the
Rust kernel), plus loader pin discipline and input-copy semantics.
"""
from __future__ import annotations
import copy
import json
import os
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
sys.path.insert(0, str(ROOT / "src" / "rune"))
import pane_authority as pa # noqa: E402
from rune import pa_loader # noqa: E402
TRUTH = json.loads(
(ROOT / "src" / "rust" / "pane_authority" / "frozen_truth.json")
.read_text())
@pytest.fixture()
def core_built():
if not pa_loader.available():
pytest.skip("rust pane_authority core not built")
return True
def drive(mode, fn, *args):
os.environ["RUNE_PA_CORE"] = mode
try:
return fn(*copy.deepcopy(args))
finally:
os.environ["RUNE_PA_CORE"] = "auto"
def test_loader_probe_and_pin(core_built, monkeypatch):
monkeypatch.delenv("RUNE_PA_CORE", raising=False)
assert pa_loader.available()
assert pa._kernel() is not None
monkeypatch.setenv("RUNE_PA_CORE", "python")
assert pa._kernel() is None
def test_frozen_classify_both_ways(core_built):
for c in TRUTH["classify_cases"]:
py = drive("python", pa.classify_utterance, c["args"]["text"])
rs = drive("auto", pa.classify_utterance, c["args"]["text"])
assert py == rs, c["name"]
assert py == c["expect"], c["name"]
def test_frozen_apply_both_ways(core_built):
for c in TRUTH["apply_cases"]:
args = (c["args"]["pane"], c["args"]["task"], c["args"]["utterance"])
py = drive("python", pa.apply_to_task, *args)
rs = drive("auto", pa.apply_to_task, *args)
assert py == rs, c["name"]
assert py == c["expect"], c["name"]
def test_inputs_not_mutated(core_built):
task = {"status": "running", "directive": "do x"}
frozen_task = copy.deepcopy(task)
drive("auto", pa.apply_to_task, "work", task, "stop everything")
drive("auto", pa.apply_to_task, "work", task, "actually do z")
assert task == frozen_task
def test_control_preempts_from_either_pane(core_built):
for pane in ("chat", "work"):
r = drive("auto", pa.apply_to_task, pane, {"status": "running"},
"stop everything")
assert r["applied"] is True
assert "stop" in r["task"]["status"]