Files
rune/scripts/gen_operator_lifecycle_truth.py
Nick 5a3f501351 feat(rune_operator): close TASK-LIFECYCLE/STATE family gaps — dichotomy wave
Name-preserving port of the 12 audit-missing lifecycle fns into
src/go/operator-deep/gaps_lifecycle.go (family lane factory-heavy-ro-life):
GetConversationWindow, TasksToDictList (list_tasks x2), RoutingProfilesPath,
ConstructionStorePath, ConstructionNoteDoc, the _construction_activation
glue (SplitScaffoldOverride/DeriveConstructionTarget/observation rows),
InitConversationFiles+MigrateLegacyTasks, LoadOperatorState,
SaveOperatorStateOrd (ordered Python json.dump byte-parity writer),
ClearPendingApproval, TerminalizeExecutionFailure, EmitTaskOutcomePayload,
EmitTaskFailedPayload. CLI dispatch via the 'lifecycle' subcommand.

Truth: truth_lifecycle.json 37 rows frozen under scripts/olc_freeze_seams
(pinned clock, deterministic uuid counter, fixed scratch, cone cores,
event_bus recorder) — freeze_truth --verify byte-identical x3. Battery
replays verbatim (ordered parse; save rows byte-compare). Live parity
35/35 (scripts/check_operator_lifecycle_parity.py) incl. env-unset derive
branches. gofmt/vet/build/test green; Python 65x2 both pins.

Dichotomy audit rune_operator 29/68 -> 42/68 (family complete; remaining
fns are sibling lanes). Divergences documented in
verification/olc-summary.md (segments-store evidence sort, migration msg-id
placeholders, checkpoint-window input, classifier-as-input seams).
2026-10-15 01:28:50 +01:00

266 lines
11 KiB
Python

