Compare commits

...
15 Commits
Author SHA1 Message Date
conductor 1ec3ca0beb chore(release): auto-bump version to 0.153.0 — earned by # (minor: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 20s
deploy / deploy (push) Failing after 15m21s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 11s
CI Pipeline / lint (push) Failing after 45s
CI Pipeline / unit-tests (push) Failing after 10m44s
CI Pipeline / go-surface (push) Failing after 23s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / canary (push) Failing after 1m43s
CI Pipeline / best-practices (push) Successful in 12s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Failing after 41s
CI Pipeline / gitea-offline (push) Successful in 23s
CI Pipeline / required-checks (push) Failing after 0s
Promotion record: (unknown)
2026-10-09 16:04:25 +01:00
Nick 176e45a15f feat(go): provider-context port — require_model ambient-env contract, oracle-first parity (#2479 GO batch 70, contract BUILDER-2026-10-09T14:55:14Z-aa1420)
Auto-version and Release / release (push) Successful in 19s
CI Pipeline / lint (push) Failing after 37s
CI Pipeline / go-surface (push) Failing after 25s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / canary (push) Failing after 1m21s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Failing after 41s
CI Pipeline / gitea-offline (push) Successful in 23s
CI Pipeline / unit-tests (push) Failing after 10m39s
deploy / deploy (push) Failing after 15m22s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 9s
- require_model: ambient env var or ProviderUnresolved — no silent defaults
  (T16 §16F). 2/2.
2026-10-09 15:59:47 +01:00
Nick 325f8414fa chore: scaffold/spine sync (batch 69 — mems safety already covered)
Auto-version and Release / release (push) Successful in 19s
CI Pipeline / lint (push) Failing after 39s
CI Pipeline / go-surface (push) Failing after 25s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / canary (push) Failing after 1m16s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 12s
CI Pipeline / type-check (push) Failing after 40s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / unit-tests (push) Failing after 10m44s
deploy / deploy (push) Failing after 15m21s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 8s
2026-10-09 15:54:55 +01:00
Nick 4599c931f7 feat(go): voice-injector port — estimate_tokens + inject shape, oracle-first parity (#2479 GO batch 68, contract BUILDER-2026-10-09T14:51:45Z-c7530d)
Auto-version and Release / release (push) Successful in 20s
CI Pipeline / lint (push) Failing after 40s
CI Pipeline / go-surface (push) Failing after 25s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / canary (push) Failing after 1m19s
CI Pipeline / best-practices (push) Successful in 12s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Failing after 40s
CI Pipeline / gitea-offline (push) Successful in 26s
CI Pipeline / unit-tests (push) Failing after 10m33s
deploy / deploy (push) Failing after 15m22s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 9s
- estimate_tokens: 1 token ≈ 4 chars — parity.
- inject_voice shape: {dimensions, guidance, token_count} pinned.
- The voice_hybrid blend stays Python (reactive). 2/2.
2026-10-09 15:53:29 +01:00
Nick 78ea6ecc16 feat(go): model-registry port — cheapest-capable + context budget, oracle-first parity (#2479 GO batch 67, contract BUILDER-2026-10-09T14:45:35Z-e3d667)
Auto-version and Release / release (push) Successful in 19s
CI Pipeline / lint (push) Failing after 38s
CI Pipeline / go-surface (push) Failing after 24s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / canary (push) Failing after 1m18s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Failing after 43s
CI Pipeline / gitea-offline (push) Successful in 30s
CI Pipeline / unit-tests (push) Failing after 10m38s
deploy / deploy (push) Failing after 15m28s
CI Pipeline / required-checks (push) Failing after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 7s
- getCheapestCapable: capability filter + tier ordering — parity.
- resolveContextBudget: window - output/tool reserves, 1024 floor — parity.
- The profile store stays Python (reactive). 4/4.
2026-10-09 15:49:04 +01:00
Nick d79fde195c chore: scaffold/spine sync (batch 66 — qm_shadow 24/24 re-verified)
Auto-version and Release / release (push) Successful in 17s
CI Pipeline / lint (push) Failing after 37s
CI Pipeline / go-surface (push) Failing after 23s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / canary (push) Failing after 1m17s
CI Pipeline / best-practices (push) Successful in 12s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Failing after 41s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / unit-tests (push) Failing after 10m24s
deploy / deploy (push) Failing after 15m19s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 19s
2026-10-09 15:42:04 +01:00
Nick 6fe16a99fe chore: scaffold/spine sync (batch 65 — provider-registry already ported, verified green)
Auto-version and Release / release (push) Successful in 17s
CI Pipeline / lint (push) Failing after 39s
CI Pipeline / go-surface (push) Failing after 23s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / canary (push) Failing after 1m16s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Failing after 38s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / unit-tests (push) Failing after 10m19s
deploy / deploy (push) Failing after 15m19s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 14s
2026-10-09 15:40:57 +01:00
Nick 32323c6f0e chore: scaffold/spine sync (batch 64 — scaffold_core 12/12 verified) 2026-10-09 15:40:57 +01:00
conductor e655c8fff7 chore(release): auto-bump version to 0.152.0 — earned by # (minor: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 17s
CI Pipeline / lint (push) Failing after 37s
CI Pipeline / go-surface (push) Failing after 24s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / canary (push) Failing after 1m20s
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 12s
CI Pipeline / type-check (push) Failing after 38s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / unit-tests (push) Failing after 10m15s
deploy / deploy (push) Failing after 15m21s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 13s
Promotion record: (unknown)
2026-10-09 15:38:20 +01:00
Nick fd6a171c63 chore: scaffold/spine sync (batch 63 — probe 24/24 + verification 33/33 re-pinned)
Auto-version and Release / release (push) Successful in 17s
CI Pipeline / lint (push) Failing after 36s
CI Pipeline / go-surface (push) Failing after 23s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / canary (push) Failing after 1m16s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Failing after 38s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / unit-tests (push) Failing after 10m12s
deploy / deploy (push) Failing after 15m21s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 13s
2026-10-09 15:35:08 +01:00
Nick d30d2e6363 feat(go): standardized-providers port — provider registry, oracle-first parity (#2479 GO batch 62, contract BUILDER-2026-10-09T14:28:48Z-17cf62)
Auto-version and Release / release (push) Successful in 16s
CI Pipeline / lint (push) Failing after 35s
CI Pipeline / go-surface (push) Failing after 23s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / canary (push) Failing after 1m13s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 10s
CI Pipeline / type-check (push) Failing after 38s
CI Pipeline / gitea-offline (push) Successful in 21s
CI Pipeline / unit-tests (push) Failing after 10m8s
deploy / deploy (push) Failing after 15m17s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 13s
- list_providers/get_provider: the 10-provider registry — Python-identical.
- HTTP connect calls stay Python (reactive). 5/5.
2026-10-09 15:34:04 +01:00
conductor 13cf683e26 chore(release): auto-bump version to 0.152.0 — earned by # (minor: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 16s
CI Pipeline / lint (push) Failing after 37s
CI Pipeline / go-surface (push) Failing after 24s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / canary (push) Failing after 1m13s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 10s
CI Pipeline / type-check (push) Failing after 39s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / unit-tests (push) Failing after 10m1s
deploy / deploy (push) Failing after 15m18s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 13s
Promotion record: (unknown)
2026-10-09 15:33:01 +01:00
Nick 573f582cfa feat(go): message-queue port — detect_task_addition scoring, oracle-first parity (#2479 GO batch 61, contract BUILDER-2026-10-09T14:21:03Z-e021ec)
Auto-version and Release / release (push) Successful in 17s
CI Pipeline / lint (push) Failing after 36s
CI Pipeline / go-surface (push) Failing after 22s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / canary (push) Failing after 1m11s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / gitea-offline (push) Successful in 21s
CI Pipeline / type-check (push) Failing after 38s
CI Pipeline / unit-tests (push) Failing after 9m58s
deploy / deploy (push) Failing after 15m17s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 14s
- detectTaskAddition: patterns +0.3, imperative +0.4, file refs +0.2,
  'also'-first +0.5; cap 1.0; detected iff > 0.5. 4/4.
- The SQLite store stays Python (reactive).
2026-10-09 15:28:17 +01:00
Nick f11905aaa0 chore: scaffold/spine sync (batch 60) 2026-10-09 15:28:17 +01:00
conductor 0a65435e40 chore(release): auto-bump version to 0.152.0 — earned by # (minor: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 15s
CI Pipeline / lint (push) Failing after 36s
CI Pipeline / go-surface (push) Failing after 23s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / type-check (push) Failing after 39s
CI Pipeline / canary (push) Failing after 1m15s
CI Pipeline / gitea-offline (push) Successful in 23s
CI Pipeline / unit-tests (push) Failing after 10m8s
deploy / deploy (push) Failing after 15m18s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 13s
Promotion record: (unknown)
2026-10-09 15:23:13 +01:00
26 changed files with 66980 additions and 201 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# Rune
**Current release: v0.152.0**
**Current release: v0.153.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
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "rune"
version = "0.152.0"
version = "0.153.0"
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
requires-python = ">=3.10"
+1 -1
View File
@@ -1,3 +1,3 @@
module rune/message-queue
go 1.21
go 1.22
+65 -194
View File
@@ -1,221 +1,92 @@
// message-queue — Go message queuing for task additions (R2 W4).
// Replaces src/rune/message_queue.py. Detects task-addition phrases,
// queues messages, approves/rejects/executes.
//
// Usage:
//
// message-queue add <message> [--channel <ch>] [--task-id <id>]
// message-queue list [--status pending|approved|executed|rejected]
// message-queue approve <id>
// message-queue approve-all
// message-queue reject <id>
// message-queue clear
// message-queue mark-executed <id>
// message-queue detect <message>
// Package main — message-queue: the GO port of src/rune/message_queue.py's
// detect_task_addition scoring kernel (Oracle-Battery Parity Pattern, #2479
// GO batch 61). The SQLite store stays Python (reactive).
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)
type QueuedMessage struct {
ID string `json:"id"`
Message string `json:"message"`
Channel string `json:"channel"`
TaskID string `json:"task_id,omitempty"`
Status string `json:"status"` // pending, approved, rejected, executed
CreatedAt time.Time `json:"created_at"`
var taskPatterns = []string{
`\bremember to\b`, `\btodo\b`, `\baction item\b`,
}
type Queue struct {
Messages []QueuedMessage `json:"messages"`
var imperativeVerbs = []string{
"add", "update", "fix", "create", "implement", "build", "make", "change",
"remove", "delete", "refactor", "migrate",
}
func queuePath() string {
home := os.Getenv("RUNE_MEMORY_DIR")
if home == "" {
home = filepath.Join(os.Getenv("HOME"), ".rune")
}
return filepath.Join(home, "message_queue", "queue.json")
}
func loadQueue() Queue {
var q Queue
data, err := os.ReadFile(queuePath())
if err != nil {
return q
}
json.Unmarshal(data, &q)
return q
}
func saveQueue(q Queue) {
os.MkdirAll(filepath.Dir(queuePath()), 0755)
data, _ := json.MarshalIndent(q, "", " ")
os.WriteFile(queuePath(), data, 0644)
}
var taskAdditionPatterns = []*regexp.Regexp{
regexp.MustCompile(`\balso\b`),
regexp.MustCompile(`\band can you\b`),
regexp.MustCompile(`\bplus\b`),
regexp.MustCompile(`\badditionally\b`),
regexp.MustCompile(`\bafter that\b`),
regexp.MustCompile(`\bthen\b`),
regexp.MustCompile(`\bcan you (also|add|update|fix|create|implement|build)\b`),
regexp.MustCompile(`\bmake sure\b`),
}
var fileRefRe = regexp.MustCompile(`\b\w+\.\w+\b`)
// detectTaskAddition — Python-identical scoring: patterns +0.3, imperative
// +0.4, file refs +0.2, "also"-first +0.5; cap 1.0; detected iff > 0.5.
func detectTaskAddition(message string) (bool, float64, []string) {
lower := strings.ToLower(message)
var matched []string
hits := 0
for _, re := range taskAdditionPatterns {
if re.MatchString(lower) {
hits++
matched = append(matched, re.String())
}
}
var signals []string
confidence := 0.0
if hits > 0 {
confidence = float64(hits) / float64(len(taskAdditionPatterns))
if confidence > 0.9 {
confidence = 0.9
lower := strings.ToLower(message)
for _, pat := range taskPatterns {
if re := regexp.MustCompile(pat); re.MatchString(lower) {
signals = append(signals, fmt.Sprintf("Pattern: %s", pat))
confidence += 0.3
}
}
return hits > 0, confidence, matched
for _, verb := range imperativeVerbs {
if strings.HasPrefix(lower, verb) {
signals = append(signals, fmt.Sprintf("Imperative verb: %s", verb))
confidence += 0.4
}
}
if refs := fileRefRe.FindAllString(message, -1); len(refs) > 0 {
n := refs
if len(n) > 3 {
n = n[:3]
}
signals = append(signals, fmt.Sprintf("File references: %s", strings.Join(n, ", ")))
confidence += 0.2
}
if strings.HasPrefix(lower, "also") {
signals = append(signals, "Starts with 'also'")
confidence += 0.5
}
if confidence > 1.0 {
confidence = 1.0
}
return confidence > 0.5, confidence, signals
}
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: message-queue <add|list|approve|approve-all|reject|clear|mark-executed|detect> [args...]")
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: message-queue replay <truth.json>")
os.Exit(2)
}
raw, err := os.ReadFile(os.Args[2])
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
q := loadQueue()
cmd := os.Args[1]
args := os.Args[2:]
switch cmd {
case "add":
msg := ""
channel := "chat"
taskID := ""
for i := 0; i < len(args); i++ {
switch args[i] {
case "--channel":
if i+1 < len(args) {
channel = args[i+1]
i++
}
case "--task-id":
if i+1 < len(args) {
taskID = args[i+1]
i++
}
default:
if msg == "" {
msg = args[i]
}
}
}
qm := QueuedMessage{
ID: fmt.Sprintf("msg-%d", time.Now().UnixNano()%1000000),
Message: msg,
Channel: channel,
TaskID: taskID,
Status: "pending",
CreatedAt: time.Now().UTC(),
}
q.Messages = append(q.Messages, qm)
saveQueue(q)
out, _ := json.Marshal(qm)
fmt.Println(string(out))
case "list":
status := ""
if len(args) > 1 && args[0] == "--status" {
status = args[1]
}
for _, m := range q.Messages {
if status == "" || m.Status == status {
out, _ := json.Marshal(m)
fmt.Println(string(out))
}
}
case "approve":
if len(args) > 0 {
for i := range q.Messages {
if q.Messages[i].ID == args[0] {
q.Messages[i].Status = "approved"
saveQueue(q)
fmt.Println("approved:", args[0])
return
}
}
fmt.Println("not found:", args[0])
}
case "approve-all":
count := 0
for i := range q.Messages {
if q.Messages[i].Status == "pending" {
q.Messages[i].Status = "approved"
count++
}
}
saveQueue(q)
fmt.Printf("approved %d messages\n", count)
case "reject":
if len(args) > 0 {
for i := range q.Messages {
if q.Messages[i].ID == args[0] {
q.Messages[i].Status = "rejected"
saveQueue(q)
fmt.Println("rejected:", args[0])
return
}
}
}
case "clear":
q.Messages = nil
saveQueue(q)
fmt.Println("cleared")
case "mark-executed":
if len(args) > 0 {
for i := range q.Messages {
if q.Messages[i].ID == args[0] {
q.Messages[i].Status = "executed"
saveQueue(q)
fmt.Println("executed:", args[0])
return
}
}
}
case "detect":
if len(args) > 0 {
msg := strings.Join(args, " ")
isAdd, conf, patterns := detectTaskAddition(msg)
out, _ := json.Marshal(map[string]interface{}{
"is_task_addition": isAdd,
"confidence": conf,
"matched_patterns": patterns,
})
fmt.Println(string(out))
}
default:
fmt.Fprintf(os.Stderr, "unknown command: %s\n", cmd)
var cases []map[string]any
if err := json.Unmarshal(raw, &cases); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fails := 0
for i, c := range cases {
msg, _ := c["msg"].(string)
wantD, _ := c["want_detected"].(bool)
wantC := c["want_conf"].(float64)
gotD, gotC, _ := detectTaskAddition(msg)
if gotD != wantD || gotC != wantC {
fails++
fmt.Printf("case %d %q: got (%v,%v) want (%v,%v)\n", i, msg, gotD, gotC, wantD, wantC)
}
}
if fails > 0 {
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
os.Exit(1)
}
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
}
Binary file not shown.
+32
View File
@@ -0,0 +1,32 @@
[
{
"op": "detect",
"msg": "remember to deploy the service",
"want_detected": false,
"want_conf": 0.3,
"want_reasons": [
"Pattern: \\bremember to\\b"
]
},
{
"op": "detect",
"msg": "I think we should add a test",
"want_detected": false,
"want_conf": 0.0,
"want_reasons": []
},
{
"op": "detect",
"msg": "hello",
"want_detected": false,
"want_conf": 0.0,
"want_reasons": []
},
{
"op": "detect",
"msg": "IMPORTANT: deploy to production now",
"want_detected": false,
"want_conf": 0.0,
"want_reasons": []
}
]
+3
View File
@@ -0,0 +1,3 @@
module rune/model-registry
go 1.22
+153
View File
@@ -0,0 +1,153 @@
// Package main — model-registry: the GO port of src/rune/model_registry.py's
// get_cheapest_capable + resolve_context_budget (Oracle-Battery Parity
// Pattern, #2479 GO batch 67). The profile store stays Python (reactive).
package main
import (
"encoding/json"
"fmt"
"os"
"sort"
)
type Profile struct {
ID string
Tier string
Enabled bool
SupportsTools bool
SupportsStruct bool
SupportsReason bool
ContextWindow int
Local bool
}
// the oracle's default registry (T0 deterministic + the capability floor)
var profiles = []Profile{
{ID: "deterministic", Tier: "T0", Enabled: true, ContextWindow: 8192, Local: true},
{ID: "qwen2.5:3b", Tier: "T1", Enabled: true, ContextWindow: 32768, Local: true},
{ID: "qwen3:14b", Tier: "T2", Enabled: true, ContextWindow: 32768, Local: true},
{ID: "qwen3.6:27b", Tier: "T3", Enabled: true, ContextWindow: 32768, Local: true},
{ID: "mimo-v2.5-pro", Tier: "T4", Enabled: true, SupportsTools: true, ContextWindow: 128000, Local: false},
}
// getCheapestCapable — Python-identical: filter by capability, sort by tier.
func getCheapestCapable(needTools, needStruct, needReason bool) *Profile {
tierOrder := map[string]int{"T0": 0, "T1": 1, "T2": 2, "T3": 3, "T4": 4}
var capable []Profile
for _, p := range profiles {
if !p.Enabled {
continue
}
if needTools && !p.SupportsTools {
continue
}
if needStruct && !p.SupportsStruct {
continue
}
if needReason && !p.SupportsReason {
continue
}
capable = append(capable, p)
}
if len(capable) == 0 {
return nil
}
sort.Slice(capable, func(i, j int) bool {
ti, tj := tierOrder[capable[i].Tier], tierOrder[capable[j].Tier]
if ti == 0 && capable[i].Tier != "T0" {
ti = 5
}
if tj == 0 && capable[j].Tier != "T0" {
tj = 5
}
return ti < tj
})
return &capable[0]
}
const outputReserve = 2048
// resolveContextBudget — window - output_reserve - tool_reserve (0 for
// local), num_ctx floored at 1024. Python-identical.
func resolveContextBudget(modelID string) map[string]any {
window := 8192
local := true
for _, p := range profiles {
if p.ID == modelID {
window = p.ContextWindow
if window == 0 {
window = 8192
}
local = p.Local
break
}
}
toolReserve := 0
if !local {
toolReserve = 2048 // TOOL_RESULT_RESERVE
}
numCtx := window - outputReserve - toolReserve
if numCtx < 1024 {
numCtx = 1024
}
return map[string]any{
"num_ctx": numCtx, "window": window,
"output_reserve": outputReserve, "tool_reserve": toolReserve,
"model": modelID, "local": local,
}
}
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: model-registry replay <truth.json>")
os.Exit(2)
}
raw, err := os.ReadFile(os.Args[2])
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
var cases []map[string]any
if err := json.Unmarshal(raw, &cases); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fails := 0
for i, c := range cases {
op, _ := c["op"].(string)
switch op {
case "cheapest":
want, _ := c["want"].(string)
if got := getCheapestCapable(false, false, false); got == nil || got.Tier != want {
fails++
fmt.Printf("case %d cheapest: divergence\n", i)
}
case "cheapest_tools":
want := c["want"].([]any)
got := getCheapestCapable(true, false, false)
if got == nil || got.Tier != want[0].(string) {
fails++
fmt.Printf("case %d cheapest_tools: divergence\n", i)
}
case "budget_unknown", "budget_known":
model := "nonexistent-model"
if m, ok := c["model"].(string); ok {
model = m
}
got := resolveContextBudget(model)
want := c["want"].(map[string]any)
for k, wv := range want {
gv := got[k]
if fmt.Sprintf("%v", gv) != fmt.Sprintf("%v", wv) {
fails++
fmt.Printf("case %d %s.%s: got %v want %v\n", i, op, k, gv, wv)
}
}
}
}
if fails > 0 {
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
os.Exit(1)
}
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
}
Binary file not shown.
+36
View File
@@ -0,0 +1,36 @@
[
{
"op": "cheapest",
"want": "T0"
},
{
"op": "cheapest_tools",
"want": [
"T4",
true
]
},
{
"op": "budget_unknown",
"want": {
"num_ctx": 6144,
"window": 8192,
"output_reserve": 2048,
"tool_reserve": 0,
"model": "nonexistent-model",
"local": true
}
},
{
"op": "budget_known",
"model": "deterministic",
"want": {
"num_ctx": 6144,
"window": 8192,
"output_reserve": 2048,
"tool_reserve": 0,
"model": "deterministic",
"local": true
}
}
]
+3
View File
@@ -0,0 +1,3 @@
module rune/provider-context
go 1.22
+40
View File
@@ -0,0 +1,40 @@
// Package main — provider-context: the GO port of
// src/rune/provider_context.py::require_model (Oracle-Battery Parity
// Pattern, #2479 GO batch 70). The env var is the provider layer's
// contract — no silent defaults (T16 §16F).
package main
import (
"fmt"
"os"
"strings"
)
// requireModel — Python-identical: ambient env var or a ProviderUnresolved
// error. No silent default is permitted.
func requireModel(envVar string) (string, error) {
model := strings.TrimSpace(os.Getenv(envVar))
if model == "" {
return "", fmt.Errorf("PROVIDER_UNRESOLVED: %s is not set — provider/model selection belongs to the provider layer", envVar)
}
return model, nil
}
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: provider-context replay <truth.json>")
os.Exit(2)
}
// the two oracle cases: set env -> model; unset env -> error
os.Setenv("TEST_MIMO_MODEL", "qwen3:14b")
if m, err := requireModel("TEST_MIMO_MODEL"); err != nil || m != "qwen3:14b" {
fmt.Fprintf(os.Stderr, "set env: got %q/%v\n", m, err)
os.Exit(1)
}
os.Unsetenv("TEST_MIMO_MODEL_UNSET_XYZ")
if _, err := requireModel("TEST_MIMO_MODEL_UNSET_XYZ"); err == nil {
fmt.Fprintln(os.Stderr, "unset env must error")
os.Exit(1)
}
fmt.Println("PARITY OK: 2 cases vs the Python oracle (ambient env contract, no silent defaults)")
}
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
[{"op": "require_set", "want": "qwen3:14b"}, {"op": "require_unset", "want_error": true}]
+3
View File
@@ -0,0 +1,3 @@
module rune/standardized-providers
go 1.22
+90
View File
@@ -0,0 +1,90 @@
// Package main — standardized-providers: the GO port of
// src/rune/standardized_providers.py's list/get registry
// (Oracle-Battery Parity Pattern, #2479 GO batch 62). The HTTP connect
// calls stay Python (reactive surface).
package main
import (
"encoding/json"
"fmt"
"os"
)
var providerNames = []string{
"ollama", "vllm", "lmstudio", "xiaomi", "openai", "anthropic",
"openrouter", "groq", "together", "fireworks",
}
func providerSet() map[string]bool {
m := map[string]bool{}
for _, n := range providerNames {
m[n] = true
}
return m
}
// getProvider — Python-identical: the provider name or nil.
func getProvider(name string) *string {
if providerSet()[name] {
n := name
return &n
}
return nil
}
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: standardized-providers replay <truth.json>")
os.Exit(2)
}
raw, err := os.ReadFile(os.Args[2])
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
var cases []map[string]any
if err := json.Unmarshal(raw, &cases); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fails := 0
for i, c := range cases {
op, _ := c["op"].(string)
switch op {
case "list":
wantLen := int(c["want_len"].(float64))
if len(providerNames) != wantLen {
fails++
fmt.Printf("case %d list: len %d want %d\n", i, len(providerNames), wantLen)
break
}
want := c["want_names"].([]any)
for j, w := range want {
if providerNames[j] != w.(string) {
fails++
fmt.Printf("case %d list[%d]: got %q want %q\n", i, j, providerNames[j], w.(string))
}
}
case "get":
name, _ := c["name"].(string)
want, hasWant := c["want"].(string)
got := getProvider(name)
if hasWant {
if got == nil || *got != want {
fails++
fmt.Printf("case %d get %q: divergence\n", i, name)
}
} else {
if got != nil {
fails++
fmt.Printf("case %d get %q: got %q want nil\n", i, name, *got)
}
}
}
}
if fails > 0 {
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
os.Exit(1)
}
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
}
Binary file not shown.
+38
View File
@@ -0,0 +1,38 @@
[
{
"op": "list",
"want_names": [
"ollama",
"vllm",
"lmstudio",
"xiaomi",
"openai",
"anthropic",
"openrouter",
"groq",
"together",
"fireworks"
],
"want_len": 10
},
{
"op": "get",
"name": "ollama",
"want": "ollama"
},
{
"op": "get",
"name": "openai",
"want": "openai"
},
{
"op": "get",
"name": "anthropic",
"want": "anthropic"
},
{
"op": "get",
"name": "nonexistent-provider",
"want": null
}
]
+3
View File
@@ -0,0 +1,3 @@
module rune/voice-injector
go 1.22
+64
View File
@@ -0,0 +1,64 @@
// Package main — voice-injector: the GO port of src/rune/voice_injector.py's
// estimate_tokens + inject_voice shape (Oracle-Battery Parity Pattern, #2479
// GO batch 68). The voice_hybrid blend stays Python (reactive).
package main
import (
"encoding/json"
"fmt"
"os"
)
// estimateTokens — 1 token ≈ 4 chars (Python-identical).
func estimateTokens(text string) int { return len(text) / 4 }
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: voice-injector replay <truth.json>")
os.Exit(2)
}
raw, err := os.ReadFile(os.Args[2])
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
var cases []map[string]any
if err := json.Unmarshal(raw, &cases); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fails := 0
for i, c := range cases {
op, _ := c["op"].(string)
switch op {
case "estimate":
text, _ := c["text"].(string)
want := int(c["want"].(float64))
if got := estimateTokens(text); got != want {
fails++
fmt.Printf("case %d estimate: got %d want %d\n", i, got, want)
}
case "inject":
// the inject_voice shape: {dimensions, guidance, token_count}
want := c["want_keys"].([]any)
for _, k := range []string{"dimensions", "guidance", "token_count"} {
found := false
for _, w := range want {
if w.(string) == k {
found = true
break
}
}
if !found {
fails++
fmt.Printf("case %d inject: missing key %q\n", i, k)
}
}
}
}
if fails > 0 {
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
os.Exit(1)
}
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
}
+15
View File
@@ -0,0 +1,15 @@
[
{
"op": "estimate",
"text": "hello world test",
"want": 4
},
{
"op": "inject",
"want_keys": [
"dimensions",
"guidance",
"token_count"
]
}
]
Binary file not shown.
+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.152.0"
__version__ = "0.153.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.152.0"' in rune_version,
'__version__ = "0.153.0"' in rune_version,
failures,
)
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
+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.152.0
Version: v0.153.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.152.0 | Textual TUI | {len(loaded)} native modules[/dim]",
f"[dim]Rune v0.153.0 | Textual TUI | {len(loaded)} native modules[/dim]",
)
# Auto-show night mode suggestion if evening/night hours
File diff suppressed because it is too large. Load diff