#!/usr/bin/env python3
"""gen_operator_lifecycle_truth.py — shared-harness truth battery for the
rune_operator TASK-LIFECYCLE/STATE family gap closure (#2479, family lane
factory-heavy-ro-life).
Uses scripts/freeze_truth.py (the ONE truth law) + the
scripts/olc_freeze_seams rig (clock/ids/cores pinned, fixed-path scratch)
to freeze src/rune/rune_operator.py behavior for the Go port at
src/go/operator-deep (new family file gaps_lifecycle.go):
* get_conversation_window (module fn) / list_tasks (method + module fn)
* _routing_profiles_path / _construction_store / _construction_note
* _construction_activation (override regex, target derivation,
DISCUSSION meaning-gate observation, activation observation)
* _init_conversation_state (legacy operator.json migration report)
* _load (defaults / unknown-key filtering / corrupt-file degrade)
* _save (Python json.dump indent=2 file bytes)
* _clear_pending_approval (state triple-clear)
* _terminalize_execution_failure (task+canonical transition+envelope+emit)
* _emit_task_outcome / _emit_task_failed (journal payload contract)
The olc_* seams live in the rig (bound onto the oracle module); every
case drives the REAL oracle function on hermetic scratch state. The
clock is pinned process-wide (freeze_rigs pin_clock), uuid minting is a
deterministic per-call counter, and every emitted id/ts is therefore
byte-identical across processes. event_bus is swapped for an in-memory
recorder by the emit seams (journal transport = event_bus's own port
surface, src/go/event-bus).
Verify (byte-identical law):
PYTHONPATH=scripts/olc_freeze_seams:src python3 scripts/freeze_truth.py \
--verify src/go/operator-deep/truth_lifecycle.json \
--module rune.rune_operator
"""
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
FROZEN_NOW = "2026-10-14T22:40:00.123456+00:00"
_TASK_A = {
"task_id": "task_alpha",
"description": "fix the parser crash",
"status": "completed",
"assigned_model": "qwen3:14b",
"result": "replaced the regex and tests pass",
"created_at": "2026-10-14T19:00:00.000000+00:00",
"completed_at": "2026-10-14T19:05:00.000000+00:00",
"route": "LOCAL_FIX",
"route_instruction": "ROUTE LOCAL_FIX: do the edit.",
}
_TASK_B = {
"task_id": "task_beta",
"description": "research storage engines",
"status": "pending",
"assigned_model": None,
"result": None,
"created_at": "2026-10-14T19:10:00.000000+00:00",
"completed_at": None,
"route": "",
"route_instruction": "",
}
_STATE_RICH = {
"identity": "Rune",
"role": "Operator",
"current_task": "task_beta",
"active_model": "qwen3:14b",
"tasks": [_TASK_A, _TASK_B],
"pending_task_id": "task_beta",
"pending_request": {"name": "run_command", "args": ["make"]},
"pending_runtime_state": {"exposed_tools": ["read_file"]},
}
_STATE_DEFAULT = {
"identity": "Rune",
"role": "Operator",
"current_task": None,
"active_model": None,
"tasks": [],
"pending_task_id": None,
"pending_request": None,
"pending_runtime_state": None,
}
_MSGS = [
{"msg_id": "msg_1", "ts": "2026-10-14T19:00:01.000000+00:00",
"role": "user", "content": "first turn", "channel_id": "operator",
"task_id": "task_alpha", "token_estimate": 2},
{"msg_id": "msg_2", "ts": "2026-10-14T19:00:02.000000+00:00",
"role": "assistant", "content": "first reply", "channel_id": "operator",
"task_id": "task_alpha", "token_estimate": 2},
{"msg_id": "msg_3", "ts": "2026-10-14T19:00:03.000000+00:00",
"role": "user", "content": "second turn", "channel_id": "operator",
"task_id": "task_alpha", "token_estimate": 2},
{"msg_id": "msg_4", "ts": "2026-10-14T19:00:04.000000+00:00",
"role": "user", "content": "other channel", "channel_id": "voice",
"task_id": None, "token_estimate": 2},
]
_FIXTURE_FULL = """{
"identity": "Rune",
"role": "Operator",
"current_task": "task_alpha",
"active_model": "qwen3:14b",
"tasks": [
{
"task_id": "task_alpha",
"description": "fix the parser crash",
"status": "completed",
"assigned_model": "qwen3:14b",
"result": "replaced the regex and tests pass",
"created_at": "2026-10-14T19:00:00.000000+00:00",
"completed_at": "2026-10-14T19:05:00.000000+00:00",
"route": "LOCAL_FIX",
"route_instruction": "ROUTE LOCAL_FIX: do the edit."
},
{
"task_id": "task_beta",
"description": "research storage engines",
"status": "pending",
"assigned_model": null,
"result": null,
"created_at": "2026-10-14T19:10:00.000000+00:00",
"completed_at": null,
"route": "",
"route_instruction": ""
},
{
"task_id": "task_unknown_keys",
"description": "legacy row with extra keys",
"status": "running",
"future_field": "dropped by the dataclass-fields filter"
}
],
"pending_task_id": null,
"pending_request": null,
"pending_runtime_state": null
}
"""
_FIXTURE_EMPTY = "{}\n"
_FIXTURE_CORRUPT = "{not json at all"
_LONG_NOTE = "n" * 299 + "tail-overflow"
_VERIF_PASS = {"verification_id": "vr_task_alpha", "passed": True}
def main():
cases = [
# -- _routing_profiles_path (env branch; derive branch covered by
# the live parity sweep -- repo anchor is machine-specific).
# Seam str()s the PosixPath the oracle returns.
("olc_routing_profiles_path", (), {}),
# -- _construction_store (env branch, same law)
{"fn": "RuneOperator._construction_store",
"recv": {"cls": "RuneOperator"}, "args": [], "kwargs": {}},
# -- _clear_pending_approval: full triple-clear on a loaded state
{"fn": "RuneOperator._clear_pending_approval",
"recv": {"cls": "RuneOperator", "state": dict(_STATE_RICH)},
"args": [], "kwargs": {}},
# -- list_tasks (method): to_dict projection, None fields ride
{"fn": "RuneOperator.list_tasks",
"recv": {"cls": "RuneOperator", "state": dict(_STATE_RICH)},
"args": [], "kwargs": {}},
{"fn": "RuneOperator.list_tasks",
"recv": {"cls": "RuneOperator", "state": dict(_STATE_DEFAULT)},
"args": [], "kwargs": {}},
# -- list_tasks (module fn) via the _operator global
("olc_module_list_tasks", (dict(_STATE_RICH),), {}),
("olc_module_list_tasks", (dict(_STATE_DEFAULT),), {}),
# -- get_conversation_window (module fn): channel bucket, n clip,
# n<=0 -> empty, n beyond bucket
("olc_module_get_conversation_window", ([dict(m) for m in _MSGS],),
{"channel": "operator", "n": 2}),
("olc_module_get_conversation_window", ([dict(m) for m in _MSGS],),
{"channel": "operator", "n": 0}),
("olc_module_get_conversation_window", ([dict(m) for m in _MSGS],),
{"channel": "operator", "n": 8}),
("olc_module_get_conversation_window", ([dict(m) for m in _MSGS],),
{"channel": "voice", "n": 3}),
("olc_module_get_conversation_window", ([],),
{"channel": "operator", "n": 8}),
# -- _emit_task_outcome: completed+verification, failed bare,
# truncation [:200], route passthrough
("olc_emit_task_outcome", (dict(_TASK_A),),
{"channel": "operator", "verification": _VERIF_PASS, "route": "LOCAL_FIX"}),
("olc_emit_task_outcome", (dict(_TASK_A, status="failed", result="boom"),),
{"channel": ""}),
("olc_emit_task_outcome",
(dict(_TASK_A, description="d" * 250, result="r" * 250,
assigned_model=None),),
{"channel": "operator"}),
# -- _emit_task_failed: error truncation [:300], model None
("olc_emit_task_failed", (dict(_TASK_A, status="failed", result="e" * 350),),
{"channel": "operator"}),
("olc_emit_task_failed",
(dict(_TASK_B, status="failed", result="short failure", assigned_model=None),),
{"channel": ""}),
# -- _save: file bytes (Python json.dump indent=2 shape)
("olc_save", (dict(_STATE_RICH),), {}),
("olc_save", (dict(_STATE_DEFAULT),), {}),
# -- _load: full fixture / empty dict defaults / unknown-key
# filtering rides _FIXTURE_FULL / corrupt JSON degrades to the
# fresh default state (logged, not silent)
("olc_load", (_FIXTURE_FULL,), {}),
("olc_load", (_FIXTURE_EMPTY,), {}),
("olc_load", (_FIXTURE_CORRUPT,), {}),
# -- _init_conversation_state: no operator.json / two-task
# migration with current_task restore / idempotent rerun /
# unknown-status degrade (report None)
("olc_init_conv_state", (None,), {}),
("olc_init_conv_state", (_FIXTURE_FULL,), {}),
("olc_init_conv_state", (_FIXTURE_FULL,), {"rerun": True}),
("olc_init_conv_state",
('{"tasks": [{"task_id": "task_bad", "status": "weird"}]}',), {}),
# -- _construction_note: append + [:300] truncation
("olc_construction_note", ("activated LOCAL_CHANGE for the parser",), {}),
("olc_construction_note", (_LONG_NOTE,), {}),
# -- _construction_activation: override marker / DISCUSSION
# meaning-gate / rune target / standalone target / existing
# target / unknown target
("olc_construction_activation",
("/scaffold-mode LOCAL_CHANGE build the parser fix", "t_ovr"),
{"channel": "operator"}),
("olc_construction_activation",
("what do you think about the storage design?", "t_disc"),
{"channel": "operator"}),
("olc_construction_activation",
("fix the rune operator bug", "t_rune"),
{"channel": "operator"}),
("olc_construction_activation",
("build the standalone timer utility", "t_new"),
{"channel": "operator"}),
("olc_construction_activation",
("change the existing project config", "t_exist"),
{"channel": "operator"}),
("olc_construction_activation",
("do the thing", "t_unk"),
{"channel": "operator"}),
# -- _terminalize_execution_failure: defaults + explicit error +
# long result text
("olc_terminalize",
(dict(_TASK_B, status="running"), "outcome-ref-1", "research dispatch failed"),
{"channel": "operator", "model": "qwen3:14b", "error": None}),
("olc_terminalize",
(dict(_TASK_B, status="running"), "outcome-ref-2", "explode"),
{"channel": "", "model": "", "error": "ValueError: bad input"}),
("olc_terminalize",
(dict(_TASK_B, status="running"), "outcome-ref-3", "x" * 350),
{"channel": "operator", "model": "", "error": None}),
]
dest = REPO / "src/go/operator-deep/truth_lifecycle.json"
sys.path.insert(0, str(REPO / "scripts"))
sys.path.insert(0, str(REPO / "src"))
from freeze_truth import freeze, write
truth = freeze("rune.rune_operator", cases, oracle_module="rune.rune_operator")
write(truth, dest)
print(f"wrote {len(truth)} cases -> {dest}")
if __name__ == "__main__":
main()