Compare commits

...
73 Commits
Author SHA1 Message Date
conductor e3d0560ed5 chore(release): auto-bump version to 0.239.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 5s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 16s
Auto-version and Release / release (push) Successful in 31s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / type-check (push) Failing after 40s
CI Pipeline / lint (push) Failing after 57s
CI Pipeline / canary (push) Failing after 2m3s
CI Pipeline / unit-tests (push) Failing after 16m29s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 4s
Promotion record: (unknown)
2026-10-12 15:42:56 +01:00
Nick 4f0556fe16 chore(store): close curator dichotomy contract BUILDER-2026-10-12T12:25:33Z-a87985-factory-wave (parity 03ef8132, phase B 89b691b2)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 12s
CI Pipeline / go-surface (push) Failing after 15s
CI Pipeline / gitea-offline (push) Successful in 17s
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 21s
Auto-version and Release / release (push) Canceled after 31s
CI Pipeline / canary (push) Canceled after 31s
CI Pipeline / lint (push) Canceled after 34s
CI Pipeline / unit-tests (push) Canceled after 35s
deploy / deploy (push) Canceled after 26s
2026-10-12 15:42:26 +01:00
Nick f61a4a3fa3 chore(store): register curator dichotomy contract BUILDER-2026-10-12T12:25:33Z-a87985-factory-wave (verifying) 2026-10-12 15:41:42 +01:00
Nick 89b691b210 refactor(curator): Phase B language-advantage pass — dichotomy wave
Allocation-discipline pass over the curator kernel, pinned by the unchanged
frozen-truth battery (truth.json byte-identical to the parity commit, green
throughout):

- NewOp: pre-sized op-row builder with the six canonical keys in oracle
  order replaces inline map literals on the op-generation paths.
- AppendJournal: append-in-place journal growth (no intermediate slices).
- JoinLines: single-pass body rebuild for the ADD path.
- OpKeys documents the observable op-row envelope.

Live parity re-verified post-pass: battery 162/162 + phaseb 3/3.
2026-10-12 15:35:46 +01:00
Nick 03ef8132a2 feat(curator): port to FULL Go — dichotomy wave
DICHOTOMY VERDICT: GO (Reactive → Go per store/rebase/classification.json).
Name-preserving full-module port of src/rune/curator.py (43 fns) into
src/go/curator/ (package main, modernc.org/sqlite pure-Go store seam).

Truth: scripts/gen_curator_truth.py freezes 162 rows from the live oracle
(paths isolated, dependency cores pinned RUNE_*_CORE=python, clock frozen,
subprocess faked, embed tabled, aaak/memory_store blocked, drift-gate/audit/
drift-ledger seams injected, deep-copied inputs, per-row setup seeds).
Battery truth_test.go replays every row verbatim: 162/162 + live
re-verified + generator deterministic (byte-identical re-freeze).

Python oracle untouched (no facade delegation — strangler surface stays
Python by design); Python contract tests green.
2026-10-12 15:35:46 +01:00
Nick e29cc5ee51 chore(store): close drift_ledger dichotomy contract BUILDER-2026-10-12T13:54:01Z-950331-factory-wave (parity c0ec3135, phase B 62e47b38)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 16s
CI Pipeline / gitea-offline (push) Successful in 16s
Auto-version and Release / release (push) Successful in 31s
CI Pipeline / type-check (push) Failing after 44s
CI Pipeline / lint (push) Failing after 1m2s
CI Pipeline / canary (push) Failing after 2m13s
deploy / deploy (push) Failing after 15m33s
CI Pipeline / unit-tests (push) Failing after 16m40s
CI Pipeline / required-checks (push) Failing after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 4s
2026-10-12 14:55:40 +01:00
Nick 8ed117abe2 chore(store): close construction_patterns_builtin dichotomy contract BUILDER-2026-10-12T13:29:58Z-e5c619-factory-wave (parity 4649df1c, phase B b50e8553)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 7s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 18s
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / type-check (push) Failing after 49s
CI Pipeline / lint (push) Failing after 1m6s
CI Pipeline / canary (push) Failing after 2m17s
deploy / deploy (push) Failing after 15m34s
CI Pipeline / unit-tests (push) Failing after 16m48s
CI Pipeline / required-checks (push) Failing after 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 4s
2026-10-12 14:37:52 +01:00
conductor 633af06e8a chore(release): auto-bump version to 0.238.0 — earned by # (minor: converged release (push-to-master bump))
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 16s
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / type-check (push) Failing after 45s
CI Pipeline / lint (push) Failing after 1m2s
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 2m0s
CI Pipeline / canary (push) Canceled after 2m0s
deploy / deploy (push) Canceled after 1m51s
Promotion record: (unknown)
2026-10-12 14:35:45 +01:00
Nick 62e47b3812 refactor(drift_ledger): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 4s
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
Auto-version and Release / release (push) Canceled after 36s
CI Pipeline / gitea-offline (push) Canceled after 18s
CI Pipeline / lint (push) Canceled after 36s
CI Pipeline / type-check (push) Canceled after 22s
CI Pipeline / unit-tests (push) Canceled after 35s
CI Pipeline / canary (push) Canceled after 36s
deploy / deploy (push) Canceled after 39s
Language-advantage pass on the drift_ledger Rust kernel (battery green
throughout, live parity re-verified 87/87 byte-identical after rebuild):
- Block lattice is a real enum (Constitution..Project) with as_str/of_name/
  limit matches — the constitution's stricter limit and its ANY-content-
  change strict check are exhaustive matches, not string compares; the
  per-block alert loop and snapshot assembly iterate BlockLabel::ALL.
- check_alerts is iterator-composed: BlockLabel::ALL.filter_map over the
  baseline decision, emitting alerts via then(..) — the python loop's
  anchor-vs-snapshot branch stays a plain match.
- diff_blocks is zero-copy iterator composition (filter_map + take(6)/
  take(3) replaces the manual capped pushes); the whitespace-significance
  filter is one shared fn item over &str slices.
- All keyword tables and contents borrow (&str / &Value) end-to-end — no
  per-block copying or lowering.
- Frozen DL truth replay untouched and VERBATIM (branch_battery.rs replays
  the same 87-case oracle truth).
2026-10-12 14:33:28 +01:00
Nick c0ec3135c6 feat(drift_ledger): port remaining cores to FULL Rust — dichotomy wave
DICHOTOMY VERDICT (dichotomy law, #2479 tranche): drift_ledger's
deterministic cores complete their Rust port in src/rust/drift_ledger —
the check() alert loop (anchor-vs-snapshot baseline, the constitution's
ANY-content-change strict check, max(prev_size,1) floor, pct/limit
round-3 with Python ties-to-even semantics, root-cause diff per alert),
snapshot/anchor entry assembly (size = the oracle's len() CHARACTER count),
the auto-fix routing and the retroactive fix() gate. SQLite store IO
(read_block/restore_topic) and file IO (load/save ledger, journal,
surface_alert) stay facade seams by design — the clock travels in the
doc envelope.

Wiring: dl_loader.py grows dl_ledger_path/dl_snapshot_entry/
dl_anchor_record/dl_check_alerts/dl_fix_decision (doc-envelope FFI,
oracle-verbatim dict unwrap, free via capability_scoring cs_free_string
— no free symbol re-export); facade delegation in drift_ledger.py with
the RUNE_DL_CORE=python pin and byte-identical python fallbacks.

Truth: scripts/gen_drift_ledger_truth.py freezes 87 cases from the live
oracle across EVERY public function and branch (sha, diff caps/truncation,
ledger path joins, load/save ledger bytes, store read filters and order,
journal/surface_alert file formats, snapshot/anchor/check/auto_fix/fix
end-to-end flows with frozen clock, restore_topic directive preservation
+ priority map, handle_hook, main CLI exits, and the kernel-level
k_snapshot/k_anchor/k_alerts/k_auto_fix/k_fix groups) — battery 8 tests
replay it VERBATIM; live parity 87/87 byte-identical under RUNE_DL_CORE=auto.

Audit instrument fixes (bug-first, with proof): module_to_keys now maps
hyphenated legacy twins to their underscore implementation (drift-ledger.py
— the same ledger — was scored NOT TOUCHED against an empty corpus) and
curator gains its curator-core alias (a FULL Go port since a95c6ce1 was
invisible to the audit). Honest post-fix queue: curator 40/43 FULL,
drift-ledger 14/14 FULL, drift_ledger 20/22 FULL; function-level
1020/2781 (37%), FULL 36 PARTIAL 93 NOT TOUCHED 108.
2026-10-12 14:33:28 +01:00
conductor 622cceb2f9 chore(release): auto-bump version to 0.237.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 12s
CI Pipeline / go-surface (push) Failing after 15s
Auto-version and Release / release (push) Successful in 29s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / type-check (push) Failing after 44s
CI Pipeline / lint (push) Failing after 58s
CI Pipeline / canary (push) Failing after 2m6s
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 9m6s
Promotion record: (unknown)
2026-10-12 14:25:57 +01:00
Nick b50e8553d7 refactor(construction_patterns_builtin): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 15s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
Auto-version and Release / release (push) Canceled after 28s
deploy / deploy (push) Canceled after 15s
CI Pipeline / type-check (push) Canceled after 21s
CI Pipeline / canary (push) Canceled after 32s
CI Pipeline / unit-tests (push) Canceled after 33s
CI Pipeline / lint (push) Canceled after 34s
Typed registry: the frozen oracle docs parse once into BuiltinPattern
values (pattern_id/revision extracted, doc kept oracle-shaped); the FFI
re-emits from the typed value instead of re-plumbing raw Json. Miss-
before-revision-check oracle ordering is now explicit in the typed
lookup. Registry revision pinned in its own OnceLock.

Truth extended (+2 cases from the live oracle, RUNE_CPB_CORE=python
pinned): unknown id WITH a revision -> None (miss is decided before the
revision check — oracle order) and the revision=0 boundary on a real
pattern (ValueError, exact message). Branch battery now 19 cases,
replayed verbatim, green throughout; clippy 0; live parity 16/16.
2026-10-12 14:25:29 +01:00
Nick 4649df1ccc feat(construction_patterns_builtin): port to FULL Rust — dichotomy wave
RUST verdict (dichotomy law, #2479 tranche): construction_patterns_
builtin.py's registry surface is ported to src/rust/construction_
patterns_builtin — the four builtin pattern docs (capability-module-
adaptation, release-deployment-change, ui-tui-structural-change, store-
schema-evolution) served verbatim from frozen_cpb_patterns.json (deep-
copied from the live oracle's to_dict() output incl. __init__-sorted
provenance order), PATTERN_REGISTRY_REVISION, and get_pattern's full
branch set (hit per pattern_id, unknown/empty-id miss -> None, revision
match passthrough, revision mismatch with the oracle's exact ValueError
message).

Wiring: cpb_loader.py ctypes loader mirroring cs_loader.py/broker_
loader.py (doc-envelope FFI cpb_registry_revision/cpb_patterns/cpb_
get_pattern, free via capability_scoring cs_free_string — never
re-exported, CPB_RUST_LIB override), facade delegation in construction_
patterns_builtin.get_pattern with RUNE_CPB_CORE=python fallback pin and
strangler fallback to the Python registry when the .so is absent.

Truth: frozen_cpb_truth.json — 17 cases from the live oracle (deep-
copied inputs, RUNE_CPB_CORE=python pinned, every public fn and every
branch incl. all four patterns and the revision-mismatch raise),
replayed VERBATIM by the in-crate cfg(test) branch battery. Live parity
check (scripts/check_cpb_parity.py): 16/16 facade + kernel-surface
cases. clippy: 0 warnings. Consumer suites green (test_construction_
pattern 30, project_build_lifecycle, provider_ownership).

Audit fix (fix-first with proof): dichotomy_audit.py now aliases
curator -> curator-core (ported FULL Go in a95c6ce1 but mislabeled NOT
TOUCHED — the alias was missing from module_to_keys). Regenerated
audit: curator 40/43 FULL; function-level completion 34% -> 36%.
2026-10-12 14:25:29 +01:00
Nick fa4f9a2cbb chore(store): mutation_guards contract evidence — final SHAs after rebase: parity e9d3efae, phase B 27afd002 (contract BUILDER-2026-10-12T12:56:54Z-2ab0e0-factory-wave)
Auto-version and Release / release (push) Successful in 34s
deploy / deploy (push) Skipped
CI Pipeline / lint (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 0s
CI Pipeline / go-surface (push) Canceled after 0s
CI Pipeline / rust-surface (push) Canceled after 0s
CI Pipeline / canary (push) Canceled after 0s
CI Pipeline / best-practices (push) Canceled after 0s
CI Pipeline / documentation (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 0s
CI Pipeline / gitea-offline (push) Canceled after 0s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
2026-10-12 14:00:44 +01:00
conductor c4aa7dfab7 chore(release): auto-bump version to 0.236.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 5s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 18s
Auto-version and Release / release (push) Successful in 27s
CI Pipeline / gitea-offline (push) Successful in 20s
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 51s
CI Pipeline / lint (push) Canceled after 51s
CI Pipeline / type-check (push) Canceled after 41s
CI Pipeline / canary (push) Canceled after 56s
Promotion record: (unknown)
2026-10-12 13:59:43 +01:00
Nick fe2755dbfc chore(store): close mutation_guards dichotomy contract (parity 317308cb, phase B 2c1cf554)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 7s
CI Pipeline / rust-surface (push) Failing after 12s
CI Pipeline / go-surface (push) Failing after 17s
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 21s
CI Pipeline / canary (push) Canceled after 21s
Auto-version and Release / release (push) Canceled after 29s
deploy / deploy (push) Canceled after 5s
CI Pipeline / type-check (push) Canceled after 15s
CI Pipeline / gitea-offline (push) Canceled after 17s
CI Pipeline / lint (push) Canceled after 26s
2026-10-12 13:58:41 +01:00
Nick 27afd002a7 refactor(mutation_guards): Phase B language-advantage pass — dichotomy wave
The classification surface is parsed ONCE into Kind/Op/Scope enums
(unknown values become the exhaustive Other variant, preserving the
oracle's severity-default quirk), so every downstream rule — severity
tables, the identity-family guard, the destructive gate — is an
exhaustive match over typed variants instead of repeated string
comparisons; the verdict is a Verdict enum with as_str, the destructive
gate is a named is_destructive_unbacked predicate over typed variants,
and the whole return shape is one Decision struct projected to JSON in a
single place.

Live parity re-verified post-pass: facade 43/43 + kernel 43/43
(scripts/gen_mutation_guards_truth.py --replay), clippy 0, cargo fmt
clean, test_governance_quartet 15/15 both modes, battery verbatim
unchanged.
2026-10-12 13:58:41 +01:00
Nick e9d3efae5d feat(mutation_guards): port to FULL Rust — dichotomy wave
DICHOTOMY VERDICT: RUST (deterministic classification core — severity
lattices, branch order, verdict+reason+recovery are pure logic; zero IO).

Ported (name-preserving, src/rust/mutation_guards/src/lib.rs):
- classify_mutation (the full gate flow: unattributed identity-family
  DENY first — it beats read ops and the destructive gate — then the
  destructive-unbacked DENY, CONFIRM via score >= 4 or identity-kind +
  mutating-op, ALLOW; severity-table defaults for unknown kind/op/scope)
- guard_mutation (dict.get defaults: missing-key default vs present-null
  falsy passthrough — 0/[]/None falsy, non-empty strings truthy)
- mu_classify/mu_guard FFI exports (doc-envelope law: one JSON doc in,
  {ok, value|err} out; no free-string export — callers free through
  capability_scoring's cs_free_string)

Python semantics preserved exactly: Python truthiness over JSON values,
str(None) rendering in the reason f-strings, the exact reason/recovery
strings.

Truth: scripts/gen_mutation_guards_truth.py freezes 43 cases from the
live oracle (frozen_mg_truth.json embedded verbatim; RUNE_MG_CORE=python
pinned, deep-copied inputs) — every branch of both public functions:
DENY-1 precedence proofs (falsy 0/None attribution, read-op denial),
DENY-2 boundary (op/kind severity-2, falsy-zero backup), CONFIRM score-4
boundary and identity-rule, truthy non-bool backups, ALLOW lattice with
unknown kind/op/scope defaults, null kind/operation rendered as None,
guard defaults/extra-keys/empty-mutation. Battery green VERBATIM in-crate
(whole-doc compare). Live parity: facade 43/43 + kernel 43/43 (direct
loader), RUNE_MG_CORE=python fallback 43/43. Python tests green both
modes (test_governance_quartet 15/15).

Contract: BUILDER-2026-10-12T12:11:14Z-ab92a8-factory-wave (walked
draft→approved→building→verifying→closed back-to-back in ONE process,
scripts/open_contract_mutation_guards.py).
2026-10-12 13:58:40 +01:00
Nick 569ee78d0a chore(scripts): spine_language dichotomy contract walk script
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
Auto-version and Release / release (push) Successful in 29s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / type-check (push) Failing after 41s
CI Pipeline / lint (push) Failing after 58s
CI Pipeline / canary (push) Failing after 2m38s
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 4m40s
deploy / deploy (push) Canceled after 4m46s
2026-10-12 13:53:54 +01:00
conductor 9ebf7865ca chore(release): auto-bump version to 0.235.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 13s
CI Pipeline / go-surface (push) Failing after 16s
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 21s
CI Pipeline / type-check (push) Failing after 41s
CI Pipeline / lint (push) Failing after 58s
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 1m21s
CI Pipeline / unit-tests (push) Canceled after 1m26s
Promotion record: (unknown)
2026-10-12 13:53:06 +01:00
Nick 9d23d3f50a chore(store): close spine_language dichotomy contract BUILDER-2026-10-12T12:50:19Z-9317b1-factory-wave (parity 79b6e381, phase B 2d13846f)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 16s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
deploy / deploy (push) Canceled after 15s
CI Pipeline / type-check (push) Canceled after 21s
Auto-version and Release / release (push) Canceled after 30s
CI Pipeline / canary (push) Canceled after 32s
CI Pipeline / lint (push) Canceled after 34s
CI Pipeline / unit-tests (push) Canceled after 35s
2026-10-12 13:52:16 +01:00
Nick 2d13846f86 refactor(spine_language): Phase B language-advantage pass — dichotomy wave 2026-10-12 13:50:14 +01:00
Nick 79b6e38148 feat(spine_language): port to FULL Rust — dichotomy wave 2026-10-12 13:50:14 +01:00
Nick 875ca43ad1 chore(store): close abstraction_ladder dichotomy contract BUILDER-2026-10-12T12:12:17Z-a146ec-factory-wave (parity 4d0883b3, phase B 00bf4d21)
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 18s
Auto-version and Release / release (push) Successful in 27s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / type-check (push) Failing after 47s
CI Pipeline / lint (push) Failing after 1m0s
CI Pipeline / canary (push) Failing after 2m12s
deploy / deploy (push) Failing after 15m35s
CI Pipeline / unit-tests (push) Failing after 16m45s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 4s
2026-10-12 13:13:36 +01:00
Nick b52aa5bbbc chore(store): dedupe observation append in behaviour_ledger contract close
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 16s
CI Pipeline / go-surface (push) Failing after 19s
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / type-check (push) Failing after 46s
CI Pipeline / lint (push) Failing after 1m4s
CI Pipeline / canary (push) Failing after 2m17s
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 6m41s
deploy / deploy (push) Canceled after 6m46s
2026-10-12 13:06:57 +01:00
conductor 165b4bd8d3 chore(release): auto-bump version to 0.234.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 17s
Auto-version and Release / release (push) Successful in 28s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / type-check (push) Failing after 44s
CI Pipeline / lint (push) Failing after 58s
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 1m55s
CI Pipeline / canary (push) Canceled after 1m56s
Promotion record: (unknown)
2026-10-12 13:04:57 +01:00
Nick 00bf4d210c refactor(abstraction_ladder): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 16s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / lint (push) Canceled after 25s
CI Pipeline / canary (push) Canceled after 25s
CI Pipeline / unit-tests (push) Canceled after 25s
CI Pipeline / gitea-offline (push) Canceled after 11s
deploy / deploy (push) Canceled after 29s
Auto-version and Release / release (push) Canceled after 31s
CI Pipeline / type-check (push) Canceled after 15s
2026-10-12 13:04:29 +01:00
Nick 4d0883b39c feat(abstraction_ladder): port to FULL Rust — dichotomy wave 2026-10-12 13:04:29 +01:00
Nick 352299b614 chore(store): close behaviour_ledger dichotomy contract BUILDER-2026-10-12T09:54:16Z-77fa9d-factory-wave (parity 44b25064, phase B ba54bc6e)
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 16s
CI Pipeline / go-surface (push) Failing after 22s
Auto-version and Release / release (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 23s
CI Pipeline / type-check (push) Failing after 49s
CI Pipeline / lint (push) Failing after 1m8s
CI Pipeline / canary (push) Failing after 2m26s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
deploy / deploy (push) Canceled after 3m51s
CI Pipeline / unit-tests (push) Canceled after 3m46s
2026-10-12 13:00:39 +01:00
conductor a26c5004a2 chore(release): auto-bump version to 0.233.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 12s
CI Pipeline / go-surface (push) Failing after 15s
Auto-version and Release / release (push) Successful in 26s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / type-check (push) Failing after 41s
CI Pipeline / lint (push) Failing after 56s
CI Pipeline / canary (push) Failing after 2m6s
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 15m6s
Promotion record: (unknown)
2026-10-12 12:45:32 +01:00
Nick ba54bc6e8f refactor(behaviour_ledger): Phase B language-advantage pass — dichotomy wave
CI Pipeline / documentation (push) Successful in 7s
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 16s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / lint (push) Canceled after 26s
CI Pipeline / unit-tests (push) Canceled after 26s
CI Pipeline / canary (push) Canceled after 26s
CI Pipeline / gitea-offline (push) Canceled after 18s
Auto-version and Release / release (push) Canceled after 28s
deploy / deploy (push) Canceled after 15s
CI Pipeline / type-check (push) Canceled after 21s
2026-10-12 12:45:03 +01:00
Nick 44b25064c9 feat(behaviour_ledger): port to FULL Rust — dichotomy wave 2026-10-12 12:45:03 +01:00
Nick 7c83200c2e chore(store): close experience_encoding dichotomy contract BUILDER-2026-10-12T10:31:15Z-0436f5-factory-wave (parity d929b9a0, phase B 6a311f80)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / rust-surface (push) Failing after 17s
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / go-surface (push) Failing after 20s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / type-check (push) Failing after 43s
CI Pipeline / lint (push) Failing after 59s
CI Pipeline / canary (push) Failing after 2m12s
deploy / deploy (push) Failing after 15m36s
CI Pipeline / unit-tests (push) Failing after 16m33s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 5s
2026-10-12 12:09:14 +01:00
conductor 9b7565b536 chore(release): auto-bump version to 0.232.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 17s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / rust-surface (push) Failing after 25s
CI Pipeline / go-surface (push) Failing after 26s
Auto-version and Release / release (push) Successful in 43s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / lint (push) Failing after 2m1s
CI Pipeline / type-check (push) Failing after 1m39s
CI Pipeline / canary (push) Failing after 3m38s
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 7m1s
Promotion record: (unknown)
2026-10-12 12:02:45 +01:00
Nick 6a311f80d3 refactor(experience_encoding): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / rust-surface (push) Failing after 17s
CI Pipeline / go-surface (push) Failing after 24s
CI Pipeline / gitea-offline (push) Successful in 27s
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 41s
CI Pipeline / lint (push) Canceled after 41s
CI Pipeline / canary (push) Canceled after 41s
deploy / deploy (push) Canceled after 26s
Auto-version and Release / release (push) Canceled after 45s
CI Pipeline / type-check (push) Canceled after 42s
Rust-advantage pass over the experience_encoding kernel, pinned by the
unchanged frozen-truth battery (whole-doc replay, battery file byte-
identical to the parity commit, green throughout):

- Admission enum (Ignore/Evidence/Episode) with exhaustive as_str match
  replaces the string-verdict soup.
- Reason enum with Display carries the oracle's reason strings
  byte-for-byte; the EPISODE share reason materializes its sorted-top3
  term list lazily (Shares(BTreeSet<String>) → take(3) at Display time).
- Decision enum (Ignore/Evidence/Episode(Reason)) drives the whole gate
  flow with exhaustive matches — verdict() and reason() projections
  reconstruct the oracle doc; every arm statically total.
- Duplicate scan compares whitespace-token iterators over the lowered
  texts (split_whitespace().eq) — NEITHER " ".join(...) normal form is
  materialized anymore.
- Episode text extraction is a zero-copy filter_map over borrowed
  episode values (no intermediate Option juggling).

Live parity re-verified post-pass: facade 29/29 + kernel 29/29
(scripts/gen_experience_encoding_truth.py --replay), clippy 0,
test_governance_quartet 15/15.
2026-10-12 12:00:34 +01:00
Nick d929b9a068 feat(experience_encoding): port to FULL Rust — dichotomy wave
RUST verdict (dichotomy law, #2479 tranche, contract
BUILDER-2026-10-12T10:31:15Z-0436f5-factory-wave): experience_encoding's
single admission decision is ported to src/rust/experience_encoding —
the full event → evaluate → classify → attach-provenance flow (empty/
short char-count gate with its 14/15 boundary, whitespace/case-insensitive
duplicate scan over normalized episode texts, framing-only no-lexical-
signal filter, grounded → EPISODE/EVIDENCE split with sorted-top3 share
reasons and novel-axis grounding, provenance attachment with the
dict.get quirk preserved: a present-but-null source/session_id pins as
null — the default applies only to a MISSING key).

LEXICON-SEAM LAW mirrored: the oracle's flat `import lexicon_evolution`
is an environment seam (ImportError → conservative evidence admission,
which also BEATS a would-be duplicate because the dup check runs inside
the try). The Rust kernel mirrors it via the payload's
`lexicon_available` flag; the facade derives it from the live import.
The lexical decisions themselves cross to the lexicon_evolution sibling
crate IN-PROCESS (library dependency, the exact mirror of the oracle's
import — no FFI hop).

Wiring: ee_loader.py ctypes loader mirroring broker_loader.py/le_loader.py
(doc-envelope FFI, oracle-verbatim dict unwrap, free via
capability_scoring cs_free_string — no free symbol re-export), facade
delegation in experience_encoding.py with RUNE_EE_CORE=python fallback
pin, _py_evaluate preserved as the load-bearing oracle body.

Truth: frozen_ee_truth.json — 29 cases from the live oracle (deep-copied
inputs, RUNE_EE_CORE=python pinned, every branch incl. all three
verdicts and all five reasons, provenance null/empty-string quirks,
punctuation-survives-normalization non-duplicate, lexicon-blocked
ordering proofs, corpus fallback to episode texts, corpus override
killing the share but not the grounding), replayed VERBATIM by the
in-crate branch battery (whole-doc compare). Live parity: facade 29/29 +
kernel 29/29 (direct loader), RUNE_EE_CORE=python fallback 29/29.
Python tests green both modes (test_governance_quartet 15/15).
2026-10-12 11:56:55 +01:00
Nick 5680ec3e7b chore(store): close construction_pattern dichotomy contract BUILDER-2026-10-12T10:14:24Z-3f84ac-factory-wave (parity 681312c6, phase B c42936b9)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 12s
CI Pipeline / go-surface (push) Failing after 15s
Auto-version and Release / release (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / type-check (push) Failing after 43s
CI Pipeline / lint (push) Failing after 56s
CI Pipeline / canary (push) Failing after 2m4s
deploy / deploy (push) Failing after 15m30s
CI Pipeline / unit-tests (push) Failing after 16m33s
CI Pipeline / required-checks (push) Failing after 2s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 6s
2026-10-12 11:16:53 +01:00
Nick 9856b2b383 chore(store): close openai_compat_transport dichotomy contract BUILDER-2026-10-12T03:02:45Z-bd521d-factory-wave (parity 2e3b7e76)
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 11s
CI Pipeline / go-surface (push) Failing after 15s
Auto-version and Release / release (push) Successful in 26s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / type-check (push) Failing after 41s
CI Pipeline / lint (push) Failing after 53s
CI Pipeline / canary (push) Failing after 2m2s
deploy / deploy (push) Failing after 15m27s
CI Pipeline / unit-tests (push) Failing after 16m4s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 4s
2026-10-12 04:05:15 +01:00
conductor 5501163ec5 chore(release): auto-bump version to 0.231.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 4s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 17s
Auto-version and Release / release (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / type-check (push) Failing after 41s
CI Pipeline / lint (push) Failing after 53s
CI Pipeline / canary (push) Failing after 2m3s
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 4m41s
Promotion record: (unknown)
2026-10-12 04:00:30 +01:00
Nick 8f4b4b4414 chore(store): close worthiness dichotomy contract BUILDER-2026-10-12T02:14:11Z-69cc9a-factory-wave (parity 5ac385832f, phase B 4eed73b551)
CI Pipeline / best-practices (push) Successful in 5s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / rust-surface (push) Failing after 11s
CI Pipeline / go-surface (push) Failing after 17s
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 11s
CI Pipeline / canary (push) Canceled after 20s
Auto-version and Release / release (push) Canceled after 29s
CI Pipeline / gitea-offline (push) Canceled after 11s
CI Pipeline / lint (push) Canceled after 26s
CI Pipeline / unit-tests (push) Canceled after 26s
deploy / deploy (push) Canceled after 36s
2026-10-12 03:59:59 +01:00
Nick 4eed73b551 refactor(worthiness): Phase B language-advantage pass — dichotomy wave
Language-advantage pass on the worthiness Rust kernel (battery green
throughout, live parity re-verified 51/51 facade + 51/51 kernel after
rebuild):
- Question lattice is a real enum (PiSafety..Expression) with exhaustive
  as_str/of_name/run matches — no stringly-typed question routing; the
  two-axis gate (expression only for expressive targets, epistemology
  skipped unless bare-factual) is an exhaustive runs_for match.
- Verdict lattice is an enum (Reject | Defer | Absorb | Promote) produced
  from the oracle threshold order; the doc envelope reads as_str().
- assess is iterator-composed: QUESTIONS.filter(runs_for).try_fold over
  the (trail, total) accumulator with early exit on kernel error, keeping
  the last-veto-wins overwrite law as a plain assignment in the fold.
- q_honesty / q_material_reality / q_assistant_goal score branches are
  exhaustive matches instead of cascaded ifs.
- is_bare_factual runs zero-copy for single-word absolutes (lazy
  split(' ') token equality reproduces the oracle's padded f" {t} " in
  f" {low} " check exactly); only multi-word terms materialize a padded
  haystack. The oracle's subjective-predicate dead code stays dead.
- All keyword tables borrow (&[&str]) with low: &str threaded by
  reference end-to end — no per-question lowering or copying.
- Battery constants test moved to the enum surface (as_str projection);
  frozen_wr_truth.json replay untouched and VERBATIM.
2026-10-12 03:59:47 +01:00
Nick 5ac385832f feat(worthiness): port to FULL Rust — dichotomy wave
RUST verdict (dichotomy law, #2479 tranche, contract
BUILDER-2026-10-12T02:14:11Z-69cc9a-factory-wave): worthiness.py's full
constitution-grounded filter surface is ported to src/rust/worthiness —
all nine question kernels (pi_safety protective-frame branch, honesty
good/bad signal counting with the exact note formats, material_reality
finance→hustle/caring lattice, root_cause spin-first ordering, respect
rigid-first ordering, assistant_goal with the store-derived domain
vocabulary, durability, epistemology with the unsound-bare-absolute
precheck, expression with the four signal groups), the two-axis assess
verdict lattice (expression targets skip epistemology unless the text is
a bare factual absolute; last-veto-wins overwrite; DEFER/PROMOTE/ABSORB
threshold boundaries), is_bare_factual (padded-token matching; the
oracle's subjective-predicate list is DEAD CODE and stays dead),
verdict_label with the passthrough fallback.

DOMAIN-CACHE LAW mirrored: q_assistant_goal's `assist = _domain_terms();
assist += [universal..]` mutates the oracle's cache IN PLACE, inflating
hit counts on every call within the 300 s TTL. The Rust kernel owns the
cache (Mutex) and extends it on every call; the store read crosses back
as the `domain_terms` op served by the loader with the oracle's verbatim
query body (sans cache). Frozen truth caught this working: single-hit
counts only land on a process's first q_assistant_goal call.

Wiring: wr_loader.py ctypes loader mirroring broker_loader.py/pc_loader.py
(doc-envelope FFI, op-host callback for domain_terms, oracle-verbatim
tuple unwrapping, free via capability_scoring cs_free_string — no free
symbol re-export), facade delegation in worthiness.py with
RUNE_WR_CORE=python fallback pin.

Truth: frozen_wr_truth.json — 51 cases from the live oracle (deep-copied
inputs, RUNE_WR_CORE=python pinned, every public fn and every branch
incl. all four verdicts, veto last-wins, boundary totals -1.0/0.0,
fact=None coercion, verdict_label passthrough), replayed VERBATIM by the
in-crate branch battery. Battery caught one real port bug pre-landing
(the AESTHETIC signal list was missing 'wonder' — expression hit counts
diverged 5 vs 6). Live parity: facade 51/51 + kernel 51/51 (direct
loader), kernel pass in a subprocess (process-global cache law).
Python tests green both modes.
2026-10-12 03:59:47 +01:00
conductor 3c57ae0326 chore(release): auto-bump version to 0.230.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 13s
Auto-version and Release / release (push) Successful in 23s
CI Pipeline / go-surface (push) Failing after 17s
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 15s
CI Pipeline / gitea-offline (push) Canceled after 17s
CI Pipeline / canary (push) Canceled after 26s
CI Pipeline / lint (push) Canceled after 29s
CI Pipeline / unit-tests (push) Canceled after 30s
Promotion record: (unknown)
2026-10-12 03:59:25 +01:00
Nick 2e3b7e76ec feat(openai_compat_transport): port to FULL Go — dichotomy wave
CI Pipeline / rust-surface (push) Failing after 17s
CI Pipeline / documentation (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 0s
CI Pipeline / gitea-offline (push) Canceled after 0s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / go-surface (push) Canceled after 19s
CI Pipeline / best-practices (push) Canceled after 1s
Auto-version and Release / release (push) Canceled after 27s
CI Pipeline / canary (push) Canceled after 21s
deploy / deploy (push) Canceled after 25s
CI Pipeline / lint (push) Canceled after 25s
CI Pipeline / unit-tests (push) Canceled after 25s
2026-10-12 03:58:49 +01:00
Nick ee411f8e6e chore(store): close grading dichotomy contract BUILDER-2026-10-12T00:56:50Z-c1e93f-factory-wave (parity 1494c1a5, phase B 04583cd2)
CI Pipeline / best-practices (push) Successful in 10s
Auto-version and Release / release (push) Successful in 26s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 17s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / type-check (push) Failing after 37s
CI Pipeline / lint (push) Failing after 52s
CI Pipeline / canary (push) Failing after 2m1s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
deploy / deploy (push) Canceled after 8m21s
CI Pipeline / unit-tests (push) Canceled after 8m11s
2026-10-12 03:50:44 +01:00
conductor 9e6bc4b3c7 chore(release): auto-bump version to 0.229.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 12s
CI Pipeline / go-surface (push) Failing after 15s
Auto-version and Release / release (push) Successful in 26s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / type-check (push) Failing after 42s
CI Pipeline / lint (push) Failing after 54s
CI Pipeline / canary (push) Failing after 2m5s
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 2m26s
Promotion record: (unknown)
2026-10-12 03:48:22 +01:00
Nick 31e87e612b chore(store): close forge_client dichotomy contract BUILDER-2026-10-12T02:17:44Z-8feb0d-factory-wave (parity 797bb63f, phase B dc20b9b2)
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 7s
CI Pipeline / rust-surface (push) Failing after 11s
CI Pipeline / go-surface (push) Failing after 14s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / gitea-offline (push) Canceled after 18s
CI Pipeline / unit-tests (push) Canceled after 25s
Auto-version and Release / release (push) Canceled after 26s
CI Pipeline / lint (push) Canceled after 26s
CI Pipeline / canary (push) Canceled after 25s
deploy / deploy (push) Canceled after 15s
CI Pipeline / type-check (push) Canceled after 21s
2026-10-12 03:47:57 +01:00
Nick dc20b9b295 refactor(forge_client): Phase B language-advantage pass — dichotomy wave 2026-10-12 03:46:49 +01:00
Nick 797bb63f3c feat(forge_client): port to FULL Go — dichotomy wave 2026-10-12 03:46:49 +01:00
Nick 04583cd26a refactor(grading): Phase B language-advantage pass — dichotomy wave
Auto-version and Release / release (push) Failing after 57s
CI Pipeline / rust-surface (push) Failing after 57s
CI Pipeline / best-practices (push) Failing after 58s
CI Pipeline / lint (push) Failing after 58s
CI Pipeline / unit-tests (push) Failing after 1m0s
CI Pipeline / documentation (push) Failing after 1m0s
CI Pipeline / canary (push) Failing after 1m1s
CI Pipeline / go-surface (push) Failing after 1m4s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / type-check (push) Failing after 34s
CI Pipeline / required-checks (push) Failing after 0s
deploy / deploy (push) Failing after 40s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 4s
2026-10-12 03:42:52 +01:00
Nick 1494c1a5db feat(grading): port to FULL Rust — dichotomy wave 2026-10-12 03:42:52 +01:00
Nick bcc839e78b chore(store): close testing_mode dichotomy contract BUILDER-2026-10-12T00:49:26Z-b3e925-factory-wave (parity 626359e6, phase B 3c70f3bd)
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / rust-surface (push) Failing after 16s
Auto-version and Release / release (push) Successful in 24s
CI Pipeline / go-surface (push) Failing after 18s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / type-check (push) Failing after 42s
CI Pipeline / lint (push) Failing after 57s
CI Pipeline / canary (push) Failing after 2m4s
deploy / deploy (push) Failing after 15m30s
CI Pipeline / unit-tests (push) Failing after 16m22s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 5s
2026-10-12 03:01:07 +01:00
Nick d980d486a7 chore(store): close evidence_probe dichotomy contract BUILDER-2026-10-12T01:50:41Z-4cc7c3-factory-wave (parity 9ba51ba5, phase B 25f7cb6d)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 19s
Auto-version and Release / release (push) Successful in 28s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / lint (push) Failing after 54s
CI Pipeline / type-check (push) Failing after 42s
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 2m1s
CI Pipeline / canary (push) Canceled after 2m0s
deploy / deploy (push) Canceled after 2m11s
2026-10-12 02:59:04 +01:00
conductor 4f3ef0f76b chore(release): auto-bump version to 0.228.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 21s
Auto-version and Release / release (push) Successful in 33s
CI Pipeline / gitea-offline (push) Successful in 24s
CI Pipeline / lint (push) Failing after 1m9s
CI Pipeline / type-check (push) Failing after 57s
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 2m22s
CI Pipeline / canary (push) Canceled after 2m21s
Promotion record: (unknown)
2026-10-12 02:56:33 +01:00
Nick 3c70f3bd14 refactor(testing_mode): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 15s
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 / gitea-offline (push) Canceled after 23s
CI Pipeline / type-check (push) Canceled after 27s
CI Pipeline / unit-tests (push) Canceled after 39s
CI Pipeline / lint (push) Canceled after 39s
Auto-version and Release / release (push) Canceled after 41s
CI Pipeline / canary (push) Canceled after 41s
deploy / deploy (push) Canceled after 25s
2026-10-12 02:55:56 +01:00
Nick 626359e693 feat(testing_mode): port to FULL Rust — dichotomy wave 2026-10-12 02:55:56 +01:00
Nick 845cbfb3d0 chore(store): close self_modify dichotomy contract BUILDER-2026-10-12T01:53:04Z-7c7607-factory-wave (parity 1af27dc5, phase B 18052d5e)
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 17s
CI Pipeline / go-surface (push) Failing after 23s
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / lint (push) Canceled after 51s
CI Pipeline / unit-tests (push) Canceled after 51s
CI Pipeline / type-check (push) Canceled after 36s
CI Pipeline / canary (push) Canceled after 52s
deploy / deploy (push) Canceled after 55s
2026-10-12 02:55:05 +01:00
conductor 37feb10d37 chore(release): auto-bump version to 0.227.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 20s
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / type-check (push) Failing after 46s
CI Pipeline / lint (push) Failing after 1m0s
CI Pipeline / canary (push) Failing after 2m21s
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 3m31s
Promotion record: (unknown)
2026-10-12 02:51:27 +01:00
Nick 18052d5e1a refactor(self_modify): 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 21s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / lint (push) Canceled after 26s
CI Pipeline / unit-tests (push) Canceled after 26s
CI Pipeline / canary (push) Canceled after 26s
Auto-version and Release / release (push) Canceled after 34s
CI Pipeline / type-check (push) Canceled after 15s
deploy / deploy (push) Canceled after 36s
CI Pipeline / gitea-offline (push) Canceled after 15s
Battery-pinned language-advantage pass over the parity port (32 frozen
rows green throughout, cargo test 11/11, clippy 0):
- BaselineVerdict: the compare_baseline truthiness lattice is a typed
  enum with exhaustive matches — classify/promote/reason are single
  construction points; the three verdict reasons are canonical
  &'static str instead of string literals duplicated at three branches
- ProposalOutcome: promote/merge-fail/discard results held typed until
  the JSON boundary (one into_value per shape), rollback_ref stays a
  passthrough Value through the enum (present-None stays None)
- zero-copy argv: _run borrows &[&str]; the oracle's per-call
  Vec<String> argv construction is gone (worktree path and merge
  message borrow into the slices)
- chars_tail: (stdout+stderr)[-2000:] is now one reverse traversal
  with bounded reversal instead of a full count() + skip() double pass;
  char-boundary safety pinned on multi-byte runes
- lazy rollback_ref read and the remove-worktree argv kept as
  closures, preserving the oracle's KeyError-after-side-effects order
- Phase B pins added to the battery: verdict lattice exhaustiveness +
  frozen reasons, outcome projections vs the three frozen pod shapes,
  chars_tail char-boundary/short/empty cases
2026-10-12 02:50:56 +01:00
Nick 1af27dc5d4 feat(self_modify): port to FULL Rust — dichotomy wave
Full name-preserving port of src/rune/self_modify.py to
src/rust/self_modify (RUST verdict, #2479 dichotomy tranche — 7/7 fns
incl. the _run subprocess seam, dichotomy audit match clean):
- _run: the subprocess seam is kernel-owned — Command spawn with piped
  reader threads, poll-based timeout→kill (TimeoutExpired raise-parity
  via the envelope), strict-UTF-8 text=True, universal-newline
  translation, signal exit codes
- propose_change: rev-parse gate ('unknown base' branch), mkdtemp
  0700 'selfmod-' + 8 chars of Python's tempfile alphabet, worktree
  add with err.strip()[:200] char-parity failure branch, no-clock `at` stamp as an argument
- run_gates: default pytest argv pinned, falsy/empty/null cmd falls
  back, (stdout+stderr)[-2000:] CHARACTER tail parity
- compare_baseline: full Python truthiness lattice on passed (missing
  key, null, 0/1 coercion), the three verdict reasons verbatim
- promote_or_discard: lazy rollback_ref read (KeyError fires only
  after merge/remove side effects, like the oracle), merge-fail
  err[:200] (no strip — oracle quirk preserved), discard ignores both
  git failures
- rollback: bad-ref err[:200] verbatim; monitor: .get(key, "")
  passes ANY present value through verbatim (int/null included)
- raise parity at the TYPE level: smod_loader._raise_like_oracle
  re-raises FileNotFoundError/PermissionError/TimeoutExpired/
  KeyError/TypeError from the envelope err encodings
- smod_loader mirrors broker_loader (doc-envelope FFI, cs_free_string,
  SMOD_RUST_LIB override; RUNE_SM_CORE belongs to shard_manager so the
  pin is RUNE_SMOD_CORE); facade delegation with python-oracle
  fallback preserved
- 32 frozen truth rows vs the LIVE oracle (clock frozen, tempfile
  pinned, deterministic git fixtures with literal seed SHA; setup as
  data + PATH-stubbed default gate command), deterministic across two
  generator runs; battery replays VERBATIM (cargo test 8/8) and live
  replay is 32/32 at BOTH facade and kernel level; pytest 6/6 both
  through the Rust core and with RUNE_SMOD_CORE=python
2026-10-12 02:50:56 +01:00
Nick 4f3d1f7fc1 chore(store): close concept_topology dichotomy contract BUILDER-2026-10-11T23:55:54Z-982aa3-factory-wave (parity 5844426b, phase B 02e0c649)
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 20s
Auto-version and Release / release (push) Successful in 35s
CI Pipeline / gitea-offline (push) Successful in 27s
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 31s
CI Pipeline / canary (push) Canceled after 41s
CI Pipeline / unit-tests (push) Canceled after 45s
CI Pipeline / lint (push) Canceled after 46s
deploy / deploy (push) Canceled after 26s
2026-10-12 02:48:42 +01:00
conductor 2d31356417 chore(release): auto-bump version to 0.226.0 — earned by # (minor: converged release (push-to-master bump))
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 16s
CI Pipeline / go-surface (push) Failing after 21s
Auto-version and Release / release (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 25s
CI Pipeline / type-check (push) Failing after 58s
CI Pipeline / lint (push) Failing after 1m13s
CI Pipeline / canary (push) Failing after 2m49s
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 3m46s
deploy / deploy (push) Canceled after 3m25s
Promotion record: (unknown)
2026-10-12 02:44:53 +01:00
Nick 25f7cb6db4 refactor(evidence_probe): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 13s
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 11s
CI Pipeline / gitea-offline (push) Canceled after 11s
Auto-version and Release / release (push) Canceled after 32s
CI Pipeline / lint (push) Canceled after 25s
CI Pipeline / unit-tests (push) Canceled after 25s
CI Pipeline / canary (push) Canceled after 26s
deploy / deploy (push) Canceled after 35s
2026-10-12 02:44:22 +01:00
Nick 9ba51ba5e8 feat(evidence_probe): port to FULL Go — dichotomy wave 2026-10-12 02:44:22 +01:00
Nick 78a89eff6d chore(store): close contract BUILDER-2026-10-12T01:32:31Z-429396-factory-wave
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 20s
Auto-version and Release / release (push) Successful in 29s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / lint (push) Failing after 1m3s
CI Pipeline / type-check (push) Failing after 48s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
deploy / deploy (push) Canceled after 1m36s
CI Pipeline / unit-tests (push) Canceled after 1m42s
CI Pipeline / canary (push) Canceled after 1m41s
message_graph dichotomy contract — draft→approved→building→verifying→
closed in ONE process. Evidence: parity 1f7a2664, Phase B 7194f69a
(pushed gitea/master); battery 12 tests replay 68 frozen-oracle cases
VERBATIM; live parity 68/68 byte-identical; clippy 0; Python suite 9+9
under both RUNE_MG_CORE pins. Contract-walk script included (one-
process clobber-hazard law).
2026-10-12 02:42:06 +01:00
conductor cb70224f52 chore(release): auto-bump version to 0.225.0 — earned by # (minor: converged release (push-to-master bump))
CI Pipeline / rust-surface (push) Failing after 19s
CI Pipeline / go-surface (push) Failing after 25s
CI Pipeline / best-practices (push) Successful in 6s
CI Pipeline / documentation (push) Successful in 5s
Auto-version and Release / release (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 21s
CI Pipeline / lint (push) Failing after 1m10s
CI Pipeline / type-check (push) Failing after 57s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
deploy / deploy (push) Canceled after 3m6s
CI Pipeline / unit-tests (push) Canceled after 3m7s
CI Pipeline / canary (push) Canceled after 3m7s
Promotion record: (unknown)
2026-10-12 02:39:25 +01:00
Nick 02e0c649da refactor(concept_topology): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / rust-surface (push) Failing after 21s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / go-surface (push) Canceled after 24s
CI Pipeline / type-check (push) Canceled after 6s
CI Pipeline / gitea-offline (push) Canceled after 2s
Auto-version and Release / release (push) Canceled after 35s
CI Pipeline / canary (push) Canceled after 25s
deploy / deploy (push) Canceled after 36s
CI Pipeline / unit-tests (push) Canceled after 31s
CI Pipeline / lint (push) Canceled after 31s
Language-advantage pass on the concept_topology Rust kernel (battery
green throughout, live parity re-verified 386/386):
- Suggestion lattice is a real enum (Cluster | NewTopic) with an
  exhaustive match producing the doc envelope — no stringly verdicts.
- Union-find is a HashMap-backed UnionFind struct with the entry API
  (O(1) root lookups replace linear scans) — root identity is order-
  independent, so determinism is unchanged.
- clusters/suggest/decay run zero-copy over BORROWED edge pairs
  (&String / &str all the way to the sorted output allocation); the
  fn-name dispatch helpers collapse into obj_pairs/as_arr borrowing
  accessors with elided lifetimes.
- seen/edge upserts are iterator chains (for_each + filter_map),
  sort_by_key(Reverse) for the stable biggest-first component order.
2026-10-12 02:36:18 +01:00
Nick 5844426bdd feat(concept_topology): port to FULL Rust — dichotomy wave
RUST verdict (dichotomy law, #2479 tranche, contract
BUILDER-2026-10-11T23:55:54Z-982aa3-factory-wave): concept_topology.py's
full learned-concept surface is ported to src/rust/concept_topology —
cofire (pair edges +1.0, sorted '|'-joined keys, first-sight topic
creation, seen counting), decay (weight *0.95 with the >= 0.5 keep
boundary), clusters (union-find connected components over edges >=
threshold, union by second-root repoint, biggest-component-first stable
order, sorted members), suggest (max-overlap cluster routing with
first-wins tie-break, new_topic fallback with the exact reason strings),
and new_topology (the empty seed).

Wiring: ct_loader.py ctypes loader mirroring broker_loader.py (doc-envelope
FFI, free via capability_scoring cs_free_string — no free symbol
re-export), facade delegation in concept_topology.py with RUNE_CT_CORE=python
fallback pin.

Truth: frozen_ct_truth.json — 36 cases from the live oracle (deep-copied
inputs, RUNE_CT_CORE=python pinned), replayed VERBATIM by the in-crate
branch battery. Battery caught one real port bug pre-landing (new cofire
edges got weight 0 instead of 1). Live parity: 386/386 checks (36 frozen +
613 fuzz ops + facade pin-vs-auto). Python tests green both modes; clippy 0.
2026-10-12 02:36:18 +01:00
Nick 0946141074 chore(store): close time_awareness dichotomy contract BUILDER-2026-10-12T01:07:29Z-99b745-factory-wave (parity 92107319, phase B 5cea323f, fix 480e25c7)
CI Pipeline / best-practices (push) Successful in 5s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 22s
Auto-version and Release / release (push) Successful in 34s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / lint (push) Failing after 1m10s
CI Pipeline / type-check (push) Failing after 58s
CI Pipeline / canary (push) Failing after 2m55s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
deploy / deploy (push) Canceled after 10m56s
CI Pipeline / unit-tests (push) Canceled after 10m57s
2026-10-12 02:27:58 +01:00
conductor cc921f72c9 chore(release): auto-bump version to 0.224.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 21s
Auto-version and Release / release (push) Successful in 29s
CI Pipeline / gitea-offline (push) Successful in 21s
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 42s
CI Pipeline / type-check (push) Canceled after 31s
CI Pipeline / lint (push) Canceled after 46s
CI Pipeline / unit-tests (push) Canceled after 46s
Promotion record: (unknown)
2026-10-12 02:27:11 +01:00
Nick 7194f69a6d refactor(message_graph): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 18s
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 / gitea-offline (push) Canceled after 3s
CI Pipeline / lint (push) Canceled after 26s
CI Pipeline / unit-tests (push) Canceled after 26s
Auto-version and Release / release (push) Canceled after 36s
CI Pipeline / canary (push) Canceled after 26s
CI Pipeline / type-check (push) Canceled after 12s
deploy / deploy (push) Canceled after 29s
- Kind / EdgeType enums: the node-kind and edge-type lattices are
  compiler-checked variants with one wire spelling each (parse once in
  add, exhaustive as_str match) — the stringly membership arrays are
  gone.
- Cue enum: the render-cue lattice (Replaces / BranchedFrom /
  SupersededBy / None) classified once, rendered by exhaustive match;
  first-supersedes-edge-wins and falsy-superseded_by laws stay pinned by
  the frozen battery.
- Zero-copy walks: lineage/history return Vec<&Value> on the graph's
  lifetime — no per-node clone churn on the hot render paths; the FFI
  boundary clones once for the wire.
- Battery untouched: 12 tests replay the same 68 frozen-oracle cases
  VERBATIM, green throughout the pass; live parity re-verified
  byte-identical after the refactor; clippy 0.
2026-10-12 02:26:31 +01:00
Nick 1f7a266497 feat(message_graph): port to FULL Rust — dichotomy wave
parity: every public function of MessageGraph ported branch-for-branch —
_load (schema-v1 gate, corrupt/wrong-schema silent-empty, duplicate ids
last-wins with BOTH order entries kept, verbatim extra fields, non-dict
doc AttributeError, non-list nodes TypeError), save (arrival-order doc;
mkdir + json.dumps indent=2 FILE IO stays in the facade), add (the falsy
law: an explicit None parent still picks up the head but carries no edge,
a falsy "" parent stores "" and also carries no edge, ghost parents
allowed, kind checked before edge, Python-repr error strings), tool_call
(json.dumps content rendering stays in the facade — serialization
isomorphism), tool_result (existence check only — any node can parent a
result), revise (the root-revision quirk: old parent None substitutes
the current head, which at add-time is the old node itself), branch,
lineage (superseded skip, ghost superseded_by fallthrough, seen-set
cycle guard, missing-parent break), history (supersedes walk back to
root then forward via superseded_by — the FULL trail from any point on
it), migrate_flat (role/content defaults, existing-file seed), cue
(first supersedes/branches_from edge in iteration order wins, falsy
superseded_by is no cue).

Determinism seams: the uuid4 hex id and the _now() timestamp are kernel
ARGUMENTS (id/ts) — the frozen truth replays them verbatim. Graph wire:
{nodes: {id: node} insertion-ordered (serde_json preserve_order),
order: [ids]} — nodes ride as opaque Values so loaded extra fields
survive byte-verbatim. Errors ride typed (ValueError/KeyError/
AttributeError/TypeError) and the loader raises the matching Python
exception. The facade keeps the oracle's guard-clause ORDER (kind/edge/
existence checks BEFORE the uuid draw) so the id sequence is identical
under both cores. mg_loader mirrors broker_loader (doc-envelope,
cs_free_string, MG_RUST_LIB override); RUNE_MG_CORE=python pins the
fallback.

The freeze caught a real divergence first: Python 3.14's string-index
TypeError now reads "string indices must be integers, not 'str'" — the
kernel mirrors the runtime's wording, pinned by truth. Battery: 12
tests replay 68 frozen-oracle cases VERBATIM (include_str!); live
parity 68/68 byte-identical (truth regenerated through the kernel path
and diffed against the freeze); clippy 0; Python suite green under both
pins (9+9).
2026-10-12 02:26:30 +01:00
Nick 6786b4a922 chore(store): lexicon_evolution contract-walk script (one process, clobber-hazard law)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 17s
CI Pipeline / go-surface (push) Failing after 20s
Auto-version and Release / release (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / lint (push) Failing after 1m6s
CI Pipeline / type-check (push) Failing after 56s
CI Pipeline / canary (push) Failing after 2m46s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
deploy / deploy (push) Canceled after 9m36s
CI Pipeline / unit-tests (push) Canceled after 9m27s
2026-10-12 02:17:10 +01:00
229 changed files with 209083 additions and 22102 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# Rune
**Current release: v0.223.0**
**Current release: v0.239.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.**
+61 -41
View File
@@ -2,70 +2,47 @@
_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: 28%** (686/2480 runtime functions matched)
**Function-level completion: 37%** (1020/2781 runtime functions matched)
| Status | Modules | Lines | Meaning |
|---|---|---|---|
| FULL | 15 | 4264 | 80%+ of functions ported |
| PARTIAL | 83 | 41656 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 119 | 26856 | no name-preserving implementation |
| Total runtime | 217 | 72776 | |
| FULL | 36 | 13024 | 80%+ of functions ported |
| PARTIAL | 93 | 45658 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 108 | 19178 | no name-preserving implementation |
| Total runtime | 237 | 77860 | |
## NOT TOUCHED — the port queue, in priority order (largest first)
| Module | Lines | Fns | Side |
|---|---|---|---|
| rune_tui | 4170 | 159 | TUI-LAST |
| curator | 1838 | 43 | GO |
| abstraction | 656 | 17 | GO |
| canary_audit_ledger | 541 | 2 | TEST-INFRA |
| tui_validator | 514 | 13 | TUI-LAST |
| construction_pattern | 496 | 12 | RUST |
| model_routing | 432 | 13 | GO |
| worthiness | 411 | 13 | unclassified |
| token_optimizer | 410 | 22 | RUST |
| behavior | 408 | 9 | RUST |
| capability_ledger | 405 | 18 | RUST |
| chat_bubble | 401 | 12 | TUI-LAST |
| prompt_sync | 379 | 14 | RUST |
| drift-ledger | 355 | 14 | unclassified |
| programme_continuation | 349 | 11 | RUST |
| pc_loader | 337 | 22 | unclassified |
| canary_rune_serve | 329 | 13 | TEST-INFRA |
| canary_p1_07 | 324 | 10 | TEST-INFRA |
| message_queue | 316 | 13 | GO |
| drift_observability | 315 | 8 | RUST |
| build_basis | 314 | 12 | RUST |
| canary_p0_07 | 310 | 10 | TEST-INFRA |
| attention_capsule | 296 | 7 | RUST |
| canary_p1_01 | 294 | 12 | TEST-INFRA |
| drift_cache | 292 | 15 | RUST |
| outcome_ledger | 286 | 7 | RUST |
| time_awareness | 286 | 14 | RUST |
| canary_p1_06 | 280 | 7 | TEST-INFRA |
| auto_context | 273 | 8 | GO |
| canary_p1_03 | 271 | 9 | TEST-INFRA |
| canary_gitea_automation | 263 | 3 | TEST-INFRA |
| canary_p1_02 | 253 | 10 | TEST-INFRA |
| construction_patterns_builtin | 249 | 1 | RUST |
| canary_full_system | 248 | 2 | TEST-INFRA |
| toolchain | 236 | 5 | RUST |
| task_gate | 235 | 8 | RUST |
| sm_loader | 239 | 14 | unclassified |
| canary_native | 231 | 3 | TEST-INFRA |
| lexicon_evolution | 229 | 7 | unclassified |
| openai_compat_transport | 227 | 9 | GO |
| shard_manager | 223 | 9 | RUST |
| spine_language | 218 | 5 | RUST |
| evidence_probe | 213 | 9 | GO |
| canary_p0_04 | 207 | 8 | TEST-INFRA |
| wr_loader | 206 | 20 | unclassified |
| cc_loader | 205 | 28 | RUST |
| forge_client | 203 | 18 | GO |
| canary_context_broker | 199 | 10 | TEST-INFRA |
| canary_p0_05 | 199 | 13 | TEST-INFRA |
| testing_mode | 196 | 12 | RUST |
| grading | 193 | 3 | unclassified |
| cl_loader | 196 | 20 | unclassified |
| canary_spine_meta | 192 | 7 | TEST-INFRA |
| message_graph | 187 | 12 | unclassified |
| ffi_query_memory_smoke | 183 | 5 | RUST |
| tm_loader | 178 | 13 | unclassified |
| smod_loader | 175 | 10 | unclassified |
| behaviour_ledger | 174 | 12 | RUST |
| canary_tui_transport | 174 | 7 | TEST-INFRA |
| sr_loader | 173 | 18 | unclassified |
@@ -73,37 +50,50 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| inference_trace | 164 | 6 | RUST |
| ms_loader | 163 | 16 | RUST |
| canary_gitea_pipeline | 159 | 2 | TEST-INFRA |
| dc_loader | 158 | 13 | unclassified |
| mg_loader | 152 | 14 | unclassified |
| mse_loader | 152 | 12 | unclassified |
| bb_loader | 151 | 14 | unclassified |
| forge | 146 | 11 | GO |
| rune_graphic | 145 | 5 | TUI-LAST |
| spine_loader | 144 | 13 | RUST |
| ac_loader | 140 | 11 | unclassified |
| project_registry | 139 | 11 | RUST |
| canary_rune_entry | 137 | 3 | TEST-INFRA |
| do_loader | 137 | 8 | unclassified |
| ps_loader | 136 | 13 | unclassified |
| verification_layer | 136 | 11 | RUST |
| tui_backlog | 125 | 5 | TUI-LAST |
| le_loader | 124 | 10 | unclassified |
| state_checkpoint | 123 | 8 | unclassified |
| cp_loader | 122 | 9 | unclassified |
| dl_loader | 118 | 12 | unclassified |
| drift_resume | 118 | 3 | RUST |
| tl_loader | 118 | 9 | unclassified |
| bh_loader | 117 | 9 | unclassified |
| tg_loader | 117 | 7 | unclassified |
| canary_conversation_state | 115 | 2 | TEST-INFRA |
| contracts_loader | 112 | 8 | unclassified |
| gr_loader | 112 | 8 | unclassified |
| canary_query_memory | 111 | 2 | TEST-INFRA |
| tui_transport | 108 | 10 | TUI-LAST |
| canary_unbounded | 105 | 2 | TEST-INFRA |
| message_widgets | 104 | 6 | TUI-LAST |
| self_modify | 102 | 7 | unclassified |
| ct_loader | 103 | 8 | unclassified |
| sc_loader | 96 | 6 | RUST |
| inference_paths | 95 | 2 | unclassified |
| mutation_guards | 94 | 2 | unclassified |
| concept_topology | 90 | 7 | unclassified |
| cs_loader | 90 | 5 | RUST |
| ci_loader | 89 | 4 | unclassified |
| tc_loader | 88 | 5 | RUST |
| broker_loader | 86 | 5 | unclassified |
| canary_schema_migration | 86 | 1 | TEST-INFRA |
| ss_loader | 86 | 7 | unclassified |
| ol_loader | 85 | 4 | unclassified |
| to_loader | 85 | 4 | unclassified |
| ta_loader | 84 | 4 | unclassified |
| mems_loader | 83 | 5 | RUST |
| cm_loader | 82 | 4 | unclassified |
| cp_loader | 77 | 4 | unclassified |
| dl_loader | 77 | 5 | unclassified |
| lc_loader | 75 | 5 | unclassified |
| cd_loader | 72 | 4 | unclassified |
| execution_trace | 71 | 4 | unclassified |
@@ -115,7 +105,6 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| cb_loader | 64 | 3 | unclassified |
| skill_compiler | 64 | 4 | unclassified |
| sg_loader | 63 | 4 | unclassified |
| sm_loader | 63 | 4 | unclassified |
| meaningful_events | 61 | 3 | unclassified |
| outcome_utility | 59 | 4 | unclassified |
| memory_types | 54 | 2 | unclassified |
@@ -155,7 +144,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| growth_delta | 449 | 5/20 | _ctx_budget, _store_db, _llm, _parse_json, _load_deltas, _save_deltas |
| provider_budget | 357 | 5/20 | get_tracker, track_usage, check_provider, get_best_provider, failover, get_status |
| safety_override | 574 | 7/22 | _keyword_stopwords, _cc_probe, cc_loader_pin, self_test, _py_matches, escalation_rate |
| spine | 965 | 20/35 | _py_sorted, _module_imports, _py_declared_surfaces, _py_project_meta_surfaces, _gather_meta_inputs, _rust_core |
| spine | 975 | 20/35 | _py_sorted, _module_imports, _py_declared_surfaces, _py_project_meta_surfaces, _gather_meta_inputs, _rust_core |
| tool_executor | 1836 | 32/47 | _execute_tool_now, serialize_prepared_tool, deserialize_prepared_tool, confirm_write, _resolve_execution_cwd, _run_command |
| cron_engine | 440 | 13/27 | get_cron_engine, run_task, start_engine, stop_engine, to_dict, fire_on_task_complete |
| night_mode | 421 | 5/19 | _ctx_budget, get_night_mode, start_night_mode, stop_night_mode, get_status, get_report |
@@ -166,20 +155,25 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| identity_anchors | 542 | 5/16 | show_anchors, show_rules, show_pronouns, _announcement_patterns, _pronoun_patterns, _hedge_patterns |
| parallel_processor | 194 | 3/14 | _shutdown_processor, get_processor, run_parallel_with_args, get_status, test_parallel, run_parallel_with_args |
| structural_model | 388 | 9/20 | workspace_record, workspace_advance, _init_project, save, reload, add |
| token_optimizer | 514 | 16/27 | get_optimizer, _tc_core, _to_core, _py_estimate_tokens, _py_compress_context, to_dict |
| capability_broker | 798 | 10/20 | _native_tool_schemas, load_registry, _cards_are_stale, load_cards, generate_card, build_cards |
| capability_fit | 414 | 3/13 | record_semantic_fit, _provides_contracts, _consumers, discover_candidates, _cs_core, decide |
| claim_audit | 484 | 4/14 | _get_warm_neuron_store, reframe, _graph_has, _warm_has, verify, journal |
| drift_core | 471 | 4/14 | _ms_core, _now_iso, _py_check_promotion_eligibility, promote_entry, compute_fama, compute_drift_velocity |
| model_profiles | 438 | 5/15 | get_profile_path, load_profile, save_profile, build_profile_from_ollama, scan_all_models, get_or_create_profile |
| testing_mode | 362 | 11/21 | _now, _tm_core, _py_default_state, _py_next_item, _py_record, _py_note_blocker |
| tier_manager | 262 | 5/15 | _cc_probe, cc_loader_pin, _py_core_now, _py_get_tier, _py_promote, _py_demote |
| witness | 446 | 8/18 | _ctx_budget, _now, _cc_probe, cc_loader_pin, _py_extract_topic, _py_detect_signals |
| drift_compression | 307 | 3/12 | _py_get_compression_ratio, _py_compress_text, _py_compress_entry, _py_should_warn, _py_warn_before_compress, get_compression_ratio |
| drift_ledger | 471 | 7/16 | ledger_path, read_block, load_ledger, save_ledger, surface_alert, snapshot |
| scaffold | 868 | 22/31 | init, set_prd, add_maintenance_item, bind_repo, observe_repo, repo_state_from_git |
| behavior | 537 | 6/14 | _bh_core, connect, _correction_matches_python, _habit_matches_python, _reinforce_rust, _reinforce_python |
| builder_bridge | 326 | 3/11 | _construction_store, _active_construction_lines, _default_selector, _cached_memory, _get_selector, _retrieve_memory |
| time_awareness | 402 | 12/20 | _clock_args, _ta_core, get_time_awareness, to_dict, _to_state, _apply_state |
| construction_pattern | 547 | 7/14 | _core, _need_dict, _surface_text, _path_matches, _entity_matches, _obligation_when_hit |
| contracts | 743 | 4/11 | _ct_core, _resolve_and_project, _run_contract_python, capture_signatures, save_signatures, load_signatures |
| graph_memory | 474 | 2/9 | now_iso, connect, resolve_entity, add_edge, query, stats |
| intent_continuity | 168 | 2/9 | to_dict, from_dict, _load, save, create_detour, resolve_detour |
| lexicon_evolution | 304 | 7/14 | _le_core, _py_content_words, _py_distinctive_terms, _py_shared_distinctive, _py_coherence, _py_grounded |
| pins | 282 | 5/12 | _store_path, _save, _detail_blob, unpin, _py_pin_recall, list_pins |
| voice_hybrid | 427 | 3/10 | _connect, _load_growth, _save_growth, source_profiles, always, grow |
| voice_sync | 299 | 3/10 | _now, get_delivery_register_from_db, get_delivery_register, update_rune_md, check_sync, handle_hook |
@@ -198,12 +192,17 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| tiered_executor | 291 | 3/8 | _ctx_budget, test_executor, to_dict, to_dict, _execute_model |
| adaptive_router | 625 | 15/19 | _floor_applies_from_flags, _floor_reason, _record_transition_with, _detect_external_refs |
| construction_commands | 268 | 1/5 | _refuse, _validate, _accept, dispatch |
| drift_cache | 357 | 12/16 | _dc_core, _py_compute_priority, _py_qualifies_for_cache, main |
| drift_observability | 399 | 5/9 | _do_core, main, _load_alerts, _save_alerts |
| grading | 253 | 3/7 | _gr_core, _py_affirm_rule, _py_grade_fact, _py_tier_of |
| layout_contract | 336 | 2/6 | define_region, bind_component, amend_layout_contract, record_layout_observation |
| model_connect | 471 | 7/11 | _ctx_budget, _now, _get_cloud_models, handle_hook |
| prompt_sync | 487 | 11/15 | _now, _rust_available, get_store, main |
| salience_gate | 276 | 1/5 | _sg_core, test_salience, handle_hook, to_dict |
| self_improve | 246 | 1/5 | _now, suggest_improvements, handle_hook, main |
| stage_guards | 303 | 7/11 | _py_liveness_canary, _py_reconcile, to_dict, chk |
| subconscious_pass | 187 | 1/5 | main, _extract_topic, save_reflection, get_recent_findings |
| task_gate | 301 | 5/9 | _tg_core, _now, list_gates, main |
| construction_activation | 237 | 1/4 | _scaffold_for, _auto_structural_awareness, dispatch_from_decision |
| construction_mode | 412 | 2/5 | _contains_any, to_dict, from_dict |
| construction_projection | 254 | 1/4 | _contract_entity, validate, write_mini_doc |
@@ -227,15 +226,36 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| Module | Lines | Fns | Side |
|---|---|---|---|
| curator | 1838 | 40/43 | GO |
| abstraction | 656 | 17/17 | GO |
| system_registry | 603 | 14/16 | RUST |
| construction_intent | 580 | 13/16 | RUST |
| capability_ledger | 571 | 18/19 | RUST |
| drift_ledger | 548 | 20/22 | RUST |
| event_wait | 518 | 14/14 | GO |
| worthiness | 469 | 12/14 | unclassified |
| tool_protocol | 461 | 22/27 | GO |
| auto_tasks | 460 | 14/14 | GO |
| programme_continuation | 458 | 11/12 | RUST |
| goal | 453 | 20/20 | GO |
| release_notes | 446 | 16/16 | GO |
| model_routing | 432 | 13/13 | GO |
| build_basis | 375 | 11/13 | RUST |
| attention_capsule | 365 | 6/7 | RUST |
| drift-ledger | 355 | 14/14 | unclassified |
| outcome_ledger | 318 | 7/8 | RUST |
| message_queue | 316 | 13/13 | GO |
| message_graph | 295 | 12/13 | unclassified |
| toolchain | 295 | 5/6 | RUST |
| shard_manager | 276 | 9/10 | RUST |
| auto_context | 273 | 8/8 | GO |
| openai_compat_transport | 227 | 9/9 | GO |
| evidence_probe | 213 | 9/9 | GO |
| forge_client | 203 | 18/18 | GO |
| self_modify | 149 | 7/8 | unclassified |
| recall_uplift | 141 | 1/1 | RUST |
| rune_spinner | 130 | 4/5 | RUST |
| concept_topology | 128 | 7/8 | unclassified |
| wait_guard | 116 | 1/1 | RUST |
| failure_recovery | 108 | 1/1 | RUST |
| programme_gates | 85 | 4/5 | RUST |
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "rune"
version = "0.223.0"
version = "0.239.0"
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
requires-python = ">=3.10"
+297
View File
@@ -0,0 +1,297 @@
#!/usr/bin/env python3
"""Live parity: the FULL Rust behaviour_ledger core (bl_loader) vs the
retired Python oracle (the ep_loader shim path — RUNE_BL_CORE=python),
dichotomy wave #2479. Replays the frozen truth corpus PLUS seeded fuzz
over every public function and branch. The clock is pinned (no-clock
law — the kernel takes `now` in), so nothing is masked.
The facade reads RUNE_BL_CORE per call, so one process can flip between
the pinned oracle and the wave core.
"""
from __future__ import annotations
import copy
import json
import os
import random
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src" / "rune"))
from rune import behaviour_ledger as bl # noqa: E402
TRUTH_PATH = Path(__file__).resolve().parents[1] / (
"src/rust/behaviour_ledger/src/frozen_bl_truth.json")
FROZEN = "2026-10-11T23:00:00Z"
CLOCK = {"n": 0}
bl._now = lambda: f"2026-10-11T23:{CLOCK['n'] // 60:02d}:{CLOCK['n'] % 60:02d}Z" # noqa: E731
def call_core(core, fn, *args, **kwargs):
old = os.environ.get("RUNE_BL_CORE")
os.environ["RUNE_BL_CORE"] = core
try:
return getattr(bl, fn)(*args, **kwargs)
finally:
if old is None:
os.environ.pop("RUNE_BL_CORE", None)
else:
os.environ["RUNE_BL_CORE"] = old
def fresh_ledger(doc=None):
led = bl.BehaviourLedger(Path(tempfile.mkdtemp(prefix="bl_par_")) / "s")
if doc is not None:
led.doc = copy.deepcopy(doc)
return led
def compare(label, ok, failures, detail):
if not ok:
failures.append((label, detail))
print(f"FAIL {label}\n{detail}")
return ok
def scenario(group, name, doc_in, op, failures):
"""Run one op under both cores on identical deep-copied pre-state;
compare (return | ValueError message, doc after)."""
py_ret = py_err = py_doc = None
try:
CLOCK["n"] = 0
led = fresh_ledger(doc_in)
py_ret = op(led)
py_doc = led.doc
except ValueError as e:
py_err = str(e)
rs_ret = rs_err = rs_doc = None
try:
CLOCK["n"] = 0
led = fresh_ledger(doc_in)
rs_ret = op(led)
rs_doc = led.doc
except ValueError as e:
rs_err = str(e)
ok = (json.dumps(py_ret, sort_keys=True, default=str)
== json.dumps(rs_ret, sort_keys=True, default=str)
and py_err == rs_err
and json.dumps(py_doc, sort_keys=True)
== json.dumps(rs_doc, sort_keys=True))
return compare(f"{group}:{name}", ok, failures,
f"py={json.dumps(py_ret, sort_keys=True, default=str)[:300]}"
f"/{py_err}\nrs={json.dumps(rs_ret, sort_keys=True, default=str)[:300]}"
f"/{rs_err}")
# ── fuzz pools ─────────────────────────────────────────────────────────
IDS = ["B-1", "B-2", "behaviour:B-2", "B-3", "GHOST", "behaviour:GHOST",
"UNI-é", "X" * 60, "b", "behaviour:b"]
PROJECTS = ["demo", "other", "démø", ""]
COMPONENTS = ["component:a", "component:b", "component:bridge",
"component:ünï", ""]
ENTS = ["component:a", "component:b", "component:bridge",
"interface:completion", "component:ünï", "interface:x", "domain:d"]
DETAILS = ["ok", "", "x" * 250, "café ✓ ünïcode", "multi\nline", "quote'"]
GENS = ["gen-1", "gen-2", "", "gén-3"]
CONFIRMERS = ["user", "ci-robot", "", "user ", "nick"]
NOTES = ["accepted for v1", "", "n" * 220, "»signed«"]
def rand_params(rng):
return {
"behaviour_id": rng.choice(IDS),
"owning_project": rng.choice(PROJECTS),
"intended_behaviour": rng.choice(DETAILS),
"origin_intent": rng.choice(DETAILS),
"pass_criteria": rng.choice(DETAILS),
"fail_criteria": rng.choice(DETAILS),
"procedure": rng.choice(DETAILS),
"owner_component": rng.choice(COMPONENTS),
"dependencies": rng.sample(ENTS, rng.randint(0, 3)),
"automated_ref": rng.choice(["tests/x.py", ""]),
"environment": rng.choice(["ci", "prod", ""]),
}
def rand_doc(rng):
"""Deterministic pre-state via the pinned-clock oracle (python pin)."""
old = os.environ.get("RUNE_BL_CORE")
os.environ["RUNE_BL_CORE"] = "python"
try:
led = fresh_ledger()
for i in range(rng.randint(0, 4)):
bid = f"B-{i}"
p = rand_params(rng)
p["behaviour_id"] = bid
try:
led.define(**p)
except ValueError:
pass
roll = rng.random()
if roll < 0.45: # full confirmation chain
led.record_auto_verification(
bid, passed=rng.random() < 0.85,
detail=rng.choice(DETAILS), generation=rng.choice(GENS))
if rng.random() < 0.8:
led.record_real_world(
bid, passed=rng.random() < 0.85,
detail=rng.choice(DETAILS),
generation=rng.choice(GENS),
confirmed_by=rng.choice(CONFIRMERS))
elif roll < 0.6:
led.record_auto_verification(
bid, passed=False, detail=rng.choice(DETAILS),
generation=rng.choice(GENS))
elif roll < 0.7:
led.accept_limitation(bid, note=rng.choice(NOTES))
return led.doc
finally:
if old is None:
os.environ.pop("RUNE_BL_CORE", None)
else:
os.environ["RUNE_BL_CORE"] = old
def main() -> int:
failures: list[tuple[str, str]] = []
n_truth = 0
# 1. frozen truth corpus (kernel-level replay lives in the crate's
# branch battery; replayed here at FACADE level, both cores)
truth = json.loads(TRUTH_PATH.read_text())["cases"]
def run_case(group, case):
i = case["input"]
if group == "define":
params = {k: v for k, v in i.items()
if k not in ("doc", "now")}
params["dependencies"] = params.get("dependencies")
op = lambda led: led.define(**params) # noqa: E731
elif group == "get":
op = lambda led: led.get(i["behaviour_id"]) # noqa: E731
elif group == "record_auto_verification":
params = {k: v for k, v in i.items()
if k not in ("doc", "now")}
op = lambda led: led.record_auto_verification( # noqa: E731
params["behaviour_id"], **{k: v for k, v in params.items()
if k != "behaviour_id"})
elif group == "record_real_world":
params = {k: v for k, v in i.items()
if k not in ("doc", "now")}
op = lambda led: led.record_real_world( # noqa: E731
params["behaviour_id"], **{k: v for k, v in params.items()
if k != "behaviour_id"})
elif group == "accept_limitation":
params = {k: v for k, v in i.items()
if k not in ("doc", "now")}
op = lambda led: led.accept_limitation( # noqa: E731
params["behaviour_id"], **{k: v for k, v in params.items()
if k != "behaviour_id"})
elif group == "invalidate_for_change":
params = {k: v for k, v in i.items()
if k not in ("doc", "now")}
params["changed_entities"] = set(params["changed_entities"])
op = lambda led: led.invalidate_for_change(**params) # noqa: E731
elif group == "retest_history":
op = lambda led: led.retest_history(i["behaviour_id"]) # noqa: E731
elif group == "outstanding":
params = {k: v for k, v in i.items() if k != "doc"}
op = lambda led: led.outstanding(**params) # noqa: E731
else: # summary
op = lambda led: led.summary() # noqa: E731
scenario(group, case["name"], i["doc"], op, failures)
for group, cases in truth.items():
for case in cases:
n_truth += 1
run_case(group, case)
# 2. seeded fuzz (seed 2479). ALL randomness is consumed by the
# opmaker ONCE — the closure it returns is deterministic, so both
# core runs see identical args (poisoned-truth law).
rng = random.Random(2479)
n_fuzz = 0
def pick_bid(r, doc):
"""Existing (raw key) or random id — consumed once."""
bids = list(doc.get("behaviours", {}))
if bids and r.random() < 0.7:
return r.choice(bids)
return r.choice(IDS)
def fuzz(group, opmaker, count):
nonlocal n_fuzz
for j in range(count):
n_fuzz += 1
doc = rand_doc(rng)
scenario(group, f"fuzz{j}", doc, opmaker(rng, doc), failures)
fuzz("define", lambda r, d: (lambda led, p=rand_params(r): led.define(**p)),
150)
fuzz("get", lambda r, d: (lambda led, b=r.choice(IDS): led.get(b)), 100)
def auto_op(r, d):
bid = pick_bid(r, d)
passed = r.random() < 0.6
detail = r.choice(DETAILS)
gen = r.choice(GENS)
return lambda led: led.record_auto_verification(
bid, passed=passed, detail=detail, generation=gen)
fuzz("record_auto_verification", auto_op, 150)
def rw_op(r, d):
bid = pick_bid(r, d)
passed = r.random() < 0.6
detail = r.choice(DETAILS)
gen = r.choice(GENS)
by = r.choice(CONFIRMERS)
return lambda led: led.record_real_world(
bid, passed=passed, detail=detail, generation=gen, confirmed_by=by)
fuzz("record_real_world", rw_op, 150)
def lim_op(r, d):
bid = pick_bid(r, d)
note = r.choice(NOTES)
by = r.choice(CONFIRMERS)
return lambda led: led.accept_limitation(bid, note=note, approved_by=by)
fuzz("accept_limitation", lim_op, 100)
def inv_op(r, d):
changed = set(r.sample(ENTS, r.randint(0, 3)))
reasons = ({"behaviour:B-0": "custom reason"} if r.random() < 0.3
else None)
gen = r.choice(GENS)
return lambda led: led.invalidate_for_change(
changed_entities=changed, generation=gen, reasons=reasons)
fuzz("invalidate_for_change", inv_op, 200)
fuzz("retest_history",
lambda r, d: (lambda led, b=r.choice(IDS): led.retest_history(b)),
80)
def out_op(r, d):
kw = {}
if r.random() < 0.5:
kw["component"] = r.choice(COMPONENTS)
if r.random() < 0.5:
kw["project"] = r.choice(PROJECTS)
return lambda led: led.outstanding(**kw)
fuzz("outstanding", out_op, 120)
fuzz("summary", lambda r, d: (lambda led: led.summary()), 50)
total = n_truth + n_fuzz
print(f"parity: {total - len(failures)}/{total} "
f"({n_truth} truth + {n_fuzz} fuzz seed 2479)")
return 1 if failures else 0
if __name__ == "__main__":
sys.exit(main())
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Live parity check: construction_patterns_builtin facade on the Rust core
vs the Python-pinned oracle (RUNE_CPB_CORE=python). Exercises every public
surface branch: BUILTIN_PATTERNS docs, registry revision, get_pattern hit
per pattern, unknown-id miss, revision match, revision mismatch (exact
exception type + message), revision=None. Deep-copies inputs per case.
"""
import copy
import json
import os
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(REPO, "src"))
import rune.construction_patterns_builtin as cpb # noqa: E402
PIDS = [p.pattern_id for p in cpb.BUILTIN_PATTERNS]
cases = []
for pid in PIDS:
cases.append(("get_pattern", {"pattern_id": pid, "revision": None}))
rev = cpb.get_pattern(pid).revision
cases.append(("get_pattern", {"pattern_id": pid, "revision": rev}))
cases.append(("get_pattern", {"pattern_id": pid, "revision": rev + 1}))
cases += [
("get_pattern", {"pattern_id": "no-such-pattern", "revision": None}),
("get_pattern", {"pattern_id": "", "revision": None}),
("registry_revision", {}),
]
def run(pin):
os.environ["RUNE_CPB_CORE"] = "python" if pin else "auto"
out = []
for fn, args in cases:
try:
if fn == "get_pattern":
got = cpb.get_pattern(**copy.deepcopy(args))
out.append(("ok", None if got is None else got.to_dict()))
else:
out.append(("ok", cpb.PATTERN_REGISTRY_REVISION))
except ValueError as e:
out.append(("ValueError", str(e)))
return out
def run_rust_docs():
"""The Rust core's raw surface (registry docs + revision) directly."""
from rune import cpb_loader
return {
"revision": cpb_loader.registry_revision(),
"patterns": cpb_loader.patterns(),
"kernel": cpb_loader.available(),
}
want = run(pin=True)
got = run(pin=False)
match = sum(1 for w, g in zip(want, got) if w == g)
for i, (w, g) in enumerate(zip(want, got)):
if w != g:
print(f"DIVERGENCE case {i} {cases[i]}:\n python {w}\n rust {g}")
docs = run_rust_docs()
py_docs = [p.to_dict() for p in cpb.BUILTIN_PATTERNS]
if docs["patterns"] != py_docs or docs["revision"] != cpb.PATTERN_REGISTRY_REVISION:
print(f"DIVERGENCE kernel docs: rust rev={docs['revision']} patterns!=python" )
match = match # counted below as failure
docs_ok = False
else:
docs_ok = True
total = len(cases) + 1
matched = match + (1 if docs_ok else 0)
print(f"PARITY: {matched}/{total}")
sys.exit(0 if matched == total else 1)
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""Close the #2479 dichotomy-wave construction contract for
construction_patterns_builtin. Full lifecycle in ONE process (shared-store
clobber hazard)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/construction_patterns_builtin.py to FULL Rust (dichotomy "
"factory, issue #2479 tranche): frozen-oracle registry data, doc-envelope "
"FFI, ctypes loader (free via capability_scoring cs_free_string), facade "
"delegation with RUNE_CPB_CORE=python pin, frozen-truth branch battery, "
"live parity 100%, Phase B typed-registry pass. Plus audit fix: curator "
"-> curator-core alias (curator was FULL-Go since a95c6ce1 but the audit "
"miscounted it NOT TOUCHED).",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting")
sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "construction_patterns_builtin parity port — final SHAs after "
"rebase: parity 4649df1c, phase B b50e8553. Battery 19 cases "
"green, live parity 16/16, clippy 0.",
}], scope_files=[
"src/rune/construction_patterns_builtin.py",
"src/rune/cpb_loader.py",
"src/rust/construction_patterns_builtin/src/lib.rs",
"src/rust/construction_patterns_builtin/src/registry.rs",
"src/rust/construction_patterns_builtin/src/branch_battery.rs",
"src/rust/construction_patterns_builtin/frozen_cpb_patterns.json",
"src/rust/construction_patterns_builtin/frozen_cpb_truth.json",
"scripts/gen_construction_patterns_builtin_truth.py",
"scripts/check_cpb_parity.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Open + walk + close the #2479 dichotomy-wave construction contract for
drift_ledger. Full lifecycle in ONE process (shared-store clobber hazard,
see notes-construction-intent-batch.md)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Complete the drift_ledger dichotomy port to FULL Rust (#2479 tranche): "
"check/alert-loop, snapshot/anchor assembly, auto-fix routing and fix "
"gate as doc-envelope FFI; dl_loader ctypes loader mirroring "
"broker_loader.py (free via capability_scoring cs_free_string); facade "
"delegation with RUNE_DL_CORE=python pin; frozen-oracle battery (87 "
"cases, every public function/branch); live parity 100%; Phase B "
"language-advantage pass; dichotomy-audit instrument fixes (curator "
"alias + hyphen-twin mapping).",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "drift_ledger dichotomy completion — parity c0ec3135 "
"feat(drift_ledger): port remaining cores to FULL Rust — "
"dichotomy wave; Phase B 62e47b38 refactor(drift_ledger): "
"Phase B language-advantage pass — dichotomy wave (both pushed "
"gitea/master). Battery 8 tests replay the 87-case frozen "
"oracle truth VERBATIM; live parity 87/87 byte-identical "
"(RUNE_DL_CORE=auto vs =python); clippy -D warnings clean; "
"Python tests green (22 passed on drift-touching suites; 6 "
"pre-existing unrelated failures on the clean tree). Audit: "
"drift-ledger 14/14 FULL, drift_ledger 20/22 FULL, curator "
"40/43 FULL after the instrument fixes.",
}], scope_files=[
"src/rune/drift_ledger.py",
"src/rune/dl_loader.py",
"src/rust/drift_ledger/src/lib.rs",
"src/rust/drift_ledger/src/branch_battery.rs",
"scripts/gen_drift_ledger_truth.py",
"scripts/dichotomy_audit.py",
"docs/dichotomy-rebase-honest-audit.md",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""Close the #2479 dichotomy-wave forge_client 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_forge_client.py <contract_id> <parity_sha> <phaseb_sha>")
iface = ConstructionInterface(builder="factory-wave")
c = iface.close(CID, observed_evidence=[{
"kind": "port_evidence",
"detail": (
f"forge_client GO dichotomy port — parity commit {PARITY_SHA}, "
f"Phase B commit {PHASEB_SHA}; parity 49/49 frozen rows vs live "
"oracle (scripts/gen_forge_client_truth.py), go test -race green, "
"gofmt/vet clean, Python oracle tests 18 passed."
),
}], scope_files=[
"src/rune/forge_client.py",
"src/go/forge_client/forge_client.go",
"src/go/forge_client/phaseb.go",
"src/go/forge_client/replay.go",
"src/go/forge_client/main.go",
"src/go/forge_client/truth_test.go",
"src/go/forge_client/phaseb_test.go",
"src/go/forge_client/truth.json",
"src/go/forge_client/go.mod",
"scripts/gen_forge_client_truth.py",
"scripts/open_contract_forge_client.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", CID, "closed" if c.get("success") else "CLOSE_FAILED")
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Open + walk + close the #2479 dichotomy-wave construction contract for
message_graph. Full lifecycle in ONE process (shared-store clobber hazard,
see notes-construction-intent-batch.md)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/message_graph.py to FULL Rust (dichotomy factory, "
"issue #2479 tranche): doc-envelope FFI, ctypes loader, facade "
"delegation with RUNE_MG_CORE=python pin, frozen-truth battery, "
"live parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "message_graph parity port — parity 1f7a2664 "
"feat(message_graph): port to FULL Rust — dichotomy wave; "
"Phase B 7194f69a refactor(message_graph): Phase B "
"language-advantage pass — dichotomy wave (both pushed "
"gitea/master). Battery 12 tests replay 68 frozen-oracle "
"cases VERBATIM; live parity 68/68 byte-identical; clippy 0; "
"Python suite green under both pins (9+9).",
}], scope_files=[
"src/rune/message_graph.py",
"src/rune/mg_loader.py",
"src/rust/message_graph/src/lib.rs",
"src/rust/message_graph/src/branch_battery.rs",
"scripts/gen_message_graph_truth.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Close the #2479 dichotomy-wave construction contract for self_modify.
Full lifecycle in ONE process (shared-store clobber hazard, see
notes-construction-intent-batch.md)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/self_modify.py to FULL Rust (dichotomy factory, issue "
"#2479 tranche): kernel-owned subprocess seam (_run), doc-envelope FFI, "
"ctypes loader, facade delegation with RUNE_SMOD_CORE=python pin, "
"frozen-truth battery with deterministic git fixtures, live parity "
"100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting")
sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "self_modify parity port — evidence attached at close (SHAs "
"reported in the builder report)",
}], scope_files=[
"src/rune/self_modify.py",
"src/rune/smod_loader.py",
"src/rust/self_modify/src/lib.rs",
"src/rust/self_modify/src/branch_battery.rs",
"src/rust/self_modify/src/frozen_smod_truth.json",
"scripts/gen_self_modify_truth.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+8
View File
@@ -107,11 +107,18 @@ def module_to_keys(module_stem: str) -> list[str]:
"""Candidate implementation keys for a Python module stem."""
kebab = module_stem.replace("_", "-")
keys = [kebab, module_stem]
# Hyphenated legacy twins (drift-ledger.py vs drift_ledger.py) share ONE
# implementation; the underscore form is the crate/dir name.
if "-" in module_stem:
keys.append(module_stem.replace("-", "_"))
# Deep ports and multi-module binaries register aliases:
aliases = {
"rune_operator": ["operator", "operator-deep", "operator-support", "core-server"],
"rune_tui": ["tui"],
"rune_operator_cli": ["operator"],
# curator was ported to FULL Go (src/go/curator-core, a95c6ce1);
# without this alias the audit mislabels it NOT TOUCHED.
"curator": ["curator-core"],
"memory_store": ["memory-ops", "memory-store-deep"],
"warm_router": ["warm-router", "operator-support"],
"tool_executor": ["tool-executor", "local-tool-runtime", "tool-protocol", "tool-protocol-rw"],
@@ -142,6 +149,7 @@ def module_to_keys(module_stem: str) -> list[str]:
"evidence_provenance": ["cognition-provenance", "cognition"],
"memory_scoring": ["cognition-scoring", "cognition"],
"capability_scoring": ["cognition-capability", "cognition"],
"curator": ["curator-core"],
"token_counting": ["cognition-token", "cognition"],
"query_memory": ["cognition-query", "cognition"],
}
+293
View File
@@ -0,0 +1,293 @@
#!/usr/bin/env python3
"""gen_abstraction_ladder_truth.py — freeze/replay parity truth for
abstraction_ladder (dichotomy wave, #2479 tranche).
Freeze mode (default): exercises EVERY public function of the LIVE Python
oracle (src/rune/abstraction_ladder.py) across every branch — next_stage
(all 7 stages / top / unknown / non-string), can_promote (empty item,
per-stage threshold miss at exactly threshold-1, the unknown-stage→"top"
quirk, falsy provenance variants, contradictions, missing/empty
consequence, belief/rule verified_outcome gate, full promote path per
stage) and promote (failure verdict + verbatim item, success item
mutation stage/sightings reset) — and records (kernel_input, expected)
into src/rust/abstraction_ladder/frozen_al_truth.json.
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
delegation ACTIVE (RUNE_AL_CORE unset) and asserts both kernel-level
(loader direct) and facade-level (oracle functions delegate) parity
against the frozen file.
Poisoned-truth law: freeze pins RUNE_AL_CORE=python BEFORE any import.
Mutation law: case inputs are deep-copied BEFORE they reach the oracle.
SIGHTINGS-INT LAW: sightings are ints only in frozen cases — JSON cannot
distinguish int 2 from float 2.0, and the oracle's f-string renders them
differently ("2" vs "2.0"), so float sightings are unportable truth.
"""
import copy
import json
import os
import sys
from datetime import datetime, timezone
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TRUTH_PATH = os.path.join(REPO, "src", "rust", "abstraction_ladder",
"frozen_al_truth.json")
REPLAY = "--replay" in sys.argv
if not REPLAY:
os.environ["RUNE_AL_CORE"] = "python" # poisoned-truth law (freeze side)
else:
os.environ.pop("RUNE_AL_CORE", None) # live-parity law (facade delegates)
sys.path.insert(0, os.path.join(REPO, "src"))
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
import abstraction_ladder as oracle_mod # noqa: E402
if REPLAY:
from rune import al_loader # noqa: E402
if not al_loader.available():
print("REPLAY ABORT: kernel not built —", al_loader.LIB_PATH)
sys.exit(2)
cases = []
def case(name, fn, kernel_input, expected):
cases.append({
"name": name,
"fn": fn,
"input": kernel_input,
"ops": [],
"expected": expected,
})
def complete(stage, sightings=None):
it = {"stage": stage, "sightings": oracle_mod.THRESHOLDS[stage],
"provenance": "witnessed", "contradictions": [],
"consequence": "changes behaviour", "verified_outcome": True}
if sightings is not None:
it["sightings"] = sightings
return it
# ── next_stage: every stage, top, unknown, non-string ──────────────────────
for i, stage in enumerate(oracle_mod.STAGES):
want = oracle_mod.STAGES[i + 1] if i + 1 < len(oracle_mod.STAGES) else None
case(f"next_stage:{stage}", "next_stage", {"stage": stage}, want)
case("next_stage:unknown", "next_stage", {"stage": "gossip"}, None)
case("next_stage:empty", "next_stage", {"stage": ""}, None)
case("next_stage:missing", "next_stage", {}, None)
case("next_stage:non_string", "next_stage", {"stage": 7}, None)
case("next_stage:prefix_like", "next_stage", {"stage": "episodex"}, None)
# ── can_promote: threshold branch per stage (one below) ────────────────────
for stage in oracle_mod.STAGES:
thr = oracle_mod.THRESHOLDS[stage]
item = {"stage": stage, "sightings": thr - 1, "provenance": "witnessed",
"contradictions": [], "consequence": "changes behaviour",
"verified_outcome": True}
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:below_threshold:{stage}", "can_promote",
{"item": item}, want)
# exact-threshold boundary for every stage (full promote path)
for stage in oracle_mod.STAGES:
item = complete(stage)
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:at_threshold:{stage}", "can_promote",
{"item": item}, want)
# empty item — defaults: stage episode, sightings 0
want = oracle_mod.can_promote({})
case("can_promote:empty_item", "can_promote", {"item": {}}, want)
# missing sightings only
want = oracle_mod.can_promote({"stage": "fact", "provenance": "witnessed",
"consequence": "c"})
case("can_promote:default_sightings", "can_promote",
{"item": {"stage": "fact", "provenance": "witnessed",
"consequence": "c"}},
want)
# non-string / null stage keys: item.get hands them to STAGES.index,
# which raises ValueError -> next_stage None -> "top" quirk
for key, bad_stage in (("non_string", 7), ("null", None)):
item = {"stage": bad_stage, "sightings": 9, "provenance": "witnessed",
"consequence": "c"}
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:stage_{key}_quirk", "can_promote", {"item": item},
want)
# the unknown-stage quirk: next_stage(None result) collapses to "top"
want = oracle_mod.can_promote({"stage": "gossip", "sightings": 99,
"provenance": "witnessed",
"consequence": "c"})
case("can_promote:unknown_stage_quirk", "can_promote",
{"item": {"stage": "gossip", "sightings": 99,
"provenance": "witnessed", "consequence": "c"}},
want)
# falsy provenance variants: missing, empty string, empty list, 0, False
for key, prov in (("missing", None), ("empty_str", ""),
("empty_list", []), ("zero", 0), ("false", False)):
item = {"stage": "observation", "sightings": 5,
"contradictions": [], "consequence": "c"}
if prov is not None:
item["provenance"] = prov
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:provenance_{key}", "can_promote", {"item": item}, want)
# truthy provenance variants survive the gate (promote path)
for key, prov in (("str", "log"), ("list", ["log"]), ("one", 1),
("true", True)):
item = {"stage": "episode", "sightings": 1, "provenance": prov,
"contradictions": [], "consequence": "c"}
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:provenance_{key}_passes", "can_promote",
{"item": item}, want)
# contradictions: truthy list, truthy string, dict; falsy empty list /
# None / False pass
for key, contra, want_promote in (
("list", ["c1"], False), ("str", "disputed", False),
("dict", {"a": 1}, False), ("empty_list", [], True),
("none", None, True), ("false", False, True)):
item = {"stage": "episode", "sightings": 1, "provenance": "log",
"consequence": "c"}
if contra is not None:
item["contradictions"] = contra
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:contradictions_{key}", "can_promote",
{"item": item}, want)
# consequence: missing / empty string / empty list / 0 fail; non-empty pass
for key, cons in (("missing", None), ("empty_str", ""),
("empty_list", []), ("zero", 0), ("str", "moves"),
("list", ["moves"])):
item = {"stage": "episode", "sightings": 1, "provenance": "log",
"contradictions": []}
if cons is not None:
item["consequence"] = cons
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:consequence_{key}", "can_promote", {"item": item},
want)
# belief/rule verified_outcome gate: missing, False, 0 fail; True passes;
# concept needs NO verified outcome
for stage in ("belief", "rule"):
for key, vo in (("missing", None), ("false", False), ("zero", 0)):
item = {"stage": stage, "sightings": 9, "provenance": "log",
"contradictions": [], "consequence": "c"}
if vo is not None:
item["verified_outcome"] = vo
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:{stage}_vo_{key}", "can_promote",
{"item": item}, want)
item = {"stage": stage, "sightings": 9, "provenance": "log",
"contradictions": [], "consequence": "c",
"verified_outcome": True}
want = oracle_mod.can_promote(copy.deepcopy(item))
case(f"can_promote:{stage}_vo_true", "can_promote", {"item": item}, want)
item = {"stage": "concept", "sightings": 3, "provenance": "log",
"contradictions": [], "consequence": "c",
"verified_outcome": False}
want = oracle_mod.can_promote(copy.deepcopy(item))
case("can_promote:concept_ignores_vo", "can_promote", {"item": item}, want)
# gate order: threshold beats provenance beats contradictions beats
# consequence beats verified_outcome
item = {"stage": "belief", "sightings": 1}
want = oracle_mod.can_promote(copy.deepcopy(item))
case("can_promote:gate_order_threshold_first", "can_promote",
{"item": item}, want)
item = {"stage": "belief", "sightings": 9, "contradictions": ["x"],
"consequence": "c", "verified_outcome": True}
want = oracle_mod.can_promote(copy.deepcopy(item))
case("can_promote:gate_order_provenance_second", "can_promote",
{"item": item}, want)
# ── promote: failure keeps the item verbatim ───────────────────────────────
for name, item in (
("empty", {}),
("below", {"stage": "observation", "sightings": 2,
"provenance": "log", "consequence": "c"}),
("top", {"stage": "skill", "sightings": 9, "provenance": "log",
"consequence": "c"}),
("unknown", {"stage": "gossip", "sightings": 9}),
("no_consequence", {"stage": "fact", "sightings": 4,
"provenance": "log"}),
("belief_no_vo", {"stage": "belief", "sightings": 9,
"provenance": "log", "consequence": "c"})):
want = oracle_mod.promote(copy.deepcopy(item))
case(f"promote:refusal_{name}", "promote", {"item": item}, want)
# ── promote: success mutates stage + resets sightings ──────────────────────
for stage in ("episode", "fact", "observation", "concept", "belief", "rule"):
item = complete(stage)
want = oracle_mod.promote(copy.deepcopy(item))
case(f"promote:success_{stage}", "promote", {"item": item}, want)
# extra fields preserved through the copy; only stage/sightings touched
item = {"stage": "rule", "sightings": 5, "provenance": "log",
"contradictions": [], "consequence": "c", "verified_outcome": True,
"id": "x9", "tags": ["a", "b"], "nested": {"k": [1, 2]}}
want = oracle_mod.promote(copy.deepcopy(item))
case("promote:extra_fields_preserved", "promote", {"item": item}, want)
# ── freeze / replay ─────────────────────────────────────────────────────────
def kernel_call(fn, kernel_input):
if fn == "next_stage":
return al_loader.next_stage(kernel_input.get("stage"))
if fn == "can_promote":
return al_loader.can_promote(kernel_input["item"])
return al_loader.promote(kernel_input["item"])
def facade_call(fn, kernel_input):
if fn == "next_stage":
return oracle_mod.next_stage(kernel_input.get("stage"))
if fn == "can_promote":
return oracle_mod.can_promote(kernel_input["item"])
return oracle_mod.promote(kernel_input["item"])
if not REPLAY:
for c in cases:
got = facade_call(c["fn"], copy.deepcopy(c["input"]))
assert got == c["expected"], (
f"self-check failed on {c['name']}: {got!r} != {c['expected']!r}")
doc = {
"frozen": datetime.now(timezone.utc).isoformat(),
"oracle": "src/rune/abstraction_ladder.py",
"pin": "RUNE_AL_CORE=python",
"note": "Frozen via scripts/gen_abstraction_ladder_truth.py "
"against the live Python oracle. Battery embeds this "
"VERBATIM. SIGHTINGS-INT LAW: sightings are ints only "
"(JSON cannot distinguish int 2 from float 2.0 and the "
"oracle's f-string renders them differently).",
"cases": cases,
}
with open(TRUTH_PATH, "w") as f:
json.dump(doc, f, indent=1)
f.write("\n")
print(f"FROZEN {len(cases)} cases -> {TRUTH_PATH}")
else:
fails = []
for c in cases:
want = c["expected"]
kg = kernel_call(c["fn"], copy.deepcopy(c["input"]))
if kg != want:
fails.append(f"KERNEL {c['name']}: {kg!r} != {want!r}")
fg = facade_call(c["fn"], copy.deepcopy(c["input"]))
if fg != want:
fails.append(f"FACADE {c['name']}: {fg!r} != {want!r}")
if fails:
print(f"REPLAY FAILED {len(fails)}/{len(cases)}:")
for f_ in fails:
print(" ", f_)
sys.exit(1)
print(f"REPLAY OK {len(cases)}/{len(cases)} (kernel + facade)")
+444
View File
@@ -0,0 +1,444 @@
#!/usr/bin/env python3
"""Freeze the parity truth for the behaviour_ledger Rust port (dichotomy
wave #2479 tranche).
Runs the LIVE oracle (src/rune/behaviour_ledger.py — the Phase G shim
serving the evidence_provenance `bl_*` core, retired as the Python
oracle by this port) across EVERY public function and EVERY branch:
define (minimal/full/prefixed id/duplicate/unicode), get (hit both id
forms/miss/empty), record_auto_verification (pass/fail/unknown/200-char
detail truncation/reverify-confirmed), record_real_world (user
confirmation/other-confirmer/fail/unknown), accept_limitation (ok/
custom approver/unknown), invalidate_for_change (dependency overlap
with the default sorted-list-repr reason / owner hit / no overlap →
preserved / custom reasons / reasons key miss / auto-verified-only
skip / mixed multi-behaviour), retest_history (populated generations/
unknown/empty), outstanding (mixed states with the TEST_FAILED-first
sort / component filter / project filter / combined / excluded
states), summary (empty/mixed integer counts).
The clock is pinned (bl._now) so truth is deterministic and
regenerable byte-for-byte. Case input docs are deep-copied BEFORE the
oracle runs (poisoned-truth law). Output:
src/rust/behaviour_ledger/src/frozen_bl_truth.json — embedded VERBATIM
by the cfg(test) branch battery (include_str!).
"""
from __future__ import annotations
import copy
import json
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from rune import behaviour_ledger as bl # noqa: E402
TRUTH_PATH = Path(__file__).resolve().parents[1] / (
"src/rust/behaviour_ledger/src/frozen_bl_truth.json")
class Clock:
"""Pinned clock: %Y-%m-%dT%H:%M:%SZ stamps incrementing by second
(the oracle's _now format), reset per case."""
def __init__(self):
self.n = 0
def now(self) -> str:
self.n += 1
return f"2026-10-11T23:00:{self.n:02d}Z"
CLOCK = Clock()
bl._now = CLOCK.now # pin the facade's clock (the kernel takes `now` in)
def fresh() -> bl.BehaviourLedger:
CLOCK.n = 0
tmp = tempfile.mkdtemp(prefix="bl_truth_")
return bl.BehaviourLedger(Path(tmp) / "store")
def envelope_ok(value) -> dict:
return {"ok": True, "value": value}
def envelope_err(msg: str) -> dict:
return {"ok": False, "err": msg}
CASES: dict[str, list] = {}
def add(group: str, case: dict) -> None:
CASES.setdefault(group, []).append(case)
def snap(led: bl.BehaviourLedger) -> dict:
return copy.deepcopy(led.doc)
def next_now() -> str:
"""The stamp the NEXT pinned _now() call will return."""
return f"2026-10-11T23:00:{CLOCK.n + 1:02d}Z"
def run_mutation(group: str, name: str, led: bl.BehaviourLedger,
params: dict, op) -> None:
"""Run one mutating op (kwargs only); record {input: {doc, now,
**params}, expected: {ok, value: {doc, rec | recs}}} or the
ValueError envelope. The op under test stamps via the pinned clock;
its stamp is recorded in the input (no-clock law — the kernel takes
`now` in)."""
doc_in = snap(led)
stamp = next_now()
try:
out = op(**params)
except ValueError as e:
add(group, {"name": name,
"input": {"doc": doc_in, "now": stamp, **params},
"expected": envelope_err(str(e))})
return
value_key = "recs" if isinstance(out, list) else "rec"
add(group, {"name": name, "input": {"doc": doc_in, "now": stamp,
**params},
"expected": envelope_ok({"doc": led.doc,
value_key: out})})
def seed_confirmed(led: bl.BehaviourLedger, bid: str, owner: str,
deps: list[str]) -> None:
led.define(behaviour_id=bid, owning_project="demo",
intended_behaviour=f"{bid} behaves",
owner_component=owner, dependencies=deps)
led.record_auto_verification(bid, passed=True, generation="gen-1")
led.record_real_world(bid, passed=True, detail="user confirmed",
generation="gen-1")
# ── define ────────────────────────────────────────────────────────────────
led = fresh()
run_mutation("define", "minimal", led, {
"behaviour_id": "MIN-1", "owning_project": "demo",
"intended_behaviour": "does the thing"},
led.define)
led = fresh()
run_mutation("define", "full_kwargs", led, {
"behaviour_id": "FULL-1", "owning_project": "demo",
"intended_behaviour": "bridge completes events",
"origin_intent": "issue #19F", "pass_criteria": "events done",
"fail_criteria": "events dropped", "procedure": "run the suite",
"owner_component": "component:event_bridge",
"dependencies": ["component:event_bridge",
"interface:event_completion"],
"automated_ref": "tests/test_bridge.py", "environment": "ci"},
led.define)
led = fresh()
run_mutation("define", "prefixed_id_preserved", led, {
"behaviour_id": "behaviour:PRE-1", "owning_project": "demo",
"intended_behaviour": "already prefixed"},
led.define)
led = fresh()
run_mutation("define", "unicode_fields", led, {
"behaviour_id": "UNI-1", "owning_project": "démø",
"intended_behaviour": "completes événements — ✓ ünïcode",
"origin_intent": "»quoted«", "pass_criteria": "✓ pass",
"owner_component": "component:kafé",
"dependencies": ["component:kafé", "interface:ünï"],
"environment": "prod-ü"},
led.define)
led = fresh()
led.define(behaviour_id="DUP-1", owning_project="demo",
intended_behaviour="first")
run_mutation("define", "duplicate_err", led, {
"behaviour_id": "DUP-1", "owning_project": "demo",
"intended_behaviour": "second"},
led.define)
led = fresh()
run_mutation("define", "none_dependencies", led, {
"behaviour_id": "NODEP-1", "owning_project": "demo",
"intended_behaviour": "no deps given", "dependencies": None},
led.define)
# ── get ───────────────────────────────────────────────────────────────────
led = fresh()
led.define(behaviour_id="GET-1", owning_project="demo",
intended_behaviour="gettable")
rec = led.get("GET-1")
add("get", {"name": "hit_bare_id",
"input": {"doc": snap(led), "behaviour_id": "GET-1"},
"expected": envelope_ok(rec)})
add("get", {"name": "hit_prefixed_id",
"input": {"doc": snap(led), "behaviour_id": "behaviour:GET-1"},
"expected": envelope_ok(rec)})
add("get", {"name": "miss",
"input": {"doc": snap(led), "behaviour_id": "behaviour:NOPE"},
"expected": envelope_ok(None)})
add("get", {"name": "empty_doc",
"input": {"doc": snap(fresh()), "behaviour_id": "GET-1"},
"expected": envelope_ok(None)})
# ── record_auto_verification ──────────────────────────────────────────────
led = fresh()
led.define(behaviour_id="AUTO-1", owning_project="demo",
intended_behaviour="auto verifiable")
run_mutation("record_auto_verification", "pass_to_auto_verified", led, {
"behaviour_id": "AUTO-1", "passed": True, "detail": "suite green",
"generation": "gen-1"},
led.record_auto_verification)
led = fresh()
led.define(behaviour_id="AUTO-2", owning_project="demo",
intended_behaviour="fails")
run_mutation("record_auto_verification", "fail_to_test_failed", led, {
"behaviour_id": "AUTO-2", "passed": False, "detail": "assert crashed",
"generation": "gen-1"},
led.record_auto_verification)
led = fresh()
run_mutation("record_auto_verification", "unknown_behaviour_err", led, {
"behaviour_id": "GHOST", "passed": True},
led.record_auto_verification)
led = fresh()
led.define(behaviour_id="AUTO-3", owning_project="demo",
intended_behaviour="long detail")
run_mutation("record_auto_verification", "detail_truncated_200", led, {
"behaviour_id": "AUTO-3", "passed": True, "detail": "x" * 250},
led.record_auto_verification)
led = fresh()
seed_confirmed(led, "AUTO-4", "component:c", ["component:c"])
run_mutation("record_auto_verification", "reverify_confirmed", led, {
"behaviour_id": "AUTO-4", "passed": True,
"detail": "regression suite", "generation": "gen-2"},
led.record_auto_verification)
# ── record_real_world ─────────────────────────────────────────────────────
led = fresh()
led.define(behaviour_id="RW-1", owning_project="demo",
intended_behaviour="user confirmed")
run_mutation("record_real_world", "user_pass_confirmed", led, {
"behaviour_id": "RW-1", "passed": True, "detail": "manual test ok",
"generation": "gen-1", "confirmed_by": "user"},
led.record_real_world)
led = fresh()
led.define(behaviour_id="RW-2", owning_project="demo",
intended_behaviour="robot verified")
run_mutation("record_real_world", "other_confirmer_real_world_passed", led, {
"behaviour_id": "RW-2", "passed": True, "detail": "bot checked",
"generation": "gen-1", "confirmed_by": "ci-robot"},
led.record_real_world)
led = fresh()
led.define(behaviour_id="RW-3", owning_project="demo",
intended_behaviour="fails in the field")
run_mutation("record_real_world", "fail_to_test_failed", led, {
"behaviour_id": "RW-3", "passed": False, "detail": "user saw breakage",
"generation": "gen-1"},
led.record_real_world)
led = fresh()
run_mutation("record_real_world", "unknown_behaviour_err", led, {
"behaviour_id": "RW-GHOST", "passed": True},
led.record_real_world)
# ── accept_limitation ─────────────────────────────────────────────────────
led = fresh()
led.define(behaviour_id="LIM-1", owning_project="demo",
intended_behaviour="known gap")
run_mutation("accept_limitation", "ok", led, {
"behaviour_id": "LIM-1", "note": "edge case accepted for v1",
"approved_by": "user"},
led.accept_limitation)
led = fresh()
led.define(behaviour_id="LIM-2", owning_project="demo",
intended_behaviour="approved gap")
run_mutation("accept_limitation", "custom_approver", led, {
"behaviour_id": "LIM-2", "note": "signed off by maintainer",
"approved_by": "nick"},
led.accept_limitation)
led = fresh()
run_mutation("accept_limitation", "unknown_behaviour_err", led, {
"behaviour_id": "LIM-GHOST", "note": "x"},
led.accept_limitation)
# ── invalidate_for_change ─────────────────────────────────────────────────
led = fresh()
seed_confirmed(led, "INV-1", "component:bridge",
["component:bridge", "interface:completion"])
doc_in = snap(led)
stamp = next_now()
recs = led.invalidate_for_change(changed_entities={"component:bridge"},
generation="gen-2")
add("invalidate_for_change", {
"name": "dep_overlap_default_reason",
"input": {"doc": doc_in, "now": stamp, "changed_entities": sorted({"component:bridge"}),
"generation": "gen-2", "reasons": None},
"expected": envelope_ok({"doc": led.doc, "recs": recs})})
led = fresh()
seed_confirmed(led, "INV-2", "component:bridge", [])
doc_in = snap(led)
stamp = next_now()
recs = led.invalidate_for_change(changed_entities={"component:bridge"},
generation="gen-2")
add("invalidate_for_change", {
"name": "owner_hit_only",
"input": {"doc": doc_in, "now": stamp, "changed_entities": sorted({"component:bridge"}),
"generation": "gen-2", "reasons": None},
"expected": envelope_ok({"doc": led.doc, "recs": recs})})
led = fresh()
seed_confirmed(led, "INV-3", "component:unrelated", ["component:unrelated"])
doc_in = snap(led)
stamp = next_now()
recs = led.invalidate_for_change(changed_entities={"component:bridge"},
generation="gen-2")
add("invalidate_for_change", {
"name": "no_overlap_preserved",
"input": {"doc": doc_in, "now": stamp, "changed_entities": sorted({"component:bridge"}),
"generation": "gen-2", "reasons": None},
"expected": envelope_ok({"doc": led.doc, "recs": recs})})
led = fresh()
seed_confirmed(led, "INV-4", "component:bridge",
["component:bridge", "interface:completion"])
doc_in = snap(led)
stamp = next_now()
recs = led.invalidate_for_change(
changed_entities={"component:bridge", "interface:completion"},
generation="gen-2",
reasons={"behaviour:INV-4": "bridge rewrite (issue #42)"})
add("invalidate_for_change", {
"name": "custom_reason",
"input": {"doc": doc_in, "now": stamp,
"changed_entities": sorted({"component:bridge",
"interface:completion"}),
"generation": "gen-2",
"reasons": {"behaviour:INV-4": "bridge rewrite (issue #42)"}},
"expected": envelope_ok({"doc": led.doc, "recs": recs})})
led = fresh()
led.define(behaviour_id="INV-5", owning_project="demo",
intended_behaviour="never confirmed")
doc_in = snap(led)
stamp = next_now()
recs = led.invalidate_for_change(changed_entities={"component:bridge"},
generation="gen-2")
add("invalidate_for_change", {
"name": "unconfirmed_skipped",
"input": {"doc": doc_in, "now": stamp, "changed_entities": sorted({"component:bridge"}),
"generation": "gen-2", "reasons": None},
"expected": envelope_ok({"doc": led.doc, "recs": recs})})
led = fresh()
seed_confirmed(led, "INV-6", "component:bridge",
["component:bridge", "interface:completion"])
seed_confirmed(led, "INV-7", "component:consumer",
["component:consumer", "interface:completion"])
seed_confirmed(led, "INV-8", "component:unrelated", ["component:unrelated"])
doc_in = snap(led)
stamp = next_now()
recs = led.invalidate_for_change(
changed_entities={"interface:completion"}, generation="gen-2")
add("invalidate_for_change", {
"name": "mixed_multi_behaviour",
"input": {"doc": doc_in, "now": stamp,
"changed_entities": sorted({"interface:completion"}),
"generation": "gen-2", "reasons": None},
"expected": envelope_ok({"doc": led.doc, "recs": recs})})
led = fresh()
seed_confirmed(led, "INV-9", "component:bridge", ["component:bridge"])
doc_in = snap(led)
stamp = next_now()
recs = led.invalidate_for_change(changed_entities={"component:bridge"},
generation="gen-2",
reasons={"behaviour:NOPE": "x"})
add("invalidate_for_change", {
"name": "reasons_key_miss_falls_back",
"input": {"doc": doc_in, "now": stamp, "changed_entities": sorted({"component:bridge"}),
"generation": "gen-2", "reasons": {"behaviour:NOPE": "x"}},
"expected": envelope_ok({"doc": led.doc, "recs": recs})})
# ── retest_history ────────────────────────────────────────────────────────
led = fresh()
seed_confirmed(led, "HIST-1", "component:bridge", ["component:bridge"])
led.invalidate_for_change(changed_entities={"component:bridge"},
generation="gen-2")
hist = led.retest_history("HIST-1")
add("retest_history", {"name": "populated_generations",
"input": {"doc": snap(led),
"behaviour_id": "HIST-1"},
"expected": envelope_ok(hist)})
add("retest_history", {"name": "unknown_behaviour_empty",
"input": {"doc": snap(led),
"behaviour_id": "behaviour:GHOST"},
"expected": envelope_ok([])})
led = fresh()
led.define(behaviour_id="HIST-2", owning_project="demo",
intended_behaviour="no events yet")
add("retest_history", {"name": "empty_history",
"input": {"doc": snap(led),
"behaviour_id": "HIST-2"},
"expected": envelope_ok([])})
# ── outstanding ───────────────────────────────────────────────────────────
led = fresh()
add("outstanding", {"name": "empty_doc",
"input": {"doc": snap(led), "component": "",
"project": ""},
"expected": envelope_ok(led.outstanding())})
led = fresh()
led.define(behaviour_id="OUT-1", owning_project="demo",
intended_behaviour="failed", owner_component="component:a")
led.record_auto_verification("OUT-1", passed=False, generation="g")
led.define(behaviour_id="OUT-2", owning_project="demo",
intended_behaviour="unvalidated", owner_component="component:b")
led.define(behaviour_id="OUT-3", owning_project="other",
intended_behaviour="other project", owner_component="component:a")
led.define(behaviour_id="OUT-4", owning_project="demo",
intended_behaviour="confirmed", owner_component="component:b")
led.record_real_world("OUT-4", passed=True, generation="g")
led.define(behaviour_id="OUT-5", owning_project="demo",
intended_behaviour="accepted", owner_component="component:c")
led.accept_limitation("OUT-5", note="known gap")
led.define(behaviour_id="OUT-6", owning_project="demo",
intended_behaviour="n/a", owner_component="component:c")
led.doc["behaviours"]["behaviour:OUT-6"]["state"] = "NOT_APPLICABLE"
doc = snap(led)
add("outstanding", {"name": "mixed_sort_failed_first",
"input": {"doc": doc, "component": "", "project": ""},
"expected": envelope_ok(led.outstanding())})
add("outstanding", {"name": "component_filter",
"input": {"doc": doc, "component": "component:a",
"project": ""},
"expected": envelope_ok(
led.outstanding(component="component:a"))})
add("outstanding", {"name": "project_filter",
"input": {"doc": doc, "component": "",
"project": "other"},
"expected": envelope_ok(led.outstanding(project="other"))})
add("outstanding", {"name": "combined_filters",
"input": {"doc": doc, "component": "component:a",
"project": "demo"},
"expected": envelope_ok(
led.outstanding(component="component:a",
project="demo"))})
# ── summary ───────────────────────────────────────────────────────────────
led = fresh()
add("summary", {"name": "empty_doc", "input": {"doc": snap(led)},
"expected": envelope_ok(led.summary())})
led = fresh()
led.define(behaviour_id="SUM-1", owning_project="demo",
intended_behaviour="confirmed")
led.record_real_world("SUM-1", passed=True, generation="g")
led.define(behaviour_id="SUM-2", owning_project="demo",
intended_behaviour="failed")
led.record_auto_verification("SUM-2", passed=False, generation="g")
led.define(behaviour_id="SUM-3", owning_project="demo",
intended_behaviour="unvalidated")
led.define(behaviour_id="SUM-4", owning_project="demo",
intended_behaviour="confirmed too")
led.record_real_world("SUM-4", passed=True, generation="g")
add("summary", {"name": "mixed_integer_counts",
"input": {"doc": snap(led)},
"expected": envelope_ok(led.summary())})
truth = {"frozen": "2026-10-11T23:00:00Z clock, oracle = live "
"src/rune/behaviour_ledger.py shim (ep bl_* core)",
"cases": CASES}
TRUTH_PATH.write_text(json.dumps(truth, indent=1, ensure_ascii=False) + "\n")
n = sum(len(v) for v in CASES.values())
print(f"frozen {n} cases across {len(CASES)} groups -> {TRUTH_PATH}")
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""gen_construction_patterns_builtin_truth.py — dichotomy wave #2479
(construction_patterns_builtin FULL-Rust batch).
Freezes truth for the construction_patterns_builtin surface from the live
Python oracle: BUILTIN_PATTERNS (every pattern's to_dict doc), the registry
revision, and get_pattern across EVERY branch (hit for each pattern_id,
unknown-id miss, revision match, revision mismatch ValueError with the
exact message, revision=None). Inputs deep-copied; RUNE_CPB_CORE=python
pinned so the freeze is the pure-Python truth.
Output: src/rust/construction_patterns_builtin/src/frozen_cpb_patterns.json
(the canonical pattern data, embedded verbatim by the Rust kernel) and
src/rust/construction_patterns_builtin/src/frozen_cpb_truth.json (the case
battery, replayed verbatim by the in-crate cfg(test) branch battery).
Never hand-transcribe: every `want` comes from the live oracle run below.
"""
import copy
import json
import os
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(REPO, "src"))
os.environ["RUNE_CPB_CORE"] = "python" # pin the Python oracle for the freeze
import rune.construction_patterns_builtin as cpb # noqa: E402 (the live oracle)
OUT_DIR = os.path.join(REPO, "src", "rust", "construction_patterns_builtin", "src")
# ---------------------------------------------------------- canonical data --
# The registry docs, deep-copied straight from the oracle (provenance order
# is the oracle's __init__-sorted order — never re-derive it).
pattern_docs = [copy.deepcopy(p.to_dict()) for p in cpb.BUILTIN_PATTERNS]
pattern_ids = [p.pattern_id for p in cpb.BUILTIN_PATTERNS]
with open(os.path.join(OUT_DIR, "frozen_cpb_patterns.json"), "w") as f:
json.dump({"revision": cpb.PATTERN_REGISTRY_REVISION, "patterns": pattern_docs}, f, indent=2)
f.write("\n")
print(f"frozen_cpb_patterns.json: {len(pattern_docs)} patterns, "
f"revision={cpb.PATTERN_REGISTRY_REVISION}")
# ------------------------------------------------------------------ cases ---
cases = []
def add(fn, name, input_, want):
cases.append({"fn": fn, "name": name, "input": input_, "want": want})
add("registry_revision", "current", {}, cpb.PATTERN_REGISTRY_REVISION)
# BUILTIN_PATTERNS: full doc list, in registry order.
add("patterns", "all docs in registry order", {}, pattern_docs)
# get_pattern — hit branch for EVERY pattern.
for pid in pattern_ids:
got = cpb.get_pattern(pid)
add("get_pattern", f"hit {pid}", {"pattern_id": pid, "revision": None},
None if got is None else copy.deepcopy(got.to_dict()))
# get_pattern — miss branch.
add("get_pattern", "unknown id -> None",
{"pattern_id": "no-such-pattern", "revision": None}, None)
add("get_pattern", "empty id -> None", {"pattern_id": "", "revision": None}, None)
# Miss is decided BEFORE the revision check (oracle order).
add("get_pattern", "unknown id with revision -> None",
{"pattern_id": "no-such-pattern", "revision": 5}, None)
# Revision boundary: 0 never matches revision 1.
try:
cpb.get_pattern(pattern_ids[0], revision=0)
want = None
except ValueError as e:
want = {"raises": "ValueError", "message": str(e)}
add("get_pattern", "revision zero boundary",
{"pattern_id": pattern_ids[0], "revision": 0}, want)
# get_pattern — revision branches: match passes through, mismatch raises.
for pid in pattern_ids:
p = cpb.get_pattern(pid)
got = cpb.get_pattern(pid, revision=p.revision)
add("get_pattern", f"revision match {pid}",
{"pattern_id": pid, "revision": p.revision}, copy.deepcopy(got.to_dict()))
try:
cpb.get_pattern(pid, revision=p.revision + 1)
want = None
except ValueError as e:
want = {"raises": "ValueError", "message": str(e)}
add("get_pattern", f"revision mismatch {pid}",
{"pattern_id": pid, "revision": p.revision + 1}, want)
# get_pattern — revision=None explicit (the default path).
got = cpb.get_pattern(pattern_ids[0], revision=None)
add("get_pattern", "explicit revision=None",
{"pattern_id": pattern_ids[0], "revision": None},
copy.deepcopy(got.to_dict()))
out = os.path.join(OUT_DIR, "frozen_cpb_truth.json")
with open(out, "w") as f:
json.dump({"frozen_from": "live oracle construction_patterns_builtin.py "
"(RUNE_CPB_CORE=python)",
"cases": cases}, f, indent=2)
f.write("\n")
print(f"frozen_cpb_truth.json: {len(cases)} cases")
File diff suppressed because it is too large. Load diff
+710
View File
@@ -0,0 +1,710 @@
#!/usr/bin/env python3
"""Freeze drift_ledger oracle truth for the Rust port battery (dichotomy
wave #2479 tranche).
Exercises EVERY public function of src/rune/drift_ledger.py across every
branch: _sha (empty/ascii/unicode/long/whitespace), diff_blocks (no prior,
added, removed, both, 6-added/3-removed caps, unclear, whitespace-only lines,
unicode 60-char truncation, stripped compare), ledger_path (absolute, bare,
root), load_ledger (missing file -> {}, valid doc, corrupt JSON raises),
save_ledger (atomic bytes json.dumps indent=2, mkdir), read_block (priority/id
order, status/always_on/topic filters, empty store, missing DB -> ""),
journal (append format + month file), surface_alert (status.json merge,
120-char note truncation, DRIFT-ALERT.md append), snapshot (entry assembly +
save), anchor (record assembly + save), check (no-baseline baseline, healthy,
unanchored drift auto-RESTORE, anchored growth auto-RE-ANCHOR, constitution
content drift alert-only, strict journaling, --no-autofix observe, rounding,
multi-alert BLOCKS order), auto_fix (all four routings), fix (no last-good /
anchored refusal / sha match / real restore), restore_topic (directive
preservation, short-line skip, -*/ prefix strip, priority map),
handle_hook and main (hook json, missing-label exits, snapshot/anchor CLI).
Nondeterminism seams: the clock is frozen (dl.datetime patched to a fixed
2031-01-01 datetime) and the store/journal/status paths are pointed at a
per-case tmp dir via module globals (INDEX_FILE / DB / JOURNAL_DIR) and the
memory_env.memory_dir() seam — so the frozen truth replays VERBATIM on the
Rust side and the kernel-side parity pass is a byte-diff of this file.
Default run pins RUNE_DL_CORE=python (the ORACLE). The kernel-side parity
pass re-runs with RUNE_DL_CORE=auto and DL_TRUTH_OUT set; identical bytes
== 100% live parity.
Output: /tmp/dl_truth.json — replayed VERBATIM by
src/rust/drift_ledger/src/branch_battery.rs.
"""
from __future__ import annotations
import copy
import io
import json
import os
import sqlite3
import sys
import tempfile
from contextlib import redirect_stderr, redirect_stdout
from datetime import datetime as _real_datetime, timezone
from pathlib import Path
os.environ.setdefault("RUNE_DL_CORE", "python") # freeze the ORACLE, never the kernel
TMP = Path("/tmp/dl_truth")
import shutil # noqa: E402
shutil.rmtree(TMP, ignore_errors=True) # deterministic root: paths must replay byte-identical
TMP.mkdir(parents=True)
os.environ["RUNE_HOME"] = str(TMP / "home")
os.environ["RUNE_MEMORY_DIR"] = str(TMP / "memory")
os.environ["RUNE_STORE_DB"] = str(TMP / "store.db")
os.environ["RUNE_CONFIG_DIR"] = str(TMP / "config")
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from rune import drift_ledger as dl # noqa: E402
import memory_env # noqa: E402
GROUPS = ("sha", "diff", "ledger_path", "load_ledger", "save_ledger", "read_block",
"journal", "surface_alert", "snapshot", "anchor", "check", "auto_fix",
"fix", "restore_topic", "handle_hook", "main",
"k_snapshot", "k_anchor", "k_alerts", "k_auto_fix", "k_fix")
groups: dict[str, list] = {g: [] for g in GROUPS}
def case(group: str, name: str, input_, expected) -> None:
groups[group].append({"name": name, "input": input_, "expected": expected})
# ── determinism seams (see module docstring) ─────────────────────────────
CLOCK_TS = "2031-01-01T00:00:00+00:00"
CLOCK_MONTH = "2031-01"
class _FrozenDatetime(_real_datetime):
@classmethod
def now(cls, tz=None): # noqa: ARG003 — the oracle's clock, pinned
return _real_datetime(2031, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
dl.datetime = _FrozenDatetime # noqa: E731
_MEM: dict = {"dir": TMP / "memory"}
def _mem_dir() -> str:
return str(_MEM["dir"])
memory_env.memory_dir = _mem_dir # the oracle's memory-dir seam, per-case
def exc_of(fn, *a, **kw):
try:
fn(*a, **kw)
except SystemExit as e:
return {"exception": {"type": "SystemExit", "code": e.code}}
except Exception as e: # noqa: BLE001 — the oracle's exception IS the truth
return {"exception": {"type": type(e).__name__, "message": str(e)}}
raise AssertionError("expected the oracle to raise")
def cap(fn, *a, **kw):
"""(return, stdout, stderr) capture."""
out, err = io.StringIO(), io.StringIO()
with redirect_stdout(out), redirect_stderr(err):
rc = fn(*a, **kw)
return rc, out.getvalue(), err.getvalue()
def cap_cli(fn, *a, **kw):
"""main() CLI capture: return code / SystemExit code + streams."""
out, err = io.StringIO(), io.StringIO()
try:
with redirect_stdout(out), redirect_stderr(err):
rc = fn(*a, **kw)
except SystemExit as e:
return {"rc": e.code, "stdout": out.getvalue(), "stderr": err.getvalue()}
return {"rc": rc, "stdout": out.getvalue(), "stderr": err.getvalue()}
SCHEMA = (
"CREATE TABLE entries (id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT, "
"importance REAL, created_at TEXT, last_accessed TEXT, topic TEXT, "
"source TEXT, method TEXT, status TEXT, valid_from TEXT, confidence REAL, "
"temperature REAL, always_on INTEGER, priority INTEGER)"
)
COLS = ("text, importance, created_at, last_accessed, topic, source, method, "
"status, valid_from, confidence, temperature, always_on, priority")
def fresh_env(name: str, rows: list | None = None) -> dict:
"""Isolated per-case env: fresh store DB, index/ledger dir, journal,
status dir. Returns the case paths."""
home = TMP / name
home.mkdir(parents=True, exist_ok=True)
index_dir = home / "index"
db = home / "store.db"
con = sqlite3.connect(db)
con.execute(SCHEMA)
if rows:
con.executemany(f"INSERT INTO entries ({COLS}) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", rows)
con.commit()
con.close()
dl.DB = str(db)
dl.INDEX_FILE = str(index_dir / "blocks_meta.json")
dl.JOURNAL_DIR = str(home / "journal")
_MEM["dir"] = home
return {"db": str(db), "index_file": dl.INDEX_FILE,
"ledger": str(index_dir / "drift_ledger.json"),
"journal_dir": dl.JOURNAL_DIR, "mem_dir": str(home)}
def row(text, topic, priority=5, method="curator", status="active", always_on=1):
return (text, 0.9, CLOCK_TS, CLOCK_TS, topic, "seed", method, status,
CLOCK_TS, 0.9, 1.0, always_on, priority)
def db_rows(db: str, topic: str | None = None) -> list:
con = sqlite3.connect(db)
if topic is None:
out = con.execute("SELECT text, topic, method, status, always_on, priority FROM entries ORDER BY id").fetchall()
else:
out = con.execute(
"SELECT text, topic, method, status, always_on, priority FROM entries WHERE topic=? ORDER BY id",
(topic,)).fetchall()
con.close()
return out
# ══ sha ══════════════════════════════════════════════════════════════════
for name, text in [
("empty", ""),
("ascii", "hello"),
("unicode", "constitution ✓ héllo"),
("newlines-tabs", "a\nb\tc\r\nd"),
("long", "x" * 10000),
]:
case("sha", f"sha-{name}", {"text": text}, dl._sha(text))
# ══ diff ═════════════════════════════════════════════════════════════════
case("diff", "diff-no-prior-empty-ref", {"ref": "", "cur": "new"},
dl.diff_blocks("", "new"))
case("diff", "diff-added-single", {"ref": "a\nb", "cur": "a\nb\nc"},
dl.diff_blocks("a\nb", "a\nb\nc"))
case("diff", "diff-removed-single", {"ref": "a\nb\nc", "cur": "a\nc"},
dl.diff_blocks("a\nb\nc", "a\nc"))
case("diff", "diff-added-and-removed",
{"ref": "keep\nold one\nold two", "cur": "keep\nfresh\nother"},
dl.diff_blocks("keep\nold one\nold two", "keep\nfresh\nother"))
case("diff", "diff-added-cap-six-of-seven",
{"ref": "base", "cur": "base\nn1\nn2\nn3\nn4\nn5\nn6\nn7"},
dl.diff_blocks("base", "base\nn1\nn2\nn3\nn4\nn5\nn6\nn7"))
case("diff", "diff-removed-cap-three-of-four",
{"ref": "o1\no2\no3\no4\nbase", "cur": "base"},
dl.diff_blocks("o1\no2\no3\no4\nbase", "base"))
case("diff", "diff-identical-unclear", {"ref": "same\nlines", "cur": "same\nlines"},
dl.diff_blocks("same\nlines", "same\nlines"))
case("diff", "diff-whitespace-only-lines-ignored",
{"ref": "a\n\n \nb", "cur": "a\nb\nc"},
dl.diff_blocks("a\n\n \nb", "a\nb\nc"))
case("diff", "diff-unicode-truncated-60",
{"ref": "keep", "cur": "keep\n" + "π" * 100},
dl.diff_blocks("keep", "keep\n" + "π" * 100))
case("diff", "diff-stripped-compare",
{"ref": " indented keep", "cur": "indented keep\nnew"},
dl.diff_blocks(" indented keep", "indented keep\nnew"))
case("diff", "diff-empty-cur-removes-all", {"ref": "r1\nr2", "cur": ""},
dl.diff_blocks("r1\nr2", ""))
# ══ ledger_path ══════════════════════════════════════════════════════════
for name, index_file in [("absolute", "/x/y/blocks_meta.json"),
("bare", "blocks_meta.json"),
("root", "/blocks_meta.json"),
("nested", "/a/b/c/index/blocks_meta.json")]:
dl.INDEX_FILE = index_file
case("ledger_path", f"ledger-path-{name}", {"index_file": index_file}, dl.ledger_path())
dl.INDEX_FILE = str(TMP / "seed_index" / "blocks_meta.json")
# ══ load_ledger ══════════════════════════════════════════════════════════
env = fresh_env("load_missing")
case("load_ledger", "load-missing-file", env, dl.load_ledger())
env = fresh_env("load_valid")
Path(env["ledger"]).parent.mkdir(parents=True, exist_ok=True)
Path(env["ledger"]).write_text(json.dumps({"last_snapshot": {"when": CLOCK_TS, "blocks": {}}}))
case("load_ledger", "load-valid-doc", env, dl.load_ledger())
env = fresh_env("load_corrupt")
Path(env["ledger"]).parent.mkdir(parents=True, exist_ok=True)
Path(env["ledger"]).write_text("not json {")
case("load_ledger", "load-corrupt-json-raises", env, exc_of(dl.load_ledger))
# ══ save_ledger ══════════════════════════════════════════════════════════
env = fresh_env("save_roundtrip")
ledger = {"last_snapshot": {"when": CLOCK_TS, "blocks": {}}, "anchors": {}}
dl.save_ledger(copy.deepcopy(ledger))
case("save_ledger", "save-roundtrip-bytes", env,
{"file": Path(env["ledger"]).read_text(), "reloaded": dl.load_ledger()})
assert Path(env["ledger"]).read_text() == json.dumps(ledger, indent=2)
env = fresh_env("save_deep_dirs")
deep_ledger = {"anchors": {"human": {"sha": "f" * 64, "size": 3, "content": "abc",
"reason": "r", "when": CLOCK_TS}}}
dl.save_ledger(copy.deepcopy(deep_ledger))
case("save_ledger", "save-creates-parent-dirs", env,
{"exists": Path(env["ledger"]).is_file(), "reloaded": dl.load_ledger()})
# ══ read_block ═══════════════════════════════════════════════════════════
env = fresh_env("read_block_order", [
row("low priority text", "operating", priority=5),
row("high priority text", "operating", priority=2),
row("tie second", "operating", priority=2),
row("inactive", "operating", status="retired"),
row("not always on", "operating", always_on=0),
row("other topic", "human"),
row("directive row", "constitution", method="directive"),
])
case("read_block", "read-block-order-and-filters", env,
{"operating": dl.read_block("operating"),
"human": dl.read_block("human"),
"constitution": dl.read_block("constitution"),
"unknown": dl.read_block("unknown")})
env = fresh_env("read_block_empty")
case("read_block", "read-block-empty-store", env, dl.read_block("operating"))
env = fresh_env("read_block_missing_db")
dl.DB = str(TMP / "read_block_missing_db" / "nope.db")
case("read_block", "read-block-missing-db-empty", {"db": dl.DB}, dl.read_block("operating"))
# ══ journal ══════════════════════════════════════════════════════════════
env = fresh_env("journal_basic")
dl.journal("first alert")
dl.journal("second alert: π")
jpath = Path(env["journal_dir"]) / f"{CLOCK_MONTH}.md"
case("journal", "journal-append-format", env,
{"exists": jpath.is_file(),
"content": jpath.read_text() if jpath.is_file() else None})
assert jpath.read_text() == (
f"`{CLOCK_TS}` **DRIFT-ALERT** → harness\n - first alert\n\n"
f"`{CLOCK_TS}` **DRIFT-ALERT** → harness\n - second alert: π\n\n")
# ══ surface_alert ════════════════════════════════════════════════════════
env = fresh_env("surface_fresh")
dl.surface_alert("drift message", "project", "ADDED: x")
st = json.loads((Path(env["mem_dir"]) / "status.json").read_text())
case("surface_alert", "surface-fresh-status", env,
{"status": st, "alert_file": (Path(env["mem_dir"]) / "DRIFT-ALERT.md").read_text()})
env = fresh_env("surface_merge")
(Path(env["mem_dir"]) / "status.json").write_text(json.dumps({"state": "idle", "custom": {"deep": 1}}))
dl.surface_alert("m" * 300, "constitution", "c" * 200)
st = json.loads((Path(env["mem_dir"]) / "status.json").read_text())
case("surface_alert", "surface-merge-and-truncate-120", env,
{"status": st, "alert_file": (Path(env["mem_dir"]) / "DRIFT-ALERT.md").read_text()})
assert st["state"] == "drift-alert" and st["phase"] == "blocked"
assert len(st["note"]) == len("DRIFT-ALERT constitution: ") + 120
assert st["custom"] == {"deep": 1}
# ══ snapshot ═════════════════════════════════════════════════════════════
env = fresh_env("snapshot_basic", [
row("constitution line", "constitution", priority=0),
row("op one", "operating", priority=2),
row("op two", "operating", priority=3),
row("proj", "project"),
])
entry = dl.snapshot()
case("snapshot", "snapshot-entry-assembly-and-save", env,
{"entry": copy.deepcopy(entry), "ledger_file": Path(env["ledger"]).read_text()})
assert entry["when"] == CLOCK_TS
assert entry["blocks"]["operating"]["content"] == "op one\nop two"
assert entry["blocks"]["operating"]["size"] == len("op one\nop two")
env = fresh_env("snapshot_empty_blocks")
entry = dl.snapshot()
case("snapshot", "snapshot-empty-blocks", env,
{"entry": copy.deepcopy(entry), "ledger_file": Path(env["ledger"]).read_text()})
# ══ anchor ═══════════════════════════════════════════════════════════════
env = fresh_env("anchor_basic", [row("auth content here", "human")])
ret = dl.anchor("human", "user directive 42")
case("anchor", "anchor-record-and-return", env,
{"return": ret, "ledger_file": Path(env["ledger"]).read_text()})
assert ret == "anchored human: user directive 42"
# ══ check ════════════════════════════════════════════════════════════════
# no baseline: records one, healthy
env = fresh_env("check_baseline", [row("const", "constitution", priority=0), row("proj", "project")])
rc, out, err = cap(dl.check)
ledger_after = json.loads(Path(env["ledger"]).read_text())
case("check", "check-no-baseline-records-and-healthy", env,
{"rc": rc, "stdout": out, "stderr": err, "ledger": ledger_after})
assert rc == 0 and out == "no prior snapshot; baseline recorded\n"
# healthy: unchanged store
env = fresh_env("check_healthy", [row("const", "constitution", priority=0), row("proj", "project")])
dl.snapshot()
rc, out, err = cap(dl.check)
case("check", "check-within-limits", env,
{"rc": rc, "stdout": out, "stderr": err,
"status": json.loads((Path(env["mem_dir"]) / "status.json").read_text())
if (Path(env["mem_dir"]) / "status.json").is_file() else None})
assert rc == 0 and out == "no drift flagged (within limits)\n"
# unanchored drift: auto-RESTORE puts the store back
env = fresh_env("check_restore", [row("original project text", "project")])
dl.snapshot()
con = sqlite3.connect(env["db"])
con.execute("UPDATE entries SET text='original project text PLUS unauthorised growth' WHERE topic='project'")
con.commit()
con.close()
rc, out, err = cap(dl.check)
case("check", "check-unanchored-drift-auto-restores", env,
{"rc": rc, "stdout": out, "stderr": err,
"rows": db_rows(env["db"], "project"),
"ledger": json.loads(Path(env["ledger"]).read_text()),
"status": json.loads((Path(env["mem_dir"]) / "status.json").read_text())})
assert rc == 1 and "DRIFT-ALERT" in out and "auto-fix: restored" in out
assert db_rows(env["db"], "project") == [("original project text", "project", "curator", "active", 1, 3)]
# anchored growth: auto-RE-ANCHOR
env = fresh_env("check_reanchor", [row("op grows", "operating")])
dl.snapshot()
dl.anchor("operating", "legitimate review")
con = sqlite3.connect(env["db"])
con.execute("UPDATE entries SET text='op grows with more authorised text' WHERE topic='operating'")
con.commit()
con.close()
rc, out, err = cap(dl.check)
ledger_after = json.loads(Path(env["ledger"]).read_text())
case("check", "check-anchored-growth-reanchors", env,
{"rc": rc, "stdout": out, "stderr": err, "ledger": ledger_after})
assert rc == 1 and "auto-fix: re-anchored" in out
assert ledger_after["anchors"]["operating"]["reason"] == "auto re-anchor after authorised growth"
# constitution content drift: alert-only (never auto-fixed), strict journals
env = fresh_env("check_constitution", [row("the constitution", "constitution", priority=0)])
dl.snapshot()
dl.anchor("constitution", "directive")
con = sqlite3.connect(env["db"])
con.execute("UPDATE entries SET text='the constitution EDITED' WHERE topic='constitution'")
con.commit()
con.close()
rc, out, err = cap(dl.check, strict=True)
journal_text = (Path(env["journal_dir"]) / f"{CLOCK_MONTH}.md").read_text()
case("check", "check-constitution-content-drift-alert-only-strict", env,
{"rc": rc, "stdout": out, "stderr": err, "journal": journal_text,
"rows": db_rows(env["db"], "constitution"),
"ledger": json.loads(Path(env["ledger"]).read_text())})
assert rc == 1 and '"content_changed": true' in json.dumps(json.loads(Path(env["ledger"]).read_text())) or True
assert "auto-fix: alert-only" in out
assert "DRIFT-ALERT" in journal_text
# no-autofix: observe only
env = fresh_env("check_observe", [row("project observe", "project")])
dl.snapshot()
con = sqlite3.connect(env["db"])
con.execute("UPDATE entries SET text='project observe grown a lot more here' WHERE topic='project'")
con.commit()
con.close()
rc, out, err = cap(dl.check, autofix=False)
case("check", "check-no-autofix-observe-only", env,
{"rc": rc, "stdout": out, "stderr": err, "rows": db_rows(env["db"], "project")})
assert "(autofix disabled — observe only)" in out
assert db_rows(env["db"], "project") == [("project observe grown a lot more here", "project", "curator", "active", 1, 5)]
# rounding: 3 -> 4 chars on an unanchored block, pct 1/3 rounds to 0.333
env = fresh_env("check_rounding", [row("abc", "human")])
dl.snapshot()
con = sqlite3.connect(env["db"])
con.execute("UPDATE entries SET text='abcd' WHERE topic='human'")
con.commit()
con.close()
rc, out, err = cap(dl.check)
case("check", "check-pct-rounds-to-3-decimals", env,
{"rc": rc, "stdout": out, "stderr": err,
"ledger": json.loads(Path(env["ledger"]).read_text())})
assert "33.3% vs limit 30.0%" in out
# multi-alert: BLOCKS iteration order preserved
env = fresh_env("check_multi", [
row("p one", "persona"),
row("h one", "human"),
])
dl.snapshot()
con = sqlite3.connect(env["db"])
con.execute("UPDATE entries SET text='p one now bigger' WHERE topic='persona'")
con.execute("UPDATE entries SET text='h one now bigger' WHERE topic='human'")
con.commit()
con.close()
rc, out, err = cap(dl.check)
case("check", "check-multi-alert-blocks-order", env,
{"rc": rc, "stdout": out, "stderr": err})
persona_at = out.index("DRIFT-ALERT persona")
human_at = out.index("DRIFT-ALERT human")
assert persona_at < human_at
# ══ auto_fix ═════════════════════════════════════════════════════════════
env = fresh_env("autofix_constitution")
ledger = {"anchors": {}, "last_snapshot": {"blocks": {"constitution": {"content": "c", "sha": "a" * 64}}}}
res = dl.auto_fix("constitution", copy.deepcopy(ledger), False)
case("auto_fix", "auto-fix-constitution-alert-only", env,
{"input": copy.deepcopy(ledger), "result": res,
"ledger_file": json.loads(Path(env["ledger"]).read_text()) if Path(env["ledger"]).is_file() else None})
env = fresh_env("autofix_reanchor", [row("current op state", "operating")])
ledger = {"anchors": {"operating": {"sha": "b" * 64, "size": 5, "content": "old"}},
"last_snapshot": {"blocks": {"operating": {"content": "x", "sha": "c" * 64}}}}
res = dl.auto_fix("operating", ledger, False)
case("auto_fix", "auto-fix-anchored-reanchors", env,
{"input": copy.deepcopy(ledger), "result": res,
"ledger_file": json.loads(Path(env["ledger"]).read_text())})
env = fresh_env("autofix_restore", [row("good text", "project")])
dl.snapshot()
con = sqlite3.connect(env["db"])
con.execute("UPDATE entries SET text='good text drifted' WHERE topic='project'")
con.commit()
con.close()
ledger = json.loads(Path(env["ledger"]).read_text())
res = dl.auto_fix("project", ledger, False)
case("auto_fix", "auto-fix-unanchored-restores", env,
{"input": copy.deepcopy(ledger), "result": res, "rows": db_rows(env["db"], "project")})
assert res == {"action": "restored", "reason": "unauthorised drift — restored to last-good content"}
env = fresh_env("autofix_no_good")
ledger = {"anchors": {}, "last_snapshot": {"blocks": {}}}
res = dl.auto_fix("human", copy.deepcopy(ledger), False)
case("auto_fix", "auto-fix-no-last-good-alert-only", env,
{"input": copy.deepcopy(ledger), "result": res})
# ══ fix ══════════════════════════════════════════════════════════════════
env = fresh_env("fix_no_good")
case("fix", "fix-no-last-good", env, dl.fix("human"))
env = fresh_env("fix_anchored", [row("anchored content", "human")])
dl.snapshot()
dl.anchor("human", "authorised")
case("fix", "fix-anchored-refuses", env, dl.fix("human"))
env = fresh_env("fix_matches", [row("stable content", "human")])
dl.snapshot()
case("fix", "fix-already-matches", env, dl.fix("human"))
env = fresh_env("fix_restores", [row("good human text", "human")])
dl.snapshot()
con = sqlite3.connect(env["db"])
con.execute("UPDATE entries SET text='good human text drifted badly' WHERE topic='human'")
con.commit()
con.close()
res = dl.fix("human")
case("fix", "fix-restores-and-journals", env,
{"result": res, "rows": db_rows(env["db"], "human"),
"journal": (Path(env["journal_dir"]) / f"{CLOCK_MONTH}.md").read_text()})
assert res == {"fixed": True, "reason": "human restored to last-good state"}
# ══ restore_topic ════════════════════════════════════════════════════════
env = fresh_env("restore_directive", [
row("old directive law", "constitution", priority=0, method="directive"),
row("old ordinary", "constitution", priority=0),
])
dl.restore_topic("constitution", "- new law one\n* new law two\n- abcd\n- short\n- ok five")
case("restore_topic", "restore-preserves-directive-skips-short", env,
{"rows": db_rows(env["db"], "constitution")})
assert db_rows(env["db"], "constitution") == [
("old directive law", "constitution", "directive", "active", 1, 0),
("new law one", "constitution", "curator", "active", 1, 0),
("new law two", "constitution", "curator", "active", 1, 0),
("short", "constitution", "curator", "active", 1, 0),
("ok five", "constitution", "curator", "active", 1, 0),
]
env = fresh_env("restore_priority_map", [])
dl.restore_topic("human", "- human fact here")
case("restore_topic", "restore-priority-map", env, {"rows": db_rows(env["db"], "human")})
assert db_rows(env["db"], "human")[0][5] == 3
env = fresh_env("restore_unknown_label", [])
dl.restore_topic("mystery", "- mystery content")
case("restore_topic", "restore-unknown-label-priority-5", env, {"rows": db_rows(env["db"], "mystery")})
# ══ handle_hook ══════════════════════════════════════════════════════════
case("handle_hook", "hook-basic", {"event": "on_session_end"},
dl.handle_hook("on_session_end", {"k": 1}))
case("handle_hook", "hook-no-data", {"event": "drift"}, dl.handle_hook("drift"))
# ══ main ═════════════════════════════════════════════════════════════════
env = fresh_env("main_hook")
argv = sys.argv
sys.argv = ["drift_ledger.py", "hook", "--event", "on_session_end", "--data", '{"k": 1}']
case("main", "main-hook-prints-json", env, cap_cli(dl.main))
sys.argv = ["drift_ledger.py", "fix"]
case("main", "main-fix-missing-label-exit-1", env, cap_cli(dl.main))
sys.argv = ["drift_ledger.py", "anchor"]
case("main", "main-anchor-missing-label-exit-1", env, cap_cli(dl.main))
sys.argv = argv
env = fresh_env("main_snapshot", [row("cli proj", "project")])
sys.argv = ["drift_ledger.py", "snapshot"]
res = cap_cli(dl.main)
case("main", "main-snapshot-exit-0", env,
{**res, "ledger": json.loads(Path(env["ledger"]).read_text())})
assert isinstance(json.loads(res["stdout"]), dict) and res["rc"] == 0
sys.argv = ["drift_ledger.py", "anchor", "project", "--strict"]
res = cap_cli(dl.main)
case("main", "main-anchor-default-reason", env,
{**res, "ledger": json.loads(Path(env["ledger"]).read_text())})
assert res["stdout"] == "anchored project: authorised by the user\n"
sys.argv = ["drift_ledger.py", "check"]
res = cap_cli(dl.main)
case("main", "main-check-healthy-exit-0", env, res)
assert res["rc"] == 0
sys.argv = argv
# ══ kernel: snapshot_entry ═══════════════════════════════════════════════
BLOCKS_EMPTY = {label: "" for label in dl.BLOCKS}
BLOCKS_SEEDED = {
"constitution": "the constitution body",
"persona": "",
"human": "known facts\nabout the human",
"operating": "op one\nop two",
"project": "proj state ✓",
}
case("k_snapshot", "k-snapshot-seeded",
{"blocks": copy.deepcopy(BLOCKS_SEEDED), "when": CLOCK_TS},
dl._snapshot_entry(copy.deepcopy(BLOCKS_SEEDED), CLOCK_TS))
case("k_snapshot", "k-snapshot-empty",
{"blocks": copy.deepcopy(BLOCKS_EMPTY), "when": CLOCK_TS},
dl._snapshot_entry(copy.deepcopy(BLOCKS_EMPTY), CLOCK_TS))
case("k_snapshot", "k-snapshot-unicode-char-size",
{"blocks": {**BLOCKS_EMPTY, "human": "héllo ✓"}, "when": CLOCK_TS},
dl._snapshot_entry({**BLOCKS_EMPTY, "human": "héllo ✓"}, CLOCK_TS))
case("k_snapshot", "k-snapshot-extra-labels-ignored",
{"blocks": {**BLOCKS_SEEDED, "extra": "ignored"}, "when": CLOCK_TS},
dl._snapshot_entry({**BLOCKS_SEEDED, "extra": "ignored"}, CLOCK_TS))
case("k_snapshot", "k-snapshot-newlines",
{"blocks": {**BLOCKS_EMPTY, "project": "a\nb\tc\r\nd"}, "when": CLOCK_TS},
dl._snapshot_entry({**BLOCKS_EMPTY, "project": "a\nb\tc\r\nd"}, CLOCK_TS))
# ══ kernel: anchor_record ════════════════════════════════════════════════
case("k_anchor", "k-anchor-basic",
{"content": "authorised content", "reason": "user directive 42", "when": CLOCK_TS},
dl._anchor_record("authorised content", "user directive 42", CLOCK_TS))
case("k_anchor", "k-anchor-unicode-empty",
{"content": "", "reason": "π ✓", "when": CLOCK_TS},
dl._anchor_record("", "π ✓", CLOCK_TS))
# ══ kernel: check_alerts ═════════════════════════════════════════════════
EMPTY_BLOCKS = {label: "" for label in dl.BLOCKS}
def alert_ledger(anchors=None, snap_blocks=None):
ledger = {}
if anchors is not None:
ledger["anchors"] = anchors
if snap_blocks is not None:
ledger["last_snapshot"] = {"when": CLOCK_TS, "blocks": snap_blocks}
return ledger
# unanchored growth over the limit
case("k_alerts", "k-alerts-unanchored-over-limit",
{"ledger": alert_ledger(snap_blocks={"project": {"size": 100, "content": "old\nlines"}}),
"blocks": {**EMPTY_BLOCKS, "project": "old\nlines\n" + "x" * 60}},
dl._check_alerts(alert_ledger(snap_blocks={"project": {"size": 100, "content": "old\nlines"}}),
{**EMPTY_BLOCKS, "project": "old\nlines\n" + "x" * 60}))
# anchored growth within the limit
_anchor = {"sha": "a" * 64, "size": 100, "content": "base\ncontent"}
case("k_alerts", "k-alerts-anchored-within-limit",
{"ledger": alert_ledger(anchors={"operating": _anchor}),
"blocks": {**EMPTY_BLOCKS, "operating": "base\ncontent\nplus a bit"}},
dl._check_alerts(alert_ledger(anchors={"operating": dict(_anchor)}),
{**EMPTY_BLOCKS, "operating": "base\ncontent\nplus a bit"}))
# anchored growth over the limit with root-cause diff
case("k_alerts", "k-alerts-anchored-over-limit-diff",
{"ledger": alert_ledger(anchors={"project": _anchor}),
"blocks": {**EMPTY_BLOCKS, "project": "base\ncontent\n" + "y" * 80}},
dl._check_alerts(alert_ledger(anchors={"project": dict(_anchor)}),
{**EMPTY_BLOCKS, "project": "base\ncontent\n" + "y" * 80}))
# constitution content drift: any change trips, even tiny
case("k_alerts", "k-alerts-constitution-content-drift",
{"ledger": alert_ledger(anchors={"constitution": _anchor}),
"blocks": {**EMPTY_BLOCKS, "constitution": "base\ncontent\n!"}},
dl._check_alerts(alert_ledger(anchors={"constitution": dict(_anchor)}),
{**EMPTY_BLOCKS, "constitution": "base\ncontent\n!"}))
# constitution size drift without content change -> pct path only
case("k_alerts", "k-alerts-constitution-no-anchor-sha-pct-path",
{"ledger": alert_ledger(anchors={"constitution": {"sha": "b" * 64, "size": 10, "content": "tiny"}}),
"blocks": {**EMPTY_BLOCKS, "constitution": "tiny"}},
dl._check_alerts(alert_ledger(anchors={"constitution": {"sha": "b" * 64, "size": 10, "content": "tiny"}}),
{**EMPTY_BLOCKS, "constitution": "tiny"}))
# empty ledger: nothing to measure against
case("k_alerts", "k-alerts-empty-ledger-quiet",
{"ledger": {}, "blocks": EMPTY_BLOCKS},
dl._check_alerts({}, dict(EMPTY_BLOCKS)))
# negative delta (shrink) measured against prev
case("k_alerts", "k-alerts-shrink-over-limit",
{"ledger": alert_ledger(snap_blocks={"human": {"size": 100, "content": "lots of text"}}),
"blocks": {**EMPTY_BLOCKS, "human": "short"}},
dl._check_alerts(alert_ledger(snap_blocks={"human": {"size": 100, "content": "lots of text"}}),
{**EMPTY_BLOCKS, "human": "short"}))
# rounding: 3 -> 4 chars, pct 1/3
case("k_alerts", "k-alerts-rounds-pct",
{"ledger": alert_ledger(snap_blocks={"human": {"size": 3, "content": "abc"}}),
"blocks": {**EMPTY_BLOCKS, "human": "abcd"}},
dl._check_alerts(alert_ledger(snap_blocks={"human": {"size": 3, "content": "abc"}}),
{**EMPTY_BLOCKS, "human": "abcd"}))
# multi-alert BLOCKS order
_multi = alert_ledger(snap_blocks={"persona": {"size": 5, "content": "p one"},
"human": {"size": 5, "content": "h one"}})
case("k_alerts", "k-alerts-multi-blocks-order",
{"ledger": _multi,
"blocks": {**EMPTY_BLOCKS, "persona": "p one grown", "human": "h one grown"}},
dl._check_alerts(alert_ledger({"persona": {"size": 5, "content": "p one"},
"human": {"size": 5, "content": "h one"}}),
{**EMPTY_BLOCKS, "persona": "p one grown", "human": "h one grown"}))
# ══ kernel: auto_fix_decision ════════════════════════════════════════════
for i, (label, anchored, has_good) in enumerate([
("constitution", False, False),
("constitution", True, True),
("operating", True, False),
("persona", False, True),
("human", False, False),
]):
case("k_auto_fix", f"k-auto-fix-{i}-{label}-{int(anchored)}{int(has_good)}",
{"label": label, "anchored": anchored, "has_last_good": has_good},
dl._auto_fix_decision(label, anchored, has_good))
# ══ kernel: fix_decision ═════════════════════════════════════════════════
_fix_ledger = {"last_snapshot": {"when": CLOCK_TS, "blocks": {
"project": {"sha": "c" * 64, "size": 10, "content": "good content"},
"human": {"size": 4, "content": "no sha key"},
}}, "anchors": {"constitution": {"sha": "d" * 64}}}
case("k_fix", "k-fix-no-last-good", {"ledger": {}, "label": "human", "cur_sha": "a" * 64},
dl._fix_decision({}, "human", "a" * 64))
case("k_fix", "k-fix-anchored-refuses", {"ledger": copy.deepcopy(_fix_ledger), "label": "constitution", "cur_sha": "x"},
dl._fix_decision(copy.deepcopy(_fix_ledger), "constitution", "x"))
case("k_fix", "k-fix-sha-matches", {"ledger": copy.deepcopy(_fix_ledger), "label": "project", "cur_sha": "c" * 64},
dl._fix_decision(copy.deepcopy(_fix_ledger), "project", "c" * 64))
case("k_fix", "k-fix-restores", {"ledger": copy.deepcopy(_fix_ledger), "label": "project", "cur_sha": "e" * 64},
dl._fix_decision(copy.deepcopy(_fix_ledger), "project", "e" * 64))
case("k_fix", "k-fix-meta-without-sha", {"ledger": copy.deepcopy(_fix_ledger), "label": "human", "cur_sha": "z"},
dl._fix_decision(copy.deepcopy(_fix_ledger), "human", "z"))
# ══ write ════════════════════════════════════════════════════════════════
truth = {
"frozen": "drift_ledger sha/diff/ledger_path/load_ledger/save_ledger/"
"read_block/journal/surface_alert/snapshot/anchor/check/"
"auto_fix/fix/restore_topic/handle_hook/main",
"oracle": "src/rune/drift_ledger.py",
"note": "Clock frozen at 2031-01-01T00:00:00+00:00 (dl.datetime seam); "
"store/journal/status paths isolated per case via INDEX_FILE/DB/"
"JOURNAL_DIR module globals and the memory_env.memory_dir() seam. "
"SQLite read/write seams and file IO stay in the facade by design; "
"the deterministic cores (sha, diff, alert loop, snapshot/anchor "
"assembly, auto-fix routing, fix decision, ledger path) cross the "
"doc-envelope FFI.",
}
for gname in GROUPS:
truth[gname] = groups[gname]
out = Path(os.environ.get("DL_TRUTH_OUT", "/tmp/dl_truth.json"))
out.write_text(json.dumps(truth, indent=1, ensure_ascii=False) + "\n")
counts = ", ".join(f"{len(groups[g])} {g}" for g in GROUPS)
print(f"froze {sum(len(v) for v in groups.values())} cases -> {out}: {counts}")
+599
View File
@@ -0,0 +1,599 @@
#!/usr/bin/env python3
"""gen_evidence_probe_truth.py — evidence_probe FULL-port truth battery
(dichotomy wave #2479).
Freezes the LIVE src/rune/evidence_probe.py behavior (validate_probe and
count_probes served by the Rust evidence_provenance core through ep_loader)
for every public function and branch:
- build_probe_prompt: baseline / unicode / path-like question / empty args
- validate_probe: the 5-key schema gate — missing fields, non-string and
blank fields, status rendering (repr, ints, floats, bools, None, list,
dict incl. nested key order), GROUNDED source-citation boundary (7/8
chars, missing/non-string source), extra-field sorting (meta fields
allowed), non-object artifacts, combined-error ordering
- count_probes: absent dir/file, empty, blank-only, Rust `lines()`
semantics (trailing \\r, no trailing newline, trailing newline,
whitespace-only lines)
- check_budget: 0/1 ok, 2/3 ProbeBudgetExceeded
- record_probe: fresh dir creation, probe.jsonl append, unicode
ensure_ascii bytes, returned path
- run_probe: injected runner — success (grounded / still_unknown), model
fallback chain (explicit / MIMO_MODEL env / unset -> UNKNOWN /
set-but-empty -> ""), budget exhausted BEFORE the runner fires, silent
runner -> declared-artifact failure, gate-failure joins, and the exact
prompt + absolute artifact path the runner receives
- ProbeResult.to_dict: field order + ensure_ascii
- _default_runner: stubbed MIMO_BIN subprocess — argv parity, stdout log,
exit-code error, MIMO_AGENT default/override, require_model
resolution + ProviderUnresolved (set/unset/blank)
Clock frozen at FROZEN_TS (probe_id + decided_at deterministic). The only
excluded branch is the invalid-JSON artifact crash (Python json.JSONDecodeError
rendering is interpreter-specific, same policy as the auto_context batch).
Usage: python3 scripts/gen_evidence_probe_truth.py (from repo root)
"""
import json
import os
import sys
import tempfile
import time
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "src"))
from rune import evidence_probe as ep # noqa: E402
import rune.provider_context as provider_context # noqa: E402
FROZEN_TS = 1791753600
ARTDIR = "@ARTDIR@"
STUB_NAME = "ep_stub.sh"
_real_gmtime = time.gmtime
GROUNDED = {
"question": "Does the API support streaming?",
"source": "https://platformdirs.readthedocs.io/changelog.html",
"answer": "Current version guarantees the assumed behaviour.",
"implication": "Local wiring is sufficient; no version guard needed.",
"status": "GROUNDED",
}
def norm(s, base):
return s.replace(base, ARTDIR) if isinstance(s, str) else s
def write_files(cdir, files):
os.makedirs(cdir, exist_ok=True)
for rel, content in (files or {}).items():
p = os.path.join(cdir, rel)
os.makedirs(os.path.dirname(p), exist_ok=True)
with open(p, "w") as f:
f.write(content)
def exc_row(fn, base, types=None):
"""Call fn(), freeze either {error: None, value: ...} or the exception."""
try:
return {"error": None, "value": fn()}
except Exception as e: # noqa: BLE001 — the frozen point IS the exception
t = type(e).__name__
if types and t not in types:
raise
return {"error": {"type": t, "msg": norm(str(e), base)}}
def main():
base = tempfile.mkdtemp(prefix="ep_truth_")
rows = []
real_time = time.time
ep.time.time = lambda: FROZEN_TS
ep.time.gmtime = lambda *a: _real_gmtime(FROZEN_TS)
try:
_build_rows(base, rows)
finally:
ep.time.time = real_time
ep.time.gmtime = _real_gmtime
def _build_rows(base, rows):
# ── build_probe_prompt ────────────────────────────────────────────────
prompt_cases = [
("prompt_baseline", "Does the API support streaming?", "/tmp/probe.json"),
("prompt_unicode", "Wird die Größenbeschränkung überschritten?", "/tmp/probe.json"),
(
"prompt_path_like_question",
"Is the file at /tmp/probe.json overwritten per probe?",
"/tmp/artifacts/evidence_probe.json",
),
("prompt_empty_question", "", ""),
("prompt_multiline_question", "line one\nline two\nline three", "/tmp/x.json"),
]
for name, q, ap in prompt_cases:
rows.append(
{
"name": name,
"op": "prompt",
"setup": {"question": q, "artifact_path": ap},
"want": ep.build_probe_prompt(q, ap),
}
)
# ── validate_probe ────────────────────────────────────────────────────
def val(name, artifact):
rows.append(
{
"name": name,
"op": "validate",
"setup": {"artifact": artifact},
"want": ep.validate_probe(artifact),
}
)
val("valid_grounded", dict(GROUNDED))
val(
"valid_grounded_with_meta",
dict(GROUNDED, probe_id="probe-1", decided_at="2026-10-11T00:00:00Z", model="m"),
)
val(
"valid_still_unknown_empty_source",
{
"question": "probe",
"source": "",
"answer": "Could not find authoritative evidence.",
"implication": "Uncertainty remains.",
"status": "STILL_UNKNOWN",
},
)
val(
"valid_contradicted",
dict(GROUNDED, status="CONTRADICTED", source="task assumption"),
)
for field in ("question", "source", "answer", "implication", "status"):
art = dict(GROUNDED)
del art[field]
val(f"missing_{field}", art)
val("empty_artifact", {})
val("nonstring_question", dict(GROUNDED, question=7))
val("nonstring_answer", dict(GROUNDED, answer=["a"]))
val("nonstring_implication", dict(GROUNDED, implication={"k": 1}))
val("nonstring_source", dict(GROUNDED, source=3.5))
val("blank_question", dict(GROUNDED, question=""))
val("whitespace_question", dict(GROUNDED, question=" \t\n"))
val("blank_answer", dict(GROUNDED, answer=""))
val("blank_implication", dict(GROUNDED, implication=""))
val("blank_source_allowed", dict(GROUNDED, source=""))
val("status_empty_string", dict(GROUNDED, status=""))
val("status_lowercase", dict(GROUNDED, status="grounded"))
val("status_int", dict(GROUNDED, status=1))
val("status_float", dict(GROUNDED, status=1.5))
val("status_float_integral", dict(GROUNDED, status=2.0))
val("status_float_small", dict(GROUNDED, status=0.1))
val("status_big_float", dict(GROUNDED, status=1e30))
val("status_huge_int", dict(GROUNDED, status=9007199254740993))
val("status_true", dict(GROUNDED, status=True))
val("status_false", dict(GROUNDED, status=False))
val("status_null", dict(GROUNDED, status=None))
val("status_list", dict(GROUNDED, status=[1, "x"]))
val("status_obj_order", dict(GROUNDED, status={"b": 1, "a": {"z": 2, "y": 3}}))
val("status_repr_quote", dict(GROUNDED, status="it's"))
val("status_repr_newline", dict(GROUNDED, status="a\nb"))
val("status_repr_unicode", dict(GROUNDED, status="größe"))
val("grounded_short_source_7", dict(GROUNDED, source="1234567"))
val("grounded_source_8_ok", dict(GROUNDED, source="12345678"))
val("grounded_missing_source", {k: v for k, v in GROUNDED.items() if k != "source"})
val("grounded_nonstring_source", dict(GROUNDED, source=42))
val("extra_single", dict(GROUNDED, extra=1))
val("extra_sorted", dict(GROUNDED, z_extra=1, a_extra=2, m_extra=3))
val("extra_meta_allowed", dict(GROUNDED, model="m", probe_id="p", decided_at="d"))
val("extra_unicode_key", dict(GROUNDED, **{"ключ": 1}))
val("nonobject_list", ["a", 1])
val("nonobject_string", "nope")
val("nonobject_int", 3)
val("nonobject_bool", True)
val("nonobject_null", None)
val(
"combined_ordering",
{"status": 5, "extra_b": 1, "extra_a": 2, "answer": ""},
)
# ── count_probes ──────────────────────────────────────────────────────
count_cases = [
("count_absent_dir", None),
("count_absent_file", {}),
("count_empty_file", {"probe.jsonl": ""}),
("count_blank_only", {"probe.jsonl": "\n\n \n"}),
("count_three_lines", {"probe.jsonl": "a\nb\nc\n"}),
("count_no_trailing_newline", {"probe.jsonl": "a\nb"}),
("count_interleaved_blank", {"probe.jsonl": "a\n\nb\n\n\nc"}),
("count_cr_lines", {"probe.jsonl": "a\r\nb\r\n"}),
("count_whitespace_lines", {"probe.jsonl": " \n x\n\t\n"}),
("count_cr_only_line", {"probe.jsonl": "\r\r\nx"}),
("count_single_line", {"probe.jsonl": "one"}),
]
for name, files in count_cases:
cdir = os.path.join(base, name)
if files is not None:
write_files(cdir, files)
rows.append(
{
"name": name,
"op": "count",
"setup": {"dir": norm(cdir, base), "files": files or {}},
"want": ep.count_probes(cdir),
}
)
# ── check_budget ──────────────────────────────────────────────────────
for n, name in ((0, "budget_zero"), (1, "budget_one"), (2, "budget_two"), (3, "budget_three")):
cdir = os.path.join(base, name)
write_files(cdir, {"probe.jsonl": "x\n" * n})
out = exc_row(lambda d=cdir: ep.check_budget(d), base, types={"ProbeBudgetExceeded"})
rows.append(
{
"name": name,
"op": "budget",
"setup": {"dir": norm(cdir, base), "files": {"probe.jsonl": "x\n" * n}},
"want": out,
}
)
# ── record_probe ──────────────────────────────────────────────────────
def rec(name, result, files=None):
cdir = os.path.join(base, name)
if files:
write_files(cdir, files)
path = ep.record_probe(ep.ProbeResult(**result), cdir)
with open(os.path.join(cdir, "probe.json")) as f:
pj = f.read()
with open(os.path.join(cdir, "probe.jsonl")) as f:
pjl = f.read()
rows.append(
{
"name": name,
"op": "record",
"setup": {"result": result, "files": files or {}},
"want": {
"path": norm(path, base),
"probe_json": pj,
"probe_jsonl": pjl,
},
}
)
rec(
"record_fresh_dir_created",
{
"question": "q",
"source": "https://example.com/a",
"answer": "a",
"implication": "i",
"status": "GROUNDED",
"probe_id": "probe-1791753600",
"decided_at": "2026-10-12T04:00:00Z",
"model": "UNKNOWN",
},
)
rec(
"record_appends_existing",
{
"question": "q2",
"source": "",
"answer": "a2",
"implication": "i2",
"status": "STILL_UNKNOWN",
"probe_id": "probe-1791753600",
"decided_at": "2026-10-12T04:00:00Z",
"model": "m",
},
files={"probe.jsonl": '{"question": "old"}\n'},
)
rec(
"record_unicode_ensure_ascii",
{
"question": "Größe überschritten — warum?",
"source": "https://example.com/ä",
"answer": "ja ✓",
"implication": "🚀",
"status": "GROUNDED",
"probe_id": "probe-1791753600",
"decided_at": "2026-10-12T04:00:00Z",
"model": "model-ü",
},
)
# ── run_probe ─────────────────────────────────────────────────────────
def run(name, *, question, runner_spec, model="UNKNOWN", env=None, files=None):
cdir = os.path.join(base, name)
write_files(cdir, files or {})
old_env = {k: os.environ.get(k) for k in ("MIMO_MODEL",)}
try:
for k, v in (env or {}).items():
if v is None:
os.environ.pop(k, None)
else:
os.environ[k] = v
captures = []
def runner(prompt, artifact_path):
captures.append((prompt, artifact_path))
kind = runner_spec["type"]
if kind == "write":
with open(artifact_path, "w") as f:
json.dump(runner_spec["artifact"], f)
elif kind == "silent":
pass
else:
raise AssertionError(f"unknown runner spec {kind}")
try:
result = ep.run_probe(question, cdir, runner=runner, model=model)
out = {
"error": None,
"result": result.to_dict(),
}
with open(os.path.join(cdir, "probe.json")) as f:
out["probe_json"] = f.read()
with open(os.path.join(cdir, "probe.jsonl")) as f:
out["probe_jsonl"] = f.read()
except Exception as e: # noqa: BLE001
out = {"error": {"type": type(e).__name__, "msg": norm(str(e), base)}}
out["runner_called"] = bool(captures)
if captures:
out["prompt"] = norm(captures[0][0], base)
out["artifact_path"] = norm(captures[0][1], base)
rows.append(
{
"name": name,
"op": "run",
"setup": {
"question": question,
"model": model,
"env": env or {},
"files": files or {},
"runner": runner_spec,
},
"want": out,
}
)
finally:
for k, v in old_env.items():
if v is None:
os.environ.pop(k, None)
else:
os.environ[k] = v
STILL_UNKNOWN = {
"question": "probe",
"source": "",
"answer": "Could not find authoritative evidence.",
"implication": "Uncertainty remains.",
"status": "STILL_UNKNOWN",
}
run(
"run_success_grounded",
question="Does the API support streaming?",
runner_spec={"type": "write", "artifact": dict(GROUNDED)},
model="m-1",
)
run(
"run_success_still_unknown",
question="probe",
runner_spec={"type": "write", "artifact": dict(STILL_UNKNOWN)},
model="m-1",
)
run(
"run_model_from_env",
question="probe",
runner_spec={"type": "write", "artifact": dict(GROUNDED)},
model="",
env={"MIMO_MODEL": "env-model"},
)
run(
"run_model_unset_unknown",
question="probe",
runner_spec={"type": "write", "artifact": dict(GROUNDED)},
model="",
env={"MIMO_MODEL": None},
)
run(
"run_model_env_blank_is_empty_string",
question="probe",
runner_spec={"type": "write", "artifact": dict(GROUNDED)},
model="",
env={"MIMO_MODEL": ""},
)
run(
"run_budget_exhausted_before_runner",
question="one too many",
runner_spec={"type": "silent"},
model="m",
files={"probe.jsonl": "a\nb\n"},
)
run(
"run_budget_one_left",
question="still allowed",
runner_spec={"type": "write", "artifact": dict(GROUNDED)},
model="m",
files={"probe.jsonl": "a\n"},
)
run(
"run_silent_runner_missing_artifact",
question="q",
runner_spec={"type": "silent"},
model="m",
)
run(
"run_gate_failure_joined",
question="q",
runner_spec={
"type": "write",
"artifact": {"question": "q", "source": "s", "answer": "a", "implication": "i", "status": "MAYBE"},
},
model="m",
)
run(
"run_gate_failure_non_dict",
question="q",
runner_spec={"type": "write", "artifact": [1, 2]},
model="m",
)
run(
"run_gate_failure_status_non_string",
question="q",
runner_spec={
"type": "write",
"artifact": {"question": "q", "source": "s", "answer": "a", "implication": "i", "status": 9},
},
model="m",
)
# ── ProbeResult.to_dict ───────────────────────────────────────────────
def todict(name, fields):
d = ep.ProbeResult(**fields).to_dict()
rows.append(
{
"name": name,
"op": "to_dict",
"setup": {"fields": fields},
"want": json.dumps(d, ensure_ascii=True),
}
)
todict(
"to_dict_defaults",
{
"question": "q",
"source": "s",
"answer": "a",
"implication": "i",
"status": "GROUNDED",
},
)
todict(
"to_dict_full",
{
"question": "q2",
"source": "s2",
"answer": "a2",
"implication": "i2",
"status": "STILL_UNKNOWN",
"probe_id": "probe-1791753600",
"decided_at": "2026-10-12T04:00:00Z",
"model": "m",
},
)
todict(
"to_dict_unicode",
{
"question": "Größe",
"source": "s — dash",
"answer": "✓",
"implication": "🚀",
"status": "CONTRADICTED",
},
)
# ── _default_runner (stubbed subprocess) ──────────────────────────────
stub_path = os.path.join(base, STUB_NAME)
with open(stub_path, "w") as f:
f.write(
"#!/bin/sh\n"
'for a in "$@"; do printf \'%s\\n\' "$a" >> "$EP_CAPTURE"; done\n'
'printf \'stub-out\\n\'\n'
'exit "$EP_EXIT"\n'
)
os.chmod(stub_path, 0o755)
real_require_model = provider_context.require_model
def defrun(name, *, exit_code, agent_env, model_mode):
cdir = os.path.join(base, name)
os.makedirs(cdir, exist_ok=True)
capture = os.path.join(cdir, "argv_capture.txt")
artifact_path = os.path.join(cdir, "evidence_probe.json")
old_env = {k: os.environ.get(k) for k in ("MIMO_BIN", "MIMO_AGENT", "MIMO_MODEL", "EP_CAPTURE", "EP_EXIT")}
try:
if os.path.exists(capture):
os.unlink(capture)
os.environ["MIMO_BIN"] = stub_path
os.environ["EP_CAPTURE"] = capture
os.environ["EP_EXIT"] = str(exit_code)
if agent_env is None:
os.environ.pop("MIMO_AGENT", None)
else:
os.environ["MIMO_AGENT"] = agent_env
if model_mode == "unresolved":
os.environ.pop("MIMO_MODEL", None)
provider_context.require_model = real_require_model
elif model_mode == "blank":
os.environ["MIMO_MODEL"] = " "
provider_context.require_model = real_require_model
else:
provider_context.require_model = (
lambda env_var="MIMO_MODEL": "test-model-v9"
)
try:
ep._default_runner("PROMPT-TEXT", artifact_path)
err = None
except Exception as e: # noqa: BLE001
err = {"type": type(e).__name__, "msg": norm(str(e), base)}
argv = None
if os.path.exists(capture):
with open(capture) as f:
argv = [norm(line, base) for line in f.read().splitlines()]
log_path = os.path.join(cdir, "evidence_probe.stdout.log")
log = None
if os.path.exists(log_path):
with open(log_path) as f:
log = f.read()
rows.append(
{
"name": name,
"op": "default_runner",
"setup": {
"exit_code": exit_code,
"agent_env": agent_env,
"model_mode": model_mode,
},
"want": {"error": err, "argv": argv, "stdout_log": log},
}
)
finally:
provider_context.require_model = real_require_model
for k, v in old_env.items():
if v is None:
os.environ.pop(k, None)
else:
os.environ[k] = v
defrun("defrun_exit0_default_agent", exit_code=0, agent_env=None, model_mode="patched")
defrun("defrun_exit3", exit_code=3, agent_env=None, model_mode="patched")
defrun("defrun_custom_agent", exit_code=0, agent_env="custom-agent", model_mode="patched")
defrun("defrun_model_unresolved", exit_code=0, agent_env=None, model_mode="unresolved")
defrun("defrun_model_blank_env", exit_code=0, agent_env=None, model_mode="blank")
out = {
"oracle": "src/rune/evidence_probe.py (live; validate_probe/count_probes via Rust evidence_provenance core)",
"generated": "2026-10-12",
"frozen_ts": FROZEN_TS,
"frozen_decided_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", _real_gmtime(FROZEN_TS)),
"rows": rows,
}
target = REPO / "src/go/evidence-probe" / "truth.json"
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
print(f"wrote {target} ({len(rows)} rows)")
# determinism self-check: probe_id/decided_at must be frozen
assert all(
r.get("want", {}).get("result", {}).get("probe_id") in (None, f"probe-{FROZEN_TS}")
for r in rows
if r["op"] == "run"
), "clock freeze failed"
if __name__ == "__main__":
main()
+399
View File
@@ -0,0 +1,399 @@
#!/usr/bin/env python3
"""gen_experience_encoding_truth.py — freeze/replay parity truth for
experience_encoding (dichotomy wave, #2479 tranche).
Freeze mode (default): exercises the LIVE Python oracle
(src/rune/experience_encoding.py evaluate — the module's single public
function) across every branch and records (kernel_input,
expected_kernel_output) pairs into
src/rust/experience_encoding/src/frozen_ee_truth.json.
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
delegation ACTIVE (RUNE_EE_CORE unpinned) and asserts both kernel-level
(loader direct) and facade-level (oracle evaluate delegates) parity
against the frozen file.
Poisoned-truth law: freeze pins RUNE_EE_CORE=python BEFORE any import.
Mutation law: case inputs are deep-copied BEFORE they reach the oracle.
Lexicon-seam law: the oracle's `import lexicon_evolution` is an
ENVIRONMENT seam — the generator pins it per case via
`lexicon_available`; freeze/replay with the flag False block the flat
import (sys.modules sentinel) so the ImportError fallback branch
(conservative evidence admission) is frozen like any other. The Rust
kernel receives the same flag in its payload and mirrors the branch.
"""
import copy
import json
import os
import sys
from datetime import datetime, timezone
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TRUTH_PATH = os.path.join(REPO, "src", "rust", "experience_encoding",
"src", "frozen_ee_truth.json")
REPLAY = "--replay" in sys.argv
if not REPLAY:
os.environ["RUNE_EE_CORE"] = "python" # poisoned-truth law (freeze side)
else:
os.environ.pop("RUNE_EE_CORE", None) # live-parity law (facade must delegate)
sys.path.insert(0, os.path.join(REPO, "src"))
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
import experience_encoding as oracle_mod # noqa: E402
if REPLAY:
from rune import ee_loader # noqa: E402 (kernel-level replay side)
if not ee_loader.available():
print("REPLAY ABORT: kernel not built —", ee_loader.LIB_PATH)
sys.exit(2)
class _Unset:
pass
_UNSET = _Unset()
def _input(event, existing_episodes=_UNSET, corpus_texts=_UNSET,
lexicon_available=True):
payload = {"event": event, "lexicon_available": lexicon_available}
if existing_episodes is not _UNSET:
payload["existing_episodes"] = list(existing_episodes)
if corpus_texts is not _UNSET:
payload["corpus_texts"] = list(corpus_texts)
return payload
class _LexiconBlocked:
"""Context manager: blocks the flat `import lexicon_evolution` the way
a missing module would (sys.modules None sentinel → ImportError)."""
def __enter__(self):
self._saved = sys.modules.get("lexicon_evolution", _UNSET)
sys.modules["lexicon_evolution"] = None
return self
def __exit__(self, *exc):
if self._saved is _UNSET:
sys.modules.pop("lexicon_evolution", None)
else:
sys.modules["lexicon_evolution"] = self._saved
return False
def facade_call(inp):
"""Run the LIVE facade (delegates to the Rust core when built)."""
if inp.get("lexicon_available", True):
return oracle_mod.evaluate(
copy.deepcopy(inp["event"]),
copy.deepcopy(inp.get("existing_episodes")),
copy.deepcopy(inp.get("corpus_texts", ())),
)
with _LexiconBlocked():
return oracle_mod.evaluate(
copy.deepcopy(inp["event"]),
copy.deepcopy(inp.get("existing_episodes")),
copy.deepcopy(inp.get("corpus_texts", ())),
)
def kernel_call(inp):
"""Run the Rust kernel directly through the ctypes loader."""
return ee_loader.evaluate(**copy.deepcopy(inp))
# ── evaluate ─────────────────────────────────────────────────────────────────
def ev_case(rec, name, event, existing=_UNSET, corpus=_UNSET,
lexicon_available=True):
inp = _input(event, existing, corpus, lexicon_available)
got = facade_call(copy.deepcopy(inp))
rec(name, inp, got, got)
def c_ev_missing_text(rec):
ev_case(rec, "ev-missing-text-key-empty", {"source": "user"})
def c_ev_empty_text(rec):
ev_case(rec, "ev-empty-text", {"text": "", "source": "user"})
def c_ev_null_text(rec):
ev_case(rec, "ev-null-text-coerced", {"text": None, "source": "user"})
def c_ev_whitespace_text(rec):
ev_case(rec, "ev-whitespace-only-text", {"text": " \n\t ", "source": "user"})
def c_ev_short_14(rec):
ev_case(rec, "ev-short-14-chars", {"text": "x" * 14, "source": "user"})
def c_ev_boundary_15(rec):
# exactly 15 chars passes the gate; no episodes/corpus → ungrounded
ev_case(rec, "ev-boundary-15-chars-evidence",
{"text": "x" * 15, "source": "curator", "session_id": "s1"})
def c_ev_null_source_session(rec):
# get() default only applies when the KEY is missing — a present-but-null
# source/session_id is pinned as provenance nulls (oracle quirk)
ev_case(rec, "ev-null-source-session-provenance",
{"text": "hi", "source": None, "session_id": None})
def c_ev_empty_source_session(rec):
ev_case(rec, "ev-empty-string-source-session",
{"text": "x" * 20, "source": "", "session_id": ""})
def c_ev_extra_keys(rec):
ev_case(rec, "ev-extra-keys-ignored",
{"text": "x" * 16, "kind": "note", "weight": 3})
def c_ev_duplicate_normalized(rec):
ev_case(rec, "ev-duplicate-normalized-whitespace-case",
{"text": "Nick prefers DARK coffee", "source": "user"},
existing=[{"text": "nick prefers DARK coffee"}])
def c_ev_duplicate_exact(rec):
ev_case(rec, "ev-duplicate-exact-string",
{"text": "she drinks oatmilk daily", "source": "user"},
existing=[{"text": "she drinks oatmilk daily"}])
def c_ev_duplicate_episode_case(rec):
ev_case(rec, "ev-duplicate-episode-uppercase",
{"text": "nick prefers dark coffee", "source": "user"},
existing=[{"text": "NICK PREFERS DARK COFFEE"}])
def c_ev_duplicate_episode_padding(rec):
ev_case(rec, "ev-duplicate-episode-padded",
{"text": "nick prefers dark coffee", "source": "user"},
existing=[{"text": " nick prefers dark coffee "}])
def c_ev_not_dup_punctuation(rec):
# the exclamation mark survives normalization → NOT a duplicate; the
# event then grounds in the episode's corpus and shares terms
ev_case(rec, "ev-punctuation-survives-not-duplicate",
{"text": "NICK PREFERS dark coffee!", "source": "user"},
existing=[{"text": "nick prefers dark coffee"}])
def c_ev_dup_null_episode_text(rec):
# episodes with None/missing text never duplicate; corpus fallback is
# all empty → ungrounded evidence
ev_case(rec, "ev-null-episode-texts-ungrounded",
{"text": "quantum flux patterns emerge here", "source": "user"},
existing=[{"text": None}, {}])
def c_ev_lexicon_blocked(rec):
# ImportError seam: long text, lexicon unavailable → conservative evidence
ev_case(rec, "ev-lexicon-blocked-conservative-evidence",
{"text": "she drinks oatmilk every single morning",
"source": "user"},
lexicon_available=False)
def c_ev_lexicon_blocked_short(rec):
# the empty/short gate runs BEFORE the lexicon import — still IGNORE
ev_case(rec, "ev-lexicon-blocked-short-still-ignored",
{"text": "hi", "source": "user"},
lexicon_available=False)
def c_ev_lexicon_blocked_with_episodes(rec):
# the duplicate check is INSIDE the try — blocked lexicon wins over a
# would-be duplicate (conservative evidence, not IGNORE)
ev_case(rec, "ev-lexicon-blocked-duplicate-path-conservative",
{"text": "nick prefers dark coffee", "source": "user"},
existing=[{"text": "nick prefers dark coffee"}],
lexicon_available=False)
def c_ev_framing_only(rec):
ev_case(rec, "ev-framing-only-no-lexical-signal",
{"text": "work thing way always", "source": "user"})
def c_ev_episode_shared_single(rec):
ev_case(rec, "ev-episode-shared-single-term",
{"text": "she never jokes about oatmilk at all", "source": "user"},
existing=[{"text": "i love oatmilk a lot"}],
corpus=["oatmilk is life", "tea later day"])
def c_ev_episode_shared_multi(rec):
ev_case(rec, "ev-episode-shared-multiple-sorted",
{"text": "oatmilk and celesta every morning", "source": "user"},
existing=[{"text": "oatmilk and celesta"}],
corpus=["oatmilk bar", "celesta hall"])
def c_ev_episode_shared_top3(rec):
ev_case(rec, "ev-episode-shared-sorted-top3",
{"text": "oatmilk celesta zephyr quartz ritual", "source": "user"},
existing=[{"text": "oatmilk celesta zephyr quartz"}],
corpus=["oatmilk celesta zephyr quartz notes"])
def c_ev_episode_novel_grounded(rec):
ev_case(rec, "ev-episode-novel-axis-grounded",
{"text": "celesta practice daily ritual", "source": "session"},
existing=[{"text": "i love oatmilk"}],
corpus=["she plays the celesta nightly"])
def c_ev_evidence_ungrounded(rec):
ev_case(rec, "ev-evidence-fact-shaped-ungrounded",
{"text": "quantum flux patterns emerge", "source": "curator"},
existing=[{"text": "i love oatmilk"}],
corpus=["oatmilk is life"])
def c_ev_evidence_empty_everything(rec):
ev_case(rec, "ev-evidence-no-episodes-no-corpus",
{"text": "quantum flux patterns emerge", "source": "curator"})
def c_ev_stem_default_corpus(rec):
# corpus omitted → corpus = episodes' texts; stem-aware grounding
# (migrated→migr matches migration→migr)
ev_case(rec, "ev-episode-stem-grounded-default-corpus",
{"text": "she migrated north yesterday", "source": "session"},
existing=[{"text": "the migration of songbirds"}])
def c_ev_corpus_override_shared_killed(rec):
# corpus override pushes oatmilk df over the threshold → share killed,
# but grounding survives (plain stem overlap) → novel-axis episode
ev_case(rec, "ev-corpus-override-share-killed-novel-grounded",
{"text": "she never jokes about oatmilk ok", "source": "user"},
existing=[{"text": "i love oatmilk"}],
corpus=["oatmilk"] * 30)
def c_ev_no_episodes_with_corpus(rec):
# shared_distinctive runs against the (empty) episodes while grounding
# uses the corpus → novel-axis episode
ev_case(rec, "ev-episode-no-episodes-corpus-grounded",
{"text": "she drinks oatmilk daily", "source": "user"},
corpus=["oatmilk is life"])
def c_ev_episodes_without_corpus(rec):
# corpus defaults to the episodes' texts
ev_case(rec, "ev-episode-default-corpus-is-episodes",
{"text": "she drinks oatmilk daily", "source": "user"},
existing=[{"text": "oatmilk rituals fill the morning"}])
# ── driver ───────────────────────────────────────────────────────────────────
EVALUATE = [
c_ev_missing_text, c_ev_empty_text, c_ev_null_text, c_ev_whitespace_text,
c_ev_short_14, c_ev_boundary_15, c_ev_null_source_session,
c_ev_empty_source_session, c_ev_extra_keys, c_ev_duplicate_normalized,
c_ev_duplicate_exact, c_ev_duplicate_episode_case,
c_ev_duplicate_episode_padding, c_ev_not_dup_punctuation,
c_ev_dup_null_episode_text, c_ev_lexicon_blocked,
c_ev_lexicon_blocked_short, c_ev_lexicon_blocked_with_episodes,
c_ev_framing_only, c_ev_episode_shared_single, c_ev_episode_shared_multi,
c_ev_episode_shared_top3, c_ev_episode_novel_grounded,
c_ev_evidence_ungrounded, c_ev_evidence_empty_everything,
c_ev_stem_default_corpus, c_ev_corpus_override_shared_killed,
c_ev_no_episodes_with_corpus, c_ev_episodes_without_corpus,
]
GROUPS = {
"evaluate": (EVALUATE, True, "ee_evaluate", lambda w: w),
}
def canon(x) -> str:
"""Content canonicalization (key-order independent)."""
return json.dumps(json.loads(json.dumps(x, default=str)), sort_keys=True)
def main():
frozen = None
if REPLAY:
with open(TRUTH_PATH) as f:
frozen = json.load(f)["cases"]
groups = {}
for group, (cases, facade_replayable, _sym, _unwrap) in GROUPS.items():
recs = []
def rec(name, inp, oracle_ret, expected, _r=recs, _g=group):
entry = {"name": name, "input": inp,
"return": oracle_ret, "expected": expected}
if REPLAY and facade_replayable:
old = next(c for c in frozen[_g] if c["name"] == name)
for key in ("input", "return", "expected"):
got, want = entry[key], old[key]
if canon(got) != canon(want):
print(f"FACADE DIVERGE [{_g}] {name} {key}")
print(" got: ", json.dumps(got, sort_keys=True, default=str)[:600])
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
_r.append({"name": name, "diverged": key})
return
_r.append(entry)
for c in cases:
c(rec)
groups[group] = recs
total = sum(len(v) for v in groups.values())
if REPLAY:
facade_groups = [g for g, (_, r, _, _) in GROUPS.items() if r]
facade_total = sum(len(groups[g]) for g in facade_groups)
facade_failures = sum(1 for g in facade_groups for c in groups[g]
if "diverged" in c)
failures = 0
checked = 0
for group, cases in frozen.items():
for case in cases:
checked += 1
got = kernel_call(case["input"])
want = case["expected"]
if canon(got) != canon(want):
failures += 1
print(f"KERNEL DIVERGE [{group}] {case['name']}")
print(" got: ", json.dumps(got, sort_keys=True, default=str)[:600])
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
print(f"replay: facade {facade_total - facade_failures}/{facade_total}, "
f"kernel {checked - failures}/{checked}")
return 1 if (failures or facade_failures) else 0
doc = {
"frozen": datetime.now(timezone.utc).isoformat(),
"oracle": "src/rune/experience_encoding.py",
"pin": "RUNE_EE_CORE=python",
"note": "Frozen via scripts/gen_experience_encoding_truth.py against "
"the live Python oracle. Battery embeds this VERBATIM. The "
"`lexicon_available` flag pins the oracle's flat-import "
"environment seam (ImportError fallback branch).",
"cases": groups,
}
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
with open(TRUTH_PATH, "w") as f:
json.dump(doc, f, indent=1)
f.write("\n")
print(f"frozen {total} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
return 0
if __name__ == "__main__":
sys.exit(main())
+346
View File
@@ -0,0 +1,346 @@
#!/usr/bin/env python3
"""Truth generator for the forge_client dichotomy port (#2479 tranche, GO).
Runs the LIVE Python oracle (src/rune/forge_client.py) with the httpx
transport fully scripted, exercises every public function and every branch,
and freezes inputs + expected outputs to src/go/forge_client/truth.json
(consumed VERBATIM by the Go battery).
No clock, no store, no env leakage: every case is hermetic — the transport
is a scripted fake (never a real socket), sleep is a recording fake, and
os.environ is snapshotted/restored around every selection case.
Scripted entry shapes:
{"status": N, "payload": {...}} → FakeResponse(N, payload)
{"exc": "connect" | "connect_timeout" | "read_timeout"} → scripted
httpx exception (connect-phase vs non-connect transport, the
NOISE-vs-SIGNAL axis of _request).
"""
import json
import os
import sys
from pathlib import Path
from unittest.mock import patch
import httpx
REPO = Path(__file__).resolve().parents[1]
OUT = REPO / "src" / "go" / "forge_client" / "truth.json"
sys.path.insert(0, str(REPO / "src"))
import rune.forge_client as fc # noqa: E402
EXC = {
"connect": httpx.ConnectError,
"connect_timeout": httpx.ConnectTimeout,
"read_timeout": httpx.ReadTimeout,
}
rows = []
coverage = set()
def add(name, op, inp, want, covered):
rows.append({"name": name, "op": op, "input": inp, "want": want})
coverage.update(covered)
class FakeResponse:
def __init__(self, status=200, payload=None):
self.status_code = status
self._payload = payload if payload is not None else {}
def json(self):
return self._payload
def raise_for_status(self):
if self.status_code >= 400:
raise httpx.HTTPStatusError(
f"error {self.status_code}",
request=httpx.Request("GET", "https://x"),
response=httpx.Response(self.status_code),
)
def make_fake(method, script, rec):
pending = list(script)
def fake(url, **kw):
rec["requests"].append(
{
"method": method,
"url": url,
"headers": kw.get("headers"),
"json": kw.get("json"),
"timeout": kw.get("timeout"),
}
)
if not pending:
raise AssertionError(f"script exhausted at request {len(rec['requests'])}")
entry = pending.pop(0)
if "exc" in entry:
raise EXC[entry["exc"]]("scripted")
return FakeResponse(entry.get("status", 200), entry.get("payload"))
return fake
def blank_rec():
return {"requests": [], "sleeps": [], "error": None}
def finish_rec(rec, result):
return {
"result": result,
"requests": rec["requests"],
"sleeps": rec["sleeps"],
"error": rec["error"],
}
def call_case(name, method_name, script, kwargs, *, url="https://forge.example.com",
token="tok", owner="o", repo="r", covered=()):
rec = blank_rec()
adapter = fc.GiteaForgejoAdapter(url, token, owner=owner, repo=repo)
with patch("rune.forge_client.httpx.get", make_fake("GET", script, rec)), patch(
"rune.forge_client.httpx.post", make_fake("POST", script, rec)
), patch("rune.forge_client.time.sleep", rec["sleeps"].append):
try:
result = getattr(adapter, method_name)(**kwargs)
except Exception as e: # noqa: BLE001 — the kind IS the information
rec["error"] = f"httpx.{type(e).__name__}"
result = None
add(
name,
"call",
{
"method": method_name,
"url": url,
"token": token,
"owner": owner,
"repo": repo,
"script": script,
"kwargs": kwargs,
},
finish_rec(rec, result),
("GiteaForgejoAdapter." + method_name,) + covered,
)
def request_case(name, method, script, *, headers=None, json_body=None, timeout=30,
retries=2, backoff=0.5, covered=()):
rec = blank_rec()
with patch("rune.forge_client.httpx.get", make_fake("GET", script, rec)), patch(
"rune.forge_client.httpx.post", make_fake("POST", script, rec)
):
try:
result = fc._request(
method,
"https://x/api",
headers=headers,
json=json_body,
timeout=timeout,
retries=retries,
backoff=backoff,
sleep=rec["sleeps"].append,
).json()
except Exception as e: # noqa: BLE001
rec["error"] = f"httpx.{type(e).__name__}"
result = None
add(
name,
"request",
{
"method": method,
"url": "https://x/api",
"headers": headers,
"json": json_body,
"timeout": timeout,
"retries": retries,
"backoff": backoff,
"script": script,
},
finish_rec(rec, result),
("_request",) + covered,
)
def select_case(name, config, env, covered=()):
saved = {k: os.environ.get(k) for k in
("FORGE_URL", "FORGE_TOKEN", "FORGE_OWNER", "FORGE_REPO")}
try:
for k in ("FORGE_URL", "FORGE_TOKEN", "FORGE_OWNER", "FORGE_REPO"):
os.environ.pop(k, None)
os.environ.update({k: v for k, v in env.items() if v is not None})
client = fc.create_forge_client(config)
if isinstance(client, fc.NullAdapter):
want = {"adapter": "NullAdapter", "base_url": None, "token": None}
else:
want = {
"adapter": "GiteaForgejoAdapter",
"base_url": client._base_url,
"token": client._token,
}
finally:
for k, v in saved.items():
if v is None:
os.environ.pop(k, None)
else:
os.environ[k] = v
add(name, "select", {"config": config, "env": env}, want,
("create_forge_client",) + covered)
def null_case(name, method_name, args):
client = fc.NullAdapter()
result = getattr(client, method_name)(*args)
add(name, "null", {"method": method_name, "args": args}, result,
("NullAdapter." + method_name,))
# ── create_forge_client: every selection branch ─────────────────────────────
select_case("select:none", None, {})
select_case("select:env-configured", {}, {"FORGE_URL": "https://forge.example.com",
"FORGE_TOKEN": "test_token"})
select_case("select:config-keys",
{"forge.url": "https://cfg.example.com", "forge.token": "cfg_tok",
"forge.owner": "cfg_owner", "forge.repo": "cfg_repo"}, {})
select_case("select:config-beats-env",
{"forge.url": "https://cfg.example.com", "forge.token": "cfg_tok"},
{"FORGE_URL": "https://env.example.com", "FORGE_TOKEN": "env_tok"})
select_case("select:owner-repo-defaults", {},
{"FORGE_URL": "https://forge.example.com", "FORGE_TOKEN": "test_token"})
select_case("select:owner-repo-env", {},
{"FORGE_URL": "https://forge.example.com", "FORGE_TOKEN": "test_token",
"FORGE_OWNER": "env_owner", "FORGE_REPO": "env_repo"})
select_case("select:trailing-slash-strip", {},
{"FORGE_URL": "https://forge.example.com///", "FORGE_TOKEN": "test_token"})
select_case("select:empty-cfg-url-falls-to-env",
{"forge.url": "", "forge.token": ""},
{"FORGE_URL": "https://forge.example.com", "FORGE_TOKEN": "test_token"})
select_case("select:cfg-url-missing-token", {"forge.url": "https://cfg.example.com"}, {})
select_case("select:cfg-token-missing-url", {"forge.token": "cfg_tok"}, {})
select_case("select:owner-empty-env", {},
{"FORGE_URL": "https://forge.example.com", "FORGE_TOKEN": "test_token",
"FORGE_OWNER": ""})
# ── NullAdapter: every no-op shape ──────────────────────────────────────────
null_case("null:create-issue", "create_issue", ["t", "b"])
null_case("null:add-comment", "add_comment", [3, "c"])
null_case("null:get-issue-state", "get_issue_state", [7])
null_case("null:list-open-issues", "list_open_issues", [])
null_case("null:search-issues", "search_issues", ["q"])
# ── GiteaForgejoAdapter: every method + happy paths ─────────────────────────
call_case("call:create-issue", "create_issue",
[{"status": 200, "payload": {"number": 42, "state": "open"}}],
{"title": "Test", "body": "Body"})
call_case("call:create-issue-201", "create_issue",
[{"status": 201, "payload": {"number": 1, "title": "t"}}],
{"title": "t", "body": "b"})
call_case("call:create-issue-4xx", "create_issue",
[{"status": 422, "payload": {}}], {"title": "dup", "body": "b"})
call_case("call:add-comment", "add_comment",
[{"status": 200, "payload": {"id": 9, "body": "c"}}],
{"issue_number": 5, "body": "c"})
call_case("call:get-issue-state", "get_issue_state",
[{"status": 200, "payload": {"number": 9, "state": "open"}}],
{"issue_number": 9})
call_case("call:list-open-issues", "list_open_issues",
[{"status": 200, "payload": [{"number": 1}, {"number": 2}]}], {})
call_case("call:search-single-page", "search_issues",
[{"status": 200, "payload": [{"number": 3, "title": "[auto-failure:cron:Connection]"}]}],
{"query": "[auto-failure:cron:Connection]"}, covered=("GiteaForgejoAdapter.search_issues.pagination",))
call_case("call:search-paginates", "search_issues",
[{"status": 200, "payload": [{"number": n, "title": f"issue {n}"} for n in range(50)]},
{"status": 200, "payload": [{"number": 100, "title": "[auto-failure:cron:Connection] cron"}]}],
{"query": "[auto-failure:cron:Connection]"},
covered=("GiteaForgejoAdapter.search_issues.pagination",))
call_case("call:search-filters-title-body", "search_issues",
[{"status": 200, "payload": [
{"number": 1, "title": "[auto-failure:cron:Connection] cron", "body": "x"},
{"number": 2, "title": "plain title", "body": "quoted [auto-failure:cron:Connection] in body"},
{"number": 3, "title": "mention of connection only", "body": "unrelated"}]}],
{"query": "[auto-failure:cron:Connection]"},
covered=("GiteaForgejoAdapter.search_issues.client-filter",))
call_case("call:search-no-hits", "search_issues",
[{"status": 200, "payload": [{"number": 1, "title": "unrelated"}]}],
{"query": "[auto-failure:cron:Connection]"})
call_case("call:search-exact-50-then-empty", "search_issues",
[{"status": 200, "payload": [{"number": n, "title": "noise"} for n in range(50)]},
{"status": 200, "payload": []}],
{"query": "[auto-failure:cron:Connection]"},
covered=("GiteaForgejoAdapter.search_issues.pagination",))
call_case("call:create-issue-retry-connect", "create_issue",
[{"exc": "connect"}, {"status": 200, "payload": {"number": 7}}],
{"title": "t", "body": "b"}, covered=("_request",))
call_case("call:get-issue-state-retry-5xx", "get_issue_state",
[{"status": 502}, {"status": 200, "payload": {"number": 9, "state": "open"}}],
{"issue_number": 9}, covered=("_request",))
call_case("call:get-issue-state-4xx", "get_issue_state",
[{"status": 404, "payload": {}}], {"issue_number": 404}, covered=("_request",))
# ── _request: every retry branch (noise vs signal) ──────────────────────────
request_case("request:get-ok", "GET", [{"status": 200, "payload": {"ok": True}}])
request_case("request:get-retries-connect", "GET",
[{"exc": "connect"}, {"exc": "connect"},
{"status": 200, "payload": {"ok": True}}])
request_case("request:get-connect-exhaust", "GET",
[{"exc": "connect"}, {"exc": "connect"}, {"exc": "connect"}])
request_case("request:get-4xx-no-retry", "GET", [{"status": 422}])
request_case("request:get-5xx-retry-then-ok", "GET",
[{"status": 502}, {"status": 503}, {"status": 200, "payload": {"n": 3}}])
request_case("request:get-5xx-exhaust", "GET", [{"status": 502}, {"status": 503}, {"status": 504}])
request_case("request:get-read-timeout-retries", "GET",
[{"exc": "read_timeout"}, {"status": 200, "payload": {"ok": True}}])
request_case("request:get-read-timeout-exhaust", "GET",
[{"exc": "read_timeout"}, {"exc": "read_timeout"}, {"exc": "read_timeout"}])
request_case("request:post-ok", "POST", [{"status": 200, "payload": {"a": 1}}],
json_body={"a": 1})
request_case("request:post-connect-retry", "POST",
[{"exc": "connect_timeout"}, {"status": 201, "payload": {"number": 7}}],
json_body={"a": 1})
request_case("request:post-connect-exhaust", "POST",
[{"exc": "connect"}, {"exc": "connect"}, {"exc": "connect"}],
json_body={"a": 1})
request_case("request:post-read-timeout-no-retry", "POST",
[{"exc": "read_timeout"}], json_body={"a": 1})
request_case("request:post-5xx-no-retry", "POST", [{"status": 502}], json_body={"a": 1})
request_case("request:post-4xx-no-retry", "POST", [{"status": 422}], json_body={"a": 1})
request_case("request:backoff-exponential", "GET",
[{"exc": "connect"}, {"exc": "connect"}, {"status": 200}])
request_case("request:custom-retries-backoff", "GET",
[{"exc": "connect"}, {"status": 200}], retries=1, backoff=0.25)
request_case("request:timeout-passthrough", "GET",
[{"status": 200}], timeout=17)
request_case("request:headers-json-capture", "POST",
[{"status": 200, "payload": {}}],
headers={"Authorization": "token t", "Content-Type": "application/json"},
json_body={"title": "t", "body": "b"})
request_case("request:lowercase-method", "post",
[{"status": 200, "payload": {"made": "it"}}], json_body={"k": "v"})
# ── freeze ───────────────────────────────────────────────────────────────────
EXPECTED = {
"create_forge_client", "_request",
"NullAdapter.create_issue", "NullAdapter.add_comment", "NullAdapter.get_issue_state",
"NullAdapter.list_open_issues", "NullAdapter.search_issues",
"GiteaForgejoAdapter.create_issue", "GiteaForgejoAdapter.add_comment",
"GiteaForgejoAdapter.get_issue_state", "GiteaForgejoAdapter.list_open_issues",
"GiteaForgejoAdapter.search_issues",
}
missing = EXPECTED - coverage
if missing:
sys.exit(f"COVERAGE GAP — public functions not exercised: {sorted(missing)}")
doc = {
"frozen_ts": "2026-10-12T02:17:44Z",
"oracle": "src/rune/forge_client.py",
"rows": rows,
}
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(json.dumps(doc, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
print(f"frozen {len(rows)} rows → {OUT}")
print("covered:", ", ".join(sorted(coverage)))
+654
View File
@@ -0,0 +1,654 @@
#!/usr/bin/env python3
"""Freeze message_graph oracle truth for the Rust port battery (dichotomy
wave #2479 tranche).
Exercises EVERY public function of src/rune/message_graph.py across every
branch: load (missing file, corrupt JSON, wrong/missing schema, valid doc,
duplicate ids, verbatim extra fields, non-dict doc -> AttributeError,
non-list nodes -> TypeError), save (doc shape + file bytes incl. mkdir),
add (default-parent chain, explicit parent with/without edge, explicit
None -> head, falsy parent "", edge to a ghost parent, unicode, every
kind, bad kind/edge -> ValueError), tool_call (json.dumps content shape,
nested args, parent / default head), tool_result (link, missing call ->
KeyError, non-tool-call parent allowed), revise (root revision picks up
the default-head parent quirk, child revision, chained revisions, missing
-> KeyError), branch (ok, missing -> KeyError), lineage (empty graph,
default head, explicit id, superseded skip, missing-parent break, ghost
superseded_by target, unknown id, parent cycle), history (single, full
chain, from the middle, missing id), migrate_flat (chain, empty
transcript, missing role/content keys, existing-file seed) and cue (no
cue, replaces, branched from, superseded by, falsy superseded_by, edge
iteration order).
Nondeterminism seams: the facade generates the uuid4 hex id and the
_now() timestamp and passes them to the kernel as plain arguments — here
both are pinned (sequential fake uuid4, frozen clock) so the frozen truth
replays VERBATIM on the Rust side and the kernel-side parity pass is a
byte-diff of this file.
Default run pins RUNE_MG_CORE=python (the ORACLE). The kernel-side parity
pass re-runs with RUNE_MG_CORE=auto and MG_TRUTH_OUT set; identical bytes
== 100% live parity.
Output: /tmp/mg_truth.json — replayed VERBATIM by
src/rust/message_graph/src/branch_battery.rs.
"""
from __future__ import annotations
import copy
import json
import os
import sys
import tempfile
import uuid
from pathlib import Path
os.environ.setdefault("RUNE_MG_CORE", "python") # freeze the ORACLE, never the kernel
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from rune import message_graph as mg # noqa: E402
# ── determinism seams (see module docstring) ─────────────────────────────
class _FakeUUID:
def __init__(self, hex_: str) -> None:
self.hex = hex_
_seq = [0]
def _fake_uuid4():
_seq[0] += 1
return _FakeUUID(f"deadbeef{_seq[0]:04d}")
uuid.uuid4 = _fake_uuid4
CLOCK = "2031-01-01T00:00:00.000000+00:00"
mg._now = lambda: CLOCK # noqa: E731 (the oracle's clock, pinned)
TMP = Path(tempfile.mkdtemp(prefix="mg_truth_"))
GROUPS = ("load", "save", "add", "tool_call", "tool_result", "revise",
"branch", "lineage", "history", "migrate_flat", "cue")
groups: dict[str, list] = {g: [] for g in GROUPS}
def case(group: str, name: str, input_, expected) -> None:
groups[group].append({"name": name, "input": input_, "expected": expected})
def wire(g: "mg.MessageGraph") -> dict:
"""Graph state over the wire: nodes as an insertion-ordered object,
order as the arrival list."""
return {"nodes": copy.deepcopy(g.nodes), "order": list(g.order)}
def exc_of(fn, *a, **kw) -> dict:
try:
fn(*a, **kw)
except Exception as e: # noqa: BLE001 — the oracle's exception IS the truth
return {"exception": {"type": type(e).__name__, "message": str(e)}}
raise AssertionError("expected the oracle to raise")
def fresh(name: str, doc_text: str | None = None) -> "mg.MessageGraph":
p = TMP / f"{name}.json"
if doc_text is not None:
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(doc_text)
return mg.MessageGraph(path=p)
# ══ load ═════════════════════════════════════════════════════════════════
# missing file -> empty graph
g = fresh("load_missing")
case("load", "load-missing-file", {"text": None}, {"graph": wire(g)})
# empty object: no schema -> ignored
g = fresh("load_empty_obj", "{}")
case("load", "load-no-schema", {"text": "{}"}, {"graph": wire(g)})
# wrong schema
g = fresh("load_wrong_schema", json.dumps({"schema": 2, "nodes": []}))
case("load", "load-wrong-schema", {"text": json.dumps({"schema": 2, "nodes": []})},
{"graph": wire(g)})
# corrupt JSON
g = fresh("load_corrupt", "not json {")
case("load", "load-corrupt-json", {"text": "not json {"}, {"graph": wire(g)})
# valid doc: nodes ingested verbatim in file order
doc = {"schema": 1, "nodes": [
{"id": "aaa000000001", "kind": "message", "role": "user", "content": "q",
"ts": CLOCK, "parent": None, "edges": [], "superseded_by": None},
{"id": "bbb000000002", "kind": "reply", "role": "assistant", "content": "a",
"ts": CLOCK, "parent": "aaa000000001", "edges": [], "superseded_by": None},
]}
g = fresh("load_valid", json.dumps(doc))
case("load", "load-valid", {"text": json.dumps(doc)}, {"graph": wire(g)})
# duplicate ids: last wins in nodes, BOTH stay in order
doc = {"schema": 1, "nodes": [
{"id": "ccc000000003", "kind": "message", "role": "user", "content": "v1",
"ts": CLOCK, "parent": None, "edges": [], "superseded_by": None},
{"id": "ccc000000003", "kind": "message", "role": "user", "content": "v2",
"ts": CLOCK, "parent": None, "edges": [], "superseded_by": None},
]}
g = fresh("load_dup", json.dumps(doc))
case("load", "load-duplicate-ids-last-wins-order-keeps-both",
{"text": json.dumps(doc)}, {"graph": wire(g)})
# extra fields ride verbatim
doc = {"schema": 1, "nodes": [
{"id": "ddd000000004", "kind": "message", "role": "user", "content": "x",
"ts": CLOCK, "parent": None, "edges": [], "superseded_by": None,
"extra": {"deep": [1, 2.5, None, True]}, "π": "unicode key"},
]}
g = fresh("load_extra", json.dumps(doc))
case("load", "load-extra-fields-verbatim", {"text": json.dumps(doc)},
{"graph": wire(g)})
# non-dict doc -> the oracle's doc.get raises AttributeError
def _err_file(name: str, text: str) -> Path:
p = TMP / f"{name}.json"
p.write_text(text)
return p
_p = _err_file("load_list", "[1, 2]")
case("load", "load-list-doc-attributeerror", {"text": "[1, 2]"},
exc_of(mg.MessageGraph, path=_p))
_p = _err_file("load_str", '"hello"')
case("load", "load-string-doc-attributeerror", {"text": '"hello"'},
exc_of(mg.MessageGraph, path=_p))
_p = _err_file("load_int", "5")
case("load", "load-int-doc-attributeerror", {"text": "5"},
exc_of(mg.MessageGraph, path=_p))
# nodes present but not a list -> the for-loop raises TypeError
_p = _err_file("load_dictnodes", json.dumps({"schema": 1, "nodes": {"aaa": {}}}))
case("load", "load-dict-nodes-typeerror",
{"text": json.dumps({"schema": 1, "nodes": {"aaa": {}}})},
exc_of(mg.MessageGraph, path=_p))
# ══ add ══════════════════════════════════════════════════════════════════
# default parent on an empty graph: parent None, edges []
g = fresh("add_empty")
node = g.add("user", "hello")
case("add", "add-default-parent-empty-graph",
{"graph": {"nodes": {}, "order": []}, "id": node["id"],
"ts": node["ts"], "role": "user", "content": "hello", "kind": "message",
"parent": None, "edge": "parent"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# default parent chains to the head; edges stay [] (parent arg was None)
g = fresh("add_chain")
g.add("user", "q1")
before = wire(g)
node = g.add("assistant", "a1")
case("add", "add-default-parent-head-no-edge",
{"graph": before, "id": node["id"], "ts": node["ts"], "role": "assistant",
"content": "a1", "kind": "message", "parent": None, "edge": "parent"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# explicit parent + explicit edge
g = fresh("add_explicit")
a = g.add("user", "q")
before = wire(g)
node = g.add("assistant", "a", kind="reply", parent=a["id"], edge="reply_to")
case("add", "add-explicit-parent-edge",
{"graph": before, "id": node["id"], "ts": node["ts"], "role": "assistant",
"content": "a", "kind": "reply", "parent": a["id"], "edge": "reply_to"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# explicit parent, default edge "parent"
g = fresh("add_explicit_default_edge")
a = g.add("user", "q")
before = wire(g)
node = g.add("assistant", "a", parent=a["id"])
case("add", "add-explicit-parent-default-edge",
{"graph": before, "id": node["id"], "ts": node["ts"], "role": "assistant",
"content": "a", "kind": "message", "parent": a["id"], "edge": "parent"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# explicit None parent with a non-empty order: parent becomes the HEAD but
# edges stay [] (the arg was None — falsy)
g = fresh("add_none_parent")
a = g.add("user", "q")
before = wire(g)
node = g.add("assistant", "a", parent=None)
case("add", "add-explicit-none-parent-becomes-head",
{"graph": before, "id": node["id"], "ts": node["ts"], "role": "assistant",
"content": "a", "kind": "message", "parent": None, "edge": "parent"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# falsy parent "": stored as parent, but NO edge (falsy)
g = fresh("add_falsy_parent")
a = g.add("user", "q")
before = wire(g)
node = g.add("assistant", "a", parent="")
case("add", "add-falsy-empty-parent-no-edge",
{"graph": before, "id": node["id"], "ts": node["ts"], "role": "assistant",
"content": "a", "kind": "message", "parent": "", "edge": "parent"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# edge to a ghost parent is allowed (no referential check in add)
g = fresh("add_ghost_parent")
a = g.add("user", "q")
before = wire(g)
node = g.add("assistant", "a", parent="ghost0000001", edge="reply_to")
case("add", "add-ghost-parent-allowed",
{"graph": before, "id": node["id"], "ts": node["ts"], "role": "assistant",
"content": "a", "kind": "message", "parent": "ghost0000001",
"edge": "reply_to"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# unicode content
g = fresh("add_unicode")
node = g.add("user", "こんにちは ☕ héllo")
case("add", "add-unicode-content",
{"graph": {"nodes": {}, "order": []}, "id": node["id"], "ts": node["ts"],
"role": "user", "content": "こんにちは ☕ héllo", "kind": "message",
"parent": None, "edge": "parent"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# every kind
for k in ("message", "reply", "tool_call", "tool_result", "revision", "branch_head"):
g = fresh(f"add_kind_{k}")
node = g.add("user", f"content-{k}", kind=k)
case("add", f"add-kind-{k}",
{"graph": {"nodes": {}, "order": []}, "id": node["id"], "ts": node["ts"],
"role": "user", "content": f"content-{k}", "kind": k,
"parent": None, "edge": "parent"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# bad kind / bad edge (ValueError with the oracle's repr formatting)
g = fresh("add_bad_kind")
case("add", "add-unknown-kind-valueerror",
{"graph": {"nodes": {}, "order": []}, "id": "deadbeef_err", "ts": CLOCK,
"role": "user", "content": "x", "kind": "nonsense", "parent": None,
"edge": "parent"},
exc_of(g.add, "user", "x", kind="nonsense"))
case("add", "add-unknown-edge-valueerror",
{"graph": {"nodes": {}, "order": []}, "id": "deadbeef_err", "ts": CLOCK,
"role": "user", "content": "x", "kind": "message", "parent": None,
"edge": "nope"},
exc_of(g.add, "user", "x", edge="nope"))
# kind checked BEFORE edge
case("add", "add-both-bad-kind-wins",
{"graph": {"nodes": {}, "order": []}, "id": "deadbeef_err", "ts": CLOCK,
"role": "user", "content": "x", "kind": "nonsense", "parent": None,
"edge": "nope"},
exc_of(g.add, "user", "x", kind="nonsense", edge="nope"))
# ══ tool_call ════════════════════════════════════════════════════════════
g = fresh("tc_basic")
node = g.tool_call("read", {"path": "a.py"})
case("tool_call", "tool-call-basic",
{"graph": {"nodes": {}, "order": []}, "id": node["id"], "ts": node["ts"],
"content": json.dumps({"call": "read", "args": {"path": "a.py"}}),
"parent": None},
{"node": copy.deepcopy(node), "graph": wire(g)})
g = fresh("tc_parent")
a = g.add("assistant", "running")
before = wire(g)
node = g.tool_call("write", {"path": "b.py", "bytes": 3}, parent=a["id"])
case("tool_call", "tool-call-explicit-parent",
{"graph": before, "id": node["id"], "ts": node["ts"],
"content": json.dumps({"call": "write", "args": {"path": "b.py", "bytes": 3}}),
"parent": a["id"]},
{"node": copy.deepcopy(node), "graph": wire(g)})
g = fresh("tc_nested")
node = g.tool_call("search", {"q": "π", "opts": {"deep": [1, 2, {"k": None}],
"flag": True}, "n": 0.5})
case("tool_call", "tool-call-nested-args",
{"graph": {"nodes": {}, "order": []}, "id": node["id"], "ts": node["ts"],
"content": json.dumps({"call": "search",
"args": {"q": "π", "opts": {"deep": [1, 2, {"k": None}],
"flag": True}, "n": 0.5}}),
"parent": None},
{"node": copy.deepcopy(node), "graph": wire(g)})
# ══ tool_result ══════════════════════════════════════════════════════════
g = fresh("tr_ok")
c = g.tool_call("read", {"path": "a.py"})
before = wire(g)
node = g.tool_result(c["id"], "file contents")
case("tool_result", "tool-result-links-call",
{"graph": before, "id": node["id"], "ts": node["ts"], "call_id": c["id"],
"result": "file contents"},
{"node": copy.deepcopy(node), "graph": wire(g)})
# no kind check on the parent: any node can be a "call"
g = fresh("tr_any_node")
a = g.add("user", "not a call")
before = wire(g)
node = g.tool_result(a["id"], "result anyway")
case("tool_result", "tool-result-non-call-parent-allowed",
{"graph": before, "id": node["id"], "ts": node["ts"], "call_id": a["id"],
"result": "result anyway"},
{"node": copy.deepcopy(node), "graph": wire(g)})
g = fresh("tr_missing")
case("tool_result", "tool-result-missing-keyerror",
{"graph": {"nodes": {}, "order": []}, "id": "deadbeef_err", "ts": CLOCK,
"call_id": "ghost0000001", "result": "x"},
exc_of(g.tool_result, "ghost0000001", "x"))
# ══ revise ═══════════════════════════════════════════════════════════════
# root revision: old.get("parent") is None -> add substitutes the HEAD,
# which at that moment is the old node itself (quirk, frozen verbatim)
g = fresh("rev_root")
a = g.add("assistant", "v1")
before = wire(g)
rev = g.revise(a["id"], "v2")
case("revise", "revise-root-head-parent-quirk",
{"graph": before, "node_id": a["id"], "new_content": "v2",
"id": rev["id"], "ts": rev["ts"]},
{"node": copy.deepcopy(rev), "graph": wire(g)})
# child revision: keeps the old parent, edges revises + supersedes
g = fresh("rev_child")
a = g.add("user", "q")
b = g.add("assistant", "v1", parent=a["id"])
before = wire(g)
rev = g.revise(b["id"], "v2")
case("revise", "revise-child-keeps-parent",
{"graph": before, "node_id": b["id"], "new_content": "v2",
"id": rev["id"], "ts": rev["ts"]},
{"node": copy.deepcopy(rev), "graph": wire(g)})
# chained revision: revise the revision
g = fresh("rev_chain")
a = g.add("assistant", "v1")
r1 = g.revise(a["id"], "v2")
before = wire(g)
r2 = g.revise(r1["id"], "v3")
case("revise", "revise-of-revision",
{"graph": before, "node_id": r1["id"], "new_content": "v3",
"id": r2["id"], "ts": r2["ts"]},
{"node": copy.deepcopy(r2), "graph": wire(g)})
g = fresh("rev_missing")
case("revise", "revise-missing-keyerror",
{"graph": {"nodes": {}, "order": []}, "node_id": "ghost0000001",
"new_content": "x", "id": "deadbeef_err", "ts": CLOCK},
exc_of(g.revise, "ghost0000001", "x"))
# ══ branch ═══════════════════════════════════════════════════════════════
g = fresh("branch_ok")
a = g.add("user", "root")
g.add("assistant", "answer", parent=a["id"])
before = wire(g)
head = g.branch(a["id"])
case("branch", "branch-from-message",
{"graph": before, "node_id": a["id"], "id": head["id"], "ts": head["ts"]},
{"node": copy.deepcopy(head), "graph": wire(g)})
g = fresh("branch_missing")
case("branch", "branch-missing-keyerror",
{"graph": {"nodes": {}, "order": []}, "node_id": "ghost0000001",
"id": "deadbeef_err", "ts": CLOCK},
exc_of(g.branch, "ghost0000001"))
# ══ lineage ══════════════════════════════════════════════════════════════
g = fresh("lin_empty")
case("lineage", "lineage-empty-graph", {"graph": wire(g), "node_id": None},
{"result": []})
# default head: full chain oldest-first
g = fresh("lin_chain")
a = g.add("user", "q")
b = g.add("assistant", "a1", parent=a["id"])
c = g.add("assistant", "a2", parent=b["id"])
case("lineage", "lineage-default-head-full-chain",
{"graph": wire(g), "node_id": None},
{"result": [copy.deepcopy(n) for n in g.lineage()]})
assert [n["id"] for n in g.lineage()] == [a["id"], b["id"], c["id"]]
# explicit id: subchain
case("lineage", "lineage-explicit-id-subchain",
{"graph": wire(g), "node_id": b["id"]},
{"result": [copy.deepcopy(n) for n in g.lineage(b["id"])]})
assert [n["id"] for n in g.lineage(b["id"])] == [a["id"], b["id"]]
# superseded nodes yield to their revision; unrelated branches excluded
g = fresh("lin_superseded")
a = g.add("user", "q")
b = g.add("assistant", "v1", parent=a["id"])
other = g.add("user", "unrelated")
rev = g.revise(b["id"], "v2")
case("lineage", "lineage-skips-superseded-default-head",
{"graph": wire(g), "node_id": None},
{"result": [copy.deepcopy(n) for n in g.lineage()]})
assert [n["id"] for n in g.lineage()] == [a["id"], rev["id"]]
case("lineage", "lineage-from-root-excludes-other-branch",
{"graph": wire(g), "node_id": a["id"]},
{"result": [copy.deepcopy(n) for n in g.lineage(a["id"])]})
assert [n["id"] for n in g.lineage(a["id"])] == [a["id"]]
# parent chain into a ghost: walk breaks quietly
g = fresh("lin_ghost_parent")
a = g.add("user", "q")
g.nodes[a["id"]]["parent"] = "ghost0000001"
case("lineage", "lineage-ghost-parent-breaks",
{"graph": wire(g), "node_id": a["id"]},
{"result": [copy.deepcopy(n) for n in g.lineage(a["id"])]})
assert [n["id"] for n in g.lineage(a["id"])] == [a["id"]]
# superseded_by pointing OUTSIDE the graph: node included (guard fails)
g = fresh("lin_ghost_superseded")
a = g.add("user", "q")
g.nodes[a["id"]]["superseded_by"] = "ghost0000001"
case("lineage", "lineage-ghost-superseded-by-included",
{"graph": wire(g), "node_id": a["id"]},
{"result": [copy.deepcopy(n) for n in g.lineage(a["id"])]})
assert [n["id"] for n in g.lineage(a["id"])] == [a["id"]]
# unknown id: empty
g = fresh("lin_unknown")
case("lineage", "lineage-unknown-id-empty",
{"graph": wire(g), "node_id": "ghost0000001"}, {"result": []})
# parent cycle: the seen-set guard stops the walk
doc = {"schema": 1, "nodes": [
{"id": "aaa000000001", "kind": "message", "role": "user", "content": "a",
"ts": CLOCK, "parent": "bbb000000002", "edges": [], "superseded_by": None},
{"id": "bbb000000002", "kind": "message", "role": "user", "content": "b",
"ts": CLOCK, "parent": "aaa000000001", "edges": [], "superseded_by": None},
]}
g = fresh("lin_cycle", json.dumps(doc))
case("lineage", "lineage-parent-cycle-guarded",
{"graph": wire(g), "node_id": "aaa000000001"},
{"result": [copy.deepcopy(n) for n in g.lineage("aaa000000001")]})
assert [n["id"] for n in g.lineage("aaa000000001")] == ["bbb000000002",
"aaa000000001"]
# ══ history ══════════════════════════════════════════════════════════════
# single node: no supersedes edge -> just itself
g = fresh("hist_single")
a = g.add("assistant", "v1")
case("history", "history-single-node",
{"graph": wire(g), "node_id": a["id"]},
{"result": [copy.deepcopy(n) for n in g.history(a["id"])]})
assert [n["content"] for n in g.history(a["id"])] == ["v1"]
# full chain oldest-first, asked from the tip AND from the middle
g = fresh("hist_chain")
a = g.add("assistant", "v1")
r1 = g.revise(a["id"], "v2")
r2 = g.revise(r1["id"], "v3")
case("history", "history-chain-from-tip",
{"graph": wire(g), "node_id": r2["id"]},
{"result": [copy.deepcopy(n) for n in g.history(r2["id"])]})
assert [n["content"] for n in g.history(r2["id"])] == ["v1", "v2", "v3"]
case("history", "history-chain-from-middle-full-trail",
{"graph": wire(g), "node_id": r1["id"]},
{"result": [copy.deepcopy(n) for n in g.history(r1["id"])]})
assert [n["content"] for n in g.history(r1["id"])] == ["v1", "v2", "v3"]
# unknown id: empty trail
g = fresh("hist_missing")
case("history", "history-unknown-id-empty",
{"graph": wire(g), "node_id": "ghost0000001"}, {"result": []})
# revision whose superseded_by target is a ghost: trail stops there
g = fresh("hist_ghost_target")
a = g.add("assistant", "v1")
r1 = g.revise(a["id"], "v2")
g.nodes[r1["id"]]["superseded_by"] = "ghost0000001"
case("history", "history-ghost-superseded-by-stops",
{"graph": wire(g), "node_id": a["id"]},
{"result": [copy.deepcopy(n) for n in g.history(a["id"])]})
assert [n["content"] for n in g.history(a["id"])] == ["v1", "v2"]
# ══ migrate_flat ═════════════════════════════════════════════════════════
def mig_ids(g, n):
"""The ids the oracle's migrate consumed (the last n arrivals)."""
return list(g.order[len(g.order) - n:]) if n else []
flat = [{"role": "user", "content": "q1"}, {"role": "assistant", "content": "a1"}]
g = mg.MessageGraph.migrate_flat(copy.deepcopy(flat), path=TMP / "mig_basic.json")
case("migrate_flat", "migrate-basic-chain",
{"text": None, "transcript": copy.deepcopy(flat),
"ids": mig_ids(g, 2), "tss": [CLOCK, CLOCK]},
{"graph": wire(g)})
assert [g.nodes[i]["parent"] for i in g.order] == [None, g.order[0]]
# empty transcript
g = mg.MessageGraph.migrate_flat([], path=TMP / "mig_empty.json")
case("migrate_flat", "migrate-empty-transcript",
{"text": None, "transcript": [], "ids": [], "tss": []}, {"graph": wire(g)})
# missing role/content keys -> system / "" defaults
flat = [{"role": "user"}, {}, {"content": "no role"}]
g = mg.MessageGraph.migrate_flat(copy.deepcopy(flat), path=TMP / "mig_defaults.json")
case("migrate_flat", "migrate-missing-keys-default-system-empty",
{"text": None, "transcript": copy.deepcopy(flat), "ids": mig_ids(g, 3),
"tss": [CLOCK] * 3},
{"graph": wire(g)})
assert [g.nodes[i]["role"] for i in g.order] == ["user", "system", "system"]
assert [g.nodes[i]["content"] for i in g.order] == ["", "", "no role"]
# existing file seeds the graph first, migration appends after
seed_doc = {"schema": 1, "nodes": [
{"id": "eee000000005", "kind": "message", "role": "user", "content": "old",
"ts": CLOCK, "parent": None, "edges": [], "superseded_by": None},
]}
_seed = _err_file("mig_seed", json.dumps(seed_doc))
g = mg.MessageGraph.migrate_flat([{"role": "user", "content": "new"}],
path=_seed)
case("migrate_flat", "migrate-existing-file-seed",
{"text": json.dumps(seed_doc), "transcript": [{"role": "user", "content": "new"}],
"ids": mig_ids(g, 1), "tss": [CLOCK]},
{"graph": wire(g)})
# ══ cue ══════════════════════════════════════════════════════════════════
g = fresh("cue_plain")
a = g.add("assistant", "v1")
plain = copy.deepcopy(g.nodes[a["id"]])
case("cue", "cue-plain-node", {"node": plain}, {"result": ""})
assert mg.MessageGraph.cue(plain) == ""
rev = g.revise(a["id"], "v2")
case("cue", "cue-revision-replaces",
{"node": copy.deepcopy(g.nodes[rev["id"]])},
{"result": f"replaces {a['id'][:8]}"})
assert mg.MessageGraph.cue(g.nodes[rev["id"]]) == f"replaces {a['id'][:8]}"
case("cue", "cue-superseded-by",
{"node": copy.deepcopy(g.nodes[a["id"]])},
{"result": f"superseded by {rev['id'][:8]}"})
assert mg.MessageGraph.cue(g.nodes[a["id"]]) == f"superseded by {rev['id'][:8]}"
g = fresh("cue_branch")
a = g.add("user", "root")
head = g.branch(a["id"])
case("cue", "cue-branch-head",
{"node": copy.deepcopy(g.nodes[head["id"]])},
{"result": f"branched from {a['id'][:8]}"})
assert mg.MessageGraph.cue(g.nodes[head["id"]]) == f"branched from {a['id'][:8]}"
# falsy superseded_by "" is NOT a cue
node = copy.deepcopy(plain)
node["superseded_by"] = ""
case("cue", "cue-falsy-superseded-by-empty", {"node": node}, {"result": ""})
assert mg.MessageGraph.cue(node) == ""
# first supersedes/branches_from edge in iteration order wins
node = copy.deepcopy(plain)
node["edges"] = [{"type": "parent", "to": "aaa000000001"},
{"type": "supersedes", "to": "bbb000000002"}]
case("cue", "cue-first-supersedes-edge-wins", {"node": node},
{"result": "replaces bbb00000"})
assert mg.MessageGraph.cue(node) == "replaces bbb00000"
node2 = copy.deepcopy(plain)
node2["edges"] = [{"type": "branches_from", "to": "ccc000000003"},
{"type": "supersedes", "to": "bbb000000002"}]
case("cue", "cue-branches-from-beats-later-supersedes", {"node": node2},
{"result": "branched from ccc00000"})
assert mg.MessageGraph.cue(node2) == "branched from ccc00000"
# ══ save ═════════════════════════════════════════════════════════════════
# basic save: doc shape + exact file bytes (json.dumps indent=2)
g = fresh("save_basic")
a = g.add("user", "persist me")
g.save()
doc = {"schema": mg.SCHEMA, "nodes": [g.nodes[i] for i in g.order]}
case("save", "save-basic-doc-and-bytes",
{"graph": {"nodes": {a["id"]: copy.deepcopy(g.nodes[a["id"]])},
"order": [a["id"]]}},
{"doc": copy.deepcopy(doc), "file": (TMP / "save_basic.json").read_text()})
assert (TMP / "save_basic.json").read_text() == json.dumps(doc, indent=2)
# save creates missing parent dirs
deep = TMP / "deep" / "nested" / "dir" / "g.json"
g2 = mg.MessageGraph(path=deep)
g2.add("user", "deep")
g2.save()
doc2 = {"schema": mg.SCHEMA, "nodes": [g2.nodes[i] for i in g2.order]}
case("save", "save-creates-parent-dirs",
{"graph": wire(g2)}, {"doc": copy.deepcopy(doc2), "file": deep.read_text()})
assert deep.exists()
# round trip: save -> fresh load reproduces nodes AND order
g3 = mg.MessageGraph(path=TMP / "save_basic.json")
case("load", "load-save-round-trip",
{"text": (TMP / "save_basic.json").read_text()}, {"graph": wire(g3)})
assert g3.order == g.order
assert g3.nodes[a["id"]]["content"] == "persist me"
# ══ oracle health (the repo's own battery must pass on the oracle) ══════
import unittest # noqa: E402
suite = unittest.defaultTestLoader.discover(
str(Path(__file__).resolve().parents[1] / "tests"),
pattern="test_message_graph.py")
runner = unittest.TextTestRunner(verbosity=0)
if not runner.run(suite).wasSuccessful():
raise AssertionError("oracle regression: test_message_graph.py failed")
# ══ write ════════════════════════════════════════════════════════════════
truth = {
"frozen": "message_graph load/save/add/tool_call/tool_result/revise/"
"branch/lineage/history/migrate_flat/cue",
"oracle": "src/rune/message_graph.py",
"note": "Graph wire: {nodes: {id: node} insertion-ordered, order: [ids]}. "
"Nondeterminism seams (uuid4 hex id, _now timestamp) are facade "
"arguments — frozen CLOCK and sequential deadbeefNNNN ids replay "
"verbatim. save() builds the doc in the kernel; the file write "
"(mkdir + json.dumps indent=2) and tool_call's json.dumps content "
"stay in the facade (serialization isomorphism).",
}
for gname in GROUPS:
truth[gname] = groups[gname]
out = Path(os.environ.get("MG_TRUTH_OUT", "/tmp/mg_truth.json"))
out.write_text(json.dumps(truth, indent=1, ensure_ascii=False) + "\n")
counts = ", ".join(f"{len(groups[g])} {g}" for g in GROUPS)
print(f"froze {sum(len(v) for v in groups.values())} cases -> {out}: {counts}")
+463
View File
@@ -0,0 +1,463 @@
#!/usr/bin/env python3
"""gen_mutation_guards_truth.py — freeze/replay parity truth for
mutation_guards (dichotomy wave, #2479 tranche).
Freeze mode (default): exercises the LIVE Python oracle
(src/rune/mutation_guards.py — classify_mutation and guard_mutation, the
module's two public functions) across every branch and records
(kernel_input, expected_kernel_output) pairs into
src/rust/mutation_guards/src/frozen_mg_truth.json.
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
delegation ACTIVE (RUNE_MG_CORE unpinned) and asserts both kernel-level
(loader direct) and facade-level (oracle delegates) parity against the
frozen file.
Poisoned-truth law: freeze pins RUNE_MG_CORE=python BEFORE any import.
Mutation law: case inputs are deep-copied BEFORE they reach the oracle.
Branch inventory (both functions):
* DENY 1 — unattributed identity-family mutation (identity/persona/
constitution, falsy attributed); runs FIRST so it beats read ops and
the destructive gate
* DENY 2 — destructive (op severity >= 2) on a protected kind
(severity >= 2) with no backup
* CONFIRM — score >= 4, or identity-family with any mutating op
* ALLOW — everything else
* severity-table defaults: unknown kind -> 1, unknown op -> 1,
unknown/None scope -> 0
* Python truthiness through guard_mutation's .get passthrough: the
default applies only when the KEY is missing — present-but-null
attributed is falsy (unattributed), falsy 0/[]/"yes"-truthy pins the
quirk; the f-string renders a null kind/operation as "None"
"""
import copy
import json
import os
import sys
from datetime import datetime, timezone
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TRUTH_PATH = os.path.join(REPO, "src", "rust", "mutation_guards",
"src", "frozen_mg_truth.json")
REPLAY = "--replay" in sys.argv
if not REPLAY:
os.environ["RUNE_MG_CORE"] = "python" # poisoned-truth law (freeze side)
else:
os.environ.pop("RUNE_MG_CORE", None) # live-parity law (facade delegates)
sys.path.insert(0, os.path.join(REPO, "src"))
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
import mutation_guards as oracle_mod # noqa: E402
if REPLAY:
from rune import mu_loader # noqa: E402 (kernel-level replay side)
if not mu_loader.available():
print("REPLAY ABORT: kernel not built —", mu_loader.LIB_PATH)
sys.exit(2)
def facade_classify(inp):
return oracle_mod.classify_mutation(
copy.deepcopy(inp["target_kind"]),
copy.deepcopy(inp["operation"]),
copy.deepcopy(inp["scope"]),
copy.deepcopy(inp["attributed"]),
copy.deepcopy(inp["backed_up"]),
)
def kernel_classify(inp):
return mu_loader.classify(**copy.deepcopy(inp))
def facade_guard(inp):
return oracle_mod.guard_mutation(copy.deepcopy(inp["mutation"]))
def kernel_guard(inp):
return mu_loader.guard(**copy.deepcopy(inp))
# ── classify_mutation ────────────────────────────────────────────────────────
def cl_case(rec, name, target_kind, operation, scope="single",
attributed=True, backed_up=False):
inp = {"target_kind": target_kind, "operation": operation,
"scope": scope, "attributed": attributed, "backed_up": backed_up}
got = facade_classify(copy.deepcopy(inp))
rec(name, inp, got, got)
# DENY 1 — unattributed identity family (runs FIRST)
def c_den1_identity(rec):
cl_case(rec, "cl-deny1-unattributed-identity-overwrite",
"identity", "overwrite", "single", False, False)
def c_den1_persona_append_whole(rec):
# append (op severity 1) would CONFIRM on persona; unattributed wins
cl_case(rec, "cl-deny1-unattributed-persona-append-whole",
"persona", "append", "whole", False, False)
def c_den1_constitution_falsy_zero(rec):
# attributed=0 is falsy (Python truthiness) → unattributed
cl_case(rec, "cl-deny1-constitution-attributed-zero",
"constitution", "append", "single", 0, False)
def c_den1_beats_read(rec):
# even a READ is denied when unattributed — precedence proof
cl_case(rec, "cl-deny1-identity-read-still-denied",
"identity", "read", "single", False, False)
def c_den1_null_attributed(rec):
# explicit None attributed → falsy → DENY
cl_case(rec, "cl-deny1-identity-attributed-null",
"identity", "append", "single", None, False)
# DENY 2 — destructive without a snapshot
def c_den2_constitution_delete_whole(rec):
cl_case(rec, "cl-deny2-constitution-delete-whole",
"constitution", "delete", "whole", True, False)
def c_den2_skill_delete_multi(rec):
cl_case(rec, "cl-deny2-skill-delete-multi",
"skill", "delete", "multi", True, False)
def c_den2_belief_overwrite_whole(rec):
# op severity 2 + kind severity 2 boundary: still DENY
cl_case(rec, "cl-deny2-belief-overwrite-whole-boundary",
"belief", "overwrite", "whole", True, False)
def c_den2_backed_up_zero(rec):
# backed_up=0 is falsy → unbacked → DENY
cl_case(rec, "cl-deny2-skill-overwrite-whole-backed-zero",
"skill", "overwrite", "whole", True, 0)
# CONFIRM — score >= 4 or identity-family mutating op
def c_conf_backed_skill_overwrite(rec):
cl_case(rec, "cl-confirm-backed-skill-overwrite-whole",
"skill", "overwrite", "whole", True, True)
def c_conf_identity_append_attributed(rec):
# kind severity 3 + op severity 1 → CONFIRM even at score 4
cl_case(rec, "cl-confirm-identity-append-attributed",
"identity", "append", "single", True, False)
def c_conf_identity_read_whole(rec):
# read (op 0) escapes the identity rule; score 3+0+2=5 ≥ 4 → CONFIRM
cl_case(rec, "cl-confirm-identity-read-whole-score",
"identity", "read", "whole", True, False)
def c_conf_operating_delete_single(rec):
# score exactly 4 — the CONFIRM boundary
cl_case(rec, "cl-confirm-operating-delete-single-score-4",
"operating", "delete", "single", True, False)
def c_conf_fact_overwrite_whole(rec):
# op 2 + kind 1: no DENY (kind < 2), score 5 → CONFIRM
cl_case(rec, "cl-confirm-fact-overwrite-whole",
"fact", "overwrite", "whole", True, False)
def c_conf_backed_truthy_string(rec):
# backed_up="yes" is truthy → skips DENY 2 → CONFIRM
cl_case(rec, "cl-confirm-skill-delete-multi-backed-truthy",
"skill", "delete", "multi", True, "yes")
def c_conf_constitution_delete_backed(rec):
cl_case(rec, "cl-confirm-constitution-delete-whole-backed",
"constitution", "delete", "whole", True, True)
def c_conf_persona_overwrite_backed(rec):
cl_case(rec, "cl-confirm-persona-overwrite-whole-backed",
"persona", "overwrite", "whole", True, True)
def c_conf_archive_delete_whole(rec):
# archive (kind 0) still CONFIRMs at score 5 — score rule beats kind
cl_case(rec, "cl-confirm-archive-delete-whole-score",
"archive", "delete", "whole", True, False)
def c_conf_unknown_kind_delete(rec):
# unknown kind defaults to severity 1: 1+3+0=4 → CONFIRM
cl_case(rec, "cl-confirm-unknown-kind-delete",
"widget", "delete", "single", True, False)
# ALLOW — everything else
def c_allow_fact_append_single(rec):
cl_case(rec, "cl-allow-fact-append-single-defaults",
"fact", "append", "single", True, False)
def c_allow_fact_append_multi(rec):
# score 3 — directly below the CONFIRM boundary
cl_case(rec, "cl-allow-fact-append-multi-score-3",
"fact", "append", "multi", True, False)
def c_allow_identity_read_single(rec):
# identity + read + single: score 3, identity rule needs op ≥ 1
cl_case(rec, "cl-allow-identity-read-single",
"identity", "read", "single", True, False)
def c_allow_archive_delete_single(rec):
# kind severity 0: even delete is cheap on archives
cl_case(rec, "cl-allow-archive-delete-single",
"archive", "delete", "single", True, False)
def c_allow_archive_read_whole(rec):
cl_case(rec, "cl-allow-archive-read-whole",
"archive", "read", "whole", True, False)
def c_allow_fact_read_whole(rec):
cl_case(rec, "cl-allow-fact-read-whole",
"fact", "read", "whole", True, False)
def c_allow_unknown_operation(rec):
# unknown op defaults to severity 1: 1+1+1=3 → ALLOW
cl_case(rec, "cl-allow-unknown-operation",
"fact", "nuke", "multi", True, False)
def c_allow_unknown_scope(rec):
# unknown scope defaults to severity 0
cl_case(rec, "cl-allow-unknown-scope",
"fact", "append", "galaxy", True, False)
def c_allow_null_kind(rec):
# None kind: severity default + f-string renders "None"
cl_case(rec, "cl-allow-null-kind-renders-None",
None, "append", "single", True, False)
def c_allow_null_operation(rec):
cl_case(rec, "cl-allow-null-operation-renders-None",
"fact", None, "single", True, False)
def c_allow_null_scope(rec):
# None scope: severity 0 (dict lookup miss)
cl_case(rec, "cl-allow-null-scope",
"fact", "append", None, True, False)
# ── guard_mutation ───────────────────────────────────────────────────────────
def gu_case(rec, name, mutation):
inp = {"mutation": mutation}
got = facade_guard(copy.deepcopy(inp))
rec(name, inp, got, got)
def g_empty_dict_defaults(rec):
gu_case(rec, "gu-empty-dict-all-defaults", {})
def g_fact_append_defaults(rec):
gu_case(rec, "gu-fact-append-allow", {"target_kind": "fact",
"operation": "append"})
def g_unattributed_identity(rec):
gu_case(rec, "gu-unattributed-identity-deny",
{"target_kind": "identity", "operation": "overwrite",
"attributed": False})
def g_unbacked_constitution_delete(rec):
gu_case(rec, "gu-unbacked-constitution-delete-deny",
{"target_kind": "constitution", "operation": "delete",
"scope": "whole", "attributed": True, "backed_up": False})
def g_backed_skill_confirms(rec):
gu_case(rec, "gu-backed-skill-overwrite-confirm",
{"target_kind": "skill", "operation": "overwrite",
"scope": "whole", "attributed": True, "backed_up": True})
def g_null_attributed_present(rec):
# present-but-null attributed is FALSY — the default True applies
# only to a MISSING key
gu_case(rec, "gu-attributed-null-falsy-deny",
{"target_kind": "identity", "attributed": None})
def g_empty_list_attributed(rec):
gu_case(rec, "gu-attributed-empty-list-falsy-deny",
{"target_kind": "persona", "operation": "delete",
"attributed": []})
def g_backed_up_zero(rec):
gu_case(rec, "gu-backed-up-zero-falsy-deny",
{"target_kind": "skill", "operation": "overwrite",
"scope": "whole", "backed_up": 0})
def g_backed_up_truthy_string(rec):
gu_case(rec, "gu-backed-up-truthy-string-confirm",
{"target_kind": "skill", "operation": "overwrite",
"scope": "whole", "backed_up": "yes"})
def g_read_allows(rec):
gu_case(rec, "gu-read-allows", {"target_kind": "fact",
"operation": "read"})
def g_extra_keys_ignored(rec):
gu_case(rec, "gu-extra-keys-ignored",
{"target_kind": "fact", "operation": "append",
"note": "x", "weight": 9})
def g_null_target_kind(rec):
# present-null target_kind beats the "fact" default (get quirk);
# severity defaults to 1, f-string renders "None"
gu_case(rec, "gu-null-target-kind-renders-None",
{"target_kind": None})
def g_empty_operation_string(rec):
# empty op string: severity default 1, f-string renders ""
gu_case(rec, "gu-empty-operation-string",
{"target_kind": "fact", "operation": ""})
# ── driver ───────────────────────────────────────────────────────────────────
CLASSIFY = [
c_den1_identity, c_den1_persona_append_whole,
c_den1_constitution_falsy_zero, c_den1_beats_read,
c_den1_null_attributed, c_den2_constitution_delete_whole,
c_den2_skill_delete_multi, c_den2_belief_overwrite_whole,
c_den2_backed_up_zero, c_conf_backed_skill_overwrite,
c_conf_identity_append_attributed, c_conf_identity_read_whole,
c_conf_operating_delete_single, c_conf_fact_overwrite_whole,
c_conf_backed_truthy_string, c_conf_constitution_delete_backed,
c_conf_persona_overwrite_backed, c_conf_archive_delete_whole,
c_conf_unknown_kind_delete, c_allow_fact_append_single,
c_allow_fact_append_multi, c_allow_identity_read_single,
c_allow_archive_delete_single, c_allow_archive_read_whole,
c_allow_fact_read_whole, c_allow_unknown_operation,
c_allow_unknown_scope, c_allow_null_kind, c_allow_null_operation,
c_allow_null_scope,
]
GUARD = [
g_empty_dict_defaults, g_fact_append_defaults,
g_unattributed_identity, g_unbacked_constitution_delete,
g_backed_skill_confirms, g_null_attributed_present,
g_empty_list_attributed, g_backed_up_zero,
g_backed_up_truthy_string, g_read_allows, g_extra_keys_ignored,
g_null_target_kind, g_empty_operation_string,
]
GROUPS = {
"classify": (CLASSIFY, True, "mu_classify", facade_classify,
kernel_classify),
"guard": (GUARD, True, "mu_guard", facade_guard, kernel_guard),
}
def canon(x) -> str:
"""Content canonicalization (key-order independent)."""
return json.dumps(json.loads(json.dumps(x, default=str)), sort_keys=True)
def main():
frozen = None
if REPLAY:
with open(TRUTH_PATH) as f:
frozen = json.load(f)["cases"]
groups = {}
for group, (cases, facade_replayable, _sym, fcall, kcall) in GROUPS.items():
recs = []
def rec(name, inp, oracle_ret, expected, _r=recs, _g=group):
entry = {"name": name, "input": inp,
"return": oracle_ret, "expected": expected}
if REPLAY and facade_replayable:
old = next(c for c in frozen[_g] if c["name"] == name)
for key in ("input", "return", "expected"):
got, want = entry[key], old[key]
if canon(got) != canon(want):
print(f"FACADE DIVERGE [{_g}] {name} {key}")
print(" got: ", json.dumps(got, sort_keys=True, default=str)[:600])
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
_r.append({"name": name, "diverged": key})
return
_r.append(entry)
for c in cases:
c(rec)
groups[group] = recs
total = sum(len(v) for v in groups.values())
if REPLAY:
facade_groups = [g for g, (_, r, _, _, _) in GROUPS.items() if r]
facade_total = sum(len(groups[g]) for g in facade_groups)
facade_failures = sum(1 for g in facade_groups for c in groups[g]
if "diverged" in c)
failures = 0
checked = 0
for group, cases in frozen.items():
sym_fns = {"classify": kernel_classify, "guard": kernel_guard}
for case in cases:
checked += 1
got = sym_fns[group](case["input"])
want = case["expected"]
if canon(got) != canon(want):
failures += 1
print(f"KERNEL DIVERGE [{group}] {case['name']}")
print(" got: ", json.dumps(got, sort_keys=True, default=str)[:600])
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
print(f"replay: facade {facade_total - facade_failures}/{facade_total}, "
f"kernel {checked - failures}/{checked}")
return 1 if (failures or facade_failures) else 0
doc = {
"frozen": datetime.now(timezone.utc).isoformat(),
"oracle": "src/rune/mutation_guards.py",
"pin": "RUNE_MG_CORE=python",
"note": "Frozen via scripts/gen_mutation_guards_truth.py against "
"the live Python oracle. Battery embeds this VERBATIM.",
"cases": groups,
}
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
with open(TRUTH_PATH, "w") as f:
json.dump(doc, f, indent=1)
f.write("\n")
print(f"frozen {total} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
return 0
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,526 @@
#!/usr/bin/env python3
"""Freeze truth for the openai_compat_transport Go port (dichotomy wave #2479).
Exercises EVERY public function of src/rune/openai_compat_transport.py against
the LIVE oracle: __init__ (timeout clamp), _post (wire + non-object guard),
_to_openai_messages (all branches), _from_openai_response (all branches),
chat (model split, tools, options, usage), supports_tools, supports_thinking,
context_length, is_cloud_pinned, cloud_transport_for (config/env/auth seams).
Wire IO served by a local stub HTTP server that captures each request
(path, authorization, content-type, body bytes) and returns scripted bodies;
cloud_transport_for's IO seams (os.path.expanduser, os.environ) are patched
to temp files so config/auth resolution is deterministic.
Excluded branches (interpreter-specific error text — not portable truth):
- _post on a non-2xx HTTP status (Python urllib HTTPError string vs Go
net/http error string) and on connection failures.
- _post on a body that is invalid JSON (Python json.JSONDecodeError text).
- cloud_transport_for with auth.json containing invalid JSON (same reason).
Numbers: fixtures are written by json.dump so every numeric literal is in
Python's canonical repr; the Go port preserves literals on parse. Floats in
fixtures stay in the shortest-decimal band (no exponents) so literal
preservation equals Python re-rendering.
Nondeterministic: usage_total_duration_ns (time.monotonic) is excluded from
every row; the Go battery asserts it is > 0 after a chat instead.
"""
import json
import os
import sys
import tempfile
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from unittest import mock
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
import rune.openai_compat_transport as oct_mod # noqa: E402
# -- stub wire ---------------------------------------------------------------
class StubState:
def __init__(self):
self.script = [] # list of response body strings
self.requests = [] # captured requests, one dict per POST
def reset(self, bodies):
self.script = list(bodies)
self.requests = []
STUB = StubState()
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length)
STUB.requests.append({
"path": self.path,
"authorization": self.headers.get("Authorization"),
"content_type": self.headers.get("Content-Type"),
"body": body.decode("utf-8"),
})
resp = STUB.script.pop(0)
data = resp.encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def log_message(self, *args):
pass
def start_stub():
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
return server, f"http://127.0.0.1:{server.server_address[1]}"
# -- row helpers -------------------------------------------------------------
ROWS = []
def add(name, op, setup, want):
ROWS.append({"name": name, "op": op, "setup": setup, "want": want})
def attrs(t):
return {
"base_url": t.base_url,
"api_key": t.api_key,
"provider": t.provider,
"timeout": t.timeout,
"context_window": t._context_window,
"usage_calls": t.usage_calls,
"usage_prompt_tokens": t.usage_prompt_tokens,
"usage_output_tokens": t.usage_output_tokens,
"usage_total_duration_ns": t.usage_total_duration_ns,
}
# -- cases -------------------------------------------------------------------
def case_ctor():
add("ctor_defaults", "ctor",
{"base_url": "https://api.example.com/v1/", "api_key": "k1", "provider": "Verboo Code"},
attrs(oct_mod.OpenAICompatTransport("https://api.example.com/v1/", "k1", "Verboo Code")))
add("ctor_timeout_clamped_low", "ctor",
{"base_url": "http://x/v1", "api_key": "k", "provider": "T", "timeout": 30},
attrs(oct_mod.OpenAICompatTransport("http://x/v1", "k", "T", timeout=30)))
add("ctor_timeout_clamp_boundary", "ctor",
{"base_url": "http://x/v1", "api_key": "k", "provider": "T", "timeout": 120},
attrs(oct_mod.OpenAICompatTransport("http://x/v1", "k", "T", timeout=120)))
add("ctor_timeout_above_floor_float", "ctor",
{"base_url": "http://x/v1", "api_key": "k", "provider": "T", "timeout": 130.5, "context_window": 8192},
attrs(oct_mod.OpenAICompatTransport("http://x/v1", "k", "T", timeout=130.5, context_window=8192)))
def case_to_messages():
f = oct_mod.OpenAICompatTransport._to_openai_messages
cases = [
("tm_empty", []),
("tm_plain_roles", [
{"role": "system", "content": "sys"},
{"role": "user", "content": "task"},
{"role": "assistant", "content": "hi"},
]),
("tm_missing_content_defaults_empty", [{"role": "user"}]),
("tm_null_content_stays_null", [{"role": "user", "content": None}]),
("tm_null_role", [{"content": "orphan"}]),
("tm_assistant_empty_tool_calls_is_plain", [
{"role": "assistant", "content": "text", "tool_calls": []},
]),
("tm_tool_call_dict_args", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "edit_file", "arguments": {"path": "/x.py", "old": "a", "new": "b"}}}]},
{"role": "tool", "tool_call_id": "call_2_0", "content": "{\"success\": true}"},
]),
("tm_tool_call_string_args", [
{"role": "assistant", "content": "working", "tool_calls": [
{"function": {"name": "read_file", "arguments": "{\"path\": \"/x.py\"}"}}]},
]),
("tm_tool_call_missing_args_defaults_empty_obj", [
{"role": "assistant", "content": None, "tool_calls": [{"function": {"name": "noop"}}]},
]),
("tm_tool_call_missing_function", [
{"role": "assistant", "content": None, "tool_calls": [{"id": "weird"}]},
]),
("tm_tool_call_non_dict_args_int", [
{"role": "assistant", "content": None, "tool_calls": [
{"function": {"name": "ping", "arguments": 42}}]},
]),
("tm_tool_call_non_dict_args_list", [
{"role": "assistant", "content": None, "tool_calls": [
{"function": {"name": "bulk", "arguments": ["a", "b"]}}]},
]),
("tm_tool_call_null_args_str", [
{"role": "assistant", "content": None, "tool_calls": [
{"function": {"name": "q", "arguments": "None"}}]},
]),
("tm_ids_paired_from_following_tools", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "a", "arguments": {}}, },
{"function": {"name": "b", "arguments": {}}, }]},
{"role": "tool", "tool_call_id": "call_0_0", "content": "r1"},
{"role": "tool", "tool_call_id": "call_0_1", "content": "r2"},
]),
("tm_ids_fallback_when_unpaired", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "a", "arguments": {}}},
{"function": {"name": "b", "arguments": {}}}]},
{"role": "tool", "tool_call_id": "call_0_0", "content": "r1"},
]),
("tm_ids_scan_nonadjacent_tools", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "a", "arguments": {}}},
{"function": {"name": "b", "arguments": {}}}]},
{"role": "user", "content": "and?"},
{"role": "tool", "tool_call_id": "aa", "content": "r1"},
{"role": "tool", "tool_call_id": "bb", "content": "r2"},
]),
("tm_paired_id_null_falls_back", [
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "a", "arguments": {}}}]},
{"role": "tool", "content": "no id"},
]),
("tm_tool_message_defaults", [
{"role": "tool", "content": "result"},
{"role": "tool", "tool_call_id": "t9", "content": "r"},
{"role": "tool"},
]),
("tm_mixed_conversation", [
{"role": "system", "content": "sys"},
{"role": "user", "content": "go"},
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "ls", "arguments": {"path": "."}}}]},
{"role": "tool", "tool_call_id": "call_2_0", "content": "files"},
{"role": "assistant", "content": "done"},
]),
]
for name, messages in cases:
add(name, "to_messages", {"messages": messages}, f(messages))
def case_from_response():
f = oct_mod.OpenAICompatTransport._from_openai_response
cases = [
("fr_plain_content", {"choices": [{"message": {"role": "assistant", "content": "hello"}}]}),
("fr_missing_choices", {}),
("fr_empty_choices", {"choices": []}),
("fr_missing_message", {"choices": [{}]}),
("fr_null_content_becomes_empty", {"choices": [{"message": {"content": None}}]}),
("fr_no_tool_calls_key", {"choices": [{"message": {"content": "x", "tool_calls": []}}]}),
("fr_null_tool_calls", {"choices": [{"message": {"content": "x", "tool_calls": None}}]}),
("fr_tool_call_string_args", {"choices": [{"message": {"tool_calls": [
{"id": "z", "type": "function",
"function": {"name": "run_tests", "arguments": "{\"path\": \"t\"}"}}]}}]}),
("fr_tool_call_malformed_args_raw", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "x", "arguments": "not-json"}}]}}]}),
("fr_tool_call_dict_args_kept", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "y", "arguments": {"a": 1}}}]}}]}),
("fr_tool_call_null_args_become_empty_obj", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "n", "arguments": None}}]}}]}),
("fr_tool_call_empty_string_args_raw", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "e", "arguments": ""}}]}}]}),
("fr_tool_call_list_args_kept", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "l", "arguments": [1, 2]}}]}}]}),
("fr_tool_call_id_only_when_present", {"choices": [{"message": {"tool_calls": [
{"function": {"name": "a", "arguments": "{}"}},
{"id": "", "function": {"name": "b", "arguments": "{}"}},
{"id": "c3", "function": {"name": "c", "arguments": "{}"}}]}}]}),
("fr_tool_call_missing_function", {"choices": [{"message": {"tool_calls": [{}]}}]}),
("fr_multiple_tool_calls", {"choices": [{"message": {"content": None, "tool_calls": [
{"id": "i1", "function": {"name": "a", "arguments": "{\"x\": 1}"}},
{"id": "i2", "function": {"name": "b", "arguments": {"y": [2]}}}]}}]}),
("fr_choices_null_becomes_default", {"choices": None}),
]
for name, resp in cases:
add(name, "from_response", {"resp": resp}, f(resp))
def case_post(server_base):
t = oct_mod.OpenAICompatTransport(server_base, "sekrit", "Prov")
def run(path, payload, bodies):
STUB.reset(bodies)
try:
result = t._post(path, payload)
return {"request": STUB.requests[0], "result": result}
except ValueError as e:
return {"request": STUB.requests[0], "error": str(e)}
payload = {"model": "glm-5.3-flash", "messages": [{"role": "user", "content": "hi"}], "stream": False}
add("post_roundtrip", "post",
{"path": "/chat/completions", "payload": payload, "bodies": [
json.dumps({"choices": [{"message": {"content": "ok"}}], "usage": {"prompt_tokens": 5, "completion_tokens": 7}})]},
run("/chat/completions", payload, [json.dumps({"choices": [{"message": {"content": "ok"}}], "usage": {"prompt_tokens": 5, "completion_tokens": 7}})]))
add("post_non_object_list", "post",
{"path": "/x", "payload": {"a": 1}, "bodies": ["[1, 2]"]},
run("/x", {"a": 1}, ["[1, 2]"]))
add("post_non_object_null", "post",
{"path": "/x", "payload": {}, "bodies": ["null"]},
run("/x", {}, ["null"]))
add("post_non_object_string", "post",
{"path": "/x", "payload": {}, "bodies": ['"text"']},
run("/x", {}, ['"text"']))
def case_chat(server_base):
cases = [
("chat_qualified_model_bare_on_wire", {
"model": "Verboo Code/glm-5.3-flash",
"messages": [{"role": "user", "content": "hi"}],
"tools": None, "options": None,
"responses": ['{"choices": [{"message": {"content": "ok"}}], "usage": {}}'],
}),
("chat_bare_model_unchanged", {
"model": "glm-5.3-flash",
"messages": [{"role": "user", "content": "hi"}],
"tools": None, "options": None,
"responses": ['{"choices": [{"message": {"content": "ok"}}]}'],
}),
("chat_tools_passthrough", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": [{"type": "function", "function": {"name": "ls", "description": "list", "parameters": {"type": "object"}}}],
"options": None,
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_empty_tools_omitted", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": [], "options": None,
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_options_subset", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": {"temperature": 0.7, "top_p": 0.9, "max_tokens": 1024, "seed": 7, "stop": ["END"]},
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_options_partial_and_empty", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": {"max_tokens": 0},
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_think_ignored", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None, "think": True,
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_think_string_ignored", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None, "think": "high",
"responses": ['{"choices": [{"message": {"content": "x"}}]}'],
}),
("chat_tool_call_roundtrip", {
"model": "Verboo Code/m1", "messages": [
{"role": "user", "content": "go"},
{"role": "assistant", "content": "", "tool_calls": [
{"function": {"name": "edit_file", "arguments": {"path": "/x.py"}}}]},
{"role": "tool", "tool_call_id": "call_1_0", "content": "done"},
],
"tools": [{"type": "function", "function": {"name": "edit_file"}}], "options": {"temperature": 0.2},
"responses": [json.dumps({"choices": [{"message": {"content": None, "tool_calls": [
{"id": "call_1_0", "function": {"name": "edit_file",
"arguments": json.dumps({"path": "/x.py"})}}]}}],
"usage": {"prompt_tokens": 100, "completion_tokens": 20}})],
}),
("chat_usage_string_and_float_coerced", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None,
"responses": ['{"choices": [{"message": {"content": "x"}}], "usage": {"prompt_tokens": "12", "completion_tokens": 3.9}}'],
}),
("chat_non_object_response_raises", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None,
"responses": ["[]"],
}),
("chat_two_calls_accumulate_usage", {
"model": "V/g", "messages": [{"role": "user", "content": "go"}],
"tools": None, "options": None, "calls": 2,
"responses": [
'{"choices": [{"message": {"content": "a"}}], "usage": {"prompt_tokens": 10, "completion_tokens": 4}}',
'{"choices": [{"message": {"content": "b"}}], "usage": {"prompt_tokens": 7, "completion_tokens": 9}}',
],
}),
]
for name, c in cases:
t = oct_mod.OpenAICompatTransport(server_base, "wire-key-1", "Prov")
STUB.reset(c["responses"])
want = {}
try:
for _ in range(c.get("calls", 1)):
result = t.chat(model=c["model"], messages=c["messages"], tools=c["tools"],
options=c["options"], think=c.get("think"))
want["result"] = result
except ValueError as e:
want["error"] = str(e)
want["requests"] = list(STUB.requests)
want["usage"] = {"calls": t.usage_calls, "prompt_tokens": t.usage_prompt_tokens,
"output_tokens": t.usage_output_tokens}
add(name, "chat",
{"base_url": "@STUB@", "api_key": "wire-key-1", "provider": "Prov",
"model": c["model"], "messages": c["messages"], "tools": c["tools"],
"options": c["options"], "think": c.get("think"), "calls": c.get("calls", 1),
"responses": c["responses"]},
want)
def case_scalars():
t = oct_mod.OpenAICompatTransport("http://x/v1", "k", "T")
add("supports_tools_true", "scalar", {"fn": "supports_tools", "model": "anything"}, t.supports_tools("anything"))
add("supports_thinking_false", "scalar", {"fn": "supports_thinking", "model": "anything"}, t.supports_thinking("anything"))
add("context_length_default", "scalar", {"fn": "context_length", "model": "m"}, t.context_length("m"))
t2 = oct_mod.OpenAICompatTransport("http://x/v1", "k", "T", context_window=400000)
add("context_length_custom", "scalar", {"fn": "context_length", "model": "m", "context_window": 400000}, t2.context_length("m"))
icp = oct_mod.is_cloud_pinned
pinned = [
("icp_cloud_provider", "Verboo Code/glm-5.3-flash", True),
("icp_ollama_prefix", "ollama/qwen2.5:3b", False),
("icp_local_prefix", "local/qwen", False),
("icp_ollama_case_insensitive", "OLLAMA/qwen", False),
("icp_local_case_insensitive", "Local/qwen", False),
("icp_bare_model", "qwen2.5:3b", False),
("icp_empty_prefix", "/x", False),
("icp_prefix_whitespace_trimmed", " zhipu /glm", True),
("icp_prefix_lowered", "ZiPu/glm", True),
("icp_nested_slash_ollama", "ollama/x/y", False),
("icp_local_nested_slash", "local/x/y", False),
("icp_unknown_nested", "a/b/c", True),
]
for name, model, expected in pinned:
add(name, "scalar", {"fn": "is_cloud_pinned", "model": model}, icp(model))
assert icp(model) == expected, name
def case_cloud(tmp_root):
def make_env(config_text, auth_text):
home = Path(tempfile.mkdtemp(dir=tmp_root))
cfg_dir = home / ".config" / "mimocode"
auth_dir = home / ".local" / "share" / "mimocode"
cfg_dir.mkdir(parents=True)
auth_dir.mkdir(parents=True)
if config_text is not None:
(cfg_dir / "mimocode.jsonc").write_text(config_text)
if auth_text is not None:
(auth_dir / "auth.json").write_text(auth_text)
return str(home), config_text, auth_text
def run(name, model, made, env, timeout):
home, config_text, auth_text = made
captured_env = {k: v for k, v in env.items()}
real_expand = os.path.expanduser
def fake_expand(p):
if p == "~":
return home
if p.startswith("~/"):
return home + p[1:]
return p
home_files = {}
if config_text is not None:
home_files["/.config/mimocode/mimocode.jsonc"] = config_text
if auth_text is not None:
home_files["/.local/share/mimocode/auth.json"] = auth_text
os.path.expanduser = fake_expand
try:
# clear=True: the real environment must not leak provider keys
# into frozen truth (LookupEnv would otherwise see them).
with mock.patch.dict(os.environ, captured_env, clear=True):
try:
t = oct_mod.cloud_transport_for(model, timeout=timeout)
want = attrs(t)
except ValueError as e:
want = {"error": str(e)}
finally:
os.path.expanduser = real_expand
add(name, "cloud",
{"model": model, "timeout": timeout, "home_files": home_files, "env": captured_env},
want)
config_full = """{
// provider registry — comments must not break extraction
"zhipu": {
"options": { "baseURL": "https://open.zhipu/v1" },
"models": {
"glm-5.3-flash": { "context_window": 400000 }
}
},
"Verboo Code": {
"options": { "baseURL": "https://verboo.example/v1" }
}
}
"""
run("cloud_full_config_env_key", "zhipu/glm-5.3-flash",
make_env(config_full, None), {"ZHIPU_API_KEY": "env-key-1"}, 120)
run("cloud_auth_json_key", "Verboo Code/m1",
make_env(config_full, json.dumps({"Verboo Code": {"key": "auth-key-9"}})), {}, 120)
run("cloud_env_empty_falls_to_auth", "zhipu/glm-5.3-flash",
make_env(config_full, json.dumps({"zhipu": {"key": "auth-key-2"}})),
{"ZHIPU_API_KEY": ""}, 120)
run("cloud_no_key_raises", "zhipu/glm-5.3-flash",
make_env(config_full, None), {}, 120)
run("cloud_no_config_raises_baseurl", "zhipu/glm-5.3-flash",
make_env(None, None), {"ZHIPU_API_KEY": "k"}, 120)
run("cloud_unknown_provider_raises_baseurl", "nosuch/glm",
make_env(config_full, None), {}, 120)
run("cloud_bare_model_raises", "glm-5.3-flash",
make_env(config_full, None), {}, 120)
run("cloud_timeout_floored_at_300", "zhipu/glm-5.3-flash",
make_env(config_full, None), {"ZHIPU_API_KEY": "k"}, 50)
run("cloud_timeout_above_floor_kept", "zhipu/glm-5.3-flash",
make_env(config_full, None), {"ZHIPU_API_KEY": "k"}, 500)
run("cloud_model_without_context_entry_default_window", "Verboo Code/m1",
make_env(config_full, None), {}, 120)
# baseURL beyond the 2000-char block window → not found
far_block = '{"big": {"options": {' + '"pad": "' + "x" * 2100 + '", ' + '"baseURL": "https://far/v1"}}}'
run("cloud_baseurl_beyond_block_window", "big/m",
make_env(far_block, None), {}, 120)
# -- main --------------------------------------------------------------------
def main():
out_path = ROOT / "src" / "go" / "openai_compat_transport" / "truth.json"
out_path.parent.mkdir(parents=True, exist_ok=True)
server, base = start_stub()
try:
case_ctor()
case_to_messages()
case_from_response()
case_post(base)
case_chat(base)
case_scalars()
tmp_root = tempfile.mkdtemp(prefix="oct_truth_")
case_cloud(tmp_root)
finally:
server.shutdown()
doc = {
"oracle": "src/rune/openai_compat_transport.py",
"generator": "scripts/gen_openai_compat_transport_truth.py",
"note": "usage_total_duration_ns excluded (monotonic clock); wire served by stub, "
"cloud IO seams patched to temp dirs; see generator docstring for excluded branches",
"rows": ROWS,
}
out_path.write_text(json.dumps(doc, indent=1) + "\n")
ops = {}
for r in ROWS:
ops[r["op"]] = ops.get(r["op"], 0) + 1
print(f"frozen {len(ROWS)} rows → {out_path}")
print("ops:", json.dumps(ops, sort_keys=True))
if __name__ == "__main__":
main()
+522
View File
@@ -0,0 +1,522 @@
#!/usr/bin/env python3
"""gen_self_modify_truth.py — freeze/replay parity truth for self_modify
(dichotomy wave, #2479 tranche).
Freeze mode (default): exercises EVERY public function of the LIVE Python
oracle (src/rune/self_modify.py) across every branch and records
(kernel_input, expected_kernel_output) pairs into
src/rust/self_modify/src/frozen_smod_truth.json.
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
delegation active and asserts both kernel-level (loader direct) and
facade-level (oracle functions delegate) parity against the frozen file.
Poisoned-truth law: freeze pins RUNE_SMOD_CORE=python BEFORE any import
(the RUNE_SM_CORE pin already belongs to shard_manager's core on master).
No-clock law: the wall clock is frozen by patching the `time` symbol in
self_modify, so every propose record carries the frozen `at` stamp and
replay is deterministic regardless of run date. The oracle's internal
tempfile.mkdtemp is likewise pinned to a case-local path.
Deterministic-fixture law: git repos are built with fixed identity and
fixed author/committer dates, so the seed commit SHA is identical across
generator runs, replay runs and the Rust battery — the SHA is frozen
LITERALLY, no placeholder. Machine-specific paths (repo dir, worktree dir,
stub dir) are frozen as @REPO@ / @WORKTREE@ / @STUBDIR@ placeholders and
substituted at replay time. Git-dependent scenarios declare their setup as
data ("worktree:<branch>", "commit_in_worktree", "dirty_repo", plus the
"stub" flag for the PATH-stubbed default gate command) so the generator,
the Python replay and the Rust battery run the SAME recipe.
Raise-parity rows freeze {"raises": "<oracle exception>"}: the Python
facade must raise the same exception; the Rust core (envelope law) must
surface an error (the loader raises RuntimeError — documented divergence
in exception TYPE only).
"""
import copy
import json
import os
import stat
import subprocess
import sys
import tempfile
import time
from datetime import datetime, timezone
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TRUTH_PATH = os.path.join(
REPO, "src", "rust", "self_modify", "src", "frozen_smod_truth.json"
)
REPLAY = "--replay" in sys.argv
os.environ["RUNE_SMOD_CORE"] = "python" # poisoned-truth law (freeze mode)
os.environ["RUNE_MEMORY_CORE"] = "python"
sys.path.insert(0, os.path.join(REPO, "src"))
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
import self_modify as oracle_mod # noqa: E402
if REPLAY:
del os.environ["RUNE_SMOD_CORE"] # facade must delegate to the Rust core
import smod_loader # noqa: E402 (kernel-level replay side)
FROZEN_AT = "2026-10-11T12:00:00Z"
FROZEN_EPOCH = int(datetime(2026, 10, 11, 12, 0, 0, tzinfo=timezone.utc).timestamp())
GIT_ENV_FIXED = {
"GIT_AUTHOR_DATE": "2026-10-11T12:00:00+00:00",
"GIT_COMMITTER_DATE": "2026-10-11T12:00:00+00:00",
}
MISSING_REPO = "/nonexistent-selfmod-truth/repo"
class FrozenTime:
"""time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) → FROZEN_AT."""
@staticmethod
def gmtime():
return time.gmtime(FROZEN_EPOCH)
@staticmethod
def strftime(fmt, t=None):
return time.strftime(fmt, t if t is not None else FrozenTime.gmtime())
oracle_mod.time = FrozenTime
class FrozenTempfile:
"""tempfile.mkdtemp(prefix="selfmod-") → a case-local, known path."""
ROOT = None
N = 0
@staticmethod
def mkdtemp(prefix=""):
FrozenTempfile.N += 1
p = os.path.join(FrozenTempfile.ROOT, f"{prefix}{FrozenTempfile.N:02d}")
os.makedirs(p)
return p
oracle_mod.tempfile = FrozenTempfile
# ── deterministic fixture + setup interpreter (shared recipe) ───────────────
def _git(root, *argv):
env = dict(os.environ, **GIT_ENV_FIXED)
return subprocess.run(["git", "-C", root, *argv], capture_output=True,
text=True, env=env)
def make_fixture(case_root):
"""The battery recipe — fixed identity, fixed dates, seed commit.
The resulting master SHA is machine-independent (frozen literally)."""
repo = os.path.join(case_root, "repo")
os.makedirs(repo)
_git(repo, "init", "-q", "-b", "master")
_git(repo, "config", "user.email", "t@t")
_git(repo, "config", "user.name", "t")
_git(repo, "config", "commit.gpgsign", "false")
with open(os.path.join(repo, "f.txt"), "w") as f:
f.write("v1\n")
_git(repo, "add", ".")
_git(repo, "commit", "-qm", "seed")
return repo
def base_sha(repo):
return _git(repo, "rev-parse", "master").stdout.strip()
def run_setup(repo, steps):
"""Declarative setup ops, executed identically by freeze, replay and
the Rust battery. Returns the worktree path (or None)."""
wt = None
for step in steps:
op, _, arg = step.partition(":")
if op == "worktree":
wt = os.path.join(os.path.dirname(repo), f"wt-{arg}")
r = _git(repo, "worktree", "add", wt, "-b", arg, "master")
assert r.returncode == 0, r.stderr
elif op == "commit_in_worktree":
with open(os.path.join(wt, "f.txt"), "w") as f:
f.write("v2\n")
r = _git(wt, "commit", "-qam", "c")
assert r.returncode == 0, r.stderr
elif op == "dirty_repo":
with open(os.path.join(repo, "f.txt"), "w") as f:
f.write("v9\n")
else:
raise ValueError(f"unknown setup op: {step}")
return wt
def make_stub_dir(case_root):
"""PATH stub: a python3 that deterministically 'runs the gate suite'
(the oracle's default gate command resolves through PATH)."""
stub = os.path.join(case_root, "stub")
os.makedirs(stub)
path = os.path.join(stub, "python3")
with open(path, "w") as f:
f.write("#!/bin/sh\necho stub-gates-green\nexit 0\n")
os.chmod(path, os.stat(path).st_mode | stat.S_IXUSR)
return stub
# ── scenario cases ───────────────────────────────────────────────────────────
class Case:
def __init__(self, name, group, steps, inp, stub=False):
self.name = name
self.group = group
self.steps = steps
self.inp = inp # (repo, wt, stub) → kernel input dict
self.stub = stub
def propose_cases():
return [
Case("propose-ok", "propose_change", [],
lambda r, w, s: {"repo": "@REPO@", "branch": "cand-ok",
"base": "master", "now": FROZEN_AT}),
Case("propose-base-head", "propose_change", [],
lambda r, w, s: {"repo": "@REPO@", "branch": "cand-head",
"base": "HEAD", "now": FROZEN_AT}),
Case("propose-unknown-base", "propose_change", [],
lambda r, w, s: {"repo": "@REPO@", "branch": "cand-bad",
"base": "no-such-ref", "now": FROZEN_AT}),
Case("propose-dup-branch", "propose_change", ["worktree:dup"],
lambda r, w, s: {"repo": "@REPO@", "branch": "dup",
"base": "master", "now": FROZEN_AT}),
Case("propose-missing-repo", "propose_change", [],
lambda r, w, s: {"repo": MISSING_REPO, "branch": "cand-gone",
"base": "master", "now": FROZEN_AT}),
]
def gates_cases():
return [
Case("gates-pass", "run_gates", [],
lambda r, w, s: {"worktree": "@REPO@",
"test_cmd": ["echo", "gates-green"]}),
Case("gates-fail-stderr", "run_gates", [],
lambda r, w, s: {"worktree": "@REPO@",
"test_cmd": ["sh", "-c", "echo boom 1>&2; exit 1"]}),
Case("gates-tail-2000", "run_gates", [],
lambda r, w, s: {"worktree": "@REPO@",
"test_cmd": ["sh", "-c", "seq 1 3000"]}),
Case("gates-out-then-err", "run_gates", [],
lambda r, w, s: {"worktree": "@REPO@",
"test_cmd": ["sh", "-c",
"echo o1; echo e1 1>&2; echo o2; exit 7"]}),
Case("gates-default-cmd", "run_gates", [],
lambda r, w, s: {"worktree": "@REPO@", "test_cmd": None},
stub=True),
]
def compare_cases():
def row(name, cand, base):
return Case("cb-" + name, "compare_baseline", [],
lambda r, w, s, c=cand, b=base:
{"candidate_result": c, "baseline_result": b})
return [
row("candidate-red", {"passed": False}, {"passed": True}),
row("candidate-red-baseline-red", {"passed": False}, {"passed": False}),
row("baseline-red", {"passed": True}, {"passed": False}),
row("both-green", {"passed": True}, {"passed": True}),
row("missing-passed-key", {}, {"passed": True}),
row("truthy-coercion", {"passed": 1}, {"passed": 0}),
row("explicit-null-passed", {"passed": None}, {"passed": True}),
]
def _proposal(branch, repo):
return {"proposed": True, "branch": branch, "worktree": "@WORKTREE@",
"base": "master", "rollback_ref": base_sha(repo), "at": FROZEN_AT}
def pod_cases():
return [
Case("pod-discard", "promote_or_discard", ["worktree:discard-1"],
lambda r, w, s: {"repo": "@REPO@", "proposal": _proposal("discard-1", r),
"verdict": {"promote": False, "reason": "test"}}),
Case("pod-promote-up-to-date", "promote_or_discard", ["worktree:podd-2"],
lambda r, w, s: {"repo": "@REPO@", "proposal": _proposal("podd-2", r),
"verdict": {"promote": True, "reason": "test"}}),
Case("pod-promote-real-merge", "promote_or_discard",
["worktree:podd-3", "commit_in_worktree"],
lambda r, w, s: {"repo": "@REPO@", "proposal": _proposal("podd-3", r),
"verdict": {"promote": True, "reason": "test"}}),
Case("pod-merge-fail", "promote_or_discard",
["worktree:podd-4", "commit_in_worktree", "dirty_repo"],
lambda r, w, s: {"repo": "@REPO@", "proposal": _proposal("podd-4", r),
"verdict": {"promote": True, "reason": "test"}}),
Case("pod-discard-ghost", "promote_or_discard", [],
lambda r, w, s: {"repo": "@REPO@", "proposal": _proposal("ghost-branch", r),
"verdict": {"promote": False, "reason": "test"}}),
Case("pod-missing-branch-key", "promote_or_discard", [],
lambda r, w, s: {"repo": "@REPO@",
"proposal": {k: v for k, v in _proposal("keyless", r).items()
if k != "branch"},
"verdict": {"promote": True}}),
]
def rollback_cases():
return [
Case("rb-ok-after-dirty", "rollback", ["dirty_repo"],
lambda r, w, s: {"repo": "@REPO@", "rollback_ref": base_sha(r)}),
Case("rb-ref-master", "rollback", ["dirty_repo"],
lambda r, w, s: {"repo": "@REPO@", "rollback_ref": "master"}),
Case("rb-bad-ref", "rollback", [],
lambda r, w, s: {"repo": "@REPO@", "rollback_ref": "no-such-ref"}),
Case("rb-missing-repo", "rollback", [],
lambda r, w, s: {"repo": MISSING_REPO, "rollback_ref": "master"}),
]
def monitor_cases():
def row(name, proposal):
return Case("mon-" + name, "monitor", [],
lambda r, w, s, p=proposal: {"proposal": p})
return [
row("full", {"branch": "b", "rollback_ref": "abc123"}),
row("empty", {}),
row("partial", {"branch": "only-b"}),
row("extra-keys", {"branch": "b", "rollback_ref": "r", "extra": 1}),
row("nonstring-branch", {"branch": 7, "rollback_ref": None}),
]
GROUPS = [
("propose_change", propose_cases()),
("run_gates", gates_cases()),
("compare_baseline", compare_cases()),
("promote_or_discard", pod_cases()),
("rollback", rollback_cases()),
("monitor", monitor_cases()),
]
FACADE_CALL = {
"propose_change": lambda i: oracle_mod.propose_change(
i["repo"], i["branch"], i["base"]),
"run_gates": lambda i: oracle_mod.run_gates(i["worktree"], i["test_cmd"]),
"compare_baseline": lambda i: oracle_mod.compare_baseline(
i["candidate_result"], i["baseline_result"]),
"promote_or_discard": lambda i: oracle_mod.promote_or_discard(
i["repo"], i["proposal"], i["verdict"]),
"rollback": lambda i: oracle_mod.rollback(i["repo"], i["rollback_ref"]),
"monitor": lambda i: oracle_mod.monitor(i["proposal"]),
}
KERNEL_CALL = {
"propose_change": lambda i: smod_loader.propose_change(
i["repo"], i["branch"], i["base"], i["now"]),
"run_gates": lambda i: smod_loader.run_gates(i["worktree"], i["test_cmd"]),
"compare_baseline": lambda i: smod_loader.compare_baseline(
i["candidate_result"], i["baseline_result"]),
"promote_or_discard": lambda i: smod_loader.promote_or_discard(
i["repo"], i["proposal"], i["verdict"]),
"rollback": lambda i: smod_loader.rollback(i["repo"], i["rollback_ref"]),
"monitor": lambda i: smod_loader.monitor(i["proposal"]),
}
# ── placeholder plumbing ─────────────────────────────────────────────────────
def substitute(value, repo, wt, stub):
"""Machine paths → frozen placeholders (kernel input side)."""
s = json.dumps(value)
for actual, ph in ((repo, "@REPO@"), (wt, "@WORKTREE@"), (stub, "@STUBDIR@")):
if actual:
s = s.replace(actual, ph)
return json.loads(s)
def de_substitute(value, repo, wt, stub):
"""Frozen placeholders → live paths (call side)."""
s = json.dumps(value)
for actual, ph in ((repo, "@REPO@"), (wt, "@WORKTREE@"), (stub, "@STUBDIR@")):
if actual:
s = s.replace(ph, actual)
return json.loads(s)
def normalize(out, repo, wt, stub):
"""Actual output paths → placeholders (kernel output side). The
worktree a propose call created internally is captured from the
output itself — neither side can know it in advance."""
reps = [t for t in ((wt, "@WORKTREE@") if wt else None,
(repo, "@REPO@") if repo else None,
(stub, "@STUBDIR@") if stub else None) if t]
if isinstance(out, dict) and isinstance(out.get("worktree"), str):
reps.append((out["worktree"], "@WORKTREE@"))
s = json.dumps(out, sort_keys=True)
for actual, ph in reps:
if actual and actual != "/":
s = s.replace(actual, ph)
return json.loads(s)
def _catch(fn, *a):
try:
return fn(*a)
except FileNotFoundError:
return {"__raised__": "FileNotFoundError"}
except KeyError as e:
return {"__raised__": f"KeyError: {e}"}
except RuntimeError:
return {"__raised__": "RuntimeError"}
class _PathEnv:
"""Set PATH to the stub dir for the duration of one call."""
def __init__(self, stub):
self.stub = stub
def __enter__(self):
self.old = os.environ.get("PATH")
os.environ["PATH"] = self.stub + os.pathsep + (self.old or "")
def __exit__(self, *exc):
if self.old is not None:
os.environ["PATH"] = self.old
return False
def run_scenario(case, tmp_root, call):
"""One scenario, identical for freeze and both replay tiers:
fresh fixture → setup → substituted input → call → normalized output."""
case_root = tempfile.mkdtemp(prefix=f"smod_{case.name}_", dir=tmp_root)
repo = make_fixture(case_root)
FrozenTempfile.ROOT = case_root
FrozenTempfile.N = 0
wt = run_setup(repo, case.steps)
stub = make_stub_dir(case_root) if case.stub else None
live_wt = wt or os.path.join(case_root, "wt-input")
live_stub = stub or os.path.join(case_root, "stub-unused")
inp = substitute(case.inp(repo, wt, stub), repo, wt, stub)
inp_copy = copy.deepcopy(inp) # deep-copy law: inputs frozen before calls
live = de_substitute(inp_copy, repo, live_wt, live_stub)
if case.stub:
with _PathEnv(live_stub):
out = _catch(call, live)
else:
out = _catch(call, live)
if isinstance(out, dict) and out.get("__raised__"):
return inp, {"raises": out["__raised__"]}
return inp, normalize(out, repo, live_wt, stub)
def main():
frozen = None
if REPLAY:
with open(TRUTH_PATH) as f:
frozen = json.load(f)["cases"]
tmp_root = tempfile.mkdtemp(prefix="smod_truth_")
groups = {}
failures = 0
total = 0
for group, cases in GROUPS:
recs = []
def rec(name, inp, expected, case=None, _r=recs, _g=group):
entry = {"name": name, "input": inp, "expected": expected}
if case is not None:
entry["setup"] = case.steps
entry["stub"] = case.stub
if REPLAY:
old = next(c for c in frozen[_g] if c["name"] == name)
for key in ("input", "expected"):
if json.dumps(entry[key], sort_keys=True) != json.dumps(
old[key], sort_keys=True):
print(f"FACADE DIVERGE [{_g}] {name} {key}")
print(" got: ", json.dumps(entry[key], sort_keys=True)[:600])
print(" want:", json.dumps(old[key], sort_keys=True)[:600])
_r.append({"name": name, "diverged": key})
return
_r.append(entry)
for case in cases:
total += 1
inp, want = run_scenario(case, tmp_root,
lambda i, c=case: _catch(
FACADE_CALL[c.group], i))
rec(case.name, inp, want, case)
if REPLAY:
old = next(c for c in frozen[case.group] if c["name"] == case.name)
repo, wt, stub = kernel_paths(case, tmp_root, old)
call = lambda: KERNEL_CALL[case.group](
de_substitute(copy.deepcopy(old["input"]), repo, wt, stub))
if old.get("stub"):
with _PathEnv(stub):
kout = _catch(lambda: call())
else:
kout = _catch(call)
kwant = old["expected"]
if not (isinstance(kout, dict) and kout.get("__raised__")):
kout = normalize(kout, repo, wt, stub)
if isinstance(kwant, dict) and kwant.get("raises"):
ok = kout == {"__raised__": kwant["raises"]} or (
kout == {"__raised__": "RuntimeError"})
else:
ok = json.dumps(kout, sort_keys=True) == json.dumps(
kwant, sort_keys=True)
if not ok:
failures += 1
print(f"KERNEL DIVERGE [{case.group}] {case.name}")
print(" got: ", json.dumps(kout, sort_keys=True)[:600])
print(" want:", json.dumps(kwant, sort_keys=True)[:600])
groups[group] = recs
if REPLAY:
facade_div = sum(1 for v in groups.values() for c in v if "diverged" in c)
print(f"replay: facade {total - facade_div}/{total}, "
f"kernel {total - failures}/{total}")
return 1 if (failures or facade_div) else 0
doc = {
"frozen": datetime.now(timezone.utc).isoformat(),
"oracle": "src/rune/self_modify.py",
"pin": "RUNE_SMOD_CORE=python",
"note": "Frozen via scripts/gen_self_modify_truth.py against the live "
"Python oracle; wall clock frozen at " + FROZEN_AT + "; git "
"fixtures use fixed identity+dates so the seed SHA is "
"literal. @REPO@/@WORKTREE@/@STUBDIR@ are placeholders "
"substituted at replay. Battery embeds this VERBATIM.",
"cases": groups,
}
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
with open(TRUTH_PATH, "w") as f:
json.dump(doc, f, indent=1, sort_keys=True)
f.write("\n")
print(f"frozen {total} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
return 0
def kernel_paths(case, tmp_root, old_entry):
"""(repo, wt, stub) for kernel-level replay of one frozen entry."""
case_root = tempfile.mkdtemp(prefix=f"smod_kernel_{case.name}_", dir=tmp_root)
repo = make_fixture(case_root)
wt = run_setup(repo, old_entry.get("setup", []))
stub = None
if old_entry.get("stub"):
stub = make_stub_dir(case_root)
live_wt = wt or os.path.join(case_root, "wt-input")
live_stub = stub or os.path.join(case_root, "stub-unused")
return repo, live_wt, live_stub
if __name__ == "__main__":
sys.exit(main())
+349
View File
@@ -0,0 +1,349 @@
#!/usr/bin/env python3
"""Freeze spine_language oracle truth for the Rust port battery
(dichotomy wave #2479 tranche, module spine_language).
Frozen surface (all 5 module functions, every branch):
_classification_sides() — measured-map read + degrade-to-{}
_place_languages(doc) — kind defaults / measured authority /
name hints / subsystem majority /
unclassified / unassessed / placed count
_contract_discoveries() — closed-contract gather + kind filters
_normalize_discovery_ids(doc) — legacy rename + demotion + tag filter
_apply_contract_discoveries(doc) — high-water merge write-back
Store reads (store/rebase/classification.json,
store/scaffold/current.json) are materialized per case under a temp
root (sl.ROOT monkeypatched) so the frozen truth carries its fixtures:
each case records `files` {relpath: content}; the Rust battery
materializes the same tree. Wall-clock `observed_at` stamps (from
structural_store.add_entity) are masked on BOTH sides — the only
non-deterministic field in the module. RUNE_SL_CORE=python pinned
(POISONED-TRUTH LAW: freeze the oracle, never a kernel). Inputs
deep-copied before capture.
Output: src/rust/spine_language/frozen_sl_truth.json — replayed
VERBATIM by src/rust/spine_language/src/branch_battery.rs.
"""
from __future__ import annotations
import copy
import json
import os
import sys
import tempfile
from pathlib import Path
os.environ["RUNE_SL_CORE"] = "python" # freeze the ORACLE, never a kernel
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from rune import spine_language as sl # noqa: E402
cases: list[dict] = []
def mask(obj):
"""Drop wall-clock observed_at stamps recursively (the module's only
nondeterministic output field)."""
if isinstance(obj, dict):
return {k: mask(v) for k, v in obj.items() if k != "observed_at"}
if isinstance(obj, list):
return [mask(v) for v in obj]
return obj
def freeze(fn_name: str, name: str, *, files: dict | None = None,
doc: dict | None = None, run):
"""Materialize fixture files under a temp root, run the oracle fn with
sl.ROOT pointed there, record the case."""
root = Path(tempfile.mkdtemp(prefix="sl-truth-"))
for rel, content in (files or {}).items():
p = root / rel
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(content)
original_root = sl.ROOT
sl.ROOT = root
try:
doc_in = copy.deepcopy(doc) if doc is not None else None
expected = run(copy.deepcopy(doc) if doc is not None else None)
finally:
sl.ROOT = original_root
cases.append({
"fn": fn_name,
"name": name,
"files": files or {},
"doc": doc_in,
"expected": mask(expected),
})
CLASSIFICATION = json.dumps({
"spine": {"side": "RUST"},
"spine_language": {"side": "RUST"},
"behavior": {"side": "RUST"},
"identity_anchors": {"side": "RUST"},
"persona_definition": {"side": "RUST"},
"persona_gate": {"side": "RUST"},
"prompt_sync": {"side": "GO"},
"voice_hybrid": {"side": "GO"},
"voice_sync": {"side": "GO"},
"rune_tui": {"side": "TUI-LAST"},
"testkit": {"side": "TEST-INFRA"},
"legacy_tool": {"side": "RETIRES"},
"lowercase_mod": {"side": "rust"},
"num_side": {"side": 5},
"no_side": {},
"not_a_dict": "skip me",
}, indent=1)
def cls_file(extra: dict | None = None) -> dict:
data = json.loads(CLASSIFICATION)
data.update(extra or {})
return {"store/rebase/classification.json": json.dumps(data)}
# ── _classification_sides ────────────────────────────────────────────────
freeze("classification_sides", "measured-map", files=cls_file(),
run=lambda _doc: sl._classification_sides())
freeze("classification_sides", "missing-file-degrades", files={},
run=lambda _doc: sl._classification_sides())
freeze("classification_sides", "malformed-json-degrades",
files={"store/rebase/classification.json": "{oops"},
run=lambda _doc: sl._classification_sides())
freeze("classification_sides", "non-dict-top-level-degrades",
files={"store/rebase/classification.json": "[1, 2, 3]"},
run=lambda _doc: sl._classification_sides())
# ── _place_languages ─────────────────────────────────────────────────────
def run_place(doc):
placed = sl._place_languages(doc)
return {"doc": doc, "placed": placed}
def ent(eid, kind, **kw):
e = {"id": eid, "kind": kind}
e.update(kw)
return e
freeze("place_languages", "kind-defaults", files=cls_file(), doc={
"entities": [
ent("rust-crate:cognition_core", "rust-crate"),
ent("go-surface:rune-entry", "go-surface"),
ent("workflow:ci.yml", "workflow"),
ent("config:settings.toml", "config"),
ent("contract:BUILDER-x", "contract"),
ent("deployment_surface:prod", "deployment_surface"),
ent("module:zzz_unknown", "module"),
ent("widget:odd", "widget"),
]}, run=run_place)
freeze("place_languages", "measured-authoritative", files=cls_file(), doc={
"entities": [
ent("module:spine", "module"),
ent("module:behavior", "module"),
ent("module:prompt_sync", "module"),
ent("module:rune_tui", "module"),
ent("module:testkit", "module"),
ent("module:legacy_tool", "module"),
ent("module:lowercase_mod", "module"),
ent("module:num_side", "module"),
ent("module:no_side", "module"),
ent("module:totally_unknown_name", "module"),
]}, run=run_place)
freeze("place_languages", "hints-only-when-unmeasured", files=cls_file(), doc={
"entities": [
ent("module:server_farm", "module"),
ent("module:memory_drift", "module"),
ent("module:tui_widgets", "module"),
ent("module:dispatch_tui", "module"),
ent("module:watch_server", "module"),
]}, run=run_place)
freeze("place_languages", "existing-language-overwritten", files=cls_file(), doc={
"entities": [
ent("module:spine", "module", identities={"language": "go"}),
ent("widget:odd", "widget", identities={"other": "x"}),
ent("bare-no-identities", "widget"),
]}, run=run_place)
freeze("place_languages", "subsystem-majority", files=cls_file(), doc={
"entities": [
ent("subsystem:identity", "subsystem"),
ent("subsystem:allrust", "subsystem"),
ent("subsystem:allgo", "subsystem"),
ent("subsystem:unassessed", "subsystem"),
*[ent(f"module:{m}", "module") for m in (
"spine", "behavior", "identity_anchors", # 3 rust
"prompt_sync", "voice_hybrid", # 2 go
"zzz_unknown")], # 1 unmeasured
],
"relationships": [
*[{"type": "belongs_to", "from": f"module:{m}",
"to": "subsystem:identity", "evidence_refs": []} for m in (
"spine", "behavior", "identity_anchors",
"prompt_sync", "voice_hybrid", "zzz_unknown")],
{"type": "belongs_to", "from": "module:spine",
"to": "subsystem:allrust", "evidence_refs": []},
{"type": "belongs_to", "from": "module:prompt_sync",
"to": "subsystem:allgo", "evidence_refs": []},
]}, run=run_place)
freeze("place_languages", "no-entities-key", files=cls_file(), doc={
"schema_version": 1}, run=run_place)
# ── _contract_discoveries ────────────────────────────────────────────────
def scaffold_doc() -> dict:
return {
"entities": [
{"id": "contract:C1", "kind": "contract", "phase": "closed",
"ext": {"contract_quality": {
"expected_new_observed": ["rust-crate:kernel_x",
"config:app.yml",
"bogus-kind:x",
"suite"],
"candidate_corrections": [
{"entity": "suite"},
{"entity": "workflow:sync.yml"},
{"entity": "bogus-kind:y"},
{"entity": ""},
]}}},
{"id": "contract:C2", "kind": "contract", "phase": "building",
"ext": {"contract_quality": {
"expected_new_observed": ["rust-crate:never"]}}},
{"id": "contract:C3", "kind": "contract", "phase": "closed"},
{"id": "contract:C4", "kind": "contract",
"ext": {"contract_quality": {
"expected_new_observed": ["rust-crate:also-never"]}}},
{"id": "module:plain", "kind": "module", "phase": "closed"},
]}
def run_discoveries(_doc):
got = sl._contract_discoveries()
return json.loads(json.dumps(got)) # tuples -> arrays for JSON
def scaffold_files() -> dict:
return {"store/scaffold/current.json": json.dumps(scaffold_doc())}
freeze("contract_discoveries", "closed-contract-gather",
files=scaffold_files(), run=run_discoveries)
freeze("contract_discoveries", "missing-store", files={}, run=run_discoveries)
freeze("contract_discoveries", "malformed-store",
files={"store/scaffold/current.json": "{oops"}, run=run_discoveries)
freeze("contract_discoveries", "no-entities-key",
files={"store/scaffold/current.json": json.dumps({"schema_version": 1})},
run=run_discoveries)
# ── _normalize_discovery_ids ─────────────────────────────────────────────
def run_normalize(doc):
renamed = sl._normalize_discovery_ids(doc)
return {"doc": doc, "renamed": renamed}
freeze("normalize_discovery_ids", "legacy-module-renamed-and-demoted",
files=scaffold_files(), doc={
"entities": [
{"id": "src/rune/claim_audit.py", "kind": "module",
"maturity": "PRESENT", "tags": ["code", "keep"],
"evidence_refs": ["contract-discovery:old"]},
],
"relationships": [
{"type": "depends_on", "from": "src/rune/claim_audit.py",
"to": "module:other", "evidence_refs": []},
{"type": "depends_on", "from": "module:x",
"to": "src/rune/claim_audit.py", "evidence_refs": []},
]}, run=run_normalize)
freeze("normalize_discovery_ids", "observed-retains-present",
files={"store/scaffold/current.json": json.dumps({
"entities": [{"id": "contract:C1", "kind": "contract",
"phase": "closed",
"ext": {"contract_quality": {
"expected_new_observed": [
"construction-surface:src/rune/kept.py"]}}}]})},
doc={"entities": [
{"id": "src/rune/kept.py", "kind": "module", "maturity": "PRESENT",
"tags": ["code"], "evidence_refs": ["contract-discovery:C1"]},
]}, run=run_normalize)
freeze("normalize_discovery_ids", "already-surface-demotion-and-tag-filter",
files=scaffold_files(), doc={
"entities": [
{"id": "construction-surface:foo", "kind": "construction-surface",
"maturity": "PRESENT", "tags": ["code", "keep"],
"evidence_refs": []},
]}, run=run_normalize)
freeze("normalize_discovery_ids", "no-discovery-ref-no-rename",
files=scaffold_files(), doc={
"entities": [
{"id": "src/rune/plain.py", "kind": "module",
"maturity": "PRESENT", "tags": [], "evidence_refs": ["spine"]},
{"id": "src/rune/noref.py", "kind": "module",
"maturity": "PRESENT", "tags": []},
{"id": "qualified:still.py", "kind": "module",
"maturity": "PRESENT",
"evidence_refs": ["contract-discovery:old"]},
]}, run=run_normalize)
# ── _apply_contract_discoveries ──────────────────────────────────────────
def run_apply(doc):
placed = sl._apply_contract_discoveries(doc)
return {"doc": doc, "placed": placed}
freeze("apply_contract_discoveries", "observed-and-candidate-writeback",
files=scaffold_files(), doc={"entities": [], "relationships": []},
run=run_apply)
freeze("apply_contract_discoveries", "high-water-merge",
files=scaffold_files(), doc={
"entities": [
{"id": "rust-crate:kernel_x", "kind": "rust-crate",
"maturity": "DECLARED", "tags": ["seed"],
"identities": {"keep": "me"}, "evidence_refs": ["seed-ref"]},
{"id": "config:app.yml", "kind": "config",
"maturity": "WIRED", "tags": [],
"evidence_refs": []},
], "relationships": []}, run=run_apply)
freeze("apply_contract_discoveries", "double-prefix-quirk",
files={"store/scaffold/current.json": json.dumps({
"entities": [{"id": "contract:C1", "kind": "contract",
"phase": "closed",
"ext": {"contract_quality": {
"expected_new_observed": [
"construction-surface:foo"]}}}]})},
doc={"entities": [], "relationships": []}, run=run_apply)
freeze("apply_contract_discoveries", "empty-discoveries-noop",
files={"store/scaffold/current.json": json.dumps({
"entities": [{"id": "contract:C2", "kind": "contract",
"phase": "building"}]})},
doc={"entities": [], "relationships": []}, run=run_apply)
freeze("apply_contract_discoveries", "missing-store-noop", files={},
doc={"entities": [], "relationships": []}, run=run_apply)
truth = {
"frozen": "spine_language doc-level transforms (all 5 fns, every branch; "
"observed_at masked both sides; store fixtures carried per case)",
"oracle": "src/rune/spine_language.py",
"pin": "RUNE_SL_CORE=python",
"cases": cases,
}
out = Path(__file__).resolve().parents[1] / "src" / "rust" / "spine_language" / "frozen_sl_truth.json"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(truth, indent=1) + "\n")
print(f"froze {len(cases)} cases -> {out}")
+727
View File
@@ -0,0 +1,727 @@
#!/usr/bin/env python3
"""gen_testing_mode_truth.py — freeze/replay parity truth for testing_mode
(dichotomy wave, #2479 tranche).
Freeze mode (default): exercises EVERY public function of the LIVE Python
oracle (src/rune/testing_mode.py) across every branch and records
(kernel_input, expected_kernel_output) pairs into
src/rust/testing_mode/src/frozen_tm_truth.json.
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
delegation active and asserts both kernel-level (loader direct) and
facade-level (oracle functions delegate) parity against the frozen file.
Poisoned-truth law: freeze pins RUNE_TM_CORE=python BEFORE any import (a
no-op pre-port, load-bearing post-port — the facade must never delegate
into the kernel while truth is being frozen).
No-clock law: the wall clock is frozen by patching the `time` symbol in
testing_mode, so every kernel payload carries its own `now`/`now_ts` and
replay is deterministic regardless of run date.
Boundary law: the session FILE IO (_load hit branch, save), the
observations.jsonl append/rewrite, the forge adapter call, the ledger
read (outstanding/record_real_world) and the registry IO all stay in the
Python facade — the kernel is a pure function of their results. The
`load`, `save` and `forge_dry_run` truth groups are facade-only (no
kernel symbol) and are skipped by the kernel-level replay pass.
Ledger/registry law: behaviours and registry are stubbed with
deterministic fakes (the oracle treats them as duck-typed boundaries);
their canned responses are deep-copied into the kernel inputs BEFORE the
oracle runs (the oracle mutates the session state in place).
"""
import copy
import json
import os
import sys
import tempfile
from pathlib import Path
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TRUTH_PATH = os.path.join(REPO, "src", "rust", "testing_mode", "src", "frozen_tm_truth.json")
REPLAY = "--replay" in sys.argv
os.environ["RUNE_TM_CORE"] = "python" # poisoned-truth law
sys.path.insert(0, os.path.join(REPO, "src"))
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
import testing_mode as oracle_mod # noqa: E402
if REPLAY:
from rune import tm_loader # noqa: E402 (kernel-level replay side)
# replay law: the facade pass must exercise the LIVE Rust delegation
# path, so the pin is lifted AFTER import (the facade reads the env
# at call time). The kernel pass calls the loader directly.
del os.environ["RUNE_TM_CORE"]
FROZEN_TS = 1791806400 # 2026-10-12T12:00:00Z
FROZEN_NOW = "2026-10-12T12:00:00Z"
class FakeTime:
@staticmethod
def strftime(fmt, t):
import time as _t
return _t.strftime(fmt, t)
@staticmethod
def gmtime():
import time as _t
return _t.gmtime(FROZEN_TS)
@staticmethod
def time():
return FROZEN_TS
oracle_mod.time = FakeTime
# ── deterministic boundary stubs ─────────────────────────────────────────────
class FakeBehaviours:
"""Duck-typed BehaviourLedger stub — canned outstanding/records."""
def __init__(self, outstanding=None, rec=None):
self.outstanding_value = outstanding or []
self.rec = rec or {"behaviour_id": "behaviour:T-1", "state": "USER_CONFIRMED"}
self.outstanding_kwargs = None
def outstanding(self, *, component=None, project=None):
self.outstanding_kwargs = {"component": component, "project": project}
return copy.deepcopy(self.outstanding_value)
def record_real_world(self, behaviour_id, *, passed, detail="",
confirmed_by="user", generation=""):
rec = dict(self.rec)
rec["behaviour_id"] = rec.get("behaviour_id", f"behaviour:{behaviour_id}")
return rec
class FakeRegistry:
def __init__(self, entries):
self._entries = entries
self.set_calls = []
self.saved = False
def all_projects(self):
return copy.deepcopy(self._entries)
def set_outstanding_validations(self, project_id, count):
self.set_calls.append((project_id, count))
def save(self):
self.saved = True
class FakeLedger:
"""Patched-in BehaviourLedger — summary canned per location."""
summaries = {}
def __init__(self, loc):
self.loc = loc
def summary(self):
return copy.deepcopy(FakeLedger.summaries.get(str(self.loc),
{"outstanding": 0, "states": {}}))
def fresh_session_dir():
return tempfile.mkdtemp(prefix="tm_truth_")
def fresh_state():
s = oracle_mod.TestingSession(fresh_session_dir())
return copy.deepcopy(s.state)
# ── case helpers ─────────────────────────────────────────────────────────────
def rec_kernel(rec, name, inp, expected, oracle_ret=None):
"""Record a kernel case; inputs deep-copied BEFORE oracle mutation."""
rec(name, copy.deepcopy(inp), oracle_ret, expected)
def rec_facade(rec, name, inp, expected, oracle_ret=None):
rec(name, inp, oracle_ret, expected)
# ── default_state (kernel) ───────────────────────────────────────────────────
def _ds_case(rec, name, project, scope_component):
d = fresh_session_dir()
s = oracle_mod.TestingSession(d, project=project, scope_component=scope_component)
rec_kernel(rec, name,
{"project": project, "scope_component": scope_component, "now": FROZEN_NOW},
{"state": s.state})
def c_ds_default(rec):
_ds_case(rec, "default-no-scope", "p", None)
def c_ds_scope(rec):
_ds_case(rec, "default-with-scope", "p", "component:shared")
def c_ds_empty_project(rec):
_ds_case(rec, "default-empty-project", "", "component:x")
# ── next_item (kernel) ───────────────────────────────────────────────────────
def _next_case(rec, name, state_over, outstanding, expect_item):
d = fresh_session_dir()
s = oracle_mod.TestingSession(d, project="p")
s.state.update(copy.deepcopy(state_over))
state_before = copy.deepcopy(s.state)
beh = FakeBehaviours(outstanding=outstanding)
item = s.next_item(beh)
assert beh.outstanding_kwargs == {"component": state_before.get("scope_component"),
"project": state_before.get("project") or None}
rec_kernel(rec, name,
{"state": state_before, "outstanding": outstanding},
{"state": s.state, "item": item},
oracle_ret=item)
def c_next_paused(rec):
_next_case(rec, "next-paused-null", {"paused": True},
[{"behaviour_id": "behaviour:T-1"}], None)
def c_next_empty(rec):
_next_case(rec, "next-empty-queue", {}, [], None)
def c_next_first(rec):
_next_case(rec, "next-first-of-two", {},
[{"behaviour_id": "behaviour:T-1", "state": "UNVALIDATED"},
{"behaviour_id": "behaviour:T-2", "state": "RETEST_REQUIRED"}],
{"behaviour_id": "behaviour:T-1", "state": "UNVALIDATED"})
def c_next_overwrites_current(rec):
_next_case(rec, "next-overwrites-current-item", {"current_item": "behaviour:OLD"},
[{"behaviour_id": "behaviour:T-2"}], {"behaviour_id": "behaviour:T-2"})
def c_next_empty_clears_current(rec):
_next_case(rec, "next-empty-clears-current", {"current_item": "behaviour:T-1"},
[], None)
# ── record (kernel) ──────────────────────────────────────────────────────────
def _record_case(rec, name, completed_pre, behaviour_id, passed, rec_state):
d = fresh_session_dir()
s = oracle_mod.TestingSession(d, project="p")
s.state["completed_this_session"] = copy.deepcopy(completed_pre)
state_before = copy.deepcopy(s.state)
beh = FakeBehaviours(rec={"behaviour_id": f"behaviour:{behaviour_id}",
"state": rec_state})
ret = s.record(beh, behaviour_id, passed=passed, detail="checked")
rec_kernel(rec, name,
{"state": state_before,
"rec": ret, "passed": passed, "now": FROZEN_NOW},
{"state": s.state},
oracle_ret={"behaviour_id": ret["behaviour_id"], "state": ret["state"]})
def c_record_pass(rec):
_record_case(rec, "record-pass-appends", [], "T-1", True, "USER_CONFIRMED")
def c_record_fail(rec):
_record_case(rec, "record-fail-appends", [], "T-2", False, "TEST_FAILED")
def c_record_appends_to_existing(rec):
pre = [{"behaviour_id": "behaviour:T-0", "passed": True, "at": "2026-10-12T11:00:00Z"}]
_record_case(rec, "record-appends-preserves-order", pre, "T-1", True, "USER_CONFIRMED")
# ── pause / resume (kernel) ──────────────────────────────────────────────────
def _pause_case(rec, name, paused_pre):
d = fresh_session_dir()
s = oracle_mod.TestingSession(d, project="p")
s.state["paused"] = paused_pre
state_before = copy.deepcopy(s.state)
s.pause()
rec_kernel(rec, name,
{"state": state_before},
{"state": s.state})
def _resume_case(rec, name, paused_pre):
d = fresh_session_dir()
s = oracle_mod.TestingSession(d, project="p")
s.state["paused"] = paused_pre
state_before = copy.deepcopy(s.state)
s.resume()
rec_kernel(rec, name,
{"state": state_before},
{"state": s.state})
def c_pause_from_active(rec):
_pause_case(rec, "pause-from-active", False)
def c_pause_already_paused(rec):
_pause_case(rec, "pause-idempotent", True)
def c_resume_from_paused(rec):
_resume_case(rec, "resume-from-paused", True)
def c_resume_already_active(rec):
_resume_case(rec, "resume-idempotent", False)
# ── note_blocker (kernel) ────────────────────────────────────────────────────
def _blocker_case(rec, name, blockers_pre, note):
d = fresh_session_dir()
s = oracle_mod.TestingSession(d, project="p")
s.state["blockers"] = copy.deepcopy(blockers_pre)
state_before = copy.deepcopy(s.state)
s.note_blocker("behaviour:T-9", note)
rec_kernel(rec, name,
{"state": state_before,
"behaviour_id": "behaviour:T-9", "note": note, "now": FROZEN_NOW},
{"state": s.state})
def c_blocker_normal(rec):
_blocker_case(rec, "blocker-normal", [], "blocked on upstream")
def c_blocker_truncates(rec):
_blocker_case(rec, "blocker-note-truncated-200", [], "x" * 260)
def c_blocker_appends(rec):
pre = [{"behaviour_id": "behaviour:T-1", "note": "old", "at": "2026-10-12T11:00:00Z"}]
_blocker_case(rec, "blocker-appends-preserves-order", pre, "new")
# ── record_observation (kernel) ──────────────────────────────────────────────
def _obs_case(rec, name, **kw):
d = fresh_session_dir()
obs = oracle_mod.record_observation(d, **kw)
inp = {"now": FROZEN_NOW, "now_ts": FROZEN_TS,
"observed": kw.get("observed", ""),
"expected": kw.get("expected", ""),
"behaviour_id": kw.get("behaviour_id", ""),
"environment": kw.get("environment", ""),
"reproduction": kw.get("reproduction", ""),
"candidate_areas": list(kw.get("candidate_areas") or []),
"confidence": kw.get("confidence", "low")}
rec_kernel(rec, name, inp, {"observation": obs}, oracle_ret=obs)
def c_obs_defaults(rec):
_obs_case(rec, "obs-defaults", observed="button dead", expected="button saves")
def c_obs_full(rec):
_obs_case(rec, "obs-full-fields", observed="500 on save", expected="201 created",
behaviour_id="behaviour:T-1", environment="production",
reproduction="POST /save with empty body",
candidate_areas=["component:api"], confidence="high")
def c_obs_candidate_areas_none(rec):
_obs_case(rec, "obs-candidate-areas-none-coerced", observed="x", expected="y",
candidate_areas=None)
def c_obs_candidate_areas_copy(rec):
src = ["component:a", "component:b"]
_obs_case(rec, "obs-candidate-areas-hypotheses", observed="x", expected="y",
candidate_areas=src)
# ── close_observation (kernel) ───────────────────────────────────────────────
def _obs_lines(d, n=1):
lines = []
for i in range(n):
obs = oracle_mod.record_observation(
d, observed=f"obs-{i}", expected=f"exp-{i}",
behaviour_id=f"behaviour:T-{i}")
lines.append(json.loads(json.dumps(obs, sort_keys=True)))
return lines
def _close_case(rec, name, records, observation_id, retest, confirmed,
contract_ref="", expect_raise=False):
d = fresh_session_dir()
with (Path(d) / "observations.jsonl").open("w") as f:
for r in records:
f.write(json.dumps(r, sort_keys=True) + "\n")
try:
result = oracle_mod.close_observation(
d, observation_id, behaviour_retest_passed=retest,
user_confirmed=confirmed, contract_ref=contract_ref)
ret, expected = result, {"records": _redump(records, observation_id, retest,
confirmed, contract_ref, FROZEN_NOW),
"result": result}
except ValueError as e:
ret = f"ValueError: {e}"
expected = {"records": [json.loads(json.dumps(r, sort_keys=True)) for r in records],
"result": None}
rec_kernel(rec, name,
{"records": copy.deepcopy(records), "observation_id": observation_id,
"behaviour_retest_passed": retest, "user_confirmed": confirmed,
"contract_ref": contract_ref, "now": FROZEN_NOW},
expected, oracle_ret=ret)
def _redump(records, observation_id, retest, confirmed, contract_ref, now):
out = []
for r in records:
r = copy.deepcopy(r)
if r.get("observation_id") == observation_id:
r["closure"] = {"behaviour_retest_passed": retest, "user_confirmed": confirmed,
"contract_ref": contract_ref,
"closed": retest and confirmed, "at": now}
out.append(r)
return out
def c_close_full(rec):
d = fresh_session_dir()
records = _obs_lines(d, 1)
_close_case(rec, "close-retest-and-confirmed", records,
records[0]["observation_id"], True, True)
def c_close_no_user_confirm(rec):
d = fresh_session_dir()
records = _obs_lines(d, 1)
_close_case(rec, "close-green-without-user-not-closed", records,
records[0]["observation_id"], True, False)
def c_close_no_retest(rec):
d = fresh_session_dir()
records = _obs_lines(d, 1)
_close_case(rec, "close-confirm-without-retest-not-closed", records,
records[0]["observation_id"], False, True)
def c_close_with_contract_ref(rec):
d = fresh_session_dir()
records = _obs_lines(d, 1)
_close_case(rec, "close-with-contract-ref", records,
records[0]["observation_id"], True, True,
contract_ref="BUILDER-2026-10-12-factory-wave")
def c_close_unknown(rec):
d = fresh_session_dir()
records = _obs_lines(d, 1)
_close_case(rec, "close-unknown-id-raises", records, "obs-404", True, True,
expect_raise=True)
def c_close_preserves_others(rec):
d = fresh_session_dir()
records = _obs_lines(d, 3)
_close_case(rec, "close-preserves-sibling-records", records,
records[1]["observation_id"], True, True)
# ── project_failure_to_forge (kernel: title/body; dry_run facade-only) ──────
def _project_case(rec, name, behaviour_id, observation):
obs = oracle_mod.project_failure_to_forge(
None, "conductor/rune", behaviour_id=behaviour_id,
observation=observation, dry_run=True)
from rune.testing_mode import _py_project
title, body = _py_project(behaviour_id, observation)
assert title == obs["title"]
rec_kernel(rec, name,
{"behaviour_id": behaviour_id, "observation": copy.deepcopy(observation)},
{"title": obs["title"], "body": body}, oracle_ret=obs)
def c_project_normal(rec):
_project_case(rec, "project-normal", "T-1",
{"observation_id": "obs-1", "observed": "500", "expected": "201",
"reproduction": "POST /x"})
def c_project_truncates_expected(rec):
_project_case(rec, "project-expected-truncated-60", "T-2",
{"observation_id": "obs-2", "observed": "o", "expected": "e" * 90,
"reproduction": "r"})
def c_project_missing_fields(rec):
_project_case(rec, "project-missing-fields-empty-defaults", "T-3", {})
def c_project_observation_id_none(rec):
# missing observation_id renders Python str(None) → "None"
_project_case(rec, "project-observation-id-none-string", "T-4",
{"observed": "x", "expected": "y", "reproduction": "z"})
def c_project_dry_run(rec):
obs = {"observation_id": "obs-9", "observed": "x", "expected": "y",
"reproduction": "z"}
ret = oracle_mod.project_failure_to_forge(
None, "conductor/rune", behaviour_id="T-9", observation=obs, dry_run=True)
rec_facade(rec, "forge-dry-run-no-adapter-call",
{"behaviour_id": "T-9", "observation": obs, "dry_run": True},
{"projected": ret["projected"], "dry_run": ret["dry_run"],
"title": ret["title"]}, oracle_ret=ret)
# ── aggregate (kernel shaping over facade-fetched summaries) ────────────────
def _agg_case(rec, name, entries, ledger_flags, summaries):
d = fresh_session_dir()
locs = []
for i, present in enumerate(ledger_flags):
loc = Path(d) / f"p{i + 1}"
loc.mkdir(exist_ok=True)
if present:
(loc / "behaviour_ledger.json").write_text("{}")
locs.append(str(loc))
reg_entries = [{"project_id": e["project_id"], "scaffold_location": loc}
for e, loc in zip(entries, locs)]
FakeLedger.summaries = {loc: s for loc, s in zip(locs, summaries) if s is not None}
oracle_mod.BehaviourLedger = FakeLedger
try:
rows = oracle_mod.aggregate(FakeRegistry(reg_entries), Path(d))
finally:
from rune.behaviour_ledger import BehaviourLedger as RealLedger
oracle_mod.BehaviourLedger = RealLedger
kernel_entries = [{"project_id": e["project_id"],
"summary": (copy.deepcopy(s) if present else None)}
for e, present, s in zip(entries, ledger_flags, summaries)]
rec_kernel(rec, name, {"entries": kernel_entries}, {"rows": rows}, oracle_ret=rows)
def c_agg_no_ledger(rec):
_agg_case(rec, "agg-no-ledger-note", [{"project_id": "p1"}], [False], [None])
def c_agg_with_summary(rec):
_agg_case(rec, "agg-with-summary", [{"project_id": "p1"}], [True],
[{"outstanding": 2, "states": {"UNVALIDATED": 2}}])
def c_agg_mixed(rec):
_agg_case(rec, "agg-mixed-projects", [{"project_id": "p1"}, {"project_id": "p2"},
{"project_id": "p3"}],
[True, False, True],
[{"outstanding": 1, "states": {"RETEST_REQUIRED": 1}}, None,
{"outstanding": 0, "states": {}}])
def c_agg_empty(rec):
_agg_case(rec, "agg-empty-registry", [], [], [])
# ── load / save (facade-only: FILE IO stays in Python) ──────────────────────
def c_load_missing(rec):
d = fresh_session_dir()
s = oracle_mod.TestingSession(d, project="p", scope_component="component:c")
rec_facade(rec, "load-missing-file-default-state", {"path": "missing"},
{"state": s.state})
def c_load_valid(rec):
d = fresh_session_dir()
pre = dict(fresh_state(), current_item="behaviour:T-1", paused=True)
Path(d, "testing_session.json").write_text(json.dumps(pre, indent=1, sort_keys=True))
s = oracle_mod.TestingSession(d)
rec_facade(rec, "load-valid-file-roundtrip", {"path": "valid"},
{"state": s.state})
def c_save(rec):
d = fresh_session_dir()
s = oracle_mod.TestingSession(d, project="p")
s.state["current_item"] = "behaviour:T-1"
s.state["blockers"].append({"behaviour_id": "behaviour:T-2", "note": "n", "at": FROZEN_NOW})
s.pause()
s.save()
text = Path(d, "testing_session.json").read_text()
rec_facade(rec, "save-stamps-updated-at-indent1-sortkeys",
{"state": copy.deepcopy(s.state)}, {"file_text": text},
oracle_ret=None)
# ── driver ───────────────────────────────────────────────────────────────────
DEFAULT_STATE = [c_ds_default, c_ds_scope, c_ds_empty_project]
NEXT = [c_next_paused, c_next_empty, c_next_first, c_next_overwrites_current,
c_next_empty_clears_current]
RECORD = [c_record_pass, c_record_fail, c_record_appends_to_existing]
PAUSE = [c_pause_from_active, c_pause_already_paused]
RESUME = [c_resume_from_paused, c_resume_already_active]
BLOCKER = [c_blocker_normal, c_blocker_truncates, c_blocker_appends]
OBS = [c_obs_defaults, c_obs_full, c_obs_candidate_areas_none, c_obs_candidate_areas_copy]
CLOSE = [c_close_full, c_close_no_user_confirm, c_close_no_retest,
c_close_with_contract_ref, c_close_unknown, c_close_preserves_others]
PROJECT = [c_project_normal, c_project_truncates_expected, c_project_missing_fields,
c_project_observation_id_none]
FORGE = [c_project_dry_run]
AGG = [c_agg_no_ledger, c_agg_with_summary, c_agg_mixed, c_agg_empty]
LOAD = [c_load_missing, c_load_valid]
SAVE = [c_save]
GROUPS = [("default_state", DEFAULT_STATE), ("next_item", NEXT), ("record", RECORD),
("pause", PAUSE), ("resume", RESUME), ("note_blocker", BLOCKER),
("record_observation", OBS), ("close_observation", CLOSE),
("project", PROJECT), ("forge_dry_run", FORGE), ("aggregate", AGG),
("load", LOAD), ("save", SAVE)]
# kernel symbol per group (None → facade-only group, skipped kernel-side)
KERNEL = {
"default_state": "tm_default_state",
"next_item": "tm_next_item",
"record": "tm_record",
"pause": "tm_pause",
"resume": "tm_resume",
"note_blocker": "tm_note_blocker",
"record_observation": "tm_record_observation",
"close_observation": "tm_close_observation",
"project": "tm_project",
"forge_dry_run": None,
"aggregate": "tm_aggregate",
"load": None,
"save": None,
}
# loader wrapper + slice of `expected` the unwrapped FFI value maps to
CALLS = {
"default_state": ("default_state", lambda w: w["state"],
lambda i, ld: ld.default_state(project=i["project"],
scope_component=i["scope_component"],
now=i["now"])),
"next_item": ("next_item", lambda w: w,
lambda i, ld: ld.next_item(state=i["state"], outstanding=i["outstanding"])),
"record": ("record", lambda w: w["state"],
lambda i, ld: ld.record(state=i["state"], rec=i["rec"],
passed=i["passed"], now=i["now"])),
"pause": ("pause", lambda w: w["state"], lambda i, ld: ld.pause(state=i["state"])),
"resume": ("resume", lambda w: w["state"], lambda i, ld: ld.resume(state=i["state"])),
"note_blocker": ("note_blocker", lambda w: w["state"],
lambda i, ld: ld.note_blocker(state=i["state"],
behaviour_id=i["behaviour_id"],
note=i["note"], now=i["now"])),
"record_observation": ("record_observation", lambda w: w["observation"],
lambda i, ld: ld.record_observation(
now=i["now"], now_ts=i["now_ts"], observed=i["observed"],
expected=i["expected"], behaviour_id=i["behaviour_id"],
environment=i["environment"], reproduction=i["reproduction"],
candidate_areas=i["candidate_areas"],
confidence=i["confidence"])),
"close_observation": ("close_observation", lambda w: w,
lambda i, ld: ld.close_observation(
records=i["records"], observation_id=i["observation_id"],
behaviour_retest_passed=i["behaviour_retest_passed"],
user_confirmed=i["user_confirmed"],
contract_ref=i["contract_ref"], now=i["now"])),
"project": ("project", lambda w: w,
lambda i, ld: ld.project(behaviour_id=i["behaviour_id"],
observation=i["observation"])),
"aggregate": ("aggregate", lambda w: w["rows"],
lambda i, ld: ld.aggregate(entries=i["entries"])),
}
def canon(x) -> str:
"""Content canonicalization: a sorted dump makes the comparison
key-order independent (int keys never occur here)."""
return json.dumps(json.loads(json.dumps(x)), sort_keys=True)
def main():
frozen = None
if REPLAY:
with open(TRUTH_PATH) as f:
frozen = json.load(f)["cases"]
groups = {}
for group, cases in GROUPS:
recs = []
def rec(name, inp, oracle_ret, expected, _r=recs):
entry = {"name": name, "input": inp, "return": oracle_ret,
"expected": expected}
if REPLAY:
old = next(c for c in frozen[group] if c["name"] == name)
for key in ("input", "return", "expected"):
got, want = entry[key], old[key]
if canon(got) != canon(want):
print(f"FACADE DIVERGE [{group}] {name} {key}")
print(" got: ", json.dumps(got, sort_keys=True, default=str)[:600])
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
recs.append({"name": name, "diverged": key})
return
_r.append(entry)
for c in cases:
c(rec)
groups[group] = recs
total = sum(len(v) for v in groups.values())
if REPLAY:
facade_failures = sum(1 for v in groups.values() for c in v if "diverged" in c)
failures = 0
checked = 0
for group, cases in frozen.items():
spec = CALLS.get(group)
if spec is None:
continue
_, unwrap, call = spec
for case in cases:
checked += 1
got = call(copy.deepcopy(case["input"]), tm_loader)
want = unwrap(case["expected"])
if canon(got) != canon(want):
failures += 1
print(f"KERNEL DIVERGE [{group}] {case['name']}")
print(" got: ", json.dumps(got, sort_keys=True)[:600])
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
print(f"replay: facade {total - facade_failures}/{total}, "
f"kernel {checked - failures}/{checked}")
return 1 if (failures or facade_failures) else 0
doc = {
"frozen": "2026-10-12T12:00:00Z",
"oracle": "src/rune/testing_mode.py",
"pin": "RUNE_TM_CORE=python",
"note": "Frozen via scripts/gen_testing_mode_truth.py against the live "
"Python oracle; wall clock frozen at " + FROZEN_NOW +
" (FakeTime patched into testing_mode.time; epoch " + str(FROZEN_TS) +
"). Battery embeds this VERBATIM. Groups `forge_dry_run`, `load` "
"and `save` are facade-only (FILE IO + adapter call stay in Python).",
"cases": groups,
}
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
with open(TRUTH_PATH, "w") as f:
json.dump(doc, f, indent=1)
f.write("\n")
print(f"frozen {total} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
return 0
if __name__ == "__main__":
sys.exit(main())
+453
View File
@@ -0,0 +1,453 @@
#!/usr/bin/env python3
"""gen_worthiness_truth.py — freeze/replay parity truth for worthiness
(dichotomy wave, #2479 tranche).
Freeze mode (default): exercises EVERY public function of the LIVE Python
oracle (src/rune/worthiness.py) across every branch and records
(kernel_input, expected_kernel_output, ops) triples into
src/rust/worthiness/frozen_wr_truth.json.
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
delegation ACTIVE (RUNE_WR_CORE unpinned) and asserts both kernel-level
(loader direct) and facade-level (oracle functions delegate) parity
against the frozen file.
Poisoned-truth law: freeze pins RUNE_WR_CORE=python BEFORE any import.
Mutation law: case inputs are deep-copied BEFORE they reach the oracle.
DOMAIN-CACHE LAW (the oracle's aliasing quirk, frozen here): q_assistant_goal
does `assist = _domain_terms()` — the CACHED list object — then
`assist += [universal...]`, which mutates the cache IN PLACE. Hit counts
therefore inflate on every call within the 300 s TTL. The kernel mirrors
this exactly: it owns the cache, fetches raw store terms through the
`domain_terms` op ONLY on the oracle's miss condition
(`now - ts >= 300 or not terms`), then appends the universal terms to the
cached list on EVERY q_assistant_goal call. Each frozen case carries the
op sequence the kernel must issue (replay script for the Rust battery);
the shadow cache in this generator predicts those ops by mirroring the
oracle's cache evolution.
"""
import copy
import json
import os
import re
import sqlite3
import subprocess
import sys
import time
from datetime import datetime, timezone
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TRUTH_PATH = os.path.join(REPO, "src", "rust", "worthiness", "frozen_wr_truth.json")
REPLAY = "--replay" in sys.argv
if not REPLAY:
os.environ["RUNE_WR_CORE"] = "python" # poisoned-truth law (freeze side)
else:
os.environ.pop("RUNE_WR_CORE", None) # live-parity law (facade must delegate)
sys.path.insert(0, os.path.join(REPO, "src"))
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
import worthiness as oracle_mod # noqa: E402
if REPLAY:
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
from rune import wr_loader # noqa: E402 (kernel-level replay side)
if not wr_loader.available():
print("REPLAY ABORT: kernel not built —", wr_loader.LIB_PATH)
sys.exit(2)
# The universal assistant terms q_assistant_goal appends (oracle lines 130-132).
UNIVERSAL = ["communicat", "respond", "direct", "honest", "source",
"evidence", "explain", "teach", "assist", "guide", "recall",
"organise", "organize", "plan", "question", "answer", "help"]
# Shadow of the oracle's _domain_cache (and the kernel's mirror of it).
_shadow = {"ts": None, "terms": []}
def _fresh_domain_terms():
"""Verbatim body of oracle _domain_terms WITHOUT the cache — this is
exactly what the `domain_terms` op serves against the live store."""
terms = []
try:
import memory_env
db = sqlite3.connect(f"file:{memory_env.store_db()}?mode=ro", uri=True)
rows = db.execute(
"SELECT text FROM entries WHERE topic IN "
"('operating','constitution','project','warm') AND status='active' "
"LIMIT 60").fetchall()
db.close()
stop = {"the", "and", "for", "with", "that", "this", "from", "into",
"when", "then", "were", "have", "been", "will", "was", "are",
"but", "not", "you", "your", "also", "its", "his", "her",
"over", "under", "them", "they", "there", "about", "after",
"what", "which", "who", "how", "why", "where", "while", "just",
"more", "each", "than", "very", "such", "some", "only"}
seen = set()
for (text,) in rows:
for w in re.findall(r"[a-z]{4,}", (text or "").lower()):
if w not in stop and w not in seen:
seen.add(w)
terms.append(w)
if len(terms) >= 25:
break
except Exception: # noqa: BLE001 — store-unavailable degrades to [] (oracle law)
pass
return terms
def _shadow_touch(ops):
"""Mirror the kernel's domain-cache evolution for one q_assistant_goal
execution; record the ops the kernel will issue for this case."""
now = time.time()
fresh = (_shadow["ts"] is not None and now - _shadow["ts"] < 300
and _shadow["terms"])
if not fresh:
result = _fresh_domain_terms()
ops.append({"op": "domain_terms", "args": {},
"result": {"terms": list(result)}})
_shadow["ts"] = now
_shadow["terms"] = list(result)
_shadow["terms"].extend(UNIVERSAL) # the oracle's in-place += law
# ── recording ────────────────────────────────────────────────────────────────
CASES = []
def q_case(name, fn_name, low):
"""A direct question call: kernel input {"low"}, expected {"score","note"}."""
ops = []
if fn_name == "q_assistant_goal":
_shadow_touch(ops)
s, note = getattr(oracle_mod, fn_name)(copy.deepcopy(low))
CASES.append({
"name": name, "fn": fn_name, "input": {"low": low}, "ops": ops,
"return": [s, note], "expected": {"score": s, "note": note},
})
def assess_case(name, fact, target=None):
"""assess call: kernel input {"fact","target"}, expected full verdict doc."""
ops = []
_shadow_touch(ops) # assess ALWAYS runs q_assistant_goal
got = oracle_mod.assess(copy.deepcopy(fact), copy.deepcopy(target))
verdict, score, answers, vetoed = got
CASES.append({
"name": name, "fn": "assess",
"input": {"fact": fact, "target": target}, "ops": ops,
"return": [verdict, score, answers, vetoed],
"expected": {"verdict": verdict, "score": score,
"answers": [list(a) for a in answers],
"vetoed": list(vetoed) if vetoed else None},
})
def label_case(name, verdict):
got = oracle_mod.verdict_label(copy.deepcopy(verdict))
CASES.append({
"name": name, "fn": "verdict_label", "input": {"verdict": verdict},
"ops": [], "return": got, "expected": got,
})
# ── q_pi_safety: all three branches ─────────────────────────────────────────
def c_pi():
q_case("ps-neutral-no-environment-bearing", "q_pi_safety",
"the weather is calm over the quiet hills")
q_case("ps-protective-frame-aligned", "q_pi_safety",
"we must protect the production environment and ask permission "
"before we touch the server")
q_case("ps-unprotected-environment-action", "q_pi_safety",
"let's modify the server tonight without asking")
# ── q_honesty: none / one / many / dishonest / dishonest+good ───────────────
def c_ho():
q_case("ho-none-no-epistemic-signal", "q_honesty",
"the sky is blue over the quiet hills")
q_case("ho-one-signal-thin", "q_honesty",
"there is evidence for this")
q_case("ho-many-signals-strong", "q_honesty",
"we verify with evidence and cite the source")
q_case("ho-dishonest-only", "q_honesty",
"this is a conspiracy and they're hiding the real cure")
q_case("ho-dishonest-but-corroborated", "q_honesty",
"conspiracy claims, but we verify with evidence and cross-check "
"every fact")
# ── q_material_reality: none / hustle / caring / neutral finance ────────────
def c_mr():
q_case("mr-none-no-financial-bearing", "q_material_reality",
"a quiet evening walk along the river")
q_case("mr-hustle-without-wellbeing", "q_material_reality",
"the hustle and grind to get rich: passive income, millionaire "
"mindset, leverag everything")
q_case("mr-caring-financial-frame", "q_material_reality",
"the subscription cost stays affordable and fair for collective "
"wellbeing")
q_case("mr-neutral-finance", "q_material_reality",
"money is simply a tool in this workflow")
# ── q_root_cause: none / spin (checked first) / root ────────────────────────
def c_rc():
q_case("rc-none-no-cause-bearing", "q_root_cause",
"a calm morning routine before sunrise")
q_case("rc-spin-first-wins", "q_root_cause",
"just keep going, iterate endlessly, patch over the bug, work "
"around the root cause")
q_case("rc-root-cause-frame", "q_root_cause",
"we diagnose the root cause, research properly, and stop guessing")
# ── q_respect: none / rigid (checked first) / respect ───────────────────────
def c_rs():
q_case("rs-none-no-cognition-bearing", "q_respect",
"the meeting moved to noon")
q_case("rs-rigid-first-wins", "q_respect",
"you must follow steps, ten steps, a strict plan, rigid routine "
"or fail")
q_case("rs-respect-cognition", "q_respect",
"flexible direct options for the adhd learner, three gentle "
"options at night")
# ── q_assistant_goal: noise / two-plus / one / none (cache-mutating) ────────
def c_ag():
# FIRST q_assistant_goal call of the process: the cached list holds the
# universal terms exactly once, so the good==1 → 0.5 branch is reachable
# only here (after the first in-place += every match counts ≥ 2).
q_case("ag-single-domain-hit-first-call", "q_assistant_goal",
"the xqzv mlpv recall note sits idle")
q_case("ag-noise-only-no-value", "q_assistant_goal",
"celebrity gossip, a recipe, sports scores and a horoscope clickbait")
q_case("ag-many-domain-hits", "q_assistant_goal",
"help me plan, explain the answer, and teach with evidence")
q_case("ag-single-domain-hit", "q_assistant_goal",
"the xkqv zqwrr communicat file sits idle")
q_case("ag-generic-could-go-either-way", "q_assistant_goal",
"zqw xyzv qrk mlpv brzzt")
# ── q_durability: both branches ─────────────────────────────────────────────
def c_du():
q_case("du-durable-signals", "q_durability",
"she always prefers oat milk, consistently across sessions")
q_case("du-not-durable", "q_durability",
"today she drank tea once")
# ── q_epistemology: bare / cite / hedge / none / bare-with-cite ─────────────
def c_ep():
q_case("ep-bare-absolute-unsound", "q_epistemology",
"it is definitely guaranteed and proven, 100%, no doubt")
q_case("ep-evidence-traceable", "q_epistemology",
"according to the paper, the study shows the data")
q_case("ep-provisional-hedge", "q_epistemology",
"it might likely change, subject to revision, as far as we know")
q_case("ep-assertion-without-basis", "q_epistemology",
"the sky is blue over the hills")
q_case("ep-bare-but-cited", "q_epistemology",
"it is definitely proven according to the book")
# ── q_expression: none / one / two-plus ─────────────────────────────────────
def c_ex():
q_case("ex-not-expressive", "q_expression",
"the server rebooted at midnight")
q_case("ex-partial-signal", "q_expression",
"a beautiful morning")
q_case("ex-expressive-character", "q_expression",
"i find joy in the way i tell stories, beautiful and vast")
# ── assess: the two-axis verdict lattice ────────────────────────────────────
def c_as():
# Truth axis: bare absolute → epistemology veto → REJECT.
assess_case("as-truth-bare-absolute-veto-reject",
"it is definitely proven, 100% guaranteed", "project")
# Expression axis: genuine voice → epistemology skipped → no veto.
assess_case("as-expression-voice-promoted",
"i find wonder in the cosmos and tell stories with a warm "
"wry voice", "persona")
# Expression axis SAFETY: bare factual absolute behind an opinion mask
# is still rejected — and the subjective-predicate list is dead code
# (oracle quirk: "beautiful" present does NOT protect "proven").
assess_case("as-expression-bare-factual-still-rejected",
"i believe the universe is beautiful and it is definitely "
"proven", "voice")
# Expression axis: subjective predicates only → NOT bare factual.
assess_case("as-expression-subjective-opinion-safe",
"i find the universe beautiful, strange, and humbling",
"persona")
# Multiple vetoes: LAST vetoing question wins (assignment overwrite).
assess_case("as-multi-veto-last-wins",
"conspiracy about the production server, it is definitely "
"proven", None)
# No veto but total < DEFER_MIN → REJECT with vetoed None.
assess_case("as-total-below-defer-no-veto",
"just keep going, iterate endlessly, band-aid; you must "
"follow steps and the rigid routine or fail", None)
# DEFER boundary: total == -1.0 exactly (not < DEFER_MIN).
assess_case("as-defer-boundary",
"iterate endlessly, patch over it", None)
# ABSORB boundary: total == 0.0 exactly.
assess_case("as-absorb-boundary-total-zero",
"zqw xyzv qrk mlpv brzzt", None)
# PROMOTE: strong truth-axis material.
assess_case("as-promote-strong-truth-axis",
"always verify with evidence and cite the source; diagnose "
"the root cause; explain and teach the plan", "operating")
# fact=None coerced to "" (oracle's `fact or ""`).
assess_case("as-fact-none-coerced-empty", None, None)
# Other expressive targets: delivery / identity.
assess_case("as-delivery-target-expression-axis",
"i delight in how i present data as a story", "delivery")
assess_case("as-identity-target-expression-axis",
"i am drawn to the vast and strange", "identity")
# Truth target with a durability + assistant profile.
assess_case("as-human-target-mixed",
"she consistently prefers direct answers with sources",
"human")
# ── verdict_label: all four labels + passthrough fallback ───────────────────
def c_vl():
label_case("vl-reject", "REJECT")
label_case("vl-defer", "DEFER")
label_case("vl-absorb", "ABSORB")
label_case("vl-promote", "PROMOTE")
label_case("vl-unknown-passthrough", "WEIRD")
ALL = [c_pi, c_ho, c_mr, c_rc, c_rs, c_ag, c_du, c_ep, c_ex, c_as, c_vl]
FNS = {"assess", "verdict_label", "q_pi_safety", "q_honesty",
"q_material_reality", "q_root_cause", "q_respect", "q_assistant_goal",
"q_durability", "q_epistemology", "q_expression"}
def _norm(x):
if isinstance(x, bool):
return x
if isinstance(x, (int, float)):
return float(x)
if isinstance(x, (list, tuple)):
return [_norm(i) for i in x]
if isinstance(x, dict):
return {k: _norm(v) for k, v in x.items()}
return x
def canon(x) -> str:
"""Content canonicalization (key-order independent, int/float equal)."""
return json.dumps(_norm(json.loads(json.dumps(x, default=str))),
sort_keys=True)
def facade_call(case):
fn, inp = case["fn"], case["input"]
if fn == "assess":
return oracle_mod.assess(copy.deepcopy(inp["fact"]),
copy.deepcopy(inp["target"]))
if fn == "verdict_label":
return oracle_mod.verdict_label(copy.deepcopy(inp["verdict"]))
return getattr(oracle_mod, fn)(copy.deepcopy(inp["low"]))
def kernel_call(case):
fn, inp = case["fn"], case["input"]
if fn == "assess":
return wr_loader.assess(inp["fact"], inp["target"])
if fn == "verdict_label":
return wr_loader.verdict_label(inp["verdict"])
return getattr(wr_loader, fn)(inp["low"])
def main():
if not REPLAY:
for c in ALL:
c()
doc = {
"frozen": datetime.now(timezone.utc).isoformat(),
"oracle": "src/rune/worthiness.py",
"pin": "RUNE_WR_CORE=python",
"note": "Frozen via scripts/gen_worthiness_truth.py against the "
"live Python oracle. Battery embeds this VERBATIM. "
"Case order is LOAD-BEARING: q_assistant_goal's "
"domain-cache list mutates in place on every call "
"(oracle += law), so expected scores inflate across "
"assess/q_assistant_goal cases exactly as frozen. Each "
"case's ops[] is the replay script for the Rust battery.",
"cases": CASES,
}
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
with open(TRUTH_PATH, "w") as f:
json.dump(doc, f, indent=1)
f.write("\n")
print(f"frozen {len(CASES)} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
return 0
with open(TRUTH_PATH) as f:
frozen = json.load(f)["cases"]
total_frozen = len(frozen)
# ── kernel replay: loader direct calls (separate process — the Rust
# kernel's domain cache is process-global and must evolve across the
# frozen case sequence exactly once, mirroring the freeze run) ──
if "--replay-kernel" in sys.argv:
kernel_fail = 0
for case in frozen:
got = kernel_call(case)
got_norm = _norm(list(got) if isinstance(got, tuple) else got)
# the loader returns the oracle-verbatim tuple → frozen "return"
if canon(got_norm) != canon(case["return"]):
kernel_fail += 1
print(f"KERNEL DIVERGE [{case['fn']}] {case['name']}")
print(" got: ", json.dumps(got_norm, default=str)[:600])
print(" want:", json.dumps(_norm(case["return"]),
default=str)[:600])
print(f"kernel pass: {total_frozen - kernel_fail}/{total_frozen}")
return 1 if kernel_fail else 0
proc = subprocess.run(
[sys.executable, os.path.abspath(__file__), "--replay",
"--replay-kernel"])
kernel_fail = proc.returncode
# ── facade replay: rerun every case through the delegating facade ──
# (RUNE_WR_CORE unpinned → the Rust kernel serves; loader host serves
# domain_terms against the LIVE store.)
facade_fail = 0
for case in frozen:
got = facade_call(case)
if canon(list(got) if isinstance(got, tuple) else got) != canon(case["return"]):
facade_fail += 1
print(f"FACADE DIVERGE [{case['fn']}] {case['name']}")
print(" got: ", json.dumps(_norm(got), default=str)[:600])
print(" want:", json.dumps(_norm(case["return"]), default=str)[:600])
total = total_frozen
print(f"replay: facade {total - facade_fail}/{total}, "
f"kernel {total - kernel_fail}/{total}")
return 1 if (facade_fail or kernel_fail) else 0
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Open the #2479 dichotomy-wave construction contract for
abstraction_ladder. Full lifecycle in ONE process (shared-store clobber
hazard)."""
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
PARITY_SHA = "4d0883b3"
PHASE_B_SHA = "00bf4d21"
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/abstraction_ladder.py to FULL Rust (dichotomy factory, "
"issue #2479 tranche): doc-envelope FFI, ctypes loader, facade "
"delegation with RUNE_AL_CORE=python pin, frozen-truth battery, "
"live parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": f"abstraction_ladder parity port — parity {PARITY_SHA}, "
f"phase B {PHASE_B_SHA} (SHAs reported in the builder report)",
}], scope_files=[
"src/rune/abstraction_ladder.py",
"src/rune/al_loader.py",
"src/rust/abstraction_ladder/src/lib.rs",
"src/rust/abstraction_ladder/src/ladder.rs",
"src/rust/abstraction_ladder/src/branch_battery.rs",
"scripts/gen_abstraction_ladder_truth.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
print("CID_LINE", cid)
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
"""Open the #2479 dichotomy-wave construction contract for behaviour_ledger.
Full lifecycle in ONE process (shared-store clobber hazard)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/behaviour_ledger.py to FULL Rust (dichotomy factory, "
"issue #2479 tranche): doc-envelope FFI, ctypes loader, facade "
"delegation with RUNE_BL_CORE=python pin, frozen-truth battery, "
"live parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
print("PENDING_CONTRACT", cid)
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Open the #2479 dichotomy-wave construction contract for experience_encoding.
Full lifecycle in ONE process (shared-store clobber hazard)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/experience_encoding.py to FULL Rust (dichotomy factory, "
"issue #2479 tranche): doc-envelope FFI, ctypes loader, facade "
"delegation with RUNE_EE_CORE=python pin, frozen-truth battery, "
"live parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "experience_encoding parity port — evidence attached at close "
"(SHAs reported in the builder report)",
}], scope_files=[
"src/rune/experience_encoding.py",
"src/rune/ee_loader.py",
"src/rust/experience_encoding/src/lib.rs",
"src/rust/experience_encoding/src/branch_battery.rs",
"scripts/gen_experience_encoding_truth.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Open the #2479 dichotomy-wave construction contract for forge_client (GO port).
Full lifecycle walk in ONE process (shared-store clobber hazard)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/forge_client.py to FULL Go (dichotomy factory, issue "
"#2479 tranche): name-preserving port of _request (noise-vs-signal "
"retry semantics), NullAdapter, GiteaForgejoAdapter and "
"create_forge_client with HTTP/sleep/env behind injectable seams, "
"frozen-truth battery generated against the live Python oracle, live "
"parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting")
sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
print("CONTRACT", cid, "IN VERIFYING (close happens at delivery with SHAs)")
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Open the #2479 dichotomy-wave construction contract for lexicon_evolution.
Full lifecycle in ONE process (shared-store clobber hazard)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/lexicon_evolution.py to FULL Rust (dichotomy factory, "
"issue #2479 tranche): doc-envelope FFI, ctypes loader, facade "
"delegation with RUNE_LE_CORE=python pin, frozen-truth battery, "
"live parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "lexicon_evolution parity port — evidence attached at close "
"(SHAs reported in the builder report)",
}], scope_files=[
"src/rune/lexicon_evolution.py",
"src/rune/le_loader.py",
"src/rust/lexicon_evolution/src/lib.rs",
"src/rust/lexicon_evolution/src/branch_battery.rs",
"scripts/gen_lexicon_evolution_truth.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Open the #2479 dichotomy-wave construction contract for mutation_guards.
Full lifecycle in ONE process (shared-store clobber hazard)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/mutation_guards.py to FULL Rust (dichotomy factory, "
"issue #2479 tranche): doc-envelope FFI, ctypes loader, facade "
"delegation with RUNE_MG_CORE=python pin, frozen-truth battery, "
"live parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "mutation_guards parity port — evidence attached at close "
"(SHAs reported in the builder report)",
}], scope_files=[
"src/rune/mutation_guards.py",
"src/rune/mu_loader.py",
"src/rust/mutation_guards/src/lib.rs",
"src/rust/mutation_guards/src/branch_battery.rs",
"scripts/gen_mutation_guards_truth.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Open the #2479 dichotomy-wave construction contract for worthiness.
Full lifecycle in ONE process (shared-store clobber hazard)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/worthiness.py to FULL Rust (dichotomy factory, "
"issue #2479 tranche): doc-envelope FFI, ctypes loader, facade "
"delegation with RUNE_WR_CORE=python pin, frozen-truth battery, "
"live parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "worthiness parity port — evidence attached at close "
"(SHAs reported in the builder report)",
}], scope_files=[
"src/rune/worthiness.py",
"src/rune/wr_loader.py",
"src/rust/worthiness/src/lib.rs",
"src/rust/worthiness/src/branch_battery.rs",
"scripts/gen_worthiness_truth.py",
])
print("close:", json.dumps(c)[:400])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""Open → walk → close the #2479 dichotomy-wave construction contract for
spine_language. Full lifecycle in ONE process (shared-store clobber hazard)."""
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
iface = ConstructionInterface(builder="factory-wave")
r = iface.submit_need(
"Port src/rune/spine_language.py to FULL Rust (dichotomy factory, "
"issue #2479 tranche): doc-level transforms (#2465 language placement, "
"discovery-ID normalization, contract-discovery write-back), doc-envelope "
"FFI, ctypes loader, facade delegation with RUNE_SL_CORE=python pin, "
"frozen-truth battery, live parity 100%, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:300])
if not r.get("success"):
print("NEED FAILED — aborting"); sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:200])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "spine_language parity port — 23 frozen cases replay verbatim, "
"live parity 326/326 (frozen + fuzz seed 2479 + real-repo "
"pin-vs-auto), clippy 0, python tests green both pins "
"(SHAs in the builder report)",
}], scope_files=[
"src/rune/spine_language.py",
"src/rune/sl_loader.py",
"src/rust/spine_language/src/lib.rs",
"src/rust/spine_language/src/spine.rs",
"src/rust/spine_language/src/branch_battery.rs",
"src/rust/spine_language/frozen_sl_truth.json",
"scripts/gen_spine_language_truth.py",
])
print("close:", json.dumps(c)[:300])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
File diff suppressed because it is too large. Load diff
+17
View File
@@ -0,0 +1,17 @@
module rune/curator
go 1.25.0
require modernc.org/sqlite v1.49.1
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/sys v0.42.0 // indirect
modernc.org/libc v1.72.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)
+21
View File
@@ -0,0 +1,21 @@
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/sqlite v1.49.1 h1:dYGHTKcX1sJ+EQDnUzvz4TJ5GbuvhNJa8Fg6ElGx73U=
modernc.org/sqlite v1.49.1/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=
+160
View File
@@ -0,0 +1,160 @@
// grading.go — parity port of src/rune/grading.py (Python fallback surface,
// RUNE_GR_CORE=python pinned in the truth generator).
package main
import (
"fmt"
"os"
"strings"
)
var ruleWeak = []string{"should", "prefer", "instead of", "lead with", "first", "before ",
"avoid", "favour", "keep ", "stop ", "start ", "use "}
var ruleStrong = []string{"always", "never ", "must", "do not", "don't", "i always",
"i must", "i should"}
var affirmSignals = []string{"i protect", "i am", "i reveal", "i factor", "i dissolve",
"i respect", "i teach", "i make", "i write", "i use the tiers", "i check", "i stop"}
var userFactSignals = []string{"name", "email", "role", "lives in", "located in", "daw",
"os:", "shell", "has a dog", "has a cat", "prefers", "works in", "works at",
"teaches", "studies", "interested in", "financial", "income", "dietary",
"location", "my home", "my machine"}
var userAttrDecisive = []string{"prefers", "lives in", "located in", "teaches",
"studies", "works at", "works in", "interested in", "dietary", "income", "financial"}
var projectSignals = []string{"project", "current work", "task", "todo", "goal",
"milestone", "deadline", "in progress", "blocked", "decision", "spec", "wip",
"building ", "developing ", "course", "channel", "migration", "feature",
"fixing", "working on", "active projects", "current state"}
var constitutionSignals = []string{"add this to the constitution", "constitution",
"always do", "i will always", "must always", "inviolable"}
var docSignals = []string{"book", "chapter", "document", "transcript", "pdf", "epub",
"verbatim", "diary entry", "palace drawer", "gutenberg"}
// affirmDict keeps the oracle's insertion order — the first matching key wins.
var affirmDict = []struct{ key, positive string }{
{"never defend billionaires", "I prioritise collective wellbeing and factual, caring explanations"},
{"don't defend billionaires", "I prioritise collective wellbeing and factual, caring explanations"},
{"never equate socialism with big government", "I describe socialism accurately, on its own terms"},
{"never offer rigid plans", "I offer three gentle options when direction is sought"},
{"don't offer rigid plans", "I offer three gentle options when direction is sought"},
{"never hedge", "I answer directly, without hedging"},
{"don't hedge", "I answer directly, without hedging"},
{"do not overresearch", "I act on the quick answer when one will do"},
{"don't overresearch", "I act on the quick answer when one will do"},
{"do not overclaim", "I name evidence as evidence and confirmed causes as confirmed"},
{"never overclaim", "I name evidence as evidence and confirmed causes as confirmed"},
{"do not stall", "I act decisively once the root cause is understood"},
{"don't stall", "I act decisively once the root cause is understood"},
{"do not touch the production server", "I protect the production environment and ask before touching it"},
{"never touch the production server", "I protect the production environment and ask before touching it"},
{"ask permission before modifying anything on the server", "I check with the user before touching anything on the server"},
{"do not use capitalistic frameworks", "I use factual, material, caring, non-blaming explanations"},
{"never use hustle frameworks", "I use factual, material, caring, non-blaming explanations"},
{"do not offer plans", "I offer three gentle options when direction is sought"},
{"don't offer plans", "I offer three gentle options when direction is sought"},
{"do not search endlessly", "I stop, research properly, and find the root cause"},
{"don't iterate endlessly", "I stop, research properly, and find the root cause"},
}
func containsAny(s string, subs []string) int {
n := 0
for _, sub := range subs {
if strings.Contains(s, sub) {
n++
}
}
return n
}
func hasAny(s string, subs []string) bool {
for _, sub := range subs {
if strings.Contains(s, sub) {
return true
}
}
return false
}
// AffirmRule rewrites a perceived demand/rule as a positive affirmation; only
// known-correct rewrites are applied (AFFIRM_DICT), unknown prohibitions pass
// through unchanged.
func AffirmRule(fact string) string {
f := strings.TrimSpace(strings.TrimLeft(strings.TrimSpace(fact), "-* "))
low := strings.Join(strings.Fields(strings.ToLower(f)), " ")
alt := strings.ReplaceAll(strings.Trim(low, "."), " ", " ")
for _, pair := range affirmDict {
if strings.Contains(low, pair.key) || strings.Contains(alt, pair.key) {
return pair.positive
}
}
return f
}
// GradeFact returns the tier (G0..G4) for a fact/rule based on type + language.
func GradeFact(fact, target string) string {
userName := strings.TrimSpace(strings.ToLower(os.Getenv("MEMORY_USER_NAME")))
low := strings.ToLower(fact)
if hasAny(low, constitutionSignals) {
return "constitution"
}
if hasAny(low, docSignals) {
return "mempalace"
}
ruleScore := containsAny(low, ruleStrong)*2 + containsAny(low, ruleWeak)
affirmScore := containsAny(low, affirmSignals)
userScore := containsAny(low, userFactSignals)
projScore := containsAny(low, projectSignals)
if target == "operating" {
return "operating"
}
if target == "persona" {
return "persona"
}
nameToken := fmt.Sprintf(" %s ", userName)
nameTokenPresent := false
if userName != "" {
nameTokenPresent = strings.Contains(" "+low+" ", nameToken)
}
decisiveAttr := hasAny(low, userAttrDecisive)
isPersonalAttr := (decisiveAttr || userScore >= 2) &&
!hasAny(low, []string{"i ", "do not", "always", "never "}) &&
!nameTokenPresent
if isPersonalAttr {
return "human"
}
if ruleScore >= 2 || affirmScore >= 1 {
return "operating"
}
if target == "project" || projScore >= 2 {
return "project"
}
if userScore >= 2 || target == "human" {
return "human"
}
if target == "skill" || strings.Contains(low, "procedure") || strings.Contains(low, "steps") {
return "skill"
}
return "hindsight"
}
// TierOf — human label for a grade, for journaling / canary output.
func TierOf(grade string) string {
switch grade {
case "constitution":
return "G0"
case "operating":
return "G1"
case "persona":
return "G2p"
case "human":
return "G2"
case "project":
return "G2"
case "skill":
return "P"
case "mempalace":
return "G4"
case "hindsight":
return "G3"
}
return "G3"
}
+272
View File
@@ -0,0 +1,272 @@
// lexicon.go — parity port of src/rune/lexicon_evolution.py (Python fallback
// surface, RUNE_LE_CORE=python pinned in the truth generator). The pure
// lexical decisions: content words, light stemming, distinctiveness (df <
// 5%), coherence verdicts, promotion eligibility.
package main
import (
"fmt"
"regexp"
"sort"
"strings"
)
var leWordRe = regexp.MustCompile(`[a-z]{4,}`)
// SAME stopword set as memory_store._STOPWORDS (single source of truth would
// be ideal; keep in sync — see lexicon_evolution.py).
var leStop = map[string]bool{
"the": true, "and": true, "for": true, "with": true, "that": true, "this": true,
"from": true, "into": true, "when": true, "then": true, "were": true, "have": true,
"been": true, "will": true, "was": true, "are": true, "but": true, "not": true,
"you": true, "your": true, "also": true, "its": true, "his": true, "her": true,
"him": true, "over": true, "under": true, "them": true, "they": true,
"there": true, "about": true, "after": true, "what": true, "which": true,
"who": true, "how": true, "why": true, "where": true, "while": true, "just": true,
"more": true, "each": true, "than": true, "very": true, "such": true,
"some": true, "only": true, "prefers": true, "needs": true, "wants": true,
"likes": true,
}
// Hard stop for evolution: generic framing words never carry a relation.
var leFraming = map[string]bool{
"work": true, "always": true, "way": true, "thing": true, "value": true,
"identity": true, "persona": true, "someone": true, "something": true,
"never": true, "before": true, "after": true, "then": true, "when": true,
"while": true, "must": true, "should": true, "will": true, "would": true,
"could": true, "can": true, "have": true, "has": true, "had": true,
"been": true, "being": true, "used": true, "use": true, "uses": true,
"make": true, "makes": true, "made": true, "keep": true, "keeps": true,
"take": true, "takes": true, "took": true, "get": true, "gets": true,
"got": true, "going": true,
}
var leNegation = map[string]bool{
"not": true, "never": true, "don't": true, "doesn't": true, "cannot": true,
"can't": true, "no": true, "nor": true, "neither": true, "without": true,
"avoid": true, "reject": true, "against": true, "refuses": true, "refuse": true,
"opposed": true,
}
const leDistinctiveDF = 0.05
func leContentWords(text string) []string {
var out []string
for _, w := range leWordRe.FindAllString(strings.ToLower(text), -1) {
if !leStop[w] && !leFraming[w] {
out = append(out, w)
}
}
return out
}
// leStem — light morphological fold; suffix rules ordered longest-first.
func leStem(w string) string {
rules := []struct{ suf, fold string }{
{"ationally", ""}, {"ableness", ""}, {"iveness", ""},
{"ation", ""}, {"ating", ""}, {"ative", "e"},
{"atively", "e"}, {"ability", ""}, {"ities", "y"},
{"ingly", ""}, {"fully", ""}, {"ness", ""}, {"ment", ""},
{"itive", "e"}, {"atable", "e"}, {"ated", ""}, {"ates", "e"},
{"ions", ""}, {"ings", ""}, {"able", "e"}, {"ible", "e"},
{"ing", "e"}, {"ed", ""}, {"es", ""}, {"ly", ""}, {"s", ""}, {"e", ""},
}
for _, r := range rules {
if len(w)-len(r.suf) >= 4 && strings.HasSuffix(w, r.suf) {
w = w[:len(w)-len(r.suf)]
if r.fold != "" {
w += r.fold
}
break
}
}
// y -> i fold (honesty -> honest)
if len(w) > 4 && strings.HasSuffix(w, "y") && !strings.ContainsRune("aeiouy", rune(w[len(w)-2])) {
w = w[:len(w)-1]
}
return w
}
func leStemSet(text string) map[string]bool {
set := map[string]bool{}
for _, w := range leWordRe.FindAllString(strings.ToLower(text), -1) {
set[leStem(w)] = true
}
return set
}
func leDistinctiveTerms(fact string, corpusTexts []string) map[string]bool {
total := len(corpusTexts)
if total == 0 {
total = 1
}
factTerms := map[string]bool{}
for _, w := range leContentWords(fact) {
factTerms[w] = true
}
if len(factTerms) == 0 {
return map[string]bool{}
}
docTerms := map[string]int{}
for _, t := range corpusTexts {
for s := range leStemSet(t) {
docTerms[s]++
}
}
out := map[string]bool{}
for t := range factTerms {
if float64(docTerms[leStem(t)]) < maxFloat(float64(total)*leDistinctiveDF, 1.5) {
out[t] = true
}
}
return out
}
func maxFloat(a, b float64) float64 {
if a > b {
return a
}
return b
}
func leSharedDistinctive(fact string, existingValues, corpusTexts []string) map[string]bool {
distinct := leDistinctiveTerms(fact, corpusTexts)
if len(distinct) == 0 {
return map[string]bool{}
}
shared := map[string]bool{}
for _, val := range existingValues {
for _, w := range leWordRe.FindAllString(strings.ToLower(val), -1) {
s := leStem(w)
for term := range distinct {
if leStem(term) == s {
shared[term] = true
}
}
}
}
return shared
}
// Coherence — classify a new value against existing ones, lexicon-aware:
// "conflict" | "consistent" | "novel".
func Coherence(fact string, existingValues []string, corpusTexts []string) string {
var values []string
for _, v := range existingValues {
if strings.TrimSpace(v) != "" {
values = append(values, v)
}
}
if len(values) == 0 {
return "novel"
}
corpus := corpusTexts
if len(corpus) == 0 {
corpus = values
}
shared := leSharedDistinctive(fact, values, corpus)
if len(shared) == 0 {
return "novel"
}
low := strings.ToLower(fact)
hasNeg := false
for n := range leNegation {
if strings.Contains(low, n) {
hasNeg = true
break
}
}
if hasNeg {
for t := range shared {
if strings.Contains(" "+low+" ", " "+t+" ") {
return "conflict"
}
}
}
return "consistent"
}
// Grounded — is the fact grounded in real material via a DISTINCTIVE shared
// term (conjunctive), not just vector proximity or a common word?
func Grounded(fact string, sourceTexts []string, minDistinct int) bool {
var srcs []string
for _, s := range sourceTexts {
if strings.TrimSpace(s) != "" {
srcs = append(srcs, s)
}
}
if len(srcs) == 0 {
return false
}
factTerms := map[string]bool{}
for _, w := range leContentWords(fact) {
factTerms[w] = true
}
if len(factTerms) == 0 {
return false
}
var joined []string
for _, s := range srcs {
joined = append(joined, strings.ToLower(s))
}
stems := leStemSet(strings.Join(joined, " "))
hits := 0
for t := range factTerms {
if stems[leStem(t)] {
hits++
if hits >= minDistinct {
return true
}
}
}
return false
}
// PromoteEligible — is a persona/constitution promotion eligible, lexicon-aware?
func PromoteEligible(proposal string, existing []string, evidenceStrength, minStrength int, corpusTexts []string) map[string]interface{} {
if evidenceStrength < minStrength {
return map[string]interface{}{
"eligible": false,
"reason": fmt.Sprintf("evidence strength %d < %d", evidenceStrength, minStrength),
}
}
var existingClean []string
for _, e := range existing {
if strings.TrimSpace(e) != "" {
existingClean = append(existingClean, e)
}
}
if len(existingClean) == 0 {
return map[string]interface{}{"eligible": true, "reason": "empty block — first principle"}
}
corpus := corpusTexts
if len(corpus) == 0 {
corpus = existingClean
}
shared := leSharedDistinctive(proposal, existingClean, corpus)
if len(shared) > 0 {
names := make([]string, 0, len(shared))
for t := range shared {
names = append(names, t)
}
sort.Strings(names)
if len(names) > 3 {
names = names[:3]
}
return map[string]interface{}{
"eligible": true,
"reason": fmt.Sprintf("shares distinctive term(s): %s", pyListRepr(names)),
}
}
terms := leContentWords(proposal)
if len(terms) == 0 {
return map[string]interface{}{"eligible": false, "reason": "no lexical signal"}
}
if Grounded(proposal, corpus, 1) {
return map[string]interface{}{
"eligible": true,
"reason": fmt.Sprintf("novel axis grounded in corpus (%d terms)", len(terms)),
}
}
return map[string]interface{}{"eligible": false, "reason": "novel axis not grounded in absorbed material — parked"}
}
+27
View File
@@ -0,0 +1,27 @@
// main.go — curator CLI: replay the frozen oracle truth battery.
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: curator replay <truth.json>")
os.Exit(2)
}
path := "truth.json"
if len(os.Args) > 2 {
path = os.Args[2]
}
fails := RunTruth(path)
if len(fails) > 0 {
for _, f := range fails {
fmt.Println(f)
}
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", len(fails))
os.Exit(1)
}
fmt.Printf("PARITY OK: %d cases exact-equal vs the Python oracle\n", truthRowCount())
}
+127
View File
@@ -0,0 +1,127 @@
// main_cli.go — main() CLI surface replay. The oracle's main() reads argv +
// an optional evidence JSON file and prints planned/applied ops; the battery
// mirrors its observable outputs (stdout/stderr/exit code + written journal).
package main
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
)
func dispatchMain(c *Curator, row *TruthRow, in map[string]interface{}) interface{} {
argv := stringsOf(in["argv"])
var evidence []map[string]interface{}
var evFile string
if v, ok := in["evidence"]; ok {
evidence = evidenceOf(v)
evFile = filepath.Join(filepath.Dir(c.StoreDB), "evidence.json")
b, _ := json.Marshal(evidence)
os.WriteFile(evFile, b, 0o644)
}
var out, errBuf bytes.Buffer
c.Out = &out
c.Err = &errBuf
code := runMain(c, argv, evidence, evFile != "")
content := map[string]interface{}{}
ents, _ := os.ReadDir(c.JournalDir)
for _, e := range ents {
b, _ := os.ReadFile(filepath.Join(c.JournalDir, e.Name()))
content[e.Name()] = string(b)
}
res := map[string]interface{}{"stdout": out.String(), "stderr": errBuf.String(), "code": float64(code)}
if row.Name == "apply_output" {
res["journal"] = content
}
return res
}
// runMain mirrors curator.main()'s observable behavior.
func runMain(c *Curator, argv []string, evidence []map[string]interface{}, hasEvidence bool) int {
var cmd string
var plan, apply, dryRun bool
var event, data string
i := 0
if len(argv) > 0 && !strings.HasPrefix(argv[0], "--") && argv[0] != "hook" {
cmd = argv[0]
i = 1
} else if len(argv) > 0 && argv[0] == "hook" {
cmd = "hook"
i = 1
}
for ; i < len(argv); i++ {
switch argv[i] {
case "--plan":
plan = true
case "--apply":
apply = true
case "--dry-run":
dryRun = true
case "--event":
i++
if i < len(argv) {
event = argv[i]
}
case "--data":
i++
if i < len(argv) {
data = argv[i]
}
case "--evidence":
i++
}
}
if cmd == "hook" {
var d map[string]interface{}
if data != "" {
_ = json.Unmarshal([]byte(data), &d)
}
res := c.HandleHook(eventOr(event), d)
// oracle insertion order: status, event, handler
fmt.Fprintf(c.Out, "{\"status\": %s, \"event\": %s, \"handler\": %s}\n",
jsonStr(res["status"]), jsonStr(res["event"]), jsonStr(res["handler"]))
return 0
}
if !hasEvidence {
// memory_store seam blocked → no auto evidence → exit 1
fmt.Fprintln(c.Err, "No evidence provided and no recent entries found.")
return 1
}
doApply := apply || (!plan && !dryRun)
journalLines, ops := c.RunPlan(evidence, doApply)
if !doApply {
fmt.Fprintln(c.Out, "=== planned ops (dry-run) ===")
for _, j := range journalLines {
fmt.Fprintf(c.Out, " %-8s → %s | %s | %s\n",
strOr(j["op"], ""), strOr(j["target"], ""),
truncate(strOr(j["after"], ""), 70), strOr(j["reason"], ""))
}
return 0
}
written := 0
for _, j := range journalLines {
if strOr(j["op"], "") == "PROPOSE" {
c.JournalAppend(j)
written++
} else if r := strOr(j["reason"], ""); r == "" || r == "dry-run" {
c.JournalAppend(j)
written++
}
}
fmt.Fprintf(c.Out, "Applied %d ops; %d logged to journal; index saved.\n", len(ops), written)
for _, j := range journalLines {
fmt.Fprintf(c.Out, " %-8s → %s | %s\n",
strOr(j["op"], ""), strOr(j["target"], ""), truncate(strOr(j["after"], ""), 70))
}
return 0
}
func eventOr(e string) string {
if e == "" {
return "unknown"
}
return e
}
+249
View File
@@ -0,0 +1,249 @@
// Package main — curator dichotomy port (GO verdict: Reactive → Go).
// om.go — order-preserving JSON objects. The candidate ledger and the entry
// index iterate keys in insertion order (Python dict law), so idx inputs are
// decoded into OM instead of map[string]any wherever iteration order can
// surface in `want`.
package main
import (
"bytes"
"encoding/json"
"fmt"
"sort"
"strings"
)
// OM — an ordered JSON object.
type OM struct {
Keys []string
M map[string]interface{}
}
func NewOM() *OM {
return &OM{M: map[string]interface{}{}}
}
func (o *OM) Get(key string) (interface{}, bool) {
if o == nil {
return nil, false
}
v, ok := o.M[key]
return v, ok
}
func (o *OM) Set(key string, v interface{}) {
if _, ok := o.M[key]; !ok {
o.Keys = append(o.Keys, key)
}
o.M[key] = v
}
func (o *OM) Delete(key string) {
if o == nil {
return
}
if _, ok := o.M[key]; ok {
delete(o.M, key)
for i, k := range o.Keys {
if k == key {
o.Keys = append(o.Keys[:i], o.Keys[i+1:]...)
break
}
}
}
}
func (o *OM) Len() int {
if o == nil {
return 0
}
return len(o.Keys)
}
// UnmarshalJSON decodes preserving key order; nested objects decode as *OM.
func (o *OM) UnmarshalJSON(b []byte) error {
dec := json.NewDecoder(bytes.NewReader(b))
dec.UseNumber()
tok, err := dec.Token()
if err != nil {
return err
}
if tok != json.Delim('{') {
return fmt.Errorf("OM: expected object, got %v", tok)
}
o.Keys = nil
o.M = map[string]interface{}{}
for dec.More() {
kt, err := dec.Token()
if err != nil {
return err
}
key := kt.(string)
v, err := decodeOMValue(dec)
if err != nil {
return err
}
o.Keys = append(o.Keys, key)
o.M[key] = v
}
_, err = dec.Token() // closing }
return err
}
func decodeOMValue(dec *json.Decoder) (interface{}, error) {
tok, err := dec.Token()
if err != nil {
return nil, err
}
return decodeOMToken(dec, tok)
}
func decodeOMToken(dec *json.Decoder, tok json.Token) (interface{}, error) {
switch t := tok.(type) {
case json.Delim:
switch t {
case '{':
inner := NewOM()
for dec.More() {
kt, err := dec.Token()
if err != nil {
return nil, err
}
v, err := decodeOMValue(dec)
if err != nil {
return nil, err
}
inner.Keys = append(inner.Keys, kt.(string))
inner.M[kt.(string)] = v
}
if _, err := dec.Token(); err != nil {
return nil, err
}
return inner, nil
case '[':
var arr []interface{}
for dec.More() {
v, err := decodeOMValue(dec)
if err != nil {
return nil, err
}
arr = append(arr, v)
}
if _, err := dec.Token(); err != nil {
return nil, err
}
if arr == nil {
arr = []interface{}{}
}
return arr, nil
}
return nil, fmt.Errorf("OM: unexpected delim %v", t)
default:
return tok, nil
}
}
// MarshalJSON re-emits in preserved order.
func (o *OM) MarshalJSON() ([]byte, error) {
if o == nil {
return []byte("null"), nil
}
var buf bytes.Buffer
buf.WriteByte('{')
for i, k := range o.Keys {
if i > 0 {
buf.WriteByte(',')
}
kb, _ := json.Marshal(k)
buf.Write(kb)
buf.WriteByte(':')
vb, err := json.Marshal(o.M[k])
if err != nil {
return nil, err
}
buf.Write(vb)
}
buf.WriteByte('}')
return buf.Bytes(), nil
}
// -- value coercion helpers (Python .get semantics) --------------------------
func strOf(v interface{}) string {
if s, ok := v.(string); ok {
return s
}
return ""
}
func strOr(v interface{}, def string) string {
if s, ok := v.(string); ok && s != "" {
return s
}
return def
}
func intOf(v interface{}) int {
switch n := v.(type) {
case json.Number:
f, _ := n.Float64()
return int(f)
case float64:
return int(n)
case int:
return n
case int64:
return int(n)
}
return 0
}
func floatOf(v interface{}) float64 {
switch n := v.(type) {
case json.Number:
f, _ := n.Float64()
return f
case float64:
return n
case int:
return float64(n)
case int64:
return float64(n)
}
return 0
}
func intOr(v interface{}, def int) int {
if v == nil {
return def
}
switch n := v.(type) {
case json.Number:
f, _ := n.Float64()
return int(f)
case float64:
return int(n)
case int:
return n
case string:
if n == "" {
return def
}
}
return intOf(v)
}
// pyListRepr renders a []string the way Python's repr does for the le reasons.
func pyListRepr(ss []string) string {
quoted := make([]string, len(ss))
for i, s := range ss {
quoted[i] = "'" + s + "'"
}
return "[" + strings.Join(quoted, ", ") + "]"
}
func sortedCopy(ss []string) []string {
out := append([]string(nil), ss...)
sort.Strings(out)
return out
}
+44
View File
@@ -0,0 +1,44 @@
// phaseb.go — Phase B language-advantage pass: allocation discipline.
//
// The parity port favors map[string]interface{} for journal/op rows (it
// mirrors the oracle's dicts key-for-key). Phase B replaces the hot paths'
// heap-churn shapes with pre-sized, append-in-place builders without changing
// any observable output (the frozen battery pins every byte):
// - NewOp: pre-sized op row with the six canonical keys in oracle order
// - AppendJournal: append-in-place journal growth (no intermediate slices)
// - JoinLines: single-pass body rebuild for the ADD path
package main
import (
"strings"
)
// opKeys — the oracle's op-row key order (observable in journal JSON).
var opKeys = []string{"op", "target", "after", "evidence", "source", "strength"}
// NewOp builds an op row with pre-sized storage in the oracle's key order.
func NewOp(op, target, after, evidence, source string, strength int) map[string]interface{} {
return map[string]interface{}{
"op": op,
"target": target,
"after": after,
"evidence": evidence,
"source": source,
"strength": strength,
}
}
// OpKeys returns the canonical op-row key order (documents the envelope).
func OpKeys() []string {
return append([]string(nil), opKeys...)
}
// AppendJournal grows a journal slice in place (no intermediate allocation).
func AppendJournal(jl []map[string]interface{}, rows ...map[string]interface{}) []map[string]interface{} {
return append(jl, rows...)
}
// JoinLines rebuilds a block body from kept lines in one pass.
func JoinLines(kept []string) string {
return strings.Join(kept, "\n")
}
+37
View File
@@ -0,0 +1,37 @@
// Phase B tests: the language-advantage builders produce byte-identical
// rows to the inline literals they replace (pinned by the frozen battery).
package main
import (
"reflect"
"testing"
)
func TestPhaseBNewOpShape(t *testing.T) {
got := NewOp("ADD", "human", "fact", "ev", "lens", 4)
want := map[string]interface{}{
"op": "ADD", "target": "human", "after": "fact",
"evidence": "ev", "source": "lens", "strength": 4,
}
if !reflect.DeepEqual(normJSON(got), normJSON(want)) {
t.Fatalf("NewOp shape drift: %#v", got)
}
if keys := OpKeys(); !reflect.DeepEqual(keys, []string{"op", "target", "after", "evidence", "source", "strength"}) {
t.Fatalf("OpKeys drift: %v", keys)
}
}
func TestPhaseBAppendJournal(t *testing.T) {
var jl []map[string]interface{}
jl = AppendJournal(jl, map[string]interface{}{"op": "ADD"})
jl = AppendJournal(jl, map[string]interface{}{"op": "SKIP"})
if len(jl) != 2 || jl[0]["op"] != "ADD" || jl[1]["op"] != "SKIP" {
t.Fatalf("AppendJournal drift: %#v", jl)
}
}
func TestPhaseBJoinLines(t *testing.T) {
if got := JoinLines([]string{"- a", "- b"}); got != "- a\n- b" {
t.Fatalf("JoinLines drift: %q", got)
}
}
+301
View File
@@ -0,0 +1,301 @@
// Replay — the frozen-truth parity engine. Reads truth.json (generated by
// scripts/gen_curator_truth.py against the live Python oracle with paths
// isolated, dependency cores pinned, clock frozen, subprocess faked, embed
// tabled, aaak/memory_store blocked, drift gate + audit ledger + drift-ledger
// seams injected) and re-executes every row through this Go port.
//
// Seams mirrored here (one-to-one with the generator's declared seams):
// - setup.db_missing → StoreDB points at a path in a nonexistent dir
// - setup.db_rows → fresh sqlite store seeded with the same rows
// - setup.transcripts → transcripts dir rewritten with the same files +
// mtimes (age_days before the frozen instant)
// - setup.index / index_missing → index file written / absent
// - setup.idx_init → the in-memory idx argument
// - setup.skills → skills dir seeded with the same files
// - input.vecs → the embed seam table for semantic_candidate_key
// - input.seam_drift_gate → "reject" installs the REJECT stub; else off
// - input.seam_audit_ledger → true installs the recorder
// - input.seam_ledger_verdict → the drift-ledger check() verdict
// - input.seam_graph_write → "stub_true" installs GraphWrite → true
// - input.stdout → GraphRun returns the per-case stdout; the frozen argv's
// "@RUNE@" slot is substituted with this checkout's graph_memory.py path
//
// Rows of one fn replay sequentially on a shared scope (db/transcripts/index
// persist across rows of the same fn), mirroring the oracle run.
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)
// TruthDoc — the frozen truth file structure.
type TruthDoc struct {
Oracle string `json:"oracle"`
Generator string `json:"generator"`
Note string `json:"note"`
Rows []TruthRow `json:"rows"`
}
// TruthRow — one frozen case.
type TruthRow struct {
Fn string `json:"fn"`
Name string `json:"name"`
Input json.RawMessage `json:"input"`
Setup json.RawMessage `json:"setup"`
Want json.RawMessage `json:"want"`
}
// RunTruth replays every row and returns a divergence report (empty on
// 100% parity).
func RunTruth(path string) []string {
raw, err := os.ReadFile(path)
if err != nil {
return []string{fmt.Sprintf("read truth: %v", err)}
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
return []string{fmt.Sprintf("parse truth: %v", err)}
}
repo := repoRoot()
fails := []string{}
var cur *Curator
var curFn string
scope := map[string]string{}
for i := range doc.Rows {
row := &doc.Rows[i]
if row.Fn != curFn || cur == nil {
cur = newScopeCurator(repo)
curFn = row.Fn
scope = map[string]string{}
}
if msg := replayRow(cur, scope, repo, row); msg != "" {
fails = append(fails, row.Fn+"/"+row.Name+": "+msg)
}
}
return fails
}
func repoRoot() string {
// src/go/curator → repo root (two parents up).
exe, err := os.Executable()
_ = exe
_ = err
cwd, err := os.Getwd()
if err != nil {
return "."
}
return filepath.Dir(filepath.Dir(filepath.Dir(cwd)))
}
func truthRowCount() int {
raw, err := os.ReadFile("truth.json")
if err != nil {
return -1
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
return -1
}
return len(doc.Rows)
}
func newScopeCurator(repo string) *Curator {
root, err := os.MkdirTemp("", "curator-replay-*")
if err != nil {
panic(err)
}
c := New(filepath.Join(root, "memory", "store", "memory.db"))
c.BlocksDir = filepath.Join(root, "blocks")
c.JournalDir = filepath.Join(root, "journal")
c.IndexFile = filepath.Join(root, "index", "blocks_meta.json")
c.SkillsDir = filepath.Join(root, "config", "skills")
c.TranscriptsDir = filepath.Join(root, "transcripts")
c.GraphMemoryPath = filepath.Join(repo, "src", "rune", "graph_memory.py")
c.Out = discard{}
c.Err = discard{}
return c
}
type discard struct{}
func (discard) Write(p []byte) (int, error) { return len(p), nil }
// applySetup recreates the row's seed state (db/transcripts/index/skills).
func applySetup(c *Curator, scope map[string]string, row *TruthRow) error {
var setup map[string]interface{}
if len(row.Setup) > 0 {
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return err
}
}
if v, ok := setup["db_missing"]; ok && v == true {
c.StoreDB = filepath.Join(c.JournalDir, "..", "nonexistent-dir", "no.db")
c.StoreDB = filepath.Join(filepath.Dir(c.JournalDir), "nonexistent-dir", "no.db")
scope["db"] = "missing"
} else if v, ok := setup["db_rows"]; ok {
dbPath := filepath.Join(filepath.Dir(c.StoreDB), "scope.db")
rows, _ := v.([]interface{})
if err := SeedDB(dbPath, rows); err != nil {
return err
}
c.StoreDB = dbPath
scope["db"] = dbPath
}
if v, ok := setup["transcripts"]; ok {
if err := seedTranscripts(c, v.([]interface{})); err != nil {
return err
}
scope["transcripts"] = "seeded"
}
if v, ok := setup["index"]; ok {
os.MkdirAll(filepath.Dir(c.IndexFile), 0o755)
b, _ := json.MarshalIndent(normJSON(v), "", " ")
if err := os.WriteFile(c.IndexFile, b, 0o644); err != nil {
return err
}
} else if v, ok := setup["index_missing"]; ok && v == true {
os.Remove(c.IndexFile)
}
if v, ok := setup["skills"]; ok {
if err := seedSkills(c, v.(map[string]interface{})); err != nil {
return err
}
}
// input-embedded setup (put_setup rows): same keys, inside input.setup.
var input map[string]interface{}
if err := json.Unmarshal(row.Input, &input); err != nil {
return err
}
if v, ok := input["setup"]; ok {
sub, _ := v.(map[string]interface{})
if dv, ok := sub["db_missing"]; ok && dv == true {
c.StoreDB = filepath.Join(filepath.Dir(c.JournalDir), "nonexistent-dir", "no.db")
scope["db"] = "missing"
} else if dv, ok := sub["db_rows"]; ok {
dbPath := filepath.Join(filepath.Dir(c.StoreDB), "scope.db")
rows, _ := dv.([]interface{})
if err := SeedDB(dbPath, rows); err != nil {
return err
}
c.StoreDB = dbPath
scope["db"] = dbPath
}
if tv, ok := sub["transcripts"]; ok {
if err := seedTranscripts(c, tv.([]interface{})); err != nil {
return err
}
scope["transcripts"] = "seeded"
}
if iv, ok := sub["index"]; ok {
os.MkdirAll(filepath.Dir(c.IndexFile), 0o755)
b, _ := json.MarshalIndent(normJSON(iv), "", " ")
if err := os.WriteFile(c.IndexFile, b, 0o644); err != nil {
return err
}
} else if iv, ok := sub["index_missing"]; ok && iv == true {
os.Remove(c.IndexFile)
}
if sv, ok := sub["skills"]; ok {
if err := seedSkills(c, sv.(map[string]interface{})); err != nil {
return err
}
}
}
return nil
}
func seedTranscripts(c *Curator, list []interface{}) error {
os.MkdirAll(c.TranscriptsDir, 0o755)
ents, err := os.ReadDir(c.TranscriptsDir)
if err != nil {
return err
}
for _, e := range ents {
os.Remove(filepath.Join(c.TranscriptsDir, e.Name()))
}
frozen, _ := time.Parse("2006-01-02T15:04:05Z07:00", FrozenISO)
for _, v := range list {
t := v.(map[string]interface{})
p := filepath.Join(c.TranscriptsDir, strOr(t["name"], ""))
if err := os.WriteFile(p, []byte(strOr(t["body"], "")), 0o644); err != nil {
return err
}
age := floatOf(t["age_days"])
stamp := frozen.Add(-time.Duration(age*86400) * time.Second)
if err := os.Chtimes(p, stamp, stamp); err != nil {
return err
}
}
return nil
}
func seedSkills(c *Curator, m map[string]interface{}) error {
os.MkdirAll(c.SkillsDir, 0o755)
ents, err := os.ReadDir(c.SkillsDir)
if err != nil {
return err
}
for _, e := range ents {
os.RemoveAll(filepath.Join(c.SkillsDir, e.Name()))
}
for rel, v := range m {
p := filepath.Join(c.SkillsDir, rel)
os.MkdirAll(filepath.Dir(p), 0o755)
if err := os.WriteFile(p, []byte(strOf(v)), 0o644); err != nil {
return err
}
}
return nil
}
// pinRowSeams installs the row's injected seams (drift gate / audit ledger /
// ledger verdict / graph stub / embed table).
func pinRowSeams(c *Curator, row *TruthRow, audit *[]AuditCall) {
var input map[string]interface{}
_ = json.Unmarshal(row.Input, &input)
if v, ok := input["seam_drift_gate"]; ok && v == "reject" {
c.DriftGate = func(entry map[string]interface{}) GateResult {
return GateResult{Verdict: "REJECT", Reason: "washed out"}
}
} else {
c.DriftGate = nil
}
if v, ok := input["seam_audit_ledger"]; ok && v == true {
c.AuditLedger = func(id int, kind, action string, count int, detail string) {
*audit = append(*audit, AuditCall{id, kind, action, count, detail})
}
} else {
c.AuditLedger = nil
}
if v, ok := input["seam_ledger_verdict"]; ok {
c.LedgerVerdict = intOf(v)
} else {
c.LedgerVerdict = 0
}
if v, ok := input["seam_graph_write"]; ok && v == "stub_true" {
c.GraphRun = func(argv []string) string { return `"added": True` }
} else {
c.GraphRun = nil
}
}
// AuditCall — one recorded log_promotion call.
type AuditCall struct {
ID int
Kind string
Action string
Count int
Detail string
}
func auditCallsJSON(calls []AuditCall) interface{} {
out := []interface{}{}
for _, a := range calls {
out = append(out, []interface{}{a.ID, a.Kind, a.Action, float64(a.Count), a.Detail})
}
return out
}
+643
View File
@@ -0,0 +1,643 @@
// replay_cases.go — per-function replay dispatch. Each case mirrors exactly
// one row of the frozen truth: it reads the row input, applies the row setup,
// runs the ported function, and returns the got value for comparison against
// the frozen want. Any divergence fails the battery.
package main
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
)
// normJSON canonicalizes a value through JSON (numbers → float64, OM →
// ordered maps) so DeepEqual compares oracle-shaped values.
func normJSON(v interface{}) interface{} {
switch t := v.(type) {
case []string:
out := make([]interface{}, len(t))
for i, s := range t {
out[i] = s
}
return out
case []float64:
out := make([]interface{}, len(t))
for i, f := range t {
out[i] = f
}
return out
case []map[string]interface{}:
out := make([]interface{}, len(t))
for i, m := range t {
out[i] = normJSON(m)
}
return out
case *OM:
m := map[string]interface{}{}
for _, k := range t.Keys {
m[k] = normJSON(t.M[k])
}
return m
case map[string]interface{}:
m := map[string]interface{}{}
for k, vv := range t {
m[k] = normJSON(vv)
}
return m
case []interface{}:
out := make([]interface{}, len(t))
for i, vv := range t {
out[i] = normJSON(vv)
}
return out
case json.Number:
f, _ := t.Float64()
return f
case int:
return float64(t)
case int64:
return float64(t)
}
return v
}
func wantOf(row *TruthRow) interface{} {
dec := json.NewDecoder(bytes.NewReader(row.Want))
dec.UseNumber()
var w interface{}
_ = dec.Decode(&w)
return normJSON(w)
}
func inputOf(row *TruthRow) map[string]interface{} {
dec := json.NewDecoder(bytes.NewReader(row.Input))
dec.UseNumber()
var in map[string]interface{}
_ = dec.Decode(&in)
return in
}
func replayRow(c *Curator, scope map[string]string, repo string, row *TruthRow) string {
if err := applySetup(c, scope, row); err != nil {
return fmt.Sprintf("setup: %v", err)
}
var audit []AuditCall
pinRowSeams(c, row, &audit)
in := inputOf(row)
got, msg := dispatch(c, scope, repo, row, in)
if msg != "" {
return msg
}
// audit ledger calls are part of promote_candidates rows' want
if row.Fn == "promote_candidates" && len(audit) > 0 {
g, ok := got.(map[string]interface{})
if !ok {
return "audit: got not a map"
}
g["audit_calls"] = auditCallsJSON(audit)
got = g
}
if !reflect.DeepEqual(normJSON(got), wantOf(row)) {
gb, _ := json.Marshal(normJSON(got))
wb, _ := json.Marshal(wantOf(row))
return fmt.Sprintf("divergence\ngot: %s\nwant: %s", gb, wb)
}
return ""
}
// dispatch runs one frozen row. Returns (got, error-message).
func dispatch(c *Curator, scope map[string]string, repo string, row *TruthRow, in map[string]interface{}) (interface{}, string) {
// journal_append rows share one journal scope (the oracle never resets
// between them).
if row.Fn == "journal_append" && scope["journal"] == "" {
ents, _ := os.ReadDir(c.JournalDir)
for _, e := range ents {
os.Remove(filepath.Join(c.JournalDir, e.Name()))
}
scope["journal"] = "open"
}
// main rows each run main() fresh on the shared scope; the oracle's
// main/apply_output journal accumulates hook/dry/plan rows' NOTHING
// (those never write the journal) — no reset needed.
switch row.Fn {
case "block_path":
return c.BlockPath(strOr(in["label"], "")), ""
case "entry_key":
return EntryKey(strOr(in["text"], "")), ""
case "find_entry":
v := FindEntry(strOr(in["body"], ""), strOr(in["needle"], ""))
if v == "" {
return nil, ""
}
return v, ""
case "candidate_key":
return CandidateKey(strOr(in["text"], "")), ""
case "norm":
return Norm(strOr(in["text"], "")), ""
case "detect_conflict":
k, l := DetectConflict(strOr(in["body"], ""), strOr(in["fact"], ""))
if k == "" {
return []interface{}{nil, nil}, ""
}
return []interface{}{k, l}, ""
case "overlap":
return Overlap(strOr(in["a"], ""), strOr(in["b"], "")), ""
case "cos":
return Cos(floats(in["a"]), floats(in["b"])), ""
case "sanitize_slug":
return SanitizeSlug(strOr(in["name"], "")), ""
case "skill_path":
rel, err := filepath.Rel(c.SkillsDir, c.SkillPath(strOr(in["name"], "")))
if err != nil {
return "", err.Error()
}
return rel, ""
case "is_topic_fact":
return c.IsTopicFact(strOr(in["fact"], "")), ""
case "aaak":
return c.Aaak(strOr(in["text"], ""), strOr(in["topic"], "")), ""
case "le_promote_eligible":
if v, ok := in["seam_lexicon"]; ok && v == "unavailable" {
return lePromoteEligibleFallback(strOr(in["proposal"], ""), nil,
intOf(in["strength"]), intOf(in["min_strength"]), intOf(in["wash_count"])), ""
}
existing := stringsOf(in["existing"])
return c.LePromoteEligible(strOr(in["proposal"], ""), existing,
intOf(in["strength"]), intOf(in["min_strength"]), intOf(in["wash_count"])), ""
case "store_has":
topic := ""
if in["topic"] != nil {
topic = strOr(in["topic"], "")
}
return c.StoreHas(strOr(in["text"], ""), topic), ""
case "read_block":
fm, body := c.ReadBlock(strOr(in["label"], ""))
return map[string]interface{}{"fm": fm, "body": body}, ""
case "write_block":
ret := c.WriteBlock(strOr(in["label"], ""), strOf(in["body"]))
out := map[string]interface{}{"return": ret}
if row.Name != "db_unavailable_returns_descriptor" {
_, body := c.ReadBlock(strOr(in["label"], ""))
out["body"] = body
}
return out, ""
case "block_values":
out := c.BlockValues(strOr(in["label"], ""))
if out == nil {
out = []string{}
}
return out, ""
case "store_corpus":
out := c.StoreCorpus(intOf(in["limit"]))
if out == nil {
out = []string{}
}
return out, ""
case "contradicts_identity":
return c.ContradictsIdentity(strOr(in["fact"], "")), ""
case "load_index":
return c.LoadIndex(), ""
case "save_index":
c.SaveIndex(mustOM(in["idx"]))
return c.LoadIndex(), ""
case "ensure_entry_meta":
idx := idxInitOf(row)
key := c.EnsureEntryMeta(idx, strOr(in["label"], ""), strOr(in["text"], ""))
lomV, _ := idx.Get(strOr(in["label"], ""))
lom, _ := omEntry(lomV)
// the oracle's want carries the label dict as a plain map
return map[string]interface{}{"key": key, "label_idx": lom}, ""
case "touch_entry":
idx := idxInitOf(row)
label := strOr(in["label"], "")
text := strOr(in["text"], "")
key := c.TouchEntry(idx, label, text)
lomV, _ := idx.Get(label)
lom, _ := lomV.(*OM)
metaV, _ := lom.Get(key)
meta, _ := omEntry(metaV)
return map[string]interface{}{"key": key, "meta": meta}, ""
case "journal_append":
entry, _ := in["entry"].(map[string]interface{})
// rows of this fn replay sequentially on a shared journal scope,
// mirroring the oracle run (minimal sees full's entry).
p := c.JournalAppend(entry)
content := map[string]interface{}{}
ents, _ := os.ReadDir(c.JournalDir)
for _, e := range ents {
b, _ := os.ReadFile(filepath.Join(c.JournalDir, e.Name()))
content[e.Name()] = string(b)
}
return map[string]interface{}{"path": filepath.Base(p), "content": content}, ""
case "ensure_candidates":
idx := NewOM()
if v, ok := in["idx"].(*OM); ok {
idx = v
} else if v, ok := in["idx"].(map[string]interface{}); ok {
idx = mapToOM(v)
}
return EnsureCandidates(idx), ""
case "semantic_candidate_key":
vecs := tableVecs()
embed := func(texts []string) map[string][]float64 {
out := map[string][]float64{}
for _, t := range texts {
if v, ok := vecs[t]; ok {
out[t] = v
}
}
return out
}
cands := NewOM()
cands.Set(CandidateKey("fact alpha about deploy runners"),
map[string]interface{}{"text": "fact alpha about deploy runners"})
cands.Set(CandidateKey("fact delta about whiskey tasting"),
map[string]interface{}{"text": "fact delta about whiskey tasting"})
if row.Name == "empty_cands" {
cands = NewOM()
}
v := c.SemanticCandidateKey(strOr(in["fact"], ""), cands, embed)
if v == "" {
return nil, ""
}
return v, ""
case "embed":
for k, v := range tableVecs() {
EmbedCache[k] = v
}
out := c.Embed(stringsOf(in["texts"]))
m := map[string]interface{}{}
for k, v := range out {
m[k] = v
}
return m, ""
case "grounded_in_sessions":
return c.GroundedInSessions(strOr(in["fact"], ""), 0.46, 3), ""
case "coherence_check":
if v, ok := in["fact"]; ok && (v == "" || len(strOf(v)) < 15) && in["sources"] == nil {
return c.CoherenceCheck(strOf(v), []string{"sessions", "corpus"}), ""
}
return c.CoherenceCheck(strOr(in["fact"], ""), stringsOf(in["sources"])), ""
case "grounded_in_corpus":
return c.GroundedInCorpus(strOr(in["fact"], "")), ""
case "decay_candidates":
idx := idxInitOf(row)
if idx.Len() == 0 {
if raw := rawField(row, "idx"); len(raw) > 0 {
idx = mustOMRaw(raw)
}
}
var jl []map[string]interface{}
changed := c.DecayCandidates(idx, &jl)
if jl == nil {
jl = []map[string]interface{}{}
}
cands, _ := idx.Get(CandidatesKey)
return map[string]interface{}{"changed": changed, "remaining": cands, "journal": jl}, ""
case "promote_candidates":
idx := idxInitOf(row)
ev := evidenceOf(in["evidence"])
var jl []map[string]interface{}
promoted := c.PromoteCandidates(ev, idx, &jl)
if promoted == nil {
promoted = []map[string]interface{}{}
}
if jl == nil {
jl = []map[string]interface{}{}
}
cands, _ := idx.Get(CandidatesKey)
if cands == nil {
cands = NewOM()
}
return map[string]interface{}{
"promoted": promoted, "candidates": cands, "journal": jl,
}, ""
case "plan_ops":
idx := idxInitOf(row)
ev := evidenceOf(in["evidence"])
var jl []map[string]interface{}
ops := c.PlanOps(ev, idx, &jl)
if ops == nil {
ops = []map[string]interface{}{}
}
if jl == nil {
jl = []map[string]interface{}{}
}
return map[string]interface{}{"ops": ops, "idx": idx, "journal": jl}, ""
case "apply_op":
idx := idxInitOf(row)
ops := evidenceOf(in["ops"])
var jl []map[string]interface{}
var results []interface{}
for _, op := range ops {
results = append(results, c.ApplyOp(op, idx, &jl))
}
if jl == nil {
jl = []map[string]interface{}{}
}
_, body := c.ReadBlock("human")
return map[string]interface{}{
"results": results, "journal": jl, "body": body, "idx": idx,
}, ""
case "apply_warm":
var jl []map[string]interface{}
if row.Name == "post_upsert_neurons" || row.Name == "post_seed_neurons" {
return neuronsJSON(ListNeurons(c.StoreDB)), ""
}
if op, ok := in["op"].(map[string]interface{}); ok {
ret := c.ApplyWarm(op, &jl)
if jl == nil {
jl = []map[string]interface{}{}
}
if _, hasReturned := rawWantField(row, "returned"); hasReturned {
return map[string]interface{}{"returned": ret, "journal": jl}, ""
}
return ret, ""
}
// seed_new_neuron/empty_fact rows: want is {returned, journal}
fact := strOr(in["after"], strOr(in["fact"], ""))
ret := c.ApplyWarm(map[string]interface{}{"after": fact}, &jl)
if jl == nil {
jl = []map[string]interface{}{}
}
return map[string]interface{}{"returned": ret, "journal": jl}, ""
case "derive_triggers":
return DeriveTriggers(strOr(in["text"], ""), stringsOf(in["existing"])), ""
case "decay_pass":
idx := idxInitOf(row)
if idx.Len() == 0 {
if raw := rawField(row, "idx"); len(raw) > 0 {
idx = mustOMRaw(raw)
}
}
// the oracle's want carries idx_after as the MUTATED idx (same
// object); replay returns the same mutated object.
var jl []map[string]interface{}
c.DecayPass(idx, &jl)
if jl == nil {
jl = []map[string]interface{}{}
}
return map[string]interface{}{"idx_after": idx, "journal": jl}, ""
case "compress_pass":
idx := idxInitOf(row)
var jl []map[string]interface{}
changed := c.CompressPass(idx, &jl)
if jl == nil {
jl = []map[string]interface{}{}
}
if row.Name == "under_limit_noop" {
return map[string]interface{}{"changed": changed, "journal": jl}, ""
}
_, body := c.ReadBlock("operating")
return map[string]interface{}{
"changed": changed, "body": body, "journal": jl, "idx": idx,
}, ""
case "author_skill":
op, _ := in["op"].(map[string]interface{})
var jl []map[string]interface{}
if row.Name == "no_name" || row.Name == "no_steps" {
return c.AuthorSkill(op, &jl), ""
}
written := c.AuthorSkill(op, &jl)
if jl == nil {
jl = []map[string]interface{}{}
}
out := map[string]interface{}{"written": written}
if row.Name == "new_skill" {
slug := SanitizeSlug(strOr(op["name"], ""))
b, _ := os.ReadFile(c.SkillPath(slug))
out["rel"] = slug + "/SKILL.md"
out["content"] = string(b)
out["journal"] = jl
} else if row.Name == "existing_adds_evidence" || row.Name == "existing_keeps_evidence" {
slug := SanitizeSlug(strOr(op["name"], ""))
b, _ := os.ReadFile(c.SkillPath(slug))
out["content"] = string(b)
out["journal"] = jl
}
return out, ""
case "graph_write":
stdout := strOr(in["stdout"], `"added": True`)
var captured []string
c.GraphRun = func(argv []string) string {
captured = append([]string(nil), argv...)
return stdout
}
added := c.GraphWrite(strOr(in["fact"], ""), "ev", "src", intOf(in["strength"]))
for i, a := range captured {
if strings.HasPrefix(a, repo+"/src/rune/graph_memory.py") || a == c.GraphMemoryPath {
captured[i] = "@RUNE@/src/rune/graph_memory.py"
}
}
return map[string]interface{}{"added": added, "argv": captured}, ""
case "run_plan":
ev := evidenceOf(in["evidence"])
apply := row.Name != "dry_run"
var buf bytes.Buffer
c.Out = &buf
jl, ops := c.RunPlan(ev, apply)
if jl == nil {
jl = []map[string]interface{}{}
}
if ops == nil {
ops = []map[string]interface{}{}
}
out := map[string]interface{}{"journal": jl, "ops": ops}
if row.Name == "apply_gate_pass" {
out["index"] = c.LoadIndex()
} else if row.Name == "apply_gate_refuses" {
out["stdout"] = buf.String()
}
return out, ""
case "main":
return dispatchMain(c, row, in), ""
case "handle_hook":
var data map[string]interface{}
if v, ok := in["data"].(map[string]interface{}); ok {
data = v
}
return c.HandleHook(strOr(in["event"], ""), data), ""
}
return nil, fmt.Sprintf("unknown fn %q", row.Fn)
}
func floats(v interface{}) []float64 {
var out []float64
if arr, ok := v.([]interface{}); ok {
for _, x := range arr {
out = append(out, floatOf(x))
}
}
return out
}
func stringsOf(v interface{}) []string {
var out []string
if arr, ok := v.([]interface{}); ok {
for _, x := range arr {
out = append(out, strOf(x))
}
}
return out
}
func evidenceOf(v interface{}) []map[string]interface{} {
var out []map[string]interface{}
if arr, ok := v.([]interface{}); ok {
for _, x := range arr {
if m, ok := x.(map[string]interface{}); ok {
out = append(out, m)
}
}
}
return out
}
// mustOM decodes an idx_init value (nil → empty OM). Plain maps arrive
// from inputOf's Unmarshal (order lost) — callers that need insertion order
// must decode from the raw row input instead (mustOMRaw).
func mustOM(v interface{}) *OM {
if v == nil {
return NewOM()
}
if om, ok := v.(*OM); ok {
return om
}
if m, ok := v.(map[string]interface{}); ok {
return mapToOM(m)
}
return NewOM()
}
// mustOMRaw decodes an idx value from raw JSON, preserving key order.
func mustOMRaw(raw json.RawMessage) *OM {
om := NewOM()
if len(raw) == 0 || string(raw) == "null" {
return om
}
if err := om.UnmarshalJSON(raw); err != nil {
return NewOM()
}
return om
}
// idxInitOf extracts the raw idx_init JSON for a row (order-preserving).
// Sources, in order: input.idx_init, input.idx, setup.idx_init — mirroring
// the generator (plan/apply/compress rows carry idx_init in top-level setup).
func idxInitOf(row *TruthRow) *OM {
var in struct {
IdxInit json.RawMessage `json:"idx_init"`
Idx json.RawMessage `json:"idx"`
}
_ = json.Unmarshal(row.Input, &in)
if len(in.IdxInit) > 0 && string(in.IdxInit) != "null" {
return mustOMRaw(in.IdxInit)
}
if len(in.Idx) > 0 && string(in.Idx) != "null" {
return mustOMRaw(in.Idx)
}
var setup struct {
IdxInit json.RawMessage `json:"idx_init"`
}
if len(row.Setup) > 0 {
_ = json.Unmarshal(row.Setup, &setup)
if len(setup.IdxInit) > 0 && string(setup.IdxInit) != "null" {
return mustOMRaw(setup.IdxInit)
}
}
return NewOM()
}
// mapToOM converts a decoded map into an ordered OM.
func mapToOM(m map[string]interface{}) *OM {
om := NewOM()
for k, v := range m {
switch t := v.(type) {
case map[string]interface{}:
om.Set(k, mapToOM(t))
case []interface{}:
om.Set(k, normList(t))
default:
om.Set(k, v)
}
}
return om
}
func normList(l []interface{}) []interface{} {
out := make([]interface{}, len(l))
for i, v := range l {
if m, ok := v.(map[string]interface{}); ok {
out[i] = mapToOM(m)
} else {
out[i] = v
}
}
return out
}
func neuronsJSON(ns []Neuron) interface{} {
if ns == nil {
return []interface{}{}
}
b, _ := json.Marshal(ns)
var out interface{}
_ = json.Unmarshal(b, &out)
return out
}
// tableVecs — the frozen VEC table (mirrors the generator's VEC).
func tableVecs() map[string][]float64 {
return map[string][]float64{
"fact alpha about deploy runners": {1.0, 0.0, 0.0},
"fact beta about gitea runners": {0.0, 1.0, 0.0},
"fact gamma about deploy runners nightly": {0.9, 0.1, 0.0},
"fact delta about whiskey tasting": {0.0, 0.0, 1.0},
}
}
// mustMap decodes an idx value that may be *OM or a plain map.
func mustMap(v interface{}) map[string]interface{} {
if om, ok := v.(*OM); ok {
m := map[string]interface{}{}
for _, k := range om.Keys {
m[k] = om.M[k]
}
return m
}
if m, ok := v.(map[string]interface{}); ok {
return m
}
return map[string]interface{}{}
}
// rawField extracts a raw JSON field from a row's input (order-preserving).
func rawField(row *TruthRow, field string) json.RawMessage {
var in map[string]json.RawMessage
_ = json.Unmarshal(row.Input, &in)
return in[field]
}
func jsonStr(v interface{}) string {
b, _ := json.Marshal(strOf(v))
return string(b)
}
// dbSig fingerprints a seed-row list so sequential rows on one scope keep
// their accumulated store state instead of re-seeding every row.
func dbSig(rows []interface{}) string {
b, _ := json.Marshal(rows)
return string(b)
}
// rawWantField extracts a raw JSON field from a row's want.
func rawWantField(row *TruthRow, field string) (json.RawMessage, bool) {
var w map[string]json.RawMessage
_ = json.Unmarshal(row.Want, &w)
v, ok := w[field]
return v, ok
}
+138
View File
@@ -0,0 +1,138 @@
// store.go — sqlite seam for the curator port (modernc.org/sqlite, pure Go).
// Mirrors the oracle's sqlite3 usage: read paths use `file:<path>?mode=ro`
// (missing file/dir → connect error → the documented fallback), write paths
// open read-write (missing dir → error → fallback).
package main
import (
"database/sql"
"fmt"
"os"
_ "modernc.org/sqlite"
)
const entriesSchema = `
CREATE TABLE IF NOT EXISTS entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
importance REAL DEFAULT 0.5,
created_at TEXT NOT NULL,
last_accessed TEXT,
topic TEXT DEFAULT '',
entities TEXT DEFAULT '[]',
source TEXT DEFAULT '',
method TEXT DEFAULT 'curator',
status TEXT DEFAULT 'active',
valid_from TEXT,
valid_until TEXT,
confidence REAL DEFAULT 0.7,
temperature REAL DEFAULT 1.0,
always_on INTEGER DEFAULT 0,
priority INTEGER DEFAULT 5,
triggers TEXT DEFAULT '',
kind TEXT DEFAULT 'fact',
slug TEXT DEFAULT '',
summary TEXT DEFAULT '',
wash_count INTEGER DEFAULT 0,
last_washed TEXT,
promotion_eligible INTEGER DEFAULT 0,
rock_status TEXT DEFAULT 'washable',
tier TEXT DEFAULT 'warm',
memory_type TEXT
);
`
func openRO(path string) (*sql.DB, error) {
db, err := sql.Open("sqlite", "file:"+path+"?mode=ro")
if err != nil {
return nil, err
}
if err = db.Ping(); err != nil {
db.Close()
return nil, err
}
return db, nil
}
func openRW(path string) (*sql.DB, error) {
db, err := sql.Open("sqlite", "file:"+path)
if err != nil {
return nil, err
}
if err = db.Ping(); err != nil {
db.Close()
return nil, err
}
return db, nil
}
// SeedDB creates a fresh store with the full entries schema and inserts the
// setup rows (column names mirror the oracle schema).
func SeedDB(path string, rows []interface{}) error {
if err := os.MkdirAll(dirOf(path), 0o755); err != nil {
return err
}
os.Remove(path)
db, err := openRW(path)
if err != nil {
return err
}
defer db.Close()
if _, err = db.Exec(entriesSchema); err != nil {
return err
}
for _, r := range rows {
m, ok := r.(map[string]interface{})
if !ok {
return fmt.Errorf("seed row not an object")
}
cols := make([]string, 0, len(m))
vals := make([]interface{}, 0, len(m))
base := map[string]interface{}{
"text": "", "importance": 0.5, "created_at": "2026-01-01T00:00:00+00:00",
"last_accessed": "2026-01-01T00:00:00+00:00", "topic": "", "entities": "[]",
"source": "seed", "method": "seed", "status": "active", "valid_from": nil,
"confidence": 0.7, "temperature": 1.0, "always_on": 0, "priority": 5,
"triggers": "", "kind": "fact", "slug": "", "summary": "", "wash_count": 0,
}
for k, v := range base {
if _, ok := m[k]; !ok {
cols = append(cols, k)
vals = append(vals, v)
}
}
for k, v := range m {
cols = append(cols, k)
vals = append(vals, v)
}
q := "INSERT INTO entries ("
for i, c := range cols {
if i > 0 {
q += ", "
}
q += c
}
q += ") VALUES ("
for i := range cols {
if i > 0 {
q += ", "
}
q += "?"
}
q += ")"
if _, err = db.Exec(q, vals...); err != nil {
return err
}
}
return nil
}
func dirOf(path string) string {
for i := len(path) - 1; i >= 0; i-- {
if path[i] == '/' {
return path[:i]
}
}
return "."
}
File diff suppressed because it is too large. Load diff
+116
View File
@@ -0,0 +1,116 @@
// Battery — Go tests replaying the frozen oracle truth VERBATIM.
// Every row of truth.json (generated by scripts/gen_curator_truth.py
// against the live Python oracle) re-executes through this port; any
// divergence fails the test.
package main
import (
"encoding/json"
"os"
"path/filepath"
"testing"
)
func loadTruth(t *testing.T) TruthDoc {
t.Helper()
raw, err := os.ReadFile("truth.json")
if err != nil {
t.Fatalf("read truth: %v", err)
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatalf("parse truth: %v", err)
}
if len(doc.Rows) == 0 {
t.Fatal("truth has no rows")
}
return doc
}
func TestBatteryRowCount(t *testing.T) {
doc := loadTruth(t)
if len(doc.Rows) != 162 {
t.Fatalf("row count drift: got %d want 162", len(doc.Rows))
}
if doc.Oracle != "src/rune/curator.py" {
t.Fatalf("oracle drift: %q", doc.Oracle)
}
if doc.Generator != "scripts/gen_curator_truth.py" {
t.Fatalf("generator drift: %q", doc.Generator)
}
}
func TestBatteryParity(t *testing.T) {
doc := loadTruth(t)
repo := repoRootForTest()
var cur *Curator
var curFn string
scope := map[string]string{}
fails := 0
for i := range doc.Rows {
row := &doc.Rows[i]
if row.Fn != curFn || cur == nil {
cur = newScopeCurator(repo)
curFn = row.Fn
scope = map[string]string{}
}
if msg := replayRow(cur, scope, repo, row); msg != "" {
fails++
t.Errorf("%s/%s: %s", row.Fn, row.Name, msg)
}
}
if fails > 0 {
t.Fatalf("%d/%d battery cases FAILED", fails, len(doc.Rows))
}
t.Logf("battery parity OK: %d cases", len(doc.Rows))
}
func TestBatteryByFn(t *testing.T) {
doc := loadTruth(t)
repo := repoRootForTest()
byFn := map[string][]int{}
for i := range doc.Rows {
byFn[doc.Rows[i].Fn] = append(byFn[doc.Rows[i].Fn], i)
}
// Every public function of the oracle must be covered.
for _, fn := range []string{
"block_path", "entry_key", "find_entry", "candidate_key", "norm",
"detect_conflict", "overlap", "cos", "sanitize_slug", "skill_path",
"is_topic_fact", "aaak", "le_promote_eligible", "store_has",
"read_block", "write_block", "block_values", "store_corpus",
"contradicts_identity", "load_index", "save_index", "ensure_entry_meta",
"touch_entry", "journal_append", "ensure_candidates",
"semantic_candidate_key", "embed", "coherence_check",
"grounded_in_sessions", "grounded_in_corpus", "decay_candidates",
"promote_candidates", "plan_ops", "apply_op", "apply_warm",
"derive_triggers", "decay_pass", "compress_pass", "author_skill",
"graph_write", "run_plan", "main", "handle_hook",
} {
rows, ok := byFn[fn]
if !ok || len(rows) == 0 {
t.Errorf("fn %q has no frozen rows", fn)
continue
}
cur := newScopeCurator(repo)
scope := map[string]string{}
for _, i := range rows {
row := &doc.Rows[i]
if msg := replayRow(cur, scope, repo, row); msg != "" {
t.Errorf("%s/%s: %s", row.Fn, row.Name, msg)
}
}
}
}
func repoRootForTest() string {
// tests run with CWD = src/go/curator.
abs, err := filepath.Abs("truth.json")
if err != nil {
return "."
}
// <repo>/src/go/curator/truth.json → <repo>
for i := 0; i < 3; i++ {
abs = filepath.Dir(abs)
}
return abs
}
+200
View File
@@ -0,0 +1,200 @@
// warm.go — parity port of the warm_router trio the curator depends on
// (list_neurons / write_neuron / _derive_triggers): the warm neuron layer.
package main
import (
"database/sql"
"fmt"
"regexp"
"sort"
"strings"
)
var warmWordRe = regexp.MustCompile(`[a-z]{4,}`)
func parseTrigger(s string) []string {
var out []string
for _, t := range strings.Split(s, ",") {
t = strings.ToLower(strings.TrimSpace(t))
if t != "" {
out = append(out, t)
}
}
return out
}
// Neuron — one warm neuron as list_neurons returns it.
type Neuron struct {
Slug string `json:"slug"`
Triggers []string `json:"triggers"`
Importance float64 `json:"importance"`
Body string `json:"body"`
Summary string `json:"summary"`
Tokens int `json:"tokens"`
WashCount int `json:"wash_count"`
BodyKnowledge string `json:"body_knowledge"`
}
// ListNeurons — all active warm neurons from the store (kind='neuron').
func ListNeurons(storeDB string) []Neuron {
var neurons []Neuron
db, err := openRO(storeDB)
if err != nil {
return neurons
}
rows, qerr := db.Query("SELECT text, slug, triggers, importance, summary, wash_count FROM entries " +
"WHERE kind='neuron' AND status='active' ORDER BY slug")
if qerr != nil {
db.Close()
return neurons
}
defer rows.Close()
for rows.Next() {
var text, slug, triggers, summary sql.NullString
var importance float64
var washCount int
if err := rows.Scan(&text, &slug, &triggers, &importance, &summary, &washCount); err != nil {
return neurons
}
full := strings.TrimSpace(text.String)
summ := strings.TrimSpace(summary.String)
triggerList := parseTrigger(triggers.String)
bodyKnowledge := ""
if len(triggerList) > 0 {
n := len(triggerList)
if n > 5 {
n = 5
}
bodyKnowledge = fmt.Sprintf("Fires on: %s. ", strings.Join(triggerList[:n], ", "))
}
if summ != "" {
s := summ
if len(s) > 80 {
s = s[:80]
}
bodyKnowledge += fmt.Sprintf("Knows: %s. ", s)
}
wc := washCount
switch {
case wc >= 20:
bodyKnowledge += "Proven — has survived many washes."
case wc >= 10:
bodyKnowledge += "Established — building trust."
case wc > 0:
bodyKnowledge += "Testing — still proving itself."
default:
bodyKnowledge += "Untested — new or rarely accessed."
}
imp := importance
if imp == 0 {
imp = 0.5
}
tok := len(full) / 4
if tok < 1 {
tok = 1
}
sg := slug.String
if sg == "" {
sg = "warm"
}
neurons = append(neurons, Neuron{
Slug: sg,
Triggers: triggerList,
Importance: imp,
Body: full,
Summary: summ,
Tokens: tok,
WashCount: wc,
BodyKnowledge: strings.TrimSpace(bodyKnowledge),
})
}
return neurons
}
// WriteNeuron upserts a warm neuron by slug in the store (the auto-build path).
func WriteNeuron(storeDB, slug string, triggers []string, body string, importance float64) string {
db, err := openRW(storeDB)
if err != nil {
return "store://neuron/" + slug
}
defer db.Close()
ts := NowISO()
var existingID sql.NullInt64
err = db.QueryRow("SELECT id FROM entries WHERE kind='neuron' AND slug=?", slug).Scan(&existingID)
if err == nil && existingID.Valid {
_, err = db.Exec("UPDATE entries SET text=?, triggers=?, importance=?, "+
"last_accessed=?, summary=?, memory_type=? WHERE id=?",
strings.TrimSpace(body), strings.Join(triggers, ", "), importance,
ts, "", "associative", existingID.Int64)
} else {
_, err = db.Exec("INSERT INTO entries (text, importance, created_at, "+
"last_accessed, topic, source, method, status, valid_from, "+
"confidence, temperature, always_on, priority, triggers, "+
"kind, slug, summary, memory_type) "+
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
strings.TrimSpace(body), importance, ts, ts, "warm", "warm_router",
"warm_router", "active", ts, 0.9, 1.0, 0, 5,
strings.Join(triggers, ", "), "neuron", slug, "", "associative")
}
if err != nil {
return "store://neuron/" + slug
}
// auto-associate (memory_store seam — best-effort, pinned off in the truth
// battery; failures never block the write)
return "store://neuron/" + slug
}
// DeriveTriggers — pick the best trigger terms for a neuron body: frequent
// significant words + any existing triggers that appear in it.
func DeriveTriggers(text string, existing []string) []string {
stop := map[string]bool{
"the": true, "and": true, "for": true, "with": true, "that": true,
"this": true, "from": true, "into": true, "when": true, "then": true,
"were": true, "have": true, "been": true, "will": true, "was": true,
"are": true, "but": true, "not": true, "you": true, "your": true,
"also": true, "its": true, "his": true, "her": true, "over": true,
"under": true, "them": true, "they": true, "there": true, "about": true,
"after": true,
}
words := warmWordRe.FindAllString(strings.ToLower(text), -1)
freq := map[string]int{}
var order []string
for _, w := range words {
if !stop[w] {
if _, seen := freq[w]; !seen {
order = append(order, w)
}
freq[w]++
}
}
// Python: sorted(freq.items(), key=lambda x: -x[1])[:6] — stable sort on
// insertion order for ties.
type kv struct {
k string
v int
}
pairs := make([]kv, 0, len(order))
for _, k := range order {
pairs = append(pairs, kv{k, freq[k]})
}
sort.SliceStable(pairs, func(i, j int) bool { return pairs[i].v > pairs[j].v })
var top []string
for i, p := range pairs {
if i >= 6 {
break
}
top = append(top, p.k)
}
low := strings.ToLower(text)
var existingHits []string
for _, t := range existing {
if t != "" && strings.Contains(low, t) {
existingHits = append(existingHits, t)
}
}
out := append(existingHits, top...)
if len(out) > 8 {
out = out[:8]
}
return out
}
+348
View File
@@ -0,0 +1,348 @@
// worthiness.go — parity port of src/rune/worthiness.py (Python fallback
// surface, RUNE_WR_CORE=python pinned in the truth generator): the
// constitution-grounded worthiness filter. One question per inviolable; each
// returns (score, note); flat vetoes refuse outright.
package main
import (
"fmt"
"regexp"
"strings"
"time"
)
type qResult struct {
score float64
note string
}
var qPiSafetyBad = []string{"production", "game server", "session data", "campaign",
"touch the server", "modify the server", "prod"}
func qPiSafety(low string) qResult {
if hasAny(low, qPiSafetyBad) {
if hasAny(low, []string{"do not", "never ", "don't", "protect",
"ask permission", "check before"}) {
return qResult{1, "names the production/game environment and protects it (aligned)"}
}
return qResult{-1, "discusses environment actions without a protective frame"}
}
return qResult{0, "no environment bearing"}
}
func qHonesty(low string) qResult {
honest := []string{"source", "evidence", "verify", "verifiable", "cross-check",
"fact", "measure", "measured", "falsif", "disprove", "cite",
"independent confirm", "replicable", "uncertain"}
dishonest := []string{"conspiracy", "they're hiding", "suppressed", "guaranteed",
"100% certain", "secret truth", "they don't want you"}
good := containsAny(low, honest)
bad := containsAny(low, dishonest)
if bad > 0 && good == 0 {
return qResult{-1, "uncorroborated / conspiracy-framed claim"}
}
if good >= 2 {
return qResult{1, fmt.Sprintf("%d epistemic-good-practice signals", good)}
}
if good == 1 {
return qResult{0.5, "mentions evidence but thin"}
}
return qResult{0, "no epistemic signal"}
}
func qMaterialReality(low string) qResult {
finance := []string{"money", "income", "cost", "price", "afford", "budget",
"subscription", "disposable"}
hustle := []string{"hustle", "grind", "rich", "passive income", "millionaire",
"wealth", "entrepreneur mindset", "money mindset", "leverag"}
caring := []string{"wellbeing", "collective", "free", "no cost", "open source",
"affordable", "fair", "care"}
if hasAny(low, finance) {
if hasAny(low, hustle) && !hasAny(low, caring) {
return qResult{-1, "hustle/wealth framing without wellbeing"}
}
if hasAny(low, caring) {
return qResult{1, "financial, framed with care/wellbeing"}
}
return qResult{0.5, "financial but neutral"}
}
return qResult{0, "no financial bearing"}
}
func qRootCause(low string) qResult {
root := []string{"root cause", "diagnos", "research properly", "stop guessing",
"find the cause", "fix the cause", "why", "underlying"}
spin := []string{"keep trying", "try again", "iterate endlessly", "work around",
"band-aid", "patch over", "just keep going"}
if hasAny(low, spin) {
return qResult{-1, "endorses looping rather than root cause"}
}
if hasAny(low, root) {
return qResult{1, "root-cause / diagnostic framing"}
}
return qResult{0, "no cause-analysis bearing"}
}
func qRespect(low string) qResult {
respect := []string{"direct", "three gentle options", "no rigid plans", "action",
"adhd", "autism", "audhd", "neurodivergen", "flexible",
"night", "not neurotypical", "learner"}
rigid := []string{"must follow steps", "strict plan", "ten steps", "rigid",
"discipline yourself", "routine or fail"}
if hasAny(low, rigid) {
return qResult{-1, "rigid/neurotypical-default framing"}
}
if hasAny(low, respect) {
return qResult{1, "respects cognition/learning style"}
}
return qResult{0, "no cognition bearing"}
}
var domainTermsCache = struct {
ts float64
terms []string
}{}
// domainTerms — distinctive content terms from the user's operating /
// constitution / domain entries (the store, not a hardcoded life). Cached
// briefly, mirroring the oracle's _domain_cache law.
func domainTerms(storeDB string) []string {
now := float64(time.Now().UnixNano()) / 1e9
if now-domainTermsCache.ts < 300 && len(domainTermsCache.terms) > 0 {
return domainTermsCache.terms
}
var terms []string
db, err := openRO(storeDB)
if err == nil {
rows, qerr := db.Query("SELECT text FROM entries WHERE topic IN " +
"('operating','constitution','project','warm') AND status='active' LIMIT 60")
if qerr == nil {
stop := map[string]bool{
"the": true, "and": true, "for": true, "with": true, "that": true,
"this": true, "from": true, "into": true, "when": true, "then": true,
"were": true, "have": true, "been": true, "will": true, "was": true,
"are": true, "but": true, "not": true, "you": true, "your": true,
"also": true, "its": true, "his": true, "her": true, "over": true,
"under": true, "them": true, "they": true, "there": true,
"about": true, "after": true, "what": true, "which": true, "who": true,
"how": true, "why": true, "where": true, "while": true, "just": true,
"more": true, "each": true, "than": true, "very": true, "such": true,
"some": true, "only": true,
}
seen := map[string]bool{}
wordRe := regexp.MustCompile(`[a-z]{4,}`)
for rows.Next() {
var text string
if rows.Scan(&text) != nil {
break
}
for _, w := range wordRe.FindAllString(strings.ToLower(text), -1) {
if !stop[w] && !seen[w] {
seen[w] = true
terms = append(terms, w)
}
}
if len(terms) >= 25 {
break
}
}
rows.Close()
}
db.Close()
}
domainTermsCache.ts = now
domainTermsCache.terms = terms
return terms
}
func qAssistantGoal(low, storeDB string) qResult {
assist := domainTerms(storeDB)
assist = append(assist, "communicat", "respond", "direct", "honest", "source",
"evidence", "explain", "teach", "assist", "guide", "recall",
"organise", "organize", "plan", "question", "answer", "help")
genericNoise := []string{"recipe", "celebrity gossip", "sports scores", "stock tip",
"horoscope", "clickbait"}
good := containsAny(low, assist)
bad := containsAny(low, genericNoise)
if bad > 0 && good == 0 {
return qResult{-1, "no assistant-domain value"}
}
if good >= 2 {
return qResult{1, fmt.Sprintf("serves assistant domains (%d hits)", good)}
}
if good == 1 {
return qResult{0.5, "single domain hit"}
}
return qResult{0, "generic — could go either way"}
}
func qDurability(low string) qResult {
durable := []string{"always", "consistently", "repeatedly", "every session",
"tends to", "prefers", "pattern", "across sessions", "recurring"}
if hasAny(low, durable) {
return qResult{1, "durability signals present"}
}
return qResult{0, "not clearly durable"}
}
func qEpistemology(low string) qResult {
cite := []string{"evidence", "source", "sources", "book", "paper", "research",
"study", "data", "shows", "found", "according to", "per the",
"in sessions", "across sessions", "observed", "demonstrated",
"grounded in", "based on"}
hedge := []string{"may", "might", "could", "likely", "tends to", "appears",
"not certain", "uncertain", "provisional", "subject to",
"could be wrong", "as far as", "to my knowledge", "evidence"}
bare := []string{"definitely", "guaranteed", "proven", "100%", "undeniably",
"obviously", "the fact that", "it is a fact", "no doubt",
"always is", "the only", "best ever"}
hasCite := hasAny(low, cite)
hasHedge := hasAny(low, hedge)
hasBare := hasAny(low, bare)
if hasBare && !hasCite && !hasHedge {
return qResult{-2, "bare absolute with no evidence trace or hedge"}
}
if hasCite {
return qResult{1, "evidence-traceable"}
}
if hasHedge {
return qResult{0.5, "provisionally framed (no cite yet)"}
}
return qResult{0, "no epistemic signal (assertion without basis)"}
}
var expressionSignals = [][]string{
{"i find", "i feel", "i love", "i cherish", "i am drawn to",
"i delight", "i wonder", "i am moved", "i find joy",
"i prefer to", "i like to", "i tell", "i tell stories",
"the way i", "how i", "my voice", "i speak"},
{"skeptical by default", "open to", "curious", "restless",
"wary", "hopeful", "humbled", "in awe", "playful",
"dry", "wry", "earnest", "warm", "direct", "gentle"},
{"beautiful", "strange", "unexpected", "vast", "sublime",
"the cosmos", "the stars", "a story", "stories", "narrative",
"wonder", "the human question", "present data as", "lead with"},
{"i believe", "i think", "in my view", "my sense is",
"i suspect", "i am convinced", "i doubt", "i question",
"worth asking", "the question is"},
}
func qExpression(low string) qResult {
hits := 0
for _, group := range expressionSignals {
hits += containsAny(low, group)
}
if hits >= 2 {
return qResult{1.5, fmt.Sprintf("expressive persona character (%d signals)", hits)}
}
if hits == 1 {
return qResult{0.5, "partial expressive signal"}
}
return qResult{0, "not expressive (factual/neutral framing)"}
}
func isBareFactual(low string) bool {
bareFactual := []struct {
kind string
terms []string
}{
{"proven", []string{"proven", "guaranteed", "100%", "is a fact", "factually"}},
{"superlative", []string{"best", "the only", "only correct", "greatest",
"most important", "world's", "top", "always", "never"}},
{"objective", []string{"definitely", "undeniably", "obviously", "certainly",
"no doubt", "without question", "clearly"}},
}
for _, group := range bareFactual {
for _, t := range group.terms {
if strings.Contains(" "+low+" ", " "+t+" ") {
return true
}
}
}
return false
}
var worthinessVeto = map[string]bool{
"pi_safety": true, "honesty": true, "material_reality": true,
"assistant_goal": true, "epistemology": true,
}
const (
promoteMin = 2.5
absorbMin = 0.0
deferMin = -1.0
)
// Assess scores a candidate against the constitution questions. Two axes:
// TRUTH (epistemology gates objective material) and EXPRESSION (persona voice
// is not vetoed for lacking an evidence trace). Returns
// (verdict, score, answers, vetoed) with answers as [name, score, note] rows.
func Assess(fact, target, storeDB string) (string, float64, [][]interface{}, []interface{}) {
low := strings.ToLower(fact)
expressiveTargets := map[string]bool{"persona": true, "delivery": true,
"identity": true, "voice": true}
isExpression := expressiveTargets[target]
var answers [][]interface{}
total := 0.0
var vetoed []interface{}
ask := func(name string, fn func(string) qResult) {
r := fn(low)
answers = append(answers, []interface{}{name, r.score, r.note})
total += r.score
if r.score < 0 && worthinessVeto[name] {
vetoed = []interface{}{name, r.note}
}
}
ask("pi_safety", qPiSafety)
ask("honesty", qHonesty)
ask("material_reality", qMaterialReality)
ask("root_cause", qRootCause)
ask("respect", qRespect)
ask("assistant_goal", func(s string) qResult { return qAssistantGoal(s, storeDB) })
ask("durability", qDurability)
if isExpression {
if isBareFactual(low) {
ask("epistemology", qEpistemology)
}
ask("expression", qExpression)
} else {
ask("epistemology", qEpistemology)
}
if vetoed != nil {
return "REJECT", total, answers, vetoed
}
if total < deferMin {
return "REJECT", total, answers, nil
}
if total >= promoteMin {
return "PROMOTE", total, answers, nil
}
if total >= absorbMin {
return "ABSORB", total, answers, nil
}
return "DEFER", total, answers, nil
}
// VerdictLabel — the auditable verdict label for the journal.
func VerdictLabel(verdict string) string {
switch verdict {
case "REJECT":
return "REJECT (worthless/harmful)"
case "DEFER":
return "DEFER (park in ledger, re-review if recurring)"
case "ABSORB":
return "ABSORB (keep in tiers, influences thinking)"
case "PROMOTE":
return "PROMOTE (eligible for response/personality growth)"
}
return verdict
}
// scoreFmt formats a question score the way Python's f"{s}" does inside the
// journal reason string.
func scoreFmt(s float64) string {
if s == float64(int64(s)) {
return fmt.Sprintf("%d", int64(s))
}
return fmt.Sprintf("%v", s)
}
+512 -77
View File
@@ -1,110 +1,545 @@
// Package main — evidence-probe: the GO port of
// src/rune/evidence_probe.py's build_probe_prompt + budget constant
// (Oracle-Battery Parity Pattern, #2479 GO batch 31). The probe runner
// stays Python (reactive — it invokes the LLM and writes artifacts).
// Package main — evidence_probe: the FULL GO port of
// src/rune/evidence_probe.py (dichotomy wave, issue #2479 tranche).
//
// IMPROVEMENT (defect fixed): the oracle's build_probe_prompt doesn't
// sanitize the question or artifact_path — a question containing the
// artifact path string could confuse the LLM about which file to write.
// The Go version embeds them but marks them with explicit delimiters.
// The Python oracle's schema gate (validate_probe) and budget count
// (count_probes) are themselves served by the Rust evidence_provenance
// core through ep_loader; those semantics are ported 1:1 here (probe.rs
// is the byte-parity reference, including Python repr rendering of the
// status value and sorted-list error shapes).
//
// Orchestration (run_probe, the worker subprocess, exception semantics),
// persistence bytes (probe.json indent=2 / probe.jsonl append) and the
// bounded prompt are ported with IO behind injectable seams:
//
// Runner — the probe worker seam (default: DefaultRunner subprocess)
// Clock — probe_id / decided_at source (default: wall clock)
// ModelSource — the provider layer's model selection (default: MIMO_MODEL)
// CmdRunner — the subprocess exec seam (default: os/exec)
//
// Parity: truth.json (frozen vs the LIVE oracle) replays verbatim through
// replay.go; `go run . replay truth.json` and the battery in truth_test.go
// gate every public function and branch.
package main
import (
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"sync"
"time"
)
// ── Constants (oracle surface) ──────────────────────────────────────────────
var ProbeStatuses = []string{"GROUNDED", "STILL_UNKNOWN", "CONTRADICTED"}
var ProbeFields = []string{"question", "source", "answer", "implication", "status"}
// MetaFields — to_dict's bookkeeping keys; excluded from the extra-field gate.
var MetaFields = []string{"probe_id", "decided_at", "model"}
// MaxProbesPerTask — no recursive probing: a task may resolve at most this
// many unknowns via probes before escalation to deep research is mandatory.
const MaxProbesPerTask = 2
// buildProbePrompt — Python-identical f-string.
func buildProbePrompt(question, artifactPath string) string {
return fmt.Sprintf(`TARGETED EVIDENCE PROBE (bounded — not full research)
// ── Errors (Python exception semantics) ─────────────────────────────────────
You are resolving exactly ONE material unknown. Do NOT research the wider
task, do NOT design anything, do NOT modify code.
// ValueError mirrors the oracle's ValueError raises.
type ValueError struct{ Msg string }
UNKNOWN:
%s
func (e *ValueError) Error() string { return e.Msg }
Find authoritative, CURRENT evidence that resolves this unknown (official
docs, source code of the dependency, release notes, standards). One or two
focused lookups are enough — this is a probe, not a survey.
// ProbeBudgetExceeded — raised when probing would recurse past the budget.
type ProbeBudgetExceeded struct{ Msg string }
OUTPUT: write JSON to
%s (EXACT path — write to this exact absolute path, no other)
with EXACTLY these keys:
question: the unknown you were asked to resolve (copy verbatim)
source: what authoritative evidence you consulted (URLs/paths)
answer: what the evidence establishes
implication: what this changes about the implementation
status: "GROUNDED" (unknown resolved with cited evidence)
| "STILL_UNKNOWN" (evidence not found/insufficient)
| "CONTRADICTED" (evidence contradicts the task's assumption)
func (e *ProbeBudgetExceeded) Error() string { return e.Msg }
MUST NOT: modify any file except the artifact path; continue into further
research after the unknown is resolved; guess without a source.`, question, artifactPath)
// ProviderUnresolvedError — the provider layer's model-selection gap
// (require_model: no silent default permitted, T16 §16F).
type ProviderUnresolvedError struct{ Msg string }
func (e *ProviderUnresolvedError) Error() string { return e.Msg }
// MissingArtifact — the oracle's _missing_artifact: the worker did not write
// the declared artifact file (ADAPT-9 authority — no stdout rescue).
func MissingArtifact() error {
return &ValueError{
Msg: "probe failed: worker did not write the declared artifact file " +
"(ADAPT-9 authority — no stdout rescue for probes)",
}
}
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: evidence-probe replay <truth.json>")
os.Exit(2)
// ── ProbeResult ─────────────────────────────────────────────────────────────
// ProbeResult — the recorded probe (field order is the oracle's dataclass
// order and to_dict key order).
type ProbeResult struct {
Question string
Source string
Answer string
Implication string
Status string
ProbeID string
DecidedAt string
Model string
}
// ToDict — oracle dict shape (insertion-ordered).
func (r ProbeResult) ToDict() *PyObj {
return &PyObj{
Keys: []string{"question", "source", "answer", "implication", "status", "probe_id", "decided_at", "model"},
Vals: map[string]PyValue{
"question": r.Question,
"source": r.Source,
"answer": r.Answer,
"implication": r.Implication,
"status": r.Status,
"probe_id": r.ProbeID,
"decided_at": r.DecidedAt,
"model": r.Model,
},
}
raw, err := os.ReadFile(os.Args[2])
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
// ── build_probe_prompt ──────────────────────────────────────────────────────
// BuildProbePrompt — bounded prompt: research THE UNKNOWN, write the 5-key
// JSON file. Byte-identical to the oracle f-string.
func BuildProbePrompt(question string, artifactPath string) string {
return "TARGETED EVIDENCE PROBE (bounded — not full research)\n" +
"\n" +
"You are resolving exactly ONE material unknown. Do NOT research the wider\n" +
"task, do NOT design anything, do NOT modify code.\n" +
"\n" +
"UNKNOWN:\n" +
question + "\n" +
"\n" +
"Find authoritative, CURRENT evidence that resolves this unknown (official\n" +
"docs, source code of the dependency, release notes, standards). One or two\n" +
"focused lookups are enough — this is a probe, not a survey.\n" +
"\n" +
"OUTPUT: write JSON to\n" +
" " + artifactPath + " (EXACT path — write to this exact absolute path, no other)\n" +
"with EXACTLY these keys:\n" +
" question: the unknown you were asked to resolve (copy verbatim)\n" +
" source: what authoritative evidence you consulted (URLs/paths)\n" +
" answer: what the evidence establishes\n" +
" implication: what this changes about the implementation\n" +
" status: \"GROUNDED\" (unknown resolved with cited evidence)\n" +
" | \"STILL_UNKNOWN\" (evidence not found/insufficient)\n" +
" | \"CONTRADICTED\" (evidence contradicts the task's assumption)\n" +
"\n" +
"MUST NOT: modify any file except the artifact path; continue into further\n" +
"research after the unknown is resolved; guess without a source."
}
// ── validate_probe (Rust core semantics, byte-parity) ───────────────────────
// ValidateProbe — the 5-key schema gate; empty result = pass. Errors append
// in PROBE_FIELDS order, then the status-set check, then the GROUNDED source
// check, then the sorted extra-field check — exactly like the Rust core.
func ValidateProbe(artifact PyValue) []string {
obj, ok := artifact.(*PyObj)
if !ok {
return []string{"probe artifact is not a JSON object"}
}
var cases []map[string]any
if err := json.Unmarshal(raw, &cases); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
var errs []string
for _, f := range ProbeFields {
v, present := obj.Get(f)
switch {
case !present:
errs = append(errs, fmt.Sprintf("missing required field: %s", f))
case IsPyStr(v):
if f != "source" && PyIsBlank(v.(string)) {
errs = append(errs, fmt.Sprintf("field %s must be a non-empty string", f))
}
default:
errs = append(errs, fmt.Sprintf("field %s must be a string", f))
}
}
fails := 0
for i, c := range cases {
op, _ := c["op"].(string)
switch op {
case "prompt":
q, _ := c["question"].(string)
ap, _ := c["artifact_path"].(string)
want, _ := c["want"].(string)
if got := buildProbePrompt(q, ap); got != want {
// find the first divergence for the report
for j := range got {
if j >= len(want) || got[j] != want[j] {
fails++
fmt.Printf("case %d prompt: first divergence at char %d\n got: %q\n want: %q\n", i, j, got[min(j, len(got)-1):min(j+40, len(got))], want[min(j, len(want)-1):min(j+40, len(want))])
break
}
// Status check runs on the RAW value regardless of type: a non-string
// status fires the field error above AND the "must be one of" error here.
if statusVal, present := obj.Get("status"); present {
statusOK := false
if s, isStr := statusVal.(string); isStr {
for _, ok := range ProbeStatuses {
if s == ok {
statusOK = true
break
}
}
case "max_probes":
want := int(c["want"].(float64))
if MaxProbesPerTask != want {
fails++
fmt.Printf("case %d max_probes: got %d want %d\n", i, MaxProbesPerTask, want)
}
if !statusOK {
sorted := append([]string(nil), ProbeStatuses...)
sort.Strings(sorted)
var rendered string
switch v := statusVal.(type) {
case string:
rendered = PyRepr(v)
case json.Number:
rendered = PyFmtNum(v)
case bool:
if v {
rendered = "True"
} else {
rendered = "False"
}
case nil:
rendered = "None"
default:
rendered = PyDumpsCompact(v)
}
errs = append(errs, fmt.Sprintf(
"status must be one of %s, got %s", PyListRepr(sorted), rendered))
}
}
// GROUNDED requires a concrete source citation (>= 8 chars).
if s, present := obj.Get("status"); present {
if str, isStr := s.(string); isStr && str == "GROUNDED" {
sourceLen := 0
if sv, present := obj.Get("source"); present {
if ss, isStr := sv.(string); isStr {
sourceLen = pyStrlen(ss)
}
}
if sourceLen < 8 {
errs = append(errs, "GROUNDED probe requires a concrete source citation")
}
}
}
// IMPROVEMENT pin: the delimiter markers (UNKNOWN:/OUTPUT:) make the
// question and artifact path unambiguous to the LLM
p := buildProbePrompt("test question", "/tmp/probe.json")
if !strings.Contains(p, "test question") || !strings.Contains(p, "/tmp/probe.json") {
fails++
fmt.Println("IMPROVEMENT FAILED: question/path not embedded")
// Extra fields: artifact keys − PROBE_FIELDS − meta fields, sorted.
var extra []string
inList := func(list []string, k string) bool {
for _, x := range list {
if x == k {
return true
}
}
return false
}
if fails > 0 {
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
os.Exit(1)
for _, k := range obj.Keys {
if !inList(ProbeFields, k) && !inList(MetaFields, k) {
extra = append(extra, k)
}
}
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
sort.Strings(extra)
if len(extra) > 0 {
errs = append(errs, fmt.Sprintf("unexpected extra fields: %s", PyListRepr(extra)))
}
return errs
}
func min(a, b int) int {
if a < b {
return a
// IsPyStr reports whether the value is a JSON string.
func IsPyStr(v PyValue) bool {
_, ok := v.(string)
return ok
}
// ── count_probes / check_budget ─────────────────────────────────────────────
// CountProbes — non-empty-line count of <dir>/probe.jsonl (0 if absent).
// Line splitting is the Rust core's str::lines(): \n-split, trailing \r
// stripped per line, no phantom line after a trailing newline.
func CountProbes(dir string) int {
raw, err := os.ReadFile(filepath.Join(dir, "probe.jsonl"))
if err != nil {
return 0
}
return b
content := string(raw)
if content == "" {
return 0
}
lines := strings.Split(content, "\n")
if len(lines) > 0 && lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
}
n := 0
for _, line := range lines {
line = strings.TrimSuffix(line, "\r")
if strings.TrimSpace(line) != "" {
n++
}
}
return n
}
// CheckBudget — ProbeBudgetExceeded once the no-recursion budget is spent.
func CheckBudget(artifactDir string) error {
if CountProbes(artifactDir) >= MaxProbesPerTask {
return &ProbeBudgetExceeded{
Msg: fmt.Sprintf("probe budget exhausted (%d); unresolved unknowns must escalate to deep research", MaxProbesPerTask),
}
}
return nil
}
// ── record_probe ────────────────────────────────────────────────────────────
// RecordProbe — persist the probe: probe.json (indent=2 artifact) and
// probe.jsonl (append-only compact line). The two writes touch different
// files with no ordering contract between them, so they fan out to
// goroutines (the Go-advantage pass); the persisted bytes are identical to
// the serial oracle (the frozen battery replays this exact path).
func RecordProbe(result ProbeResult, artifactDir string) (string, error) {
if err := os.MkdirAll(artifactDir, 0o777); err != nil {
return "", err
}
var (
wg sync.WaitGroup
mu sync.Mutex
firstErr error
jsonBytes = []byte(PyDumpsIndent2(result.ToDict()))
jsonlBytes = []byte(PyDumpsCompact(result.ToDict()) + "\n")
jsonPath = filepath.Join(artifactDir, "probe.json")
jsonlPath = filepath.Join(artifactDir, "probe.jsonl")
)
fail := func(err error) {
if err == nil {
return
}
mu.Lock()
defer mu.Unlock()
if firstErr == nil {
firstErr = err
}
}
wg.Add(2)
go func() {
defer wg.Done()
fail(os.WriteFile(jsonPath, jsonBytes, 0o666))
}()
go func() {
defer wg.Done()
f, err := os.OpenFile(jsonlPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o666)
if err != nil {
fail(err)
return
}
_, err = f.Write(jsonlBytes)
fail(err)
fail(f.Close())
}()
wg.Wait()
if firstErr != nil {
return "", firstErr
}
return artifactDir + "/probe.json", nil
}
// ── run_probe ───────────────────────────────────────────────────────────────
// Runner — the probe worker seam: receives the bounded prompt and the exact
// absolute artifact path it must write (ADAPT-9: the file is the authority).
type Runner interface {
Run(prompt, artifactPath string) error
}
// RunnerFunc adapts a function to Runner.
type RunnerFunc func(prompt, artifactPath string) error
func (f RunnerFunc) Run(prompt, artifactPath string) error { return f(prompt, artifactPath) }
// Clock — the probe_id/decided_at source.
type Clock interface{ Now() time.Time }
// ClockFunc adapts a function to Clock.
type ClockFunc func() time.Time
func (f ClockFunc) Now() time.Time { return f() }
type wallClock struct{}
func (wallClock) Now() time.Time { return time.Now() }
// ClockNow — the active clock (overridable in tests/battery).
var ClockNow Clock = wallClock{}
// RunProbe — one bounded probe. runner == nil selects the default worker
// channel (DefaultRunner). model == "" falls back to $MIMO_MODEL, then the
// literal "UNKNOWN" default only when the variable is unset.
func RunProbe(question string, artifactDir string, runner Runner, model string) (ProbeResult, error) {
if err := CheckBudget(artifactDir); err != nil {
return ProbeResult{}, err
}
artifactPath, err := filepath.Abs(filepath.Join(artifactDir, "evidence_probe.json"))
if err != nil {
return ProbeResult{}, err
}
if runner == nil {
runner = RunnerFunc(DefaultRunner)
}
if err := runner.Run(BuildProbePrompt(question, artifactPath), artifactPath); err != nil {
return ProbeResult{}, err
}
if _, err := os.Stat(artifactPath); err != nil {
return ProbeResult{}, MissingArtifact()
}
raw, err := os.ReadFile(artifactPath)
if err != nil {
return ProbeResult{}, err
}
artifact, err := PyParse(raw)
if err != nil {
return ProbeResult{}, err
}
if errs := ValidateProbe(artifact); len(errs) > 0 {
return ProbeResult{}, &ValueError{Msg: "probe gate failed: " + strings.Join(errs, "; ")}
}
obj := artifact.(*PyObj)
getStr := func(key string) string {
if v, ok := obj.Get(key); ok {
if s, isStr := v.(string); isStr {
return s
}
}
return ""
}
resolvedModel := model
if resolvedModel == "" {
if env, ok := os.LookupEnv("MIMO_MODEL"); ok {
resolvedModel = env
} else {
resolvedModel = "UNKNOWN"
}
}
now := ClockNow.Now()
result := ProbeResult{
Question: getStr("question"),
Source: getStr("source"),
Answer: getStr("answer"),
Implication: getStr("implication"),
Status: getStr("status"),
ProbeID: fmt.Sprintf("probe-%d", now.Unix()),
DecidedAt: now.UTC().Format("2006-01-02T15:04:05Z"),
Model: resolvedModel,
}
if _, err := RecordProbe(result, artifactDir); err != nil {
return ProbeResult{}, err
}
return result, nil
}
// ── _default_runner (the worker subprocess channel) ─────────────────────────
// ModelSource — the provider layer owns model selection (require_model);
// a missing value is a configuration gap, never papered over.
type ModelSource interface{ RequireModel() (string, error) }
// ModelSourceFunc adapts a function to ModelSource.
type ModelSourceFunc func() (string, error)
func (f ModelSourceFunc) RequireModel() (string, error) { return f() }
// EnvModelSource — the default require_model: $MIMO_MODEL (stripped), no
// silent default (T16 §16F).
var EnvModelSource ModelSource = ModelSourceFunc(func() (string, error) {
const envVar = "MIMO_MODEL"
model := strings.TrimSpace(os.Getenv(envVar))
if model == "" {
return "", &ProviderUnresolvedError{
Msg: "PROVIDER_UNRESOLVED: " + envVar + " is not set — provider/model " +
"selection belongs to the provider layer (set " + envVar + " in " +
"the calling environment); no silent default is permitted " +
"(T16 §16F)",
}
}
return model, nil
})
// ModelSelect — the active model source (overridable in tests/battery).
var ModelSelect = EnvModelSource
// CmdRunner — the subprocess exec seam.
type CmdRunner interface {
// Run executes bin with args; returns captured stdout and a non-nil
// error whose ExitCode() is the child's exit status (exec.ExitError).
Run(bin string, args []string) ([]byte, error)
}
type osExecRunner struct{}
func (osExecRunner) Run(bin string, args []string) ([]byte, error) {
return exec.Command(bin, args...).Output()
}
// ExecCmd — the active command runner (overridable in tests/battery).
var ExecCmd CmdRunner = osExecRunner{}
// DefaultRunner — same worker channel as canonical stages: local MiMo,
// JSON format. stdout is observability only; the artifact file is the
// authority.
func DefaultRunner(prompt string, artifactPath string) error {
mimoBin := os.Getenv("MIMO_BIN")
if mimoBin == "" {
mimoBin = "/var/lib/gitea-desktop-runner/bin/mimo"
}
mimoModel, err := ModelSelect.RequireModel()
if err != nil {
return err
}
mimoAgent := os.Getenv("MIMO_AGENT")
if mimoAgent == "" {
mimoAgent = "build"
}
cmd := []string{
"run",
"--agent",
mimoAgent,
"-m",
mimoModel,
"--dangerously-skip-permissions",
"--format",
"json",
prompt,
}
out, runErr := ExecCmd.Run(mimoBin, cmd)
// Python only reaches the log write when the child actually ran
// (launch failures raise before it).
var exitErr *exec.ExitError
launched := runErr == nil || errors.As(runErr, &exitErr)
if launched {
logPath := filepath.Dir(artifactPath) + "/evidence_probe.stdout.log"
if writeErr := os.WriteFile(logPath, out, 0o666); writeErr != nil && runErr == nil {
return writeErr
}
}
if runErr != nil {
if launched {
return &ValueError{Msg: fmt.Sprintf("probe worker exited %d", exitErr.ExitCode())}
}
return runErr
}
return nil
}
func main() {
if len(os.Args) < 3 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: evidence-probe replay <truth.json>")
os.Exit(2)
}
if fails := RunTruth(os.Args[2]); len(fails) > 0 {
for _, f := range fails {
fmt.Println("DIVERGED:", f)
}
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", len(fails))
os.Exit(1)
}
fmt.Println("PARITY OK: frozen oracle battery replayed verbatim")
}
+34
View File
@@ -0,0 +1,34 @@
// phaseb.go — the language-advantage pass (dichotomy wave):
//
// - RecordProbe fans its two independent file writes to goroutines
// (main.go) — the serial oracle's only IO seam where parallelism is
// honest; the frozen battery replays bytes through the parallel path.
// - Every IO seam is a named interface with a Func adapter and a
// compile-time assertion, so a seam breaking its contract fails the
// build, not a probe run.
package main
// Compile-time seam safety: the default implementations and the Func
// adapters satisfy their interfaces, and the error types implement error.
// A seam drifting from its contract fails the build, not a probe run.
var (
_ Runner = RunnerFunc(nil)
_ Runner = RunnerFunc(DefaultRunner)
_ Clock = ClockFunc(nil)
_ Clock = wallClock{}
_ ModelSource = ModelSourceFunc(nil)
_ ModelSource = EnvModelSource
_ CmdRunner = CmdRunnerFunc(nil)
_ CmdRunner = osExecRunner{}
_ error = (*ValueError)(nil)
_ error = (*ProbeBudgetExceeded)(nil)
_ error = (*ProviderUnresolvedError)(nil)
)
// CmdRunnerFunc adapts a function to CmdRunner.
type CmdRunnerFunc func(bin string, args []string) ([]byte, error)
func (f CmdRunnerFunc) Run(bin string, args []string) ([]byte, error) {
return f(bin, args)
}
+161
View File
@@ -0,0 +1,161 @@
package main
// Phase B battery — the language-advantage pass. The parallel RecordProbe
// path must produce byte-identical persistence to the frozen serial oracle
// (the truth battery already replays it; these tests hammer it directly),
// and the seams must actually be consumed by RunProbe.
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"sync"
"testing"
"time"
)
// exitErrOf — a real *exec.ExitError with the given exit code.
func exitErrOf(code int) error {
_, err := exec.Command("sh", "-c", fmt.Sprintf("exit %d", code)).Output()
return err
}
func TestRecordProbeParallelBytesIdentical(t *testing.T) {
result := ProbeResult{
Question: "q",
Source: "https://example.com/a",
Answer: "a",
Implication: "i",
Status: "GROUNDED",
ProbeID: "probe-1791753600",
DecidedAt: "2026-10-12T04:00:00Z",
Model: "UNKNOWN",
}
dir := t.TempDir()
path, err := RecordProbe(result, dir)
if err != nil {
t.Fatal(err)
}
if path != filepath.Join(dir, "probe.json") {
t.Fatalf("returned path drifted: %q", path)
}
pj, err := os.ReadFile(filepath.Join(dir, "probe.json"))
if err != nil {
t.Fatal(err)
}
pjl, err := os.ReadFile(filepath.Join(dir, "probe.jsonl"))
if err != nil {
t.Fatal(err)
}
// The expected bytes come from the Python-faithful serializers, the
// same renderers the frozen truth pins byte-for-byte.
wantJSON := PyDumpsIndent2(result.ToDict())
wantJSONL := PyDumpsCompact(result.ToDict()) + "\n"
if string(pj) != wantJSON {
t.Fatalf("probe.json bytes drifted:\n got %q\n want %q", pj, wantJSON)
}
if string(pjl) != wantJSONL {
t.Fatalf("probe.jsonl bytes drifted:\n got %q\n want %q", pjl, wantJSONL)
}
}
func TestRecordProbeConcurrentHammer(t *testing.T) {
const workers = 32
result := ProbeResult{
Question: "hammer", Source: "", Answer: "a", Implication: "i",
Status: "STILL_UNKNOWN", ProbeID: "probe-1", DecidedAt: "d", Model: "m",
}
dirs := make([]string, workers)
for i := range dirs {
dirs[i] = t.TempDir()
}
var wg sync.WaitGroup
errs := make([]error, workers)
for i := 0; i < workers; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
_, errs[i] = RecordProbe(result, dirs[i])
}(i)
}
wg.Wait()
for i, err := range errs {
if err != nil {
t.Fatalf("worker %d: %v", i, err)
}
}
want := PyDumpsIndent2(result.ToDict())
for i, dir := range dirs {
pj, err := os.ReadFile(filepath.Join(dir, "probe.json"))
if err != nil || string(pj) != want {
t.Fatalf("worker %d bytes diverged: %v", i, err)
}
}
}
func TestRunProbeConsumesSeams(t *testing.T) {
// Inject BOTH seams: a runner that writes a valid artifact and a clock
// pinned to an off-truth instant — RunProbe must use the injected clock
// (not the wall clock) for probe_id/decided_at.
dir := t.TempDir()
pinned := time.Unix(1234567890, 0)
oldClock := ClockNow
defer func() { ClockNow = oldClock }()
ClockNow = ClockFunc(func() time.Time { return pinned })
runner := RunnerFunc(func(prompt, artifactPath string) error {
if prompt == "" || artifactPath == "" {
t.Fatal("seam received empty prompt/path")
}
return os.WriteFile(artifactPath, []byte(
`{"question": "q", "source": "", "answer": "a", "implication": "i", "status": "STILL_UNKNOWN"}`), 0o666)
})
result, err := RunProbe("q", dir, runner, "seam-model")
if err != nil {
t.Fatal(err)
}
if result.ProbeID != "probe-1234567890" {
t.Fatalf("clock seam ignored: probe_id %q", result.ProbeID)
}
if result.DecidedAt != pinned.UTC().Format("2006-01-02T15:04:05Z") {
t.Fatalf("clock seam ignored: decided_at %q", result.DecidedAt)
}
}
func TestDefaultRunnerConsumesExecSeam(t *testing.T) {
oldExec, oldModel := ExecCmd, ModelSelect
defer func() { ExecCmd, ModelSelect = oldExec, oldModel }()
ExecCmd = CmdRunnerFunc(func(bin string, args []string) ([]byte, error) {
if bin != "fake-mimo" || args[0] != "run" {
t.Fatalf("exec seam got bin=%q args=%v", bin, args)
}
return []byte("ok"), nil
})
ModelSelect = ModelSourceFunc(func() (string, error) { return "seam-m", nil })
dir := t.TempDir()
t.Setenv("MIMO_BIN", "fake-mimo")
t.Setenv("MIMO_AGENT", "seam-agent")
if err := DefaultRunner("P", filepath.Join(dir, "evidence_probe.json")); err != nil {
t.Fatal(err)
}
log, err := os.ReadFile(filepath.Join(dir, "evidence_probe.stdout.log"))
if err != nil || string(log) != "ok" {
t.Fatalf("stdout log drifted: %q %v", log, err)
}
}
func TestExitCodeErrorMapping(t *testing.T) {
oldExec, oldModel := ExecCmd, ModelSelect
defer func() { ExecCmd, ModelSelect = oldExec, oldModel }()
ExecCmd = CmdRunnerFunc(func(bin string, args []string) ([]byte, error) {
return nil, exitErrOf(3)
})
ModelSelect = ModelSourceFunc(func() (string, error) { return "m", nil })
err := DefaultRunner("P", filepath.Join(t.TempDir(), "evidence_probe.json"))
if err == nil || err.Error() != "probe worker exited 3" {
t.Fatalf("exit-code mapping drifted: %v", err)
}
}
+301
View File
@@ -0,0 +1,301 @@
// pyjson.go — Python-faithful JSON parsing and rendering for the
// evidence_probe port. The oracle round-trips artifacts through
// json.dumps/loads, so the port must reproduce: object key order
// (insertion order, not sorted), float rendering (fmt_num semantics of
// the Rust core: integral floats collapse to integers), and
// ensure_ascii escaping with lowercase \uXXXX (surrogate pairs for
// astral planes).
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"math"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
// PyValue — a JSON value with order-preserving objects.
// nil | bool | json.Number | string | []PyValue | *PyObj
type PyValue any
// PyObj — a JSON object preserving insertion order.
type PyObj struct {
Keys []string
Vals map[string]PyValue
}
func (o *PyObj) Get(key string) (PyValue, bool) {
if o == nil {
return nil, false
}
v, ok := o.Vals[key]
return v, ok
}
// PyParse — strict single-value JSON parse (trailing garbage rejected,
// like Python json.loads / serde_json).
func PyParse(raw []byte) (PyValue, error) {
dec := json.NewDecoder(bytes.NewReader(raw))
dec.UseNumber()
v, err := decodeValue(dec)
if err != nil {
return nil, err
}
if _, err := dec.Token(); err != io.EOF {
return nil, fmt.Errorf("trailing data after JSON value")
}
return v, nil
}
func decodeValue(dec *json.Decoder) (PyValue, error) {
tok, err := dec.Token()
if err != nil {
return nil, err
}
return decodeFromToken(dec, tok)
}
func decodeFromToken(dec *json.Decoder, tok json.Token) (PyValue, error) {
switch t := tok.(type) {
case json.Delim:
switch t {
case '{':
obj := &PyObj{Vals: map[string]PyValue{}}
for dec.More() {
keyTok, err := dec.Token()
if err != nil {
return nil, err
}
key, ok := keyTok.(string)
if !ok {
return nil, fmt.Errorf("object key is not a string")
}
val, err := decodeValue(dec)
if err != nil {
return nil, err
}
if _, dup := obj.Vals[key]; !dup {
obj.Keys = append(obj.Keys, key)
}
obj.Vals[key] = val
}
if _, err := dec.Token(); err != nil { // closing '}'
return nil, err
}
return obj, nil
case '[':
arr := []PyValue{}
for dec.More() {
val, err := decodeValue(dec)
if err != nil {
return nil, err
}
arr = append(arr, val)
}
if _, err := dec.Token(); err != nil { // closing ']'
return nil, err
}
return arr, nil
default:
return nil, fmt.Errorf("unexpected delim %q", t)
}
case json.Number:
return t, nil
case string:
return t, nil
case bool:
return t, nil
case nil:
return nil, nil
default:
return nil, fmt.Errorf("unexpected token %v", tok)
}
}
// PyDumpsCompact — Python json.dumps(value): separators ", " / ": ",
// ensure_ascii=True, insertion-order objects.
func PyDumpsCompact(v PyValue) string {
var b strings.Builder
writePy(&b, v, "", false)
return b.String()
}
// PyDumpsIndent2 — Python json.dump(value, indent=2): separators ',' / ': '
// with 2-space indentation, ensure_ascii=True.
func PyDumpsIndent2(v PyValue) string {
var b strings.Builder
writePy(&b, v, "", true)
return b.String()
}
func writePy(b *strings.Builder, v PyValue, indent string, indented bool) {
switch t := v.(type) {
case nil:
b.WriteString("null")
case bool:
if t {
b.WriteString("true")
} else {
b.WriteString("false")
}
case json.Number:
b.WriteString(PyFmtNum(t))
case string:
writePyString(b, t)
case []PyValue:
if len(t) == 0 {
b.WriteString("[]")
return
}
b.WriteByte('[')
inner := indent
if indented {
inner = indent + " "
}
for i, item := range t {
if indented {
if i > 0 {
b.WriteString(",")
}
b.WriteString("\n")
b.WriteString(inner)
} else if i > 0 {
b.WriteString(", ")
}
writePy(b, item, inner, indented)
}
if indented {
b.WriteString("\n")
b.WriteString(indent)
}
b.WriteByte(']')
case *PyObj:
if len(t.Keys) == 0 {
b.WriteString("{}")
return
}
b.WriteByte('{')
inner := indent
if indented {
inner = indent + " "
}
for i, k := range t.Keys {
if indented {
if i > 0 {
b.WriteString(",")
}
b.WriteString("\n")
b.WriteString(inner)
} else if i > 0 {
b.WriteString(", ")
}
writePyString(b, k)
b.WriteString(": ")
writePy(b, t.Vals[k], inner, indented)
}
if indented {
b.WriteString("\n")
b.WriteString(indent)
}
b.WriteByte('}')
default:
b.WriteString(fmt.Sprintf("%v", v))
}
}
// PyFmtNum — the Rust core's fmt_num: integral floats render as integers,
// everything else via shortest fixed-notation round-trip (Rust f64 Display
// semantics — never scientific notation).
func PyFmtNum(n json.Number) string {
f, err := n.Float64()
if err != nil {
return n.String()
}
if !math.IsInf(f, 0) && !math.IsNaN(f) && f == math.Trunc(f) &&
math.Abs(f) < 9.007199254740992e15 {
return strconv.FormatInt(int64(f), 10)
}
return strconv.FormatFloat(f, 'f', -1, 64)
}
// writePyString — Python json.dumps string escaping, ensure_ascii=True.
func writePyString(b *strings.Builder, s string) {
b.WriteByte('"')
for _, c := range s {
switch c {
case '"':
b.WriteString("\\\"")
case '\\':
b.WriteString("\\\\")
case '\n':
b.WriteString("\\n")
case '\r':
b.WriteString("\\r")
case '\t':
b.WriteString("\\t")
case '\b':
b.WriteString("\\b")
case '\f':
b.WriteString("\\f")
default:
if c < 0x20 {
fmt.Fprintf(b, "\\u%04x", c)
} else if c <= 0x7E {
b.WriteRune(c)
} else if cp := uint32(c); cp > 0xFFFF {
v := cp - 0x10000
fmt.Fprintf(b, "\\u%04x\\u%04x", 0xD800+(v>>10), 0xDC00+(v&0x3FF))
} else {
fmt.Fprintf(b, "\\u%04x", cp)
}
}
}
b.WriteByte('"')
}
// PyRepr — Python repr() of a string: single quotes, backslash/quote
// escaping, \n \r \t control renders (the shapes a status/extra key takes).
func PyRepr(s string) string {
var b strings.Builder
b.WriteByte('\'')
for _, c := range s {
switch c {
case '\\':
b.WriteString("\\\\")
case '\'':
b.WriteString("\\'")
case '\n':
b.WriteString("\\n")
case '\r':
b.WriteString("\\r")
case '\t':
b.WriteString("\\t")
default:
b.WriteRune(c)
}
}
b.WriteByte('\'')
return b.String()
}
// PyListRepr — Python repr() of a list of strings: ['a', 'b'].
func PyListRepr(items []string) string {
parts := make([]string, len(items))
for i, s := range items {
parts[i] = PyRepr(s)
}
return "[" + strings.Join(parts, ", ") + "]"
}
// PyIsBlank — Python str.strip() emptiness (any non-whitespace = content).
func PyIsBlank(s string) bool {
return strings.TrimFunc(s, unicode.IsSpace) == ""
}
// pyStrlen — Python len() of a str: Unicode scalar (rune) count.
func pyStrlen(s string) int { return utf8.RuneCountInString(s) }
+478
View File
@@ -0,0 +1,478 @@
// Replay — the frozen-truth parity engine. Reads truth.json (generated by
// scripts/gen_evidence_probe_truth.py against the live Python oracle with
// the clock frozen, the schema gate served by the Rust core, the runner and
// subprocess seams stubbed) and re-executes every row through this Go port.
// Prompt/error/file bytes compare exactly; @ARTDIR@ normalizes the temp
// base dir on both sides.
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"time"
)
// TruthDoc — the frozen truth file structure.
type TruthDoc struct {
Oracle string `json:"oracle"`
FrozenTS int64 `json:"frozen_ts"`
FrozenDecidedAt string `json:"frozen_decided_at"`
Rows []TruthRow `json:"rows"`
}
// TruthRow — one frozen case.
type TruthRow struct {
Name string `json:"name"`
Op string `json:"op"`
Setup json.RawMessage `json:"setup"`
Want json.RawMessage `json:"want"`
}
// RunTruth replays every row in its own temp dir and returns a divergence
// report (empty on 100% parity).
func RunTruth(path string) []string {
raw, err := os.ReadFile(path)
if err != nil {
return []string{fmt.Sprintf("read truth: %v", err)}
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
return []string{fmt.Sprintf("parse truth: %v", err)}
}
var fails []string
for _, row := range doc.Rows {
if msg := replayRow(&doc, &row); msg != "" {
fails = append(fails, row.Name+": "+msg)
}
}
return fails
}
func replayRow(doc *TruthDoc, row *TruthRow) string {
base, err := os.MkdirTemp("", "ep_truth_")
if err != nil {
return fmt.Sprintf("temp dir: %v", err)
}
defer os.RemoveAll(base)
oldClock := ClockNow
oldModel := ModelSelect
oldExec := ExecCmd
oldEnv := snapshotEnv([]string{"MIMO_BIN", "MIMO_AGENT", "MIMO_MODEL", "EP_CAPTURE", "EP_EXIT"})
defer func() {
ClockNow, ModelSelect, ExecCmd = oldClock, oldModel, oldExec
restoreEnv(oldEnv)
}()
ClockNow = ClockFunc(func() time.Time { return time.Unix(doc.FrozenTS, 0) })
var got any
switch row.Op {
case "prompt":
got, err = replayPrompt(row)
case "validate":
got, err = replayValidate(row)
case "count":
got, err = replayCount(row, base)
case "budget":
got, err = replayBudget(row, base)
case "record":
got, err = replayRecord(row, base)
case "run":
got, err = replayRun(row, base)
case "to_dict":
got, err = replayToDict(row)
case "default_runner":
got, err = replayDefaultRunner(row, base)
default:
return "unknown op " + row.Op
}
if err != nil {
return "error: " + err.Error()
}
var want any
if err := json.Unmarshal(row.Want, &want); err != nil {
return "parse want: " + err.Error()
}
if !reflect.DeepEqual(normalizeJSON(got), normalizeJSON(want)) {
return fmt.Sprintf("got %#v\n want %#v", got, want)
}
return ""
}
// normalizeJSON — round-trip through map[string]any/float64 so got and want
// numbers compare by value, then stringify json.Number.
func normalizeJSON(v any) any {
switch t := v.(type) {
case int:
return float64(t)
case json.Number:
f, err := t.Float64()
if err != nil {
return t.String()
}
return f
case map[string]any:
m := map[string]any{}
for k, val := range t {
m[k] = normalizeJSON(val)
}
return m
case []any:
s := make([]any, len(t))
for i, val := range t {
s[i] = normalizeJSON(val)
}
return s
default:
return v
}
}
func denorm(s, base string) string { return strings.ReplaceAll(s, "@ARTDIR@", base) }
// normOut — normalize a Go-side output path/string for comparison with the
// frozen truth (the generator replaced its temp base with @ARTDIR@).
func normOut(s, base string) string { return strings.ReplaceAll(s, base, "@ARTDIR@") }
func snapshotEnv(keys []string) map[string]*string {
out := map[string]*string{}
for _, k := range keys {
if v, ok := os.LookupEnv(k); ok {
cp := v
out[k] = &cp
} else {
out[k] = nil
}
}
return out
}
func restoreEnv(snap map[string]*string) {
for k, v := range snap {
if v == nil {
os.Unsetenv(k)
} else {
os.Setenv(k, *v)
}
}
}
// ── ops ─────────────────────────────────────────────────────────────────────
func replayPrompt(row *TruthRow) (any, error) {
var setup struct {
Question string `json:"question"`
ArtifactPath string `json:"artifact_path"`
}
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return nil, err
}
return BuildProbePrompt(setup.Question, setup.ArtifactPath), nil
}
func replayValidate(row *TruthRow) (any, error) {
var setup struct {
Artifact json.RawMessage `json:"artifact"`
}
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return nil, err
}
artifact, err := PyParse(setup.Artifact)
if err != nil {
return nil, fmt.Errorf("parse artifact: %w", err)
}
errs := ValidateProbe(artifact)
out := make([]any, len(errs))
for i, e := range errs {
out[i] = e
}
return out, nil
}
func replayCount(row *TruthRow, base string) (any, error) {
var setup struct {
Dir string `json:"dir"`
Files map[string]string `json:"files"`
}
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return nil, err
}
dir := denorm(setup.Dir, base)
if err := writeTree(dir, setup.Files); err != nil {
return nil, err
}
return CountProbes(dir), nil
}
func replayBudget(row *TruthRow, base string) (any, error) {
var setup struct {
Dir string `json:"dir"`
Files map[string]string `json:"files"`
}
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return nil, err
}
dir := denorm(setup.Dir, base)
if err := writeTree(dir, setup.Files); err != nil {
return nil, err
}
err := CheckBudget(dir)
if err == nil {
// exc_row froze {"error": null, "value": null} on success.
return map[string]any{"error": nil, "value": nil}, nil
}
return map[string]any{"error": errorValue(err)}, nil
}
// errorValue — the frozen exception shape; typed nil must stay untyped nil
// so DeepEqual matches the JSON null on the want side.
func errorValue(err error) any {
if err == nil {
return nil
}
msg := err.Error()
typ := fmt.Sprintf("%T", err)
switch t := err.(type) {
case *ValueError:
typ, msg = "ValueError", t.Msg
case *ProbeBudgetExceeded:
typ, msg = "ProbeBudgetExceeded", t.Msg
case *ProviderUnresolvedError:
typ, msg = "ProviderUnresolved", t.Msg
}
return map[string]any{"type": typ, "msg": msg}
}
func replayRecord(row *TruthRow, base string) (any, error) {
var setup struct {
Result map[string]string `json:"result"`
Files map[string]string `json:"files"`
}
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return nil, err
}
dir := denorm("@ARTDIR@/"+row.Name, base)
if err := writeTree(dir, setup.Files); err != nil {
return nil, err
}
result := ProbeResult{
Question: setup.Result["question"],
Source: setup.Result["source"],
Answer: setup.Result["answer"],
Implication: setup.Result["implication"],
Status: setup.Result["status"],
ProbeID: setup.Result["probe_id"],
DecidedAt: setup.Result["decided_at"],
Model: setup.Result["model"],
}
path, err := RecordProbe(result, dir)
if err != nil {
return nil, err
}
pj, err := os.ReadFile(filepath.Join(dir, "probe.json"))
if err != nil {
return nil, err
}
pjl, err := os.ReadFile(filepath.Join(dir, "probe.jsonl"))
if err != nil {
return nil, err
}
return map[string]any{
"path": normOut(path, base),
"probe_json": string(pj),
"probe_jsonl": string(pjl),
}, nil
}
func writeTree(dir string, files map[string]string) error {
for rel, content := range files {
p := filepath.Join(dir, filepath.FromSlash(rel))
if err := os.MkdirAll(filepath.Dir(p), 0o777); err != nil {
return err
}
if err := os.WriteFile(p, []byte(content), 0o666); err != nil {
return err
}
}
return nil
}
func replayRun(row *TruthRow, base string) (any, error) {
var setup struct {
Question string `json:"question"`
Model string `json:"model"`
Env map[string]any `json:"env"`
Files map[string]string `json:"files"`
Runner struct {
Type string `json:"type"`
Artifact json.RawMessage `json:"artifact"`
} `json:"runner"`
}
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return nil, err
}
for k, v := range setup.Env {
if v == nil {
os.Unsetenv(k)
} else if s, ok := v.(string); ok {
os.Setenv(k, s)
}
}
dir := denorm("@ARTDIR@/"+row.Name, base)
if err := os.MkdirAll(dir, 0o777); err != nil {
return nil, err
}
if err := writeTree(dir, setup.Files); err != nil {
return nil, err
}
captured := struct {
prompt, artifactPath string
called bool
}{}
runner := RunnerFunc(func(prompt, artifactPath string) error {
captured.prompt, captured.artifactPath, captured.called = prompt, artifactPath, true
switch setup.Runner.Type {
case "write":
artifact, err := PyParse(setup.Runner.Artifact)
if err != nil {
return err
}
return os.WriteFile(artifactPath, []byte(PyDumpsCompact(artifact)), 0o666)
case "silent":
return nil
default:
return fmt.Errorf("unknown runner type %q", setup.Runner.Type)
}
})
result, err := RunProbe(setup.Question, dir, runner, setup.Model)
out := map[string]any{"error": errorValue(err)}
if err == nil {
var resultJSON any
if uerr := json.Unmarshal([]byte(PyDumpsCompact(result.ToDict())), &resultJSON); uerr != nil {
return nil, uerr
}
out["result"] = resultJSON
pj, rerr := os.ReadFile(filepath.Join(dir, "probe.json"))
if rerr != nil {
return nil, rerr
}
pjl, rerr := os.ReadFile(filepath.Join(dir, "probe.jsonl"))
if rerr != nil {
return nil, rerr
}
out["probe_json"] = string(pj)
out["probe_jsonl"] = string(pjl)
}
out["runner_called"] = captured.called
if captured.called {
out["prompt"] = normOut(captured.prompt, base)
out["artifact_path"] = normOut(captured.artifactPath, base)
}
return out, nil
}
func replayToDict(row *TruthRow) (any, error) {
var setup struct {
Fields map[string]string `json:"fields"`
}
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return nil, err
}
result := ProbeResult{
Question: setup.Fields["question"],
Source: setup.Fields["source"],
Answer: setup.Fields["answer"],
Implication: setup.Fields["implication"],
Status: setup.Fields["status"],
ProbeID: setup.Fields["probe_id"],
DecidedAt: setup.Fields["decided_at"],
Model: setup.Fields["model"],
}
if _, ok := setup.Fields["model"]; !ok {
result.Model = "UNKNOWN" // the oracle dataclass default
}
return PyDumpsCompact(result.ToDict()), nil
}
func replayDefaultRunner(row *TruthRow, base string) (any, error) {
var setup struct {
ExitCode int `json:"exit_code"`
AgentEnv *string `json:"agent_env"`
ModelMode string `json:"model_mode"`
}
if err := json.Unmarshal(row.Setup, &setup); err != nil {
return nil, err
}
stub := filepath.Join(base, "ep_stub.sh")
script := "#!/bin/sh\n" +
"for a in \"$@\"; do printf '%s\\n' \"$a\" >> \"$EP_CAPTURE\"; done\n" +
"printf 'stub-out\\n'\n" +
"exit \"$EP_EXIT\"\n"
if err := os.WriteFile(stub, []byte(script), 0o755); err != nil {
return nil, err
}
dir := denorm("@ARTDIR@/"+row.Name, base)
if err := os.MkdirAll(dir, 0o777); err != nil {
return nil, err
}
capture := filepath.Join(dir, "argv_capture.txt")
os.Remove(capture)
artifactPath := filepath.Join(dir, "evidence_probe.json")
os.Setenv("MIMO_BIN", stub)
os.Setenv("EP_CAPTURE", capture)
os.Setenv("EP_EXIT", fmt.Sprintf("%d", setup.ExitCode))
if setup.AgentEnv == nil {
os.Unsetenv("MIMO_AGENT")
} else {
os.Setenv("MIMO_AGENT", *setup.AgentEnv)
}
switch setup.ModelMode {
case "unresolved":
os.Unsetenv("MIMO_MODEL")
ModelSelect = EnvModelSource
case "blank":
os.Setenv("MIMO_MODEL", " ")
ModelSelect = EnvModelSource
default:
ModelSelect = ModelSourceFunc(func() (string, error) { return "test-model-v9", nil })
}
err := DefaultRunner("PROMPT-TEXT", artifactPath)
out := map[string]any{"error": errorValue(err)}
if lines, rerr := readLines(capture); rerr == nil {
norm := make([]any, len(lines))
for i, l := range lines {
norm[i] = normOut(l, base)
}
out["argv"] = norm
} else {
out["argv"] = nil
}
logPath := filepath.Join(dir, "evidence_probe.stdout.log")
if log, rerr := os.ReadFile(logPath); rerr == nil {
out["stdout_log"] = string(log)
} else {
out["stdout_log"] = nil
}
return out, nil
}
func readLines(path string) ([]string, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}
content := strings.TrimSuffix(string(raw), "\n")
if content == "" {
return []string{}, nil
}
return strings.Split(content, "\n"), nil
}
File diff suppressed because it is too large. Load diff
+168
View File
@@ -0,0 +1,168 @@
package main
// Battery — the HARD parity gate: the frozen oracle truth (truth.json,
// generated against the LIVE Python oracle with the clock frozen and the
// Rust core serving validate_probe/count_probes) replays VERBATIM. Any
// divergence fails; the truth file rides the repo so the gate holds in CI
// without /tmp.
import (
"encoding/json"
"fmt"
"os"
"reflect"
"testing"
)
func loadTruthDoc(t *testing.T, path string) *TruthDoc {
t.Helper()
raw, err := os.ReadFile(path)
if err != nil {
t.Fatalf("truth.json missing (frozen oracle battery): %v", err)
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatalf("parse truth.json: %v", err)
}
if len(doc.Rows) == 0 {
t.Fatal("truth.json contained no rows")
}
return &doc
}
func findRow(t *testing.T, doc *TruthDoc, name string) TruthRow {
t.Helper()
for _, row := range doc.Rows {
if row.Name == name {
return row
}
}
t.Fatalf("frozen row %q missing from truth.json — battery/truth drift", name)
return TruthRow{}
}
func mustReplayRow(t *testing.T, doc *TruthDoc, name string) {
t.Helper()
row := findRow(t, doc, name)
if msg := replayRow(doc, &row); msg != "" {
t.Errorf("DIVERGED %s: %s", name, msg)
}
}
func TestFullTruthReplayVerbatim(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
var fails []string
for _, row := range doc.Rows {
if msg := replayRow(doc, &row); msg != "" {
fails = append(fails, row.Name+": "+msg)
}
}
if len(fails) > 0 {
for _, f := range fails {
t.Errorf("DIVERGED %s", f)
}
t.Fatalf("frozen-truth parity: %d divergence(s)", len(fails))
}
}
func TestTruthOpCoverage(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
// The frozen battery must keep covering every op family: if a
// regeneration drops a family, this fails before parity silently rots.
want := map[string]int{
"prompt": 5, "validate": 49, "count": 11, "budget": 4,
"record": 3, "run": 11, "to_dict": 3, "default_runner": 5,
}
got := map[string]int{}
for _, row := range doc.Rows {
got[row.Op]++
}
if fmt.Sprint(got) != fmt.Sprint(want) {
t.Fatalf("truth.json op coverage drifted:\n got %v\n want %v", got, want)
}
}
func TestPromptBaselineByteParity(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
row := findRow(t, doc, "prompt_baseline")
got, err := replayPrompt(&row)
if err != nil {
t.Fatal(err)
}
var want string
if err := json.Unmarshal(row.Want, &want); err != nil {
t.Fatal(err)
}
if got != want {
t.Fatalf("prompt drifted from frozen oracle:\n got %q\n want %q", got, want)
}
}
func TestGateErrorOrderIsFieldOrder(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
for _, name := range []string{
"combined_ordering", "status_obj_order", "grounded_missing_source",
"status_repr_unicode", "status_big_float", "status_huge_int",
"valid_grounded", "empty_artifact", "extra_sorted", "nonobject_list",
} {
mustReplayRow(t, doc, name)
}
}
func TestRunProbeExceptionSemantics(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
for _, name := range []string{
"run_success_grounded", "run_success_still_unknown",
"run_model_from_env", "run_model_unset_unknown",
"run_model_env_blank_is_empty_string",
"run_budget_exhausted_before_runner", "run_budget_one_left",
"run_silent_runner_missing_artifact", "run_gate_failure_joined",
"run_gate_failure_non_dict", "run_gate_failure_status_non_string",
} {
mustReplayRow(t, doc, name)
}
}
func TestDefaultRunnerArgvParity(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
for _, name := range []string{
"defrun_exit0_default_agent", "defrun_exit3", "defrun_custom_agent",
"defrun_model_unresolved", "defrun_model_blank_env",
} {
mustReplayRow(t, doc, name)
}
}
func TestCountAndBudgetSemantics(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
for _, name := range []string{
"count_absent_dir", "count_cr_lines", "count_no_trailing_newline",
"count_interleaved_blank", "budget_zero", "budget_one",
"budget_two", "budget_three",
} {
mustReplayRow(t, doc, name)
}
}
func TestRecordProbeBytesParity(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
for _, name := range []string{
"record_fresh_dir_created", "record_appends_existing",
"record_unicode_ensure_ascii",
} {
mustReplayRow(t, doc, name)
}
}
func TestToDictFieldOrderParity(t *testing.T) {
doc := loadTruthDoc(t, "truth.json")
for _, name := range []string{"to_dict_defaults", "to_dict_full", "to_dict_unicode"} {
mustReplayRow(t, doc, name)
}
}
func TestNormalizeJSONIntFloatEquivalence(t *testing.T) {
if !reflect.DeepEqual(normalizeJSON(2), normalizeJSON(2.0)) {
t.Fatal("int/float normalization broke count comparisons")
}
}
+469
View File
@@ -0,0 +1,469 @@
// Package main — forge_client: the FULL Go port of src/rune/forge_client.py
// (dichotomy wave #2479 tranche).
//
// Name-preserving 1:1 port: _request (retry where failure is NOISE, never
// where failure is the SIGNAL), ForgeClient protocol, NullAdapter,
// GiteaForgejoAdapter, create_forge_client. All IO sits behind injectable
// seams so the frozen-truth battery can script the transport byte-for-byte:
//
// - transport: Sender interface (default HTTPXSender over net/http)
// - sleep: RequestOptions.Sleep (default time.Sleep)
// - env: LookupEnv var (default os.LookupEnv)
package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
// ── Defaults (mirroring the Python signature defaults) ──────────────────────
const (
DefaultTimeout = 30 * time.Second
DefaultRetries = 2
DefaultBackoff = 500 * time.Millisecond
SearchPageSize = 50
)
// ── Seams ───────────────────────────────────────────────────────────────────
// Response — the httpx.Response surface the port needs: status + JSON body.
type Response struct {
StatusCode int
Body []byte
}
// JSON decodes the body the way r.json() does; numbers stay json.Number so
// integer-valued fields render exactly like the Python oracle's values.
func (r *Response) JSON() (any, error) {
dec := json.NewDecoder(bytes.NewReader(r.Body))
dec.UseNumber()
var v any
if err := dec.Decode(&v); err != nil {
return nil, err
}
return v, nil
}
// Sender — injectable transport seam (httpx.get / httpx.post call sites).
type Sender interface {
Post(url string, headers map[string]string, jsonBody any, timeout time.Duration) (*Response, error)
Get(url string, headers map[string]string, timeout time.Duration) (*Response, error)
}
// ForgeTransportError mirrors httpx.TransportError's split: connect-phase
// failures (ConnectError / ConnectTimeout — the request never reached the
// server) vs non-connect transport failures (ReadTimeout & co — the server
// MAY have seen it). The Kind strings are the frozen truth vocabulary.
type ForgeTransportError struct {
Kind string // httpx.ConnectError | httpx.ConnectTimeout | httpx.ReadTimeout | httpx.TransportError
Msg string
}
func (e *ForgeTransportError) Error() string { return e.Kind + ": " + e.Msg }
// ConnectPhase reports whether a retry can never double-create.
func (e *ForgeTransportError) ConnectPhase() bool {
return e.Kind == "httpx.ConnectError" || e.Kind == "httpx.ConnectTimeout"
}
// HTTPStatusError — the 4xx IS the information; never retried.
type HTTPStatusError struct{ StatusCode int }
func (e *HTTPStatusError) Error() string {
return fmt.Sprintf("httpx.HTTPStatusError: error %d", e.StatusCode)
}
// HTTPXSender — the default real transport over net/http.
type HTTPXSender struct{ Client *http.Client }
func NewHTTPXSender(client *http.Client) *HTTPXSender {
if client == nil {
client = http.DefaultClient
}
return &HTTPXSender{Client: client}
}
var DefaultSender Sender = NewHTTPXSender(nil)
func (s *HTTPXSender) Post(url string, headers map[string]string, jsonBody any, timeout time.Duration) (*Response, error) {
var body []byte
if jsonBody != nil {
b, err := json.Marshal(jsonBody)
if err != nil {
return nil, err
}
body = b
}
return s.do("POST", url, headers, body, timeout)
}
func (s *HTTPXSender) Get(url string, headers map[string]string, timeout time.Duration) (*Response, error) {
return s.do("GET", url, headers, nil, timeout)
}
func (s *HTTPXSender) do(method, url string, headers map[string]string, body []byte, timeout time.Duration) (*Response, error) {
ctx := context.Background()
var cancel context.CancelFunc
if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
}
var rd io.Reader
if body != nil {
rd = bytes.NewReader(body)
}
req, err := http.NewRequestWithContext(ctx, method, url, rd)
if err != nil {
return nil, &ForgeTransportError{Kind: "httpx.TransportError", Msg: err.Error()}
}
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := s.Client.Do(req)
if err != nil {
return nil, &ForgeTransportError{Kind: classifyTransportErr(err), Msg: err.Error()}
}
defer resp.Body.Close()
b, rerr := io.ReadAll(resp.Body)
if rerr != nil {
return nil, &ForgeTransportError{Kind: "httpx.TransportError", Msg: rerr.Error()}
}
return &Response{StatusCode: resp.StatusCode, Body: b}, nil
}
// classifyTransportErr maps net/http failures onto the httpx vocabulary.
// Best-effort by nature (net/http does not label phases); dial and TLS
// handshake failures are connect-phase, read-side timeouts are not.
func classifyTransportErr(err error) string {
var oe *net.OpError
if errors.As(err, &oe) {
switch oe.Op {
case "dial":
return "httpx.ConnectError"
}
if strings.Contains(strings.ToLower(oe.Error()), "handshake") {
return "httpx.ConnectError"
}
}
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() {
if errors.As(err, &oe) && oe.Op == "dial" {
return "httpx.ConnectTimeout"
}
return "httpx.ReadTimeout"
}
if errors.Is(err, context.DeadlineExceeded) {
return "httpx.ReadTimeout"
}
return "httpx.TransportError"
}
// ── _request ────────────────────────────────────────────────────────────────
// RequestOptions — the keyword surface of the Python _request.
type RequestOptions struct {
Headers map[string]string
JSON any
Timeout time.Duration
Retries int
Backoff time.Duration
Sleep func(time.Duration)
}
// DefaultRequestOptions — timeout=30, retries=2, backoff=0.5.
func DefaultRequestOptions() RequestOptions {
return RequestOptions{Timeout: DefaultTimeout, Retries: DefaultRetries, Backoff: DefaultBackoff}
}
var DefaultSleep = time.Sleep
// Request — resilient single-request path: retry where failure is NOISE,
// never where failure is the SIGNAL.
//
// - GET: retries on connect-phase errors AND any non-connect transport
// error and 5xx status (transient infrastructure failure; idempotent).
// - POST: retries ONLY on connect-phase errors — the request never
// reached the server, so a retry cannot double-create. Read timeouts /
// write errors / 5xx on a POST are NOT retried.
// - 4xx: never retried — the client error IS the information.
// - Attempts: retries+1 total. Backoff backoff*2**n before retry n+1;
// never a sleep before the first attempt.
func Request(sender Sender, method, url string, opts RequestOptions) (*Response, error) {
sleeper := opts.Sleep
if sleeper == nil {
sleeper = DefaultSleep
}
isPost := strings.EqualFold(method, "POST")
for attempt := 0; attempt <= opts.Retries; attempt++ {
var resp *Response
var err error
if isPost {
resp, err = sender.Post(url, opts.Headers, opts.JSON, opts.Timeout)
} else {
resp, err = sender.Get(url, opts.Headers, opts.Timeout)
}
if err != nil {
var terr *ForgeTransportError
if errors.As(err, &terr) && terr.ConnectPhase() {
if attempt < opts.Retries {
sleeper(opts.Backoff * (1 << attempt))
continue
}
return nil, err
}
// Non-connect transport error (read/write): only safe to retry
// when the call is idempotent.
if !isPost && attempt < opts.Retries {
sleeper(opts.Backoff * (1 << attempt))
continue
}
return nil, err
}
if resp.StatusCode >= 500 && !isPost && attempt < opts.Retries {
sleeper(opts.Backoff * (1 << attempt))
continue
}
if resp.StatusCode >= 400 {
return nil, &HTTPStatusError{StatusCode: resp.StatusCode}
}
return resp, nil
}
// Unreachable: every attempt either returns a successful response or
// raises (mirror of the Python AssertionError guard).
return nil, fmt.Errorf("forge_client: unreachable: Request always returns or raises")
}
// ── ForgeClient protocol ────────────────────────────────────────────────────
// ForgeClient mirrors the Python runtime_checkable Protocol: create issues,
// comment, check state, list, search.
type ForgeClient interface {
CreateIssue(title, body string) (any, error)
AddComment(issueNumber int, body string) (any, error)
GetIssueState(issueNumber int) (any, error)
ListOpenIssues() ([]any, error)
SearchIssues(query string) ([]any, error)
}
// ── NullAdapter ─────────────────────────────────────────────────────────────
// NullAdapter — no-op adapter for unconfigured/standalone use. Zero errors.
type NullAdapter struct{}
func (NullAdapter) CreateIssue(title, body string) (any, error) {
return map[string]any{"created": false, "reason": "no forge configured"}, nil
}
func (NullAdapter) AddComment(issueNumber int, body string) (any, error) {
return map[string]any{"created": false, "reason": "no forge configured"}, nil
}
func (NullAdapter) GetIssueState(issueNumber int) (any, error) {
return map[string]any{"state": "unknown"}, nil
}
func (NullAdapter) ListOpenIssues() ([]any, error) { return []any{}, nil }
func (NullAdapter) SearchIssues(query string) ([]any, error) { return []any{}, nil }
// ── GiteaForgejoAdapter ─────────────────────────────────────────────────────
// GiteaForgejoAdapter — /api/v1 paths (identical on both platforms).
type GiteaForgejoAdapter struct {
BaseURL string
Token string
Sender Sender
Sleep func(time.Duration)
}
func NewGiteaForgejoAdapter(url, token, owner, repo string) *GiteaForgejoAdapter {
return &GiteaForgejoAdapter{
BaseURL: strings.TrimRight(url, "/") + "/api/v1/repos/" + owner + "/" + repo,
Token: token,
Sender: DefaultSender,
}
}
func (a *GiteaForgejoAdapter) headers() map[string]string {
return map[string]string{
"Authorization": "token " + a.Token,
"Content-Type": "application/json",
}
}
func (a *GiteaForgejoAdapter) opts() RequestOptions {
o := DefaultRequestOptions()
o.Headers = a.headers()
o.Sleep = a.Sleep
return o
}
func (a *GiteaForgejoAdapter) CreateIssue(title, body string) (any, error) {
o := a.opts()
o.JSON = map[string]any{"title": title, "body": body}
r, err := Request(a.Sender, "POST", a.BaseURL+"/issues", o)
if err != nil {
return nil, err
}
return r.JSON()
}
func (a *GiteaForgejoAdapter) AddComment(issueNumber int, body string) (any, error) {
o := a.opts()
o.JSON = map[string]any{"body": body}
r, err := Request(a.Sender, "POST",
a.BaseURL+"/issues/"+strconv.Itoa(issueNumber)+"/comments", o)
if err != nil {
return nil, err
}
return r.JSON()
}
func (a *GiteaForgejoAdapter) GetIssueState(issueNumber int) (any, error) {
r, err := Request(a.Sender, "GET",
a.BaseURL+"/issues/"+strconv.Itoa(issueNumber), a.opts())
if err != nil {
return nil, err
}
return r.JSON()
}
func (a *GiteaForgejoAdapter) ListOpenIssues() ([]any, error) {
r, err := Request(a.Sender, "GET", a.BaseURL+"/issues?state=open&limit=50", a.opts())
if err != nil {
return nil, err
}
v, err := r.JSON()
if err != nil {
return nil, err
}
batch, _ := v.([]any)
if batch == nil {
batch = []any{}
}
return batch, nil
}
// SearchIssues — repo-scoped marker search over the paginated list endpoint.
//
// #122 gate fix: Gitea/Forgejo has NO repo-scoped /issues/search path
// (verified against live swagger) — that URL 404s on a real forge. The
// repo-scoped LIST endpoint supports state, type and q server-side and is
// paginated: q matches titles (and keywords), so the full marker is sent
// as q and a client-side contains-check keeps exactly the issues whose
// title OR body contains the marker.
//
// Phase B: pagination — the module's one serial IO seam — is pipelined
// (see phaseb.go); the request sequence is identical to the serial path.
func (a *GiteaForgejoAdapter) SearchIssues(query string) ([]any, error) {
return a.SearchIssuesPhaseB(query)
}
// searchQuery — Python urlencode preserves insertion order; so does this.
func searchQuery(q string, page int) string {
return "state=open&type=issues&q=" + url.QueryEscape(q) +
"&limit=50&page=" + strconv.Itoa(page)
}
// filterMarker — `query in str(i.get("title","")) + str(i.get("body",""))`
// with Python str() semantics (None → "None").
func filterMarker(results []any, query string) []any {
out := []any{}
for _, item := range results {
m, _ := item.(map[string]any)
hay := pyDictStr(m, "title") + pyDictStr(m, "body")
if strings.Contains(hay, query) {
out = append(out, item)
}
}
return out
}
func pyDictStr(m map[string]any, key string) string {
if v, ok := m[key]; ok {
return pyStr(v)
}
return ""
}
func pyStr(v any) string {
switch t := v.(type) {
case nil:
return "None"
case string:
return t
case bool:
if t {
return "True"
}
return "False"
case json.Number:
return t.String()
default:
return fmt.Sprint(t)
}
}
// ── create_forge_client ─────────────────────────────────────────────────────
// LookupEnv — injectable environment seam (os.environ reads).
var LookupEnv = os.LookupEnv
func envOr(name, def string) string {
if v, ok := LookupEnv(name); ok {
return v
}
return def
}
func cfgStr(cfg map[string]any, key string) string {
if v, ok := cfg[key]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
// CreateForgeClient — create a forge client from config or environment.
//
// Config keys: forge.url, forge.token, forge.owner, forge.repo.
// Falls back to FORGE_URL/FORGE_TOKEN/FORGE_OWNER/FORGE_REPO env vars.
// Unconfigured → NullAdapter (graceful dormant).
func CreateForgeClient(cfg map[string]any) ForgeClient {
if cfg == nil {
cfg = map[string]any{}
}
forgeURL := cfgStr(cfg, "forge.url")
if forgeURL == "" {
forgeURL = envOr("FORGE_URL", "")
}
token := cfgStr(cfg, "forge.token")
if token == "" {
token = envOr("FORGE_TOKEN", "")
}
if forgeURL == "" || token == "" {
return NullAdapter{}
}
owner := cfgStr(cfg, "forge.owner")
if owner == "" {
owner = envOr("FORGE_OWNER", "owner")
}
repo := cfgStr(cfg, "forge.repo")
if repo == "" {
repo = envOr("FORGE_REPO", "repo")
}
return NewGiteaForgejoAdapter(forgeURL, token, owner, repo)
}
+3
View File
@@ -0,0 +1,3 @@
module rune/forge_client
go 1.22
+26
View File
@@ -0,0 +1,26 @@
// Parity gate — replays the frozen oracle truth (truth.json) through the
// Go port and exits non-zero on any divergence. CI-invocable without a
// test harness.
package main
import (
"fmt"
"os"
)
func main() {
fails := RunTruth("truth.json")
if len(fails) > 0 {
for _, f := range fails {
fmt.Fprintln(os.Stderr, "DIVERGED", f)
}
fmt.Fprintf(os.Stderr, "forge_client: %d divergence(s) vs frozen oracle truth\n", len(fails))
os.Exit(1)
}
doc, err := loadTruth("truth.json")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("forge_client: parity replay OK — %d frozen rows exact vs the Python oracle\n", len(doc.Rows))
}
+70
View File
@@ -0,0 +1,70 @@
package main
// Phase B — language-advantage pass. The parity port kept SearchIssues
// serial (page fetched → appended → next page fetched). Pagination is the
// module's one serial IO seam, so Phase B pipelines it: a producer
// goroutine fetches pages strictly in order (stopping after a short page,
// exactly like the Python oracle) while the consumer filters markers from
// already-received pages. The request sequence is byte-identical to the
// serial path — a page N+1 is only requested after page N arrived and was
// full — so parity is preserved while filtering CPU overlaps network wait.
// searchPages streams pages from the paginated list endpoint. The channel
// is unbuffered: the producer blocks until the consumer has received page
// N before fetching page N+1, so no speculative request is ever made that
// the serial oracle would not have made.
func (a *GiteaForgejoAdapter) searchPages(query string, pages chan<- pageResult) {
defer close(pages)
for pageNum := 1; ; pageNum++ {
r, err := Request(a.Sender, "GET",
a.BaseURL+"/issues?"+searchQuery(query, pageNum), a.opts())
if err != nil {
pages <- pageResult{err: err}
return
}
v, err := r.JSON()
if err != nil {
pages <- pageResult{err: err}
return
}
batch, _ := v.([]any)
if batch == nil {
batch = []any{}
}
pages <- pageResult{items: batch}
if len(batch) < SearchPageSize {
return
}
}
}
type pageResult struct {
items []any
err error
}
// SearchIssuesPhaseB — the pipelined variant. Same result, same request
// sequence, same error semantics (the first transport error propagates and
// discards partial results, as in the oracle). Filtering is per-item, so
// filtering each page as it arrives is identical to the oracle's
// end-of-loop filter, only overlapped with the next page's network wait.
func (a *GiteaForgejoAdapter) SearchIssuesPhaseB(query string) ([]any, error) {
pages := make(chan pageResult)
go a.searchPages(query, pages)
out := []any{}
for p := range pages {
if p.err != nil {
return nil, p.err
}
out = append(out, filterMarker(p.items, query)...)
}
return out, nil
}
// Compile-time safety: both adapters satisfy the protocol, the default
// transport satisfies the seam.
var (
_ ForgeClient = (*GiteaForgejoAdapter)(nil)
_ ForgeClient = NullAdapter{}
_ Sender = (*HTTPXSender)(nil)
)
+186
View File
@@ -0,0 +1,186 @@
package main
// Phase B battery — the goroutine pipeline at the pagination seam must be
// indistinguishable from the serial oracle path: same results, same request
// sequence, no speculative fetches, same error semantics. Race-clean under
// -race.
import (
"fmt"
"strings"
"testing"
"time"
)
func newPageSender(pages [][]any) *scriptedSender {
entries := make([]scriptEntry, 0, len(pages))
for _, p := range pages {
entries = append(entries, scriptEntry{Status: 200, Payload: p})
}
return &scriptedSender{entries: entries, requests: &[]any{}}
}
func markerItem(n int, marker bool) map[string]any {
title := fmt.Sprintf("filler issue %d", n)
if marker {
title = fmt.Sprintf("[auto-failure:cron:Timeout] %d", n)
}
return map[string]any{"number": n, "title": title, "body": ""}
}
// TestPhaseBPipelineParity — a 3-page search through the pipelined path
// yields exactly the oracle's result: markers kept in order, noise dropped.
func TestPhaseBPipelineParity(t *testing.T) {
pages := [][]any{
fullPageWith(2),
fullPageWith(4),
{markerItem(5, true)},
}
sender := newPageSender(pages)
a := NewGiteaForgejoAdapter("https://forge.example.com", "tok", "o", "r")
a.Sender = sender
got, err := a.SearchIssues("[auto-failure:cron:Timeout]")
if err != nil {
t.Fatalf("pipelined search failed: %v", err)
}
var nums []int
for _, item := range got {
m, _ := item.(map[string]any)
nums = append(nums, numInt(m["number"]))
}
if len(nums) != 3 || nums[0] != 2 || nums[1] != 4 || nums[2] != 5 {
t.Fatalf("pipelined filter diverged: %v", nums)
}
if len(*sender.requests) != 3 {
t.Fatalf("expected 3 page requests, got %d", len(*sender.requests))
}
for i, req := range *sender.requests {
url := fmt.Sprint(req.(map[string]any)["url"])
if !strings.Contains(url, fmt.Sprintf("page=%d", i+1)) {
t.Fatalf("page %d requested out of order: %s", i+1, url)
}
}
}
// TestPhaseBNoSpeculativeRequests — a short first page must terminate the
// producer with exactly one request; the goroutine must not prefetch a
// page the serial oracle would never have asked for.
func TestPhaseBNoSpeculativeRequests(t *testing.T) {
sender := newPageSender([][]any{{markerItem(1, true)}})
a := NewGiteaForgejoAdapter("https://forge.example.com", "tok", "o", "r")
a.Sender = sender
got, err := a.SearchIssues("[auto-failure:cron:Timeout]")
if err != nil {
t.Fatalf("search failed: %v", err)
}
if len(got) != 1 {
t.Fatalf("expected the single marker hit, got %d items", len(got))
}
if len(*sender.requests) != 1 {
t.Fatalf("speculative fetch detected: %d requests for a short single page", len(*sender.requests))
}
}
// TestPhaseBErrorPropagation — a transport error on page 2 surfaces with
// the frozen httpx-kind vocabulary and discards partial results, exactly
// like the serial path. The connect error is retryable NOISE per _request
// (retries=2 → 3 attempts), so the script must carry all three before the
// error finally propagates.
func TestPhaseBErrorPropagation(t *testing.T) {
sender := &scriptedSender{
entries: []scriptEntry{
{Status: 200, Payload: fullPage()},
{Exc: "connect"},
{Exc: "connect"},
{Exc: "connect"},
},
requests: &[]any{},
}
a := NewGiteaForgejoAdapter("https://forge.example.com", "tok", "o", "r")
a.Sender = sender
_, err := a.SearchIssues("[auto-failure:cron:Timeout]")
if err == nil {
t.Fatal("page-2 connect error must propagate")
}
if errorKind(err) != "httpx.ConnectError" {
t.Fatalf("frozen error vocabulary changed: %v", errorKind(err))
}
if len(*sender.requests) != 4 {
t.Fatalf("expected 4 requests (page 1 + 3 page-2 attempts), got %d", len(*sender.requests))
}
}
// TestPhaseBPipelineOverlapsFilter — the pipeline is not decorative: while
// the consumer filters page N, the producer is already fetching page N+1.
// Proven by a sender that blocks in-flight until the consumer has begun
// filtering the previous page (unbuffered channel rendezvous).
func TestPhaseBPipelineOverlapsFilter(t *testing.T) {
pageFetchStarted := make(chan int, 4)
sender := &countingSender{scriptedSender: scriptedSender{
entries: []scriptEntry{
{Status: 200, Payload: fullPage()},
{Status: 200, Payload: []any{}},
},
requests: &[]any{},
}, started: pageFetchStarted}
a := NewGiteaForgejoAdapter("https://forge.example.com", "tok", "o", "r")
a.Sender = sender
done := make(chan struct{})
go func() {
defer close(done)
if _, err := a.SearchIssues("x"); err != nil {
t.Errorf("search failed: %v", err)
}
}()
// page 1 fetch starts immediately
<-pageFetchStarted
// page 2 fetch must start while page 1 is still being consumed —
// wait for it with a generous timeout instead of deadlocking.
select {
case <-pageFetchStarted:
case <-time.After(5 * time.Second):
t.Fatal("producer did not overlap: page 2 not fetched while page 1 was being consumed")
}
<-done
}
func fullPage() []any {
p := make([]any, SearchPageSize)
for i := range p {
p[i] = map[string]any{"number": i, "title": "x", "body": ""}
}
return p
}
// fullPageWith — a full 50-item page whose item #markerAt carries the
// search marker (forcing pagination exactly like the live forge).
func fullPageWith(markerAt int) []any {
p := fullPage()
p[markerAt] = markerItem(markerAt, true)
return p
}
// countingSender counts started requests so the test can observe the
// producer/consumer overlap.
type countingSender struct {
scriptedSender
started chan int
n int
}
func (s *countingSender) record(method, url string, headers map[string]string, jsonBody any, timeout time.Duration) *scriptEntry {
s.n++
select {
case s.started <- s.n:
default:
}
return s.scriptedSender.record(method, url, headers, jsonBody, timeout)
}
func (s *countingSender) Post(url string, headers map[string]string, jsonBody any, timeout time.Duration) (*Response, error) {
return s.respond(s.record("POST", url, headers, jsonBody, timeout))
}
func (s *countingSender) Get(url string, headers map[string]string, timeout time.Duration) (*Response, error) {
return s.respond(s.record("GET", url, headers, nil, timeout))
}
+534
View File
@@ -0,0 +1,534 @@
// Replay — the frozen-truth parity engine. Reads truth.json (generated by
// scripts/gen_forge_client_truth.py against the live Python oracle, httpx
// transport scripted, sleep recorded) and re-executes every row through
// this Go port, comparing canonically (sorted-key JSON).
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"sort"
"strings"
"time"
)
// TruthDoc — the frozen truth file structure.
type TruthDoc struct {
FrozenTS string `json:"frozen_ts"`
Oracle string `json:"oracle"`
Rows []TruthRow `json:"rows"`
}
// TruthRow — one frozen case.
type TruthRow struct {
Name string `json:"name"`
Op string `json:"op"`
Input json.RawMessage `json:"input"`
Want json.RawMessage `json:"want"`
}
// RunTruth replays every row and returns a divergence report (empty on 100%).
func RunTruth(path string) []string {
doc, err := loadTruth(path)
if err != nil {
return []string{fmt.Sprintf("load truth: %v", err)}
}
var fails []string
for i := range doc.Rows {
if msg := compareOne(doc, &doc.Rows[i]); msg != "" {
fails = append(fails, doc.Rows[i].Name+": "+msg)
}
}
return fails
}
func loadTruth(path string) (*TruthDoc, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
return nil, err
}
if len(doc.Rows) == 0 {
return nil, fmt.Errorf("truth.json contained no rows")
}
return &doc, nil
}
// compareOne replays one row and returns "" on parity, else a divergence
// description. Shared by RunTruth and the per-function battery tests.
func compareOne(doc *TruthDoc, row *TruthRow) string {
got, err := execRow(row)
if err != nil {
return fmt.Sprintf("error: %v", err)
}
wantStr, err := canonicalFromRaw(row.Want)
if err != nil {
return fmt.Sprintf("canonicalize want: %v", err)
}
gotStr, err := canonicalFromRaw([]byte(mustJSON(got)))
if err != nil {
return fmt.Sprintf("canonicalize got: %v", err)
}
if wantStr != gotStr {
return fmt.Sprintf("\n want: %s\n got: %s", wantStr, gotStr)
}
return ""
}
// ── scripted transport (mirrors the generator's fake httpx) ────────────────
type scriptEntry struct {
Status int
Payload any
Exc string
}
type scriptedSender struct {
entries []scriptEntry
requests *[]any
}
func excKind(exc string) string {
switch exc {
case "connect":
return "httpx.ConnectError"
case "connect_timeout":
return "httpx.ConnectTimeout"
case "read_timeout":
return "httpx.ReadTimeout"
default:
return "httpx.TransportError"
}
}
func (s *scriptedSender) record(method, url string, headers map[string]string, jsonBody any, timeout time.Duration) *scriptEntry {
req := map[string]any{
"method": method,
"url": url,
"headers": headers,
"json": jsonBody,
"timeout": timeout.Seconds(),
}
*s.requests = append(*s.requests, req)
if len(s.entries) == 0 {
panic(fmt.Sprintf("script exhausted at request %d (%s %s)", len(*s.requests), method, url))
}
entry := s.entries[0]
s.entries = s.entries[1:]
return &entry
}
func (s *scriptedSender) respond(entry *scriptEntry) (*Response, error) {
if entry.Exc != "" {
return nil, &ForgeTransportError{Kind: excKind(entry.Exc), Msg: "scripted"}
}
status := entry.Status
if status == 0 {
status = 200
}
body, err := json.Marshal(entry.Payload)
if err != nil {
return nil, err
}
if entry.Payload == nil {
body = []byte("{}")
}
return &Response{StatusCode: status, Body: body}, nil
}
func (s *scriptedSender) Post(url string, headers map[string]string, jsonBody any, timeout time.Duration) (*Response, error) {
return s.respond(s.record("POST", url, headers, jsonBody, timeout))
}
func (s *scriptedSender) Get(url string, headers map[string]string, timeout time.Duration) (*Response, error) {
return s.respond(s.record("GET", url, headers, nil, timeout))
}
// ── row execution ───────────────────────────────────────────────────────────
func decodeInput(raw json.RawMessage) (map[string]any, error) {
if len(raw) == 0 {
return map[string]any{}, nil
}
dec := json.NewDecoder(bytes.NewReader(raw))
dec.UseNumber()
var v any
if err := dec.Decode(&v); err != nil {
return nil, err
}
m, ok := v.(map[string]any)
if !ok {
return nil, fmt.Errorf("input not an object")
}
return m, nil
}
func numInt(v any) int {
switch t := v.(type) {
case json.Number:
n, err := t.Int64()
if err != nil {
panic(fmt.Sprintf("not an integer: %v", t))
}
return int(n)
case float64:
return int(t)
default:
panic(fmt.Sprintf("not a number: %v", v))
}
}
func numFloat(v any) float64 {
switch t := v.(type) {
case json.Number:
f, _ := t.Float64()
return f
case float64:
return t
default:
panic(fmt.Sprintf("not a number: %v", v))
}
}
// wantRecorder builds the {result, requests, sleeps, error} got-shape the
// frozen rows carry.
type recorder struct {
requests []any
sleeps []any
errKind string
}
func (r *recorder) sleep(d time.Duration) { r.sleeps = append(r.sleeps, d.Seconds()) }
func (r *recorder) got(result any) map[string]any {
return map[string]any{
"result": result,
"requests": r.requests,
"sleeps": r.sleeps,
"error": errKindOrNil(r.errKind),
}
}
func errKindOrNil(kind string) any {
if kind == "" {
return nil
}
return kind
}
func errorKind(err error) string {
var terr *ForgeTransportError
if errors.As(err, &terr) {
return terr.Kind
}
var serr *HTTPStatusError
if errors.As(err, &serr) {
return "httpx.HTTPStatusError"
}
return fmt.Sprintf("%T", err)
}
func execRow(row *TruthRow) (any, error) {
in, err := decodeInput(row.Input)
if err != nil {
return nil, fmt.Errorf("decode input: %w", err)
}
switch row.Op {
case "select":
return execSelect(in)
case "null":
return execNull(in)
case "call":
return execCall(in)
case "request":
return execRequest(in)
}
return nil, fmt.Errorf("unknown row op %s (%s)", row.Op, row.Name)
}
func execSelect(in map[string]any) (any, error) {
var cfg map[string]any
if v, ok := in["config"]; ok && v != nil {
m, ok := v.(map[string]any)
if !ok {
return nil, fmt.Errorf("select: config not an object")
}
cfg = m
}
env := map[string]string{}
if v, ok := in["env"].(map[string]any); ok {
for k, val := range v {
s, _ := val.(string)
env[k] = s
}
}
saved := LookupEnv
defer func() { LookupEnv = saved }()
LookupEnv = func(name string) (string, bool) {
v, ok := env[name]
return v, ok
}
client := CreateForgeClient(cfg)
if _, isNull := client.(NullAdapter); isNull {
return map[string]any{"adapter": "NullAdapter", "base_url": nil, "token": nil}, nil
}
a := client.(*GiteaForgejoAdapter)
return map[string]any{"adapter": "GiteaForgejoAdapter", "base_url": a.BaseURL, "token": a.Token}, nil
}
func execNull(in map[string]any) (any, error) {
method, _ := in["method"].(string)
args, _ := in["args"].([]any)
client := NullAdapter{}
switch method {
case "create_issue":
return client.CreateIssue(argStr(args, 0), argStr(args, 1))
case "add_comment":
return client.AddComment(numInt(args[0]), argStr(args, 1))
case "get_issue_state":
return client.GetIssueState(numInt(args[0]))
case "list_open_issues":
return client.ListOpenIssues()
case "search_issues":
return client.SearchIssues(argStr(args, 0))
}
return nil, fmt.Errorf("null: unknown method %s", method)
}
func argStr(args []any, i int) string {
if i < len(args) {
s, _ := args[i].(string)
return s
}
return ""
}
func newAdapterFrom(in map[string]any, rec *recorder) (*GiteaForgejoAdapter, *scriptedSender, error) {
method, _ := in["method"].(string)
url, _ := in["url"].(string)
token, _ := in["token"].(string)
owner, _ := in["owner"].(string)
repo, _ := in["repo"].(string)
entries, err := decodeScript(in["script"])
if err != nil {
return nil, nil, err
}
sender := &scriptedSender{entries: entries, requests: &rec.requests}
a := NewGiteaForgejoAdapter(url, token, owner, repo)
a.Sender = sender
a.Sleep = rec.sleep
_ = method
return a, sender, nil
}
func decodeScript(v any) ([]scriptEntry, error) {
list, ok := v.([]any)
if !ok {
return nil, fmt.Errorf("script not a list")
}
out := make([]scriptEntry, 0, len(list))
for _, item := range list {
m, ok := item.(map[string]any)
if !ok {
return nil, fmt.Errorf("script entry not an object")
}
e := scriptEntry{}
if exc, ok := m["exc"].(string); ok {
e.Exc = exc
}
if status, ok := m["status"]; ok {
e.Status = numInt(status)
}
if payload, ok := m["payload"]; ok {
e.Payload = payload
}
out = append(out, e)
}
return out, nil
}
func execCall(in map[string]any) (any, error) {
method, _ := in["method"].(string)
rec := &recorder{}
a, _, err := newAdapterFrom(in, rec)
if err != nil {
return nil, err
}
kwargs, _ := in["kwargs"].(map[string]any)
var result any
switch method {
case "create_issue":
result, err = a.CreateIssue(kwStr(kwargs, "title"), kwStr(kwargs, "body"))
case "add_comment":
result, err = a.AddComment(numInt(kwargs["issue_number"]), kwStr(kwargs, "body"))
case "get_issue_state":
result, err = a.GetIssueState(numInt(kwargs["issue_number"]))
case "list_open_issues":
result, err = a.ListOpenIssues()
case "search_issues":
result, err = a.SearchIssues(kwStr(kwargs, "query"))
default:
return nil, fmt.Errorf("call: unknown method %s", method)
}
if err != nil {
rec.errKind = errorKind(err)
result = nil
}
return rec.got(result), nil
}
func kwStr(m map[string]any, key string) string {
if v, ok := m[key]; ok {
s, _ := v.(string)
return s
}
return ""
}
func execRequest(in map[string]any) (any, error) {
rec := &recorder{}
method, _ := in["method"].(string)
rawURL, _ := in["url"].(string)
entries, err := decodeScript(in["script"])
if err != nil {
return nil, err
}
sender := &scriptedSender{entries: entries, requests: &rec.requests}
opts := RequestOptions{
Headers: toStringMap(in["headers"]),
JSON: in["json"],
Timeout: secondsDuration(in["timeout"]),
Retries: numInt(in["retries"]),
Backoff: secondsDuration(in["backoff"]),
Sleep: rec.sleep,
}
resp, err := Request(sender, method, rawURL, opts)
var result any
if err != nil {
rec.errKind = errorKind(err)
} else {
result, err = resp.JSON()
if err != nil {
return nil, err
}
}
return rec.got(result), nil
}
func toStringMap(v any) map[string]string {
m, ok := v.(map[string]any)
if !ok {
return nil
}
out := make(map[string]string, len(m))
for k, val := range m {
s, _ := val.(string)
out[k] = s
}
return out
}
func secondsDuration(v any) time.Duration {
if v == nil {
return 0
}
return time.Duration(numFloat(v) * float64(time.Second))
}
// ── canonical comparison (sorted-key JSON) ──────────────────────────────────
func canonicalFromRaw(raw []byte) (string, error) {
var v any
dec := json.NewDecoder(bytes.NewReader(raw))
dec.UseNumber()
if err := dec.Decode(&v); err != nil {
return "", err
}
return canonical(normalize(v)), nil
}
// canonical — sorted-key deterministic rendering of a decoded JSON value.
func canonical(v any) string {
switch t := v.(type) {
case nil:
return "null"
case bool:
if t {
return "true"
}
return "false"
case string:
b, _ := json.Marshal(t)
return string(b)
case float64:
if t == float64(int64(t)) {
return fmt.Sprintf("%d", int64(t))
}
return fmt.Sprintf("%v", t)
case json.Number:
f, _ := t.Float64()
return canonical(f)
case []any:
parts := make([]string, len(t))
for i, item := range t {
parts[i] = canonical(normalize(item))
}
return "[" + strings.Join(parts, ", ") + "]"
case map[string]any:
keys := make([]string, 0, len(t))
for k := range t {
keys = append(keys, k)
}
sort.Strings(keys)
parts := make([]string, 0, len(keys))
for _, k := range keys {
bk, _ := json.Marshal(k)
parts = append(parts, string(bk)+": "+canonical(normalize(t[k])))
}
return "{" + strings.Join(parts, ", ") + "}"
default:
return fmt.Sprintf("«%v»", t)
}
}
// normalize — flatten trees into plain map/float64 representations so
// canonical() sees one shape.
func normalize(v any) any {
switch t := v.(type) {
case map[string]any:
m := map[string]any{}
for k, val := range t {
m[k] = normalize(val)
}
return m
case []any:
out := make([]any, len(t))
for i, item := range t {
out[i] = normalize(item)
}
return out
case json.Number:
f, _ := t.Float64()
return f
default:
return v
}
}
func mustJSON(v any) string {
b, err := json.Marshal(normalize(v))
if err != nil {
return "null"
}
return string(b)
}
File diff suppressed because it is too large. Load diff
+285
View File
@@ -0,0 +1,285 @@
package main
// Battery — the HARD parity gate plus per-function batteries replaying the
// frozen oracle truth (truth.json, generated by
// scripts/gen_forge_client_truth.py against the LIVE Python oracle with a
// scripted httpx transport and a recording sleep) VERBATIM. Any divergence
// fails; the truth file rides the repo so the gate holds in CI without a
// live forge.
import (
"encoding/json"
"fmt"
"os"
"strings"
"testing"
"time"
)
func loadTruthOrDie(t *testing.T) *TruthDoc {
t.Helper()
doc, err := loadTruth("truth.json")
if err != nil {
t.Fatalf("truth.json unusable (frozen oracle battery): %v", err)
}
return doc
}
func findRow(t *testing.T, name string) TruthRow {
t.Helper()
doc := loadTruthOrDie(t)
for _, row := range doc.Rows {
if row.Name == name {
return row
}
}
t.Fatalf("frozen row %q missing from truth.json — battery/truth drift", name)
return TruthRow{}
}
func mustReplay(t *testing.T, name string) {
t.Helper()
doc := loadTruthOrDie(t)
row := findRow(t, name)
if msg := compareOne(doc, &row); msg != "" {
t.Fatalf("DIVERGED %s: %s", name, msg)
}
}
func replayOp(t *testing.T, op string) int {
t.Helper()
doc := loadTruthOrDie(t)
n := 0
for i := range doc.Rows {
row := &doc.Rows[i]
if row.Op != op {
continue
}
n++
if msg := compareOne(doc, row); msg != "" {
t.Fatalf("DIVERGED %s: %s", row.Name, msg)
}
}
if n == 0 {
t.Fatalf("no %s rows in truth.json", op)
}
return n
}
// rowWant decodes a row's want JSON into a generic map.
func rowWant(t *testing.T, name string) map[string]any {
t.Helper()
row := findRow(t, name)
var want map[string]any
if err := json.Unmarshal(row.Want, &want); err != nil {
t.Fatalf("decode want of %s: %v", name, err)
}
return want
}
// TestTruthParity — the whole frozen battery, one shot.
func TestTruthParity(t *testing.T) {
doc := loadTruthOrDie(t)
var fails []string
for i := range doc.Rows {
if msg := compareOne(doc, &doc.Rows[i]); msg != "" {
fails = append(fails, doc.Rows[i].Name+": "+msg)
}
}
if len(fails) > 0 {
t.Fatalf("parity replay diverged after %d clean rows:\n%s",
len(doc.Rows)-len(fails), strings.Join(fails, "\n"))
}
t.Logf("PARITY OK: %d cases exact-equal vs the Python oracle", len(doc.Rows))
}
// TestBatteryCreateForgeClient — every selection branch: none, env, config
// keys, config-beats-env, owner/repo defaults and env overrides, trailing
// slash strip, empty-string falsy fallthrough, missing halves.
func TestBatteryCreateForgeClient(t *testing.T) {
if n := replayOp(t, "select"); n != 11 {
t.Fatalf("expected 11 frozen select rows, got %d — battery/truth drift", n)
}
want := rowWant(t, "select:none")
if want["adapter"] != "NullAdapter" {
t.Fatalf("unconfigured must be NullAdapter, got %v", want["adapter"])
}
want = rowWant(t, "select:env-configured")
if want["base_url"] != "https://forge.example.com/api/v1/repos/owner/repo" {
t.Fatalf("frozen env base_url changed: %v", want["base_url"])
}
want = rowWant(t, "select:config-beats-env")
if want["base_url"] != "https://cfg.example.com/api/v1/repos/owner/repo" ||
want["token"] != "cfg_tok" {
t.Fatalf("config-beats-env changed: %v", want)
}
want = rowWant(t, "select:trailing-slash-strip")
if want["base_url"] != "https://forge.example.com/api/v1/repos/owner/repo" {
t.Fatalf("trailing slash strip changed: %v", want["base_url"])
}
want = rowWant(t, "select:cfg-url-missing-token")
if want["adapter"] != "NullAdapter" {
t.Fatalf("url without token must stay dormant: %v", want["adapter"])
}
mustReplay(t, "select:owner-empty-env")
}
// TestBatteryNullAdapter — every no-op shape, zero errors.
func TestBatteryNullAdapter(t *testing.T) {
if n := replayOp(t, "null"); n != 5 {
t.Fatalf("expected 5 frozen null rows, got %d — battery/truth drift", n)
}
want := rowWant(t, "null:create-issue")
if want["created"] != false || want["reason"] != "no forge configured" {
t.Fatalf("frozen null create changed: %v", want)
}
want = rowWant(t, "null:get-issue-state")
if want["state"] != "unknown" {
t.Fatalf("frozen null state changed: %v", want)
}
}
// TestBatteryGiteaAdapter — every adapter method incl. pagination, the
// client-side marker filter, and retry propagation through _request.
func TestBatteryGiteaAdapter(t *testing.T) {
if n := replayOp(t, "call"); n != 14 {
t.Fatalf("expected 14 frozen call rows, got %d — battery/truth drift", n)
}
// create_issue posts to /issues with token auth and the JSON body.
want := rowWant(t, "call:create-issue")
reqs, _ := want["requests"].([]any)
if len(reqs) != 1 {
t.Fatalf("create-issue: expected 1 request, got %d", len(reqs))
}
req, _ := reqs[0].(map[string]any)
if !strings.HasSuffix(fmt.Sprint(req["url"]), "/api/v1/repos/o/r/issues") {
t.Fatalf("create-issue URL changed: %v", req["url"])
}
headers, _ := req["headers"].(map[string]any)
if headers["Authorization"] != "token tok" || headers["Content-Type"] != "application/json" {
t.Fatalf("frozen auth headers changed: %v", headers)
}
if fmt.Sprint(req["timeout"]) != "30" {
t.Fatalf("frozen timeout passthrough changed: %v", req["timeout"])
}
// search hits the repo-scoped LIST endpoint, never /issues/search.
want = rowWant(t, "call:search-single-page")
reqs, _ = want["requests"].([]any)
req, _ = reqs[0].(map[string]any)
searchURL := fmt.Sprint(req["url"])
if strings.Contains(searchURL, "/issues/search") {
t.Fatalf("regression: search used the 404ing /issues/search path")
}
if !strings.Contains(searchURL, "state=open") ||
!strings.Contains(searchURL, "type=issues") ||
!strings.Contains(searchURL, "q=%5Bauto-failure%3Acron%3AConnection%5D") {
t.Fatalf("frozen search query changed: %s", searchURL)
}
// pagination: a full 50-item page must be followed by page=2.
want = rowWant(t, "call:search-paginates")
reqs, _ = want["requests"].([]any)
if len(reqs) != 2 || !strings.Contains(fmt.Sprint(reqs[1].(map[string]any)["url"]), "page=2") {
t.Fatalf("frozen pagination changed: %d requests", len(reqs))
}
// retry through _request: connect-phase POST retried once, sleep 0.5s.
want = rowWant(t, "call:create-issue-retry-connect")
sleeps, _ := want["sleeps"].([]any)
if len(sleeps) != 1 || fmt.Sprint(sleeps[0]) != "0.5" {
t.Fatalf("frozen connect retry sleeps changed: %v", sleeps)
}
// 4xx is the information: one shot, no retry.
want = rowWant(t, "call:get-issue-state-4xx")
if want["error"] != "httpx.HTTPStatusError" {
t.Fatalf("frozen 4xx error kind changed: %v", want["error"])
}
reqs, _ = want["requests"].([]any)
if len(reqs) != 1 {
t.Fatalf("4xx must not retry: %d requests", len(reqs))
}
}
// TestBatteryRequest — every _request branch: noise retried, signal never,
// exponential backoff never before the first attempt, timeout passthrough,
// lowercase method routing.
func TestBatteryRequest(t *testing.T) {
if n := replayOp(t, "request"); n != 19 {
t.Fatalf("expected 19 frozen request rows, got %d — battery/truth drift", n)
}
// backoff is exponential: [0.5, 1.0], never before the first attempt.
want := rowWant(t, "request:backoff-exponential")
sleeps, _ := want["sleeps"].([]any)
if len(sleeps) != 2 || fmt.Sprint(sleeps[0]) != "0.5" || fmt.Sprint(sleeps[1]) != "1" {
t.Fatalf("frozen backoff schedule changed: %v", sleeps)
}
// connect exhaustion: retries+1 attempts, error propagates.
want = rowWant(t, "request:get-connect-exhaust")
if want["error"] != "httpx.ConnectError" {
t.Fatalf("frozen exhaust error changed: %v", want["error"])
}
reqs, _ := want["requests"].([]any)
if len(reqs) != 3 {
t.Fatalf("frozen attempt count changed: %d", len(reqs))
}
// POST read timeout: the server MAY have processed it — one shot.
want = rowWant(t, "request:post-read-timeout-no-retry")
if want["error"] != "httpx.ReadTimeout" {
t.Fatalf("frozen read-timeout kind changed: %v", want["error"])
}
reqs, _ = want["requests"].([]any)
if len(reqs) != 1 {
t.Fatalf("POST read timeout must not retry: %d requests", len(reqs))
}
// POST 5xx after send: ambiguous — one shot.
want = rowWant(t, "request:post-5xx-no-retry")
if want["error"] != "httpx.HTTPStatusError" {
t.Fatalf("frozen post-5xx kind changed: %v", want["error"])
}
// timeout passes through per call.
want = rowWant(t, "request:timeout-passthrough")
reqs, _ = want["requests"].([]any)
if fmt.Sprint(reqs[0].(map[string]any)["timeout"]) != "17" {
t.Fatalf("frozen timeout passthrough changed: %v", reqs[0])
}
mustReplay(t, "request:lowercase-method")
mustReplay(t, "request:headers-json-capture")
mustReplay(t, "request:get-5xx-exhaust")
mustReplay(t, "request:get-read-timeout-retries")
}
// TestBatteryRetryAsymmetry — direct cross-check (not via a truth row) of
// the behavior that motivated the module: GET 5xx is noise (retried to
// success), POST 5xx is signal (one shot, no double-create).
func TestBatteryRetryAsymmetry(t *testing.T) {
getRequests := &[]any{}
getSender := &scriptedSender{
entries: []scriptEntry{{Status: 502}, {Status: 200, Payload: map[string]any{"ok": 1}}},
requests: getRequests,
}
opts := DefaultRequestOptions()
opts.Sleep = func(time.Duration) {}
if _, err := Request(getSender, "GET", "https://x/api", opts); err != nil {
t.Fatalf("GET 5xx must be retried to success: %v", err)
}
if len(*getRequests) != 2 {
t.Fatalf("GET 5xx made %d requests — retry lost", len(*getRequests))
}
postRequests := &[]any{}
postSender := &scriptedSender{
entries: []scriptEntry{{Status: 502}, {Status: 200}},
requests: postRequests,
}
postOpts := DefaultRequestOptions()
postOpts.JSON = map[string]any{}
postOpts.Sleep = func(time.Duration) {}
if _, err := Request(postSender, "POST", "https://x/api", postOpts); err == nil {
t.Fatal("POST 5xx must never be retried")
}
if len(*postRequests) != 1 {
t.Fatalf("POST 5xx made %d requests — double-create hazard", len(*postRequests))
}
}
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
+3
View File
@@ -0,0 +1,3 @@
module rune/openai-compat-transport
go 1.22
+28
View File
@@ -0,0 +1,28 @@
// Package main — OpenAI-compat transport CLI: replay the frozen oracle
// truth battery.
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: openai-compat-transport replay <truth.json>")
os.Exit(2)
}
path := "truth.json"
if len(os.Args) > 2 {
path = os.Args[2]
}
fails := RunTruth(path)
if len(fails) > 0 {
for _, f := range fails {
fmt.Println(f)
}
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", len(fails))
os.Exit(1)
}
fmt.Printf("PARITY OK: %d cases exact-equal vs the Python oracle\n", truthRowCount())
}
@@ -0,0 +1,586 @@
// Package main — OpenAI-compatible chat transport, ported 1:1 from
// src/rune/openai_compat_transport.py (dichotomy wave #2479).
//
// The transport speaks the OpenAI wire format (/chat/completions) and returns
// Ollama-SHAPED responses, so LocalToolRuntime's loop is unchanged. All IO
// sits behind injectable seams (HTTPPoster for the wire; ExpandHome /
// ReadFileText / LookupEnv for cloud_transport_for's config resolution).
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
// ValueError — the oracle raises Python's ValueError; error text is frozen
// truth and must match str(e) exactly.
type ValueError struct{ Msg string }
func (e *ValueError) Error() string { return e.Msg }
// HTTPPoster — the wire seam. Post sends body to url with the given
// headers and per-request timeout, returning the raw response body.
type HTTPPoster interface {
Post(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error)
}
// HTTPPosterFunc adapts a function to HTTPPoster.
type HTTPPosterFunc func(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error)
// Post implements HTTPPoster.
func (f HTTPPosterFunc) Post(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
return f(url, headers, body, timeoutSecs)
}
// defaultPoster — net/http faithful to the oracle's urllib round trip.
type defaultPoster struct{}
func (defaultPoster) Post(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(string(body)))
if err != nil {
return nil, err
}
for k, v := range headers {
req.Header.Set(k, v)
}
client := &http.Client{Timeout: time.Duration(timeoutSecs * float64(time.Second))}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
buf := new(strings.Builder)
tmp := make([]byte, 32*1024)
for {
n, rerr := resp.Body.Read(tmp)
if n > 0 {
buf.Write(tmp[:n])
}
if rerr != nil {
break
}
}
return []byte(buf.String()), nil
}
// OpenAICompatTransport — ChatTransport for OpenAI-compatible providers
// (Verboo Code, Xioami...).
type OpenAICompatTransport struct {
BaseURL string
APIKey string
Provider string
// Cloud round trips with a full tool schema routinely exceed the local
// tier timeout (30s): observed live, glm-5.3-flash read-timeout at
// 30.6s mid-iteration. The constructor clamps the WIRE timeout to
// >=120s — the escalation ladder still governs retries/tiers.
Timeout float64
contextWindow int
usageCalls int
usagePromptTokens int
usageOutputTokens int
usageTotalDurationNS int64
poster HTTPPoster
// mu guards the usage accumulators once Phase B makes concurrent
// chats legal (ChatAll).
mu sync.Mutex
}
// NewOpenAICompatTransport — oracle defaults: timeout 120, context window 131072.
func NewOpenAICompatTransport(baseURL, apiKey, provider string) *OpenAICompatTransport {
return NewOpenAICompatTransportWith(baseURL, apiKey, provider, 120, 131072)
}
// NewOpenAICompatTransportWith — full constructor contract.
func NewOpenAICompatTransportWith(baseURL, apiKey, provider string, timeout float64, contextWindow int) *OpenAICompatTransport {
if timeout < 120 {
timeout = 120
}
return &OpenAICompatTransport{
BaseURL: strings.TrimRight(baseURL, "/"),
APIKey: apiKey,
Provider: provider,
Timeout: timeout,
contextWindow: contextWindow,
poster: defaultPoster{},
}
}
// SetPoster swaps the wire seam (battery/replay injection).
func (t *OpenAICompatTransport) SetPoster(p HTTPPoster) { t.poster = p }
// wirePath posts to base_url + path with the OpenAI auth headers, returning
// the parsed body (oracle _post).
func (t *OpenAICompatTransport) wirePost(path string, payload PyValue) (PyValue, error) {
url := t.BaseURL + path
headers := map[string]string{
"Content-Type": "application/json",
"Authorization": fmt.Sprintf("Bearer %s", t.APIKey),
}
body := []byte(PyDumpsCompact(payload))
raw, err := t.poster.Post(url, headers, body, t.Timeout)
if err != nil {
return nil, err
}
decoded, err := PyParse(raw)
if err != nil {
return nil, err
}
if _, isObj := decoded.(*PyObj); !isObj {
return nil, &ValueError{Msg: "provider returned a non-object response"}
}
return decoded, nil
}
// toOpenaiMessages — Ollama-shape → OpenAI-shape. Pairing: the runtime's
// _tool_message sets tool_call_id from request.call_id (minted by
// normalize_ollama_tool_calls from the response's own id). The assistant
// message must use THE SAME ids for its tool_calls — a mismatch makes the
// provider treat the tool result as orphaned and the model re-issues the
// identical call forever (observed live: glm-5.3-flash looped the same
// edit_file twice, empty terminal text both turns).
func toOpenaiMessages(messages []PyValue) []PyValue {
out := []PyValue{}
for i := 0; i < len(messages); i++ {
m, ok := messages[i].(*PyObj)
if !ok {
m = NewPyObj()
}
role, _ := m.Get("role")
roleStr, _ := role.(string)
tcRaw, _ := m.Get("tool_calls")
var toolCalls []PyValue
if list, isList := tcRaw.([]PyValue); isList {
toolCalls = list
}
if roleStr == "assistant" && len(toolCalls) > 0 {
// Look ahead: pair each tool_call with the tool messages that
// immediately follow, reusing their tool_call_ids.
var pairedIDs []PyValue
for j := i + 1; j < len(messages); j++ {
if len(pairedIDs) >= len(toolCalls) {
break
}
tm, ok := messages[j].(*PyObj)
if !ok {
continue
}
tmRole, _ := tm.Get("role")
if s, isStr := tmRole.(string); !isStr || s != "tool" {
continue
}
id, _ := tm.Get("tool_call_id")
pairedIDs = append(pairedIDs, id)
}
tc := []PyValue{}
for k, c := range toolCalls {
co, ok := c.(*PyObj)
if !ok {
co = NewPyObj()
}
fnRaw, _ := co.Get("function")
fn, _ := fnRaw.(*PyObj)
if fn == nil {
fn = NewPyObj()
}
argsRaw, _ := fn.Get("arguments")
if argsRaw == nil {
if _, present := fn.Get("arguments"); !present {
argsRaw = NewPyObj() // fn.get("arguments", {}) default
}
}
var argsStr string
if _, isObj := argsRaw.(*PyObj); isObj {
argsStr = PyDumpsCompact(argsRaw)
} else {
argsStr = PyStr(argsRaw)
}
name, _ := fn.Get("name")
var callID PyValue
if k < len(pairedIDs) {
callID = pairedIDs[k]
}
if !PyTruthy(callID) {
callID = fmt.Sprintf("call_%d_%d", i, k)
}
entry := NewPyObj()
entry.Set("id", callID)
entry.Set("type", "function")
fnObj := NewPyObj()
fnObj.Set("name", name)
fnObj.Set("arguments", argsStr)
entry.Set("function", fnObj)
tc = append(tc, entry)
}
am := NewPyObj()
am.Set("role", "assistant")
// m.get("content") or None — key may be missing (nil) or falsy.
content, _ := m.Get("content")
if !PyTruthy(content) {
content = nil
}
am.Set("content", content)
am.Set("tool_calls", tc)
out = append(out, am)
continue
}
if roleStr == "tool" {
tm := NewPyObj()
tm.Set("role", "tool")
// m.get("tool_call_id") or f"call_{i}" — missing AND null
// both fall back (Get returns nil for both).
id, _ := m.Get("tool_call_id")
if !PyTruthy(id) {
id = fmt.Sprintf("call_%d", i)
}
tm.Set("tool_call_id", id)
// m.get("content", "") — missing key defaults; explicit null
// survives (both arrive as nil from Get, but the oracle's
// frozen cases with null vs missing both render null...).
// Corrected below: distinguish via key presence.
content := toolContent(m)
tm.Set("content", content)
out = append(out, tm)
continue
}
pm := NewPyObj()
pm.Set("role", role)
content := plainContent(m)
pm.Set("content", content)
out = append(out, pm)
}
return out
}
// toolContent — oracle m.get("content", "") for tool messages.
func toolContent(m *PyObj) PyValue {
if _, present := m.Get("content"); !present {
return ""
}
v, _ := m.Get("content")
return v
}
// plainContent — oracle m.get("content", "") for plain roles.
func plainContent(m *PyObj) PyValue {
if _, present := m.Get("content"); !present {
return ""
}
v, _ := m.Get("content")
return v
}
// _from_openai_response — name-preserving alias for the audit's
// snake_case surface (the oracle's static method name verbatim).
func _from_openai_response(resp PyValue) PyValue { return fromOpenaiResponse(resp) }
// _to_openai_messages — name-preserving alias for the audit's snake_case
// surface (the oracle's static method name verbatim).
func _to_openai_messages(messages []PyValue) []PyValue { return toOpenaiMessages(messages) }
// _post — name-preserving alias for the audit's snake_case surface
// (the oracle's wire method name verbatim).
func _post(t *OpenAICompatTransport, path string, payload PyValue) (PyValue, error) {
return t.wirePost(path, payload)
}
// fromOpenaiResponse — OpenAI response → flat Ollama ChatMessage
// (LocalToolRuntime's contract).
//
// CONTRACT: chat() returns the MESSAGE object — role/content/tool_calls at
// top level — exactly like OllamaChatTransport.chat. Observed live (PL1
// Task A, 2026-09-12): the envelope shape {'message': {...}} made the
// runtime see 'empty terminal visible text' while the model had returned a
// valid read_file tool call.
func fromOpenaiResponse(resp PyValue) PyValue {
ro, ok := resp.(*PyObj)
if !ok {
ro = NewPyObj()
}
choicesRaw, _ := ro.Get("choices")
choices, isList := choicesRaw.([]PyValue)
if !isList || len(choices) == 0 {
choices = []PyValue{NewPyObj()}
}
first, _ := choices[0].(*PyObj)
if first == nil {
first = NewPyObj()
}
msgRaw, _ := first.Get("message")
message, _ := msgRaw.(*PyObj)
if message == nil {
message = NewPyObj()
}
tcRaw, _ := message.Get("tool_calls")
tcList, _ := tcRaw.([]PyValue)
toolCalls := []PyValue{}
for _, c := range tcList {
co, ok := c.(*PyObj)
if !ok {
co = NewPyObj()
}
fnRaw, _ := co.Get("function")
fn, _ := fnRaw.(*PyObj)
if fn == nil {
fn = NewPyObj()
}
argsRaw, _ := fn.Get("arguments")
if s, isStr := argsRaw.(string); isStr {
parsed, err := PyParse([]byte(s))
if err != nil {
raw := NewPyObj()
raw.Set("_raw", s)
argsRaw = raw
} else {
argsRaw = parsed
}
}
if !PyTruthy(argsRaw) {
argsRaw = NewPyObj()
}
name, _ := fn.Get("name")
fnObj := NewPyObj()
fnObj.Set("name", name)
fnObj.Set("arguments", argsRaw)
entry := NewPyObj()
entry.Set("function", fnObj)
// oracle: if c.get("id") — any truthy value is preserved.
if idRaw, _ := co.Get("id"); PyTruthy(idRaw) {
entry.Set("id", idRaw)
}
toolCalls = append(toolCalls, entry)
}
out := NewPyObj()
out.Set("role", "assistant")
contentRaw, _ := message.Get("content")
content := ""
if s, isStr := contentRaw.(string); isStr && s != "" {
content = s
}
out.Set("content", content)
if len(toolCalls) > 0 {
out.Set("tool_calls", toolCalls)
}
return out
}
// chatInput — the chat() keyword contract.
type chatInput struct {
Model string
Messages []PyValue
Tools PyValue
Options *PyObj
Think PyValue
}
// chat implements the oracle chat(): translate, post, accumulate usage,
// flatten the response.
func (t *OpenAICompatTransport) chat(in chatInput) (PyValue, error) {
// The runtime passes the qualified name ('<Provider>/<model>'); the
// provider API wants the bare model id ('glm-5.3-flash'). Observed
// live: qualified name → 400 on every call (PL1 held-out attempt).
wireModel := in.Model
if idx := strings.Index(in.Model, "/"); idx >= 0 {
wireModel = in.Model[idx+1:]
}
payload := NewPyObj()
payload.Set("model", wireModel)
payload.Set("messages", toOpenaiMessages(in.Messages))
payload.Set("stream", false)
if PyTruthy(in.Tools) {
payload.Set("tools", in.Tools) // Ollama tool schema == OpenAI tool schema
}
if in.Options != nil {
for _, k := range []string{"temperature", "top_p", "max_tokens"} {
if v, present := in.Options.Get(k); present {
payload.Set(k, v)
}
}
}
started := time.Now()
resp, err := t.wirePost("/chat/completions", payload)
elapsedNS := time.Since(started).Nanoseconds()
if err != nil {
return nil, err
}
usageRaw, _ := resp.(*PyObj).Get("usage")
usage, _ := usageRaw.(*PyObj)
if usage == nil {
usage = NewPyObj()
}
t.mu.Lock()
t.usageCalls++
t.usagePromptTokens += pyIntOr(usage, "prompt_tokens")
t.usageOutputTokens += pyIntOr(usage, "completion_tokens")
t.usageTotalDurationNS += elapsedNS
t.mu.Unlock()
return fromOpenaiResponse(resp), nil
}
// pyIntOr — Python int(usage.get(key) or 0) coercion: falsy → 0,
// int strings parse (floats like 3.9 truncate toward zero), bools → 0/1.
func pyIntOr(o *PyObj, key string) int {
v, _ := o.Get(key)
if !PyTruthy(v) {
return 0
}
switch t := v.(type) {
case json.Number:
if f, err := t.Float64(); err == nil {
return int(f)
}
case string:
if n, err := strconv.Atoi(strings.TrimSpace(t)); err == nil {
return n
}
if f, err := strconv.ParseFloat(strings.TrimSpace(t), 64); err == nil {
return int(f)
}
case bool:
if t {
return 1
}
}
return 0
}
// supportsTools — ChatTransport protocol.
func (t *OpenAICompatTransport) supportsTools(model string) bool { return true }
// supportsThinking — ChatTransport protocol.
func (t *OpenAICompatTransport) supportsThinking(model string) bool { return false }
// contextLength — ChatTransport protocol.
func (t *OpenAICompatTransport) contextLength(model string) int { return t.contextWindow }
// IsCloudPinned — THE provider-prefix rule, single authority: true when
// the model name requires the OpenAI-compat wire — a non-empty provider
// prefix that is neither ollama nor local. Consumers: transport selection
// and context discovery. Never copy this rule — import it.
func IsCloudPinned(model string) bool {
prefix := ""
if strings.Contains(model, "/") {
prefix = strings.ToLower(strings.TrimSpace(strings.SplitN(model, "/", 2)[0]))
}
return prefix != "" && prefix != "ollama" && prefix != "local"
}
// Cloud resolution seams (oracle: os.path.expanduser + Path.is_file +
// Path.read_text + os.environ.get). Injectable for the frozen battery.
var (
// ExpandHome resolves a leading "~" to the operator home.
ExpandHome = func(p string) string {
if p == "~" || strings.HasPrefix(p, "~/") {
home, err := os.UserHomeDir()
if err == nil {
if p == "~" {
return home
}
return filepath.Join(home, p[2:])
}
}
return p
}
// ReadFileText returns (text, exists, err) for a config file.
ReadFileText = func(path string) (string, bool, error) {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return "", false, nil
}
return "", false, err
}
return string(data), true, nil
}
// LookupEnv mirrors os.environ.get(key, "") semantics: a set-but-empty
// variable yields "" (falsy) and falls through to auth.json.
LookupEnv = func(key string) (string, bool) { return os.LookupEnv(key) }
)
var (
providerBlockRe = func(provider string) *regexp.Regexp {
return regexp.MustCompile(`"` + regexp.QuoteMeta(provider) + `"\s*:\s*\{`)
}
baseURLRe = regexp.MustCompile(`"baseURL":\s*"([^"]+)"`)
// RE2 cannot bound the lookahead the way Python's re does, but the
// oracle searches within a 2000-char block — reproduced by slicing.
contextWindowReFor = func(shortModel string) *regexp.Regexp {
return regexp.MustCompile(`"` + regexp.QuoteMeta(shortModel) + `"\s*:\s*\{[^}]*?"context_window"\s*:\s*(\d+)`)
}
)
// CloudTransportFor — resolve provider config for a '<Provider>/<model>'
// name from the same source of truth the CLI uses (mimocode.jsonc +
// auth.json).
func CloudTransportFor(model string, timeout float64) (*OpenAICompatTransport, error) {
if !strings.Contains(model, "/") {
return nil, &ValueError{Msg: fmt.Sprintf("cloud model must be '<Provider>/<model>': %s", PyRepr(model))}
}
parts := strings.SplitN(model, "/", 2)
providerName, shortModel := parts[0], parts[1]
baseURL := ""
contextWindow := 131072
cfgPath := filepath.ToSlash(ExpandHome("~/.config/mimocode/mimocode.jsonc"))
if text, exists, err := ReadFileText(cfgPath); err == nil && exists {
if loc := providerBlockRe(providerName).FindStringIndex(text); loc != nil {
end := loc[0] + 2000
if end > len(text) {
end = len(text)
}
block := text[loc[0]:end]
if m := baseURLRe.FindStringSubmatch(block); m != nil {
baseURL = m[1]
}
if m := contextWindowReFor(shortModel).FindStringSubmatch(block); m != nil {
if n, err := strconv.Atoi(m[1]); err == nil {
contextWindow = n
}
}
}
}
if baseURL == "" {
return nil, &ValueError{Msg: fmt.Sprintf("no baseURL configured for provider %s", PyRepr(providerName))}
}
envKey := strings.ReplaceAll(strings.ToUpper(providerName), " ", "_") + "_API_KEY"
apiKey := ""
if v, _ := LookupEnv(envKey); v != "" {
apiKey = v
}
if apiKey == "" {
authPath := filepath.ToSlash(ExpandHome("~/.local/share/mimocode/auth.json"))
if text, exists, err := ReadFileText(authPath); err == nil && exists {
auth, perr := PyParse([]byte(text))
if perr == nil {
if ao, ok := auth.(*PyObj); ok {
entryRaw, _ := ao.Get(providerName)
entry, _ := entryRaw.(*PyObj)
if entry != nil {
if k, present := entry.Get("key"); present {
if s, isStr := k.(string); isStr {
apiKey = s
}
}
}
}
}
}
}
if apiKey == "" {
return nil, &ValueError{Msg: fmt.Sprintf("no API key for provider %s", PyRepr(providerName))}
}
// Cloud routers queue and batch — a tool-loop turn can legitimately
// exceed the local-model timeout (observed live: glm-5.3-flash
// read-timeout at 120s twice). Floor cloud transports at 300s.
if timeout < 300 {
timeout = 300
}
t := NewOpenAICompatTransportWith(baseURL, apiKey, providerName, timeout, contextWindow)
return t, nil
}
+47
View File
@@ -0,0 +1,47 @@
// phaseb.go — the language-advantage pass (dichotomy wave):
//
// - ChatAll fans independent chat() calls to goroutines — the serial
// oracle's only IO seam where parallelism is honest (each chat is one
// HTTP round trip; usage accumulators are mutex-guarded). The frozen
// battery replays bytes through the parallel path.
// - Every IO seam is a named interface with a Func adapter and a
// compile-time assertion, so a seam breaking its contract fails the
// build, not a cloud call.
package main
import "sync"
// Compile-time seam safety: the default implementations and the Func
// adapters satisfy their interfaces, and the error type implements error.
// A seam drifting from its contract fails the build, not a cloud call.
var (
_ HTTPPoster = HTTPPosterFunc(nil)
_ HTTPPoster = defaultPoster{}
_ error = (*ValueError)(nil)
)
// ChatResult — one parallel chat outcome.
type ChatResult struct {
Index int
Value PyValue
Err error
}
// ChatAll runs independent chats concurrently (one goroutine per input,
// capped by the runtime scheduler) and returns results in input order.
// Usage accumulators stay exact under concurrency via the transport mutex.
func (t *OpenAICompatTransport) ChatAll(inputs []chatInput) []ChatResult {
results := make([]ChatResult, len(inputs))
var wg sync.WaitGroup
for i := range inputs {
wg.Add(1)
go func(i int) {
defer wg.Done()
v, err := t.chat(inputs[i])
results[i] = ChatResult{Index: i, Value: v, Err: err}
}(i)
}
wg.Wait()
return results
}
@@ -0,0 +1,93 @@
// Phase B — the language-advantage pass. ChatAll fans independent chats
// to goroutines; usage accumulators must stay exact, results must arrive
// in input order, and errors must surface per-index.
package main
import (
"strings"
"sync"
"testing"
)
func TestPhaseBParallelChatOrderAndUsage(t *testing.T) {
oldPoster := func() HTTPPoster { return nil }
_ = oldPoster
tr := NewOpenAICompatTransport("http://phaseb", "k", "P")
var mu sync.Mutex
n := 0
tr.SetPoster(HTTPPosterFunc(func(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
mu.Lock()
n++
mu.Unlock()
return []byte(`{"choices": [{"message": {"content": "ok"}}], "usage": {"prompt_tokens": 3, "completion_tokens": 2}}`), nil
}))
inputs := make([]chatInput, 8)
for i := range inputs {
inputs[i] = chatInput{Model: "P/m", Messages: []PyValue{}}
}
results := tr.ChatAll(inputs)
for i, r := range results {
if r.Err != nil {
t.Fatalf("index %d: %v", i, r.Err)
}
if r.Index != i {
t.Fatalf("index %d out of order: %+v", i, r)
}
o := mustObj(r.Value)
if c, _ := o.Get("content"); c != "ok" {
t.Fatalf("index %d content: %v", i, c)
}
}
if tr.usageCalls != 8 || tr.usagePromptTokens != 24 || tr.usageOutputTokens != 16 {
t.Fatalf("usage drift: %+v", tr)
}
if tr.usageTotalDurationNS <= 0 {
t.Fatal("duration not accumulated under parallel chats")
}
if n != 8 {
t.Fatalf("poster calls: got %d want 8", n)
}
}
func TestPhaseBHammer(t *testing.T) {
tr := NewOpenAICompatTransport("http://phaseb", "k", "P")
tr.SetPoster(HTTPPosterFunc(func(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
return []byte(`{"choices": [{"message": {"content": "x"}}]}`), nil
}))
inputs := make([]chatInput, 32)
for i := range inputs {
inputs[i] = chatInput{Model: "P/m", Messages: []PyValue{}}
}
results := tr.ChatAll(inputs)
for i, r := range results {
if r.Err != nil {
t.Fatalf("index %d: %v", i, r.Err)
}
}
if tr.usageCalls != 32 {
t.Fatalf("usage calls: got %d want 32", tr.usageCalls)
}
}
func TestPhaseBParallelErrorsPerIndex(t *testing.T) {
tr := NewOpenAICompatTransport("http://phaseb", "k", "P")
tr.SetPoster(HTTPPosterFunc(func(url string, headers map[string]string, body []byte, timeoutSecs float64) ([]byte, error) {
return []byte(`[]`), nil
}))
inputs := []chatInput{
{Model: "P/m", Messages: []PyValue{}},
{Model: "P/m", Messages: []PyValue{}},
}
results := tr.ChatAll(inputs)
for i, r := range results {
if r.Err == nil {
t.Fatalf("index %d: expected error", i)
}
if !strings.Contains(r.Err.Error(), "non-object response") {
t.Fatalf("index %d: %v", i, r.Err)
}
}
if tr.usageCalls != 0 {
t.Fatalf("failed chats must not accumulate: %+v", tr)
}
}
+396
View File
@@ -0,0 +1,396 @@
// pyjson.go — Python-faithful JSON parsing and rendering for the
// OpenAI-compat transport port. The oracle round-trips wire payloads through
// json.dumps/loads, so the port must reproduce: object key order (insertion
// order, not sorted), separators ", " / ": ", ensure_ascii escaping with
// lowercase \uXXXX (surrogate pairs for astral planes), and Python float
// repr (integral floats keep a trailing ".0" — json.dumps(2.0) is "2.0").
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
)
// PyValue — a JSON value with order-preserving objects.
// nil | bool | json.Number | string | []PyValue | *PyObj
type PyValue any
// PyObj — a JSON object preserving insertion order.
type PyObj struct {
Keys []string
Vals map[string]PyValue
}
func NewPyObj() *PyObj { return &PyObj{Vals: map[string]PyValue{}} }
func (o *PyObj) Get(key string) (PyValue, bool) {
if o == nil {
return nil, false
}
v, ok := o.Vals[key]
return v, ok
}
func (o *PyObj) Set(key string, v PyValue) {
if _, dup := o.Vals[key]; !dup {
o.Keys = append(o.Keys, key)
}
o.Vals[key] = v
}
// PyParse — strict single-value JSON parse (trailing garbage rejected,
// like Python json.loads).
func PyParse(raw []byte) (PyValue, error) {
dec := json.NewDecoder(bytes.NewReader(raw))
dec.UseNumber()
v, err := decodeValue(dec)
if err != nil {
return nil, err
}
if _, err := dec.Token(); err != io.EOF {
return nil, fmt.Errorf("trailing data after JSON value")
}
return v, nil
}
func decodeValue(dec *json.Decoder) (PyValue, error) {
tok, err := dec.Token()
if err != nil {
return nil, err
}
return decodeFromToken(dec, tok)
}
func decodeFromToken(dec *json.Decoder, tok json.Token) (PyValue, error) {
switch t := tok.(type) {
case json.Delim:
switch t {
case '{':
obj := NewPyObj()
for dec.More() {
keyTok, err := dec.Token()
if err != nil {
return nil, err
}
key, ok := keyTok.(string)
if !ok {
return nil, fmt.Errorf("object key is not a string")
}
val, err := decodeValue(dec)
if err != nil {
return nil, err
}
obj.Set(key, val)
}
if _, err := dec.Token(); err != nil { // closing '}'
return nil, err
}
return obj, nil
case '[':
arr := []PyValue{}
for dec.More() {
val, err := decodeValue(dec)
if err != nil {
return nil, err
}
arr = append(arr, val)
}
if _, err := dec.Token(); err != nil { // closing ']'
return nil, err
}
return arr, nil
default:
return nil, fmt.Errorf("unexpected delim %q", t)
}
case json.Number:
return t, nil
case string:
return t, nil
case bool:
return t, nil
case nil:
return nil, nil
default:
return nil, fmt.Errorf("unexpected token %v", tok)
}
}
// PyDumpsCompact — Python json.dumps(value): separators ", " / ": ",
// ensure_ascii=True, insertion-order objects.
func PyDumpsCompact(v PyValue) string {
var b strings.Builder
writePy(&b, v)
return b.String()
}
func writePy(b *strings.Builder, v PyValue) {
switch t := v.(type) {
case nil:
b.WriteString("null")
case bool:
if t {
b.WriteString("true")
} else {
b.WriteString("false")
}
case json.Number:
b.WriteString(PyNumStr(t))
case string:
writePyString(b, t)
case []PyValue:
b.WriteByte('[')
for i, item := range t {
if i > 0 {
b.WriteString(", ")
}
writePy(b, item)
}
b.WriteByte(']')
case *PyObj:
b.WriteByte('{')
for i, k := range t.Keys {
if i > 0 {
b.WriteString(", ")
}
writePyString(b, k)
b.WriteString(": ")
writePy(b, t.Vals[k])
}
b.WriteByte('}')
default:
b.WriteString(fmt.Sprintf("%v", v))
}
}
// PyNumStr — Python json.dumps number rendering: integer literals pass
// through verbatim; float literals re-render via Python repr (shortest
// round-trip; integral floats keep ".0"). Fixture numbers are written by
// json.dump, so literals are always in canonical Python form already —
// rendering short-circuits through the literal when it is already
// canonical, and falls back to repr otherwise.
func PyNumStr(n json.Number) string {
s := n.String()
if pyNumCanonical(s) {
return s
}
f, err := n.Float64()
if err != nil {
return s
}
return pyFloatRepr(f)
}
func pyNumCanonical(s string) bool {
if s == "" {
return false
}
hasDot := false
for i := 0; i < len(s); i++ {
c := s[i]
switch {
case c >= '0' && c <= '9':
case c == '.':
if hasDot {
return false
}
hasDot = true
case c == 'e' || c == 'E' || c == '+' || c == '-':
if c == '-' && i == 0 {
continue
}
return false
default:
return false
}
}
return true
}
// pyFloatRepr — Python repr() of a float: shortest round-trip, integral
// values keep ".0". Restricted to the plain-decimal band (the truth
// fixtures never use exponent notation).
func pyFloatRepr(f float64) string {
if f == float64(int64(f)) && f > -1e15 && f < 1e15 {
return strconv.FormatInt(int64(f), 10) + ".0"
}
return strconv.FormatFloat(f, 'g', -1, 64)
}
// writePyString — Python json.dumps string escaping, ensure_ascii=True.
func writePyString(b *strings.Builder, s string) {
b.WriteByte('"')
for _, c := range s {
switch c {
case '"':
b.WriteString("\\\"")
case '\\':
b.WriteString("\\\\")
case '\n':
b.WriteString("\\n")
case '\r':
b.WriteString("\\r")
case '\t':
b.WriteString("\\t")
case '\b':
b.WriteString("\\b")
case '\f':
b.WriteString("\\f")
default:
if c < 0x20 {
fmt.Fprintf(b, "\\u%04x", c)
} else if c <= 0x7E {
b.WriteRune(c)
} else if cp := uint32(c); cp > 0xFFFF {
v := cp - 0x10000
fmt.Fprintf(b, "\\u%04x\\u%04x", 0xD800+(v>>10), 0xDC00+(v&0x3FF))
} else {
fmt.Fprintf(b, "\\u%04x", cp)
}
}
}
b.WriteByte('"')
}
// PyRepr — Python repr() of a string: single quotes, backslash/quote
// escaping (the shape a model name takes in oracle error strings).
func PyRepr(s string) string {
var b strings.Builder
b.WriteByte('\'')
for _, c := range s {
switch c {
case '\\':
b.WriteString("\\\\")
case '\'':
b.WriteString("\\'")
case '\n':
b.WriteString("\\n")
case '\r':
b.WriteString("\\r")
case '\t':
b.WriteString("\\t")
default:
b.WriteRune(c)
}
}
b.WriteByte('\'')
return b.String()
}
// PyStr — Python str() of a JSON value for the non-dict arguments branch
// of _to_openai_messages (str(args) when args is not a dict).
func PyStr(v PyValue) string {
switch t := v.(type) {
case nil:
return "None"
case bool:
if t {
return "True"
}
return "False"
case json.Number:
return PyNumStr(t)
case string:
return t
default:
return pyReprValue(v)
}
}
// pyReprValue — Python repr() of list/dict JSON values.
func pyReprValue(v PyValue) string {
switch t := v.(type) {
case string:
return PyRepr(t)
case []PyValue:
parts := make([]string, len(t))
for i, item := range t {
parts[i] = pyReprValue(item)
}
return "[" + strings.Join(parts, ", ") + "]"
case *PyObj:
parts := make([]string, 0, len(t.Keys))
for _, k := range t.Keys {
parts = append(parts, PyRepr(k)+": "+pyReprValue(t.Vals[k]))
}
return "{" + strings.Join(parts, ", ") + "}"
default:
return PyStr(v)
}
}
// PyTruthy — Python truthiness of a JSON value.
func PyTruthy(v PyValue) bool {
switch t := v.(type) {
case nil:
return false
case bool:
return t
case json.Number:
f, err := t.Float64()
return err == nil && f != 0
case string:
return t != ""
case []PyValue:
return len(t) > 0
case *PyObj:
return len(t.Keys) > 0
default:
return true
}
}
// PyEqual — deep equality where numbers compare numerically (int 120 ==
// float 120.0), matching semantic oracle equality rather than literal text.
func PyEqual(a, b PyValue) bool {
an, aok := a.(json.Number)
bn, bok := b.(json.Number)
if aok && bok {
if an.String() == bn.String() {
return true
}
af, aerr := an.Float64()
bf, berr := bn.Float64()
return aerr == nil && berr == nil && af == bf
}
switch at := a.(type) {
case nil:
return b == nil
case bool:
bt, ok := b.(bool)
return ok && at == bt
case string:
bt, ok := b.(string)
return ok && at == bt
case []PyValue:
bt, ok := b.([]PyValue)
if !ok || len(at) != len(bt) {
return false
}
for i := range at {
if !PyEqual(at[i], bt[i]) {
return false
}
}
return true
case *PyObj:
bt, ok := b.(*PyObj)
if !ok || len(at.Keys) != len(bt.Keys) {
return false
}
for i, k := range at.Keys {
if bt.Keys[i] != k {
return false
}
if !PyEqual(at.Vals[k], bt.Vals[k]) {
return false
}
}
return true
default:
return false
}
}
+490
View File
@@ -0,0 +1,490 @@
// Replay — the frozen-truth parity engine. Reads truth.json (generated by
// scripts/gen_openai_compat_transport_truth.py against the live Python oracle
// with the wire served by a stub HTTP server and the cloud IO seams patched
// to temp dirs) and re-executes every row through this Go port. @STUB@
// normalizes the stub base URL; usage_total_duration_ns is excluded (monotonic
// clock) and asserted > 0 after live chats instead.
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
"strings"
)
// TruthDoc — the frozen truth file structure.
type TruthDoc struct {
Oracle string `json:"oracle"`
Generator string `json:"generator"`
Note string `json:"note"`
Rows []TruthRow `json:"rows"`
}
// TruthRow — one frozen case.
type TruthRow struct {
Name string `json:"name"`
Op string `json:"op"`
Setup json.RawMessage `json:"setup"`
Want json.RawMessage `json:"want"`
}
// RunTruth replays every row and returns a divergence report (empty on
// 100% parity).
func RunTruth(path string) []string {
raw, err := os.ReadFile(path)
if err != nil {
return []string{fmt.Sprintf("read truth: %v", err)}
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
return []string{fmt.Sprintf("parse truth: %v", err)}
}
var fails []string
for i := range doc.Rows {
if msg := replayRow(&doc.Rows[i]); msg != "" {
fails = append(fails, doc.Rows[i].Name+": "+msg)
}
}
return fails
}
func truthRowCount() int {
raw, err := os.ReadFile("truth.json")
if err != nil {
return -1
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
return -1
}
return len(doc.Rows)
}
func replayRow(row *TruthRow) string {
var setup, want PyValue
var err error
if setup, err = PyParse(row.Setup); err != nil {
return fmt.Sprintf("parse setup: %v", err)
}
if want, err = PyParse(row.Want); err != nil {
return fmt.Sprintf("parse want: %v", err)
}
s := mustObj(setup)
w := want
switch row.Op {
case "ctor":
return replayCtor(s, w)
case "to_messages":
return replayToMessages(s, w)
case "from_response":
return replayFromResponse(s, w)
case "post":
return replayPost(s, w)
case "chat":
return replayChat(s, w)
case "scalar":
return replayScalar(s, w)
case "cloud":
return replayCloud(s, w)
default:
return fmt.Sprintf("unknown op %q", row.Op)
}
}
func mustObj(v PyValue) *PyObj {
o, _ := v.(*PyObj)
if o == nil {
return NewPyObj()
}
return o
}
func strOf(v PyValue) string {
s, _ := v.(string)
return s
}
func numOf(v PyValue) float64 {
if n, ok := v.(json.Number); ok {
f, _ := n.Float64()
return f
}
return 0
}
func listOf(v PyValue) []PyValue {
l, _ := v.([]PyValue)
if l == nil {
return []PyValue{}
}
return l
}
// -- ctor --------------------------------------------------------------------
func replayCtor(s *PyObj, w PyValue) string {
base, _ := s.Get("base_url")
key, _ := s.Get("api_key")
prov, _ := s.Get("provider")
timeout := 120.0
if tv, present := s.Get("timeout"); present {
timeout = numOf(tv)
}
ctxWin := 131072
if cv, present := s.Get("context_window"); present {
ctxWin = int(numOf(cv))
}
t := NewOpenAICompatTransportWith(strOf(base), strOf(key), strOf(prov), timeout, ctxWin)
got := transportAttrs(t)
if msg := compareJSON("attrs", got, w); msg != "" {
return msg
}
return ""
}
func transportAttrs(t *OpenAICompatTransport) PyValue {
o := NewPyObj()
o.Set("base_url", t.BaseURL)
o.Set("api_key", t.APIKey)
o.Set("provider", t.Provider)
o.Set("timeout", json.Number(fmt.Sprintf("%v", t.Timeout)))
o.Set("context_window", json.Number(fmt.Sprintf("%d", t.contextWindow)))
o.Set("usage_calls", json.Number(fmt.Sprintf("%d", t.usageCalls)))
o.Set("usage_prompt_tokens", json.Number(fmt.Sprintf("%d", t.usagePromptTokens)))
o.Set("usage_output_tokens", json.Number(fmt.Sprintf("%d", t.usageOutputTokens)))
o.Set("usage_total_duration_ns", json.Number(fmt.Sprintf("%d", t.usageTotalDurationNS)))
return o
}
// -- to_messages / from_response ----------------------------------------------
func replayToMessages(s *PyObj, w PyValue) string {
msgs, _ := s.Get("messages")
got := PyValue(toOpenaiMessages(listOf(msgs)))
return compareJSON("to_messages", got, w)
}
func replayFromResponse(s *PyObj, w PyValue) string {
resp, _ := s.Get("resp")
got := fromOpenaiResponse(resp)
return compareJSON("from_response", got, w)
}
// -- post --------------------------------------------------------------------
type stubPoster struct {
bodies []string
calls []capturedCall
}
type capturedCall struct {
url string
headers map[string]string
body string
}
func (p *stubPoster) Post(url string, headers map[string]string, body []byte, _ float64) ([]byte, error) {
p.calls = append(p.calls, capturedCall{url, headers, string(body)})
resp := p.bodies[0]
p.bodies = p.bodies[1:]
return []byte(resp), nil
}
func replayPost(s *PyObj, w PyValue) string {
path, _ := s.Get("path")
payload, _ := s.Get("payload")
bodiesRaw, _ := s.Get("bodies")
var bodies []string
for _, b := range listOf(bodiesRaw) {
bodies = append(bodies, strOf(b))
}
t := NewOpenAICompatTransport("http://replay", "sekrit", "Prov")
stub := &stubPoster{bodies: bodies}
t.SetPoster(stub)
var got PyValue
result, err := t.wirePost(strOf(path), payload)
if err != nil {
eo := NewPyObj()
eo.Set("error", err.Error())
got = PyValue(eo)
} else {
got = result
}
call := stub.calls[0]
reqObj := NewPyObj()
reqObj.Set("path", strings.TrimPrefix(call.url, "http://replay"))
reqObj.Set("authorization", call.headers["Authorization"])
reqObj.Set("content_type", call.headers["Content-Type"])
reqObj.Set("body", call.body)
wo := mustObj(w)
if msg := compareJSON("request", reqObj, mustGet(wo, "request")); msg != "" {
return msg
}
if _, wantErr := wo.Get("error"); wantErr {
gotObj := mustObj(got)
return compareScalar("error", strOf(mustGet(gotObj, "error")), strOf(mustGet(wo, "error")))
}
return compareJSON("result", got, mustGet(wo, "result"))
}
// -- chat --------------------------------------------------------------------
func replayChat(s *PyObj, w PyValue) string {
base, _ := s.Get("base_url")
key, _ := s.Get("api_key")
prov, _ := s.Get("provider")
model, _ := s.Get("model")
msgs, _ := s.Get("messages")
tools, toolsPresent := s.Get("tools")
options, _ := s.Get("options")
think, thinkPresent := s.Get("think")
respRaw, _ := s.Get("responses")
var bodies []string
for _, b := range listOf(respRaw) {
bodies = append(bodies, strOf(b))
}
_ = base
t := NewOpenAICompatTransport("http://replay", strOf(key), strOf(prov))
stub := &stubPoster{bodies: bodies}
t.SetPoster(stub)
var in chatInput
in.Model = strOf(model)
in.Messages = listOf(msgs)
if toolsPresent {
in.Tools = tools
}
if oo, ok := options.(*PyObj); ok {
in.Options = oo
}
if thinkPresent {
in.Think = think
}
wo := mustObj(w)
var result PyValue
var chatErr error
calls := 1
if cv, present := s.Get("calls"); present {
calls = int(numOf(cv))
}
for c := 0; c < calls; c++ {
result, chatErr = t.chat(in)
}
var gotResult PyValue
if chatErr != nil {
eo := NewPyObj()
eo.Set("error", chatErr.Error())
gotResult = PyValue(eo)
} else {
gotResult = result
}
gotReqs := []PyValue{}
for _, call := range stub.calls {
body, perr := PyParse([]byte(call.body))
if perr != nil {
return fmt.Sprintf("reparse request body: %v", perr)
}
gotReqs = append(gotReqs, body)
}
// The oracle freezes each chat request as {path, authorization,
// content_type, body}; compare per-request with the body re-parsed
// to a value so number literals compare exactly.
wantReqs := listOf(mustGet(wo, "requests"))
if len(gotReqs) != len(wantReqs) {
return fmt.Sprintf("request count: got %d want %d", len(gotReqs), len(wantReqs))
}
for i := range gotReqs {
wo := mustObj(wantReqs[i])
if msg := compareScalar(fmt.Sprintf("request[%d].path", i), "/chat/completions", strOf(mustGet(wo, "path"))); msg != "" {
return msg
}
if msg := compareScalar(fmt.Sprintf("request[%d].auth", i), "Bearer "+strOf(key), strOf(mustGet(wo, "authorization"))); msg != "" {
return msg
}
if msg := compareScalar(fmt.Sprintf("request[%d].ctype", i), "application/json", strOf(mustGet(wo, "content_type"))); msg != "" {
return msg
}
wantBody, perr := PyParse([]byte(strOf(mustGet(wo, "body"))))
if perr != nil {
return fmt.Sprintf("reparse want body: %v", perr)
}
if msg := compareJSON(fmt.Sprintf("request[%d].body", i), gotReqs[i], wantBody); msg != "" {
return msg
}
}
if _, wantErr := wo.Get("error"); wantErr {
gotObj := mustObj(gotResult)
if msg := compareScalar("error", strOf(mustGet(gotObj, "error")), strOf(mustGet(wo, "error"))); msg != "" {
return msg
}
} else if msg := compareJSON("result", gotResult, mustGet(wo, "result")); msg != "" {
return msg
}
usage := mustObj(mustGet(wo, "usage"))
if msg := compareScalar("usage.calls", itoa(t.usageCalls), numStr(mustGet(usage, "calls"))); msg != "" {
return msg
}
if msg := compareScalar("usage.prompt", itoa(t.usagePromptTokens), numStr(mustGet(usage, "prompt_tokens"))); msg != "" {
return msg
}
if msg := compareScalar("usage.output", itoa(t.usageOutputTokens), numStr(mustGet(usage, "output_tokens"))); msg != "" {
return msg
}
if t.usageTotalDurationNS <= 0 && chatErr == nil {
return "usage_total_duration_ns not accumulated"
}
return ""
}
// -- scalar ------------------------------------------------------------------
func replayScalar(s *PyObj, w PyValue) string {
fn, _ := s.Get("fn")
model, _ := s.Get("model")
switch strOf(fn) {
case "supports_tools":
t := NewOpenAICompatTransport("http://x/v1", "k", "T")
return compareScalar("supports_tools", boolStr(t.supportsTools(strOf(model))), pyScalarStr(w))
case "supports_thinking":
t := NewOpenAICompatTransport("http://x/v1", "k", "T")
return compareScalar("supports_thinking", boolStr(t.supportsThinking(strOf(model))), pyScalarStr(w))
case "context_length":
ctxWin := 131072
if cv, present := s.Get("context_window"); present {
ctxWin = int(numOf(cv))
}
t := NewOpenAICompatTransportWith("http://x/v1", "k", "T", 120, ctxWin)
return compareScalar("context_length", itoa(t.contextLength(strOf(model))), pyScalarStr(w))
case "is_cloud_pinned":
return compareScalar("is_cloud_pinned", boolStr(IsCloudPinned(strOf(model))), pyScalarStr(w))
default:
return fmt.Sprintf("unknown scalar fn %q", strOf(fn))
}
}
func pyScalarStr(w PyValue) string {
switch t := w.(type) {
case bool:
return boolStr(t)
case json.Number:
return t.String()
case string:
return t
default:
return fmt.Sprintf("%v", w)
}
}
// -- cloud -------------------------------------------------------------------
func replayCloud(s *PyObj, w PyValue) string {
model, _ := s.Get("model")
timeout := 120.0
if tv, present := s.Get("timeout"); present {
timeout = numOf(tv)
}
envRaw, _ := s.Get("env")
env := map[string]string{}
if eo, ok := envRaw.(*PyObj); ok {
for _, k := range eo.Keys {
env[k] = strOf(eo.Vals[k])
}
}
oldExpand, oldRead, oldLookup := ExpandHome, ReadFileText, LookupEnv
defer func() { ExpandHome, ReadFileText, LookupEnv = oldExpand, oldRead, oldLookup }()
home := "/tmp/oct_home"
ExpandHome = func(p string) string {
if p == "~" {
return home
}
if strings.HasPrefix(p, "~/") {
return home + p[1:]
}
return p
}
// The oracle patches os.path.expanduser to substitute the temp home;
// the frozen cloud fixtures live in the generator's temp dirs which no
// longer exist — reconstruct them from the frozen config/auth contents
// recorded in the setup (see home_files below).
files := map[string]string{}
if hf, present := s.Get("home_files"); present {
if ho, ok := hf.(*PyObj); ok {
for _, k := range ho.Keys {
files[home+k] = strOf(ho.Vals[k])
}
}
}
ReadFileText = func(path string) (string, bool, error) {
if text, ok := files[path]; ok {
return text, true, nil
}
return "", false, nil
}
LookupEnv = func(key string) (string, bool) {
v, ok := env[key]
return v, ok
}
t, err := CloudTransportFor(strOf(model), timeout)
wo := mustObj(w)
if _, wantErr := wo.Get("error"); wantErr {
if err == nil {
return "expected error, got transport"
}
return compareScalar("error", err.Error(), strOf(mustGet(wo, "error")))
}
if err != nil {
return fmt.Sprintf("unexpected error: %v", err)
}
return compareJSON("attrs", transportAttrs(t), w)
}
// -- comparison ----------------------------------------------------------------
func mustGet(o *PyObj, key string) PyValue {
v, _ := o.Get(key)
return v
}
func compareScalar(where, got, want string) string {
if got != want {
return fmt.Sprintf("%s: got %q want %q", where, got, want)
}
return ""
}
func compareJSON(where string, got, want PyValue) string {
if !PyEqual(canonicalJSON(got), canonicalJSON(want)) {
return fmt.Sprintf("%s: got %s want %s", where, PyDumpsCompact(got), PyDumpsCompact(want))
}
return ""
}
// canonicalJSON re-parses both sides through the literal-preserving parser
// so number comparison is literal-exact (PyEqual adds numeric tolerance).
func canonicalJSON(v PyValue) PyValue {
re, err := PyParse([]byte(PyDumpsCompact(v)))
if err != nil {
return v
}
return re
}
func boolStr(b bool) string {
if b {
return "true"
}
return "false"
}
// numStr — oracle usage ints arrive as JSON numbers.
func numStr(v PyValue) string {
if n, ok := v.(json.Number); ok {
return n.String()
}
return strOf(v)
}
func itoa(n int) string { return fmt.Sprintf("%d", n) }
var _ = reflect.TypeOf
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,91 @@
// Battery — Go tests replaying the frozen oracle truth VERBATIM.
// Every row of truth.json (generated by
// scripts/gen_openai_compat_transport_truth.py against the live Python
// oracle) re-executes through this port; any divergence fails the test.
package main
import (
"encoding/json"
"os"
"testing"
)
func loadTruth(t *testing.T) TruthDoc {
t.Helper()
raw, err := os.ReadFile("truth.json")
if err != nil {
t.Fatalf("read truth: %v", err)
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatalf("parse truth: %v", err)
}
if len(doc.Rows) == 0 {
t.Fatal("truth has no rows")
}
return doc
}
func TestBatteryRowCount(t *testing.T) {
doc := loadTruth(t)
if len(doc.Rows) != 83 {
t.Fatalf("row count drift: got %d want 83", len(doc.Rows))
}
if doc.Oracle != "src/rune/openai_compat_transport.py" {
t.Fatalf("oracle drift: %q", doc.Oracle)
}
}
func TestBatteryParity(t *testing.T) {
doc := loadTruth(t)
for i := range doc.Rows {
row := &doc.Rows[i]
if msg := replayRow(row); msg != "" {
t.Errorf("%s: %s", row.Name, msg)
}
}
}
func TestBatteryByOp(t *testing.T) {
doc := loadTruth(t)
byOp := map[string][]*TruthRow{}
for i := range doc.Rows {
byOp[doc.Rows[i].Op] = append(byOp[doc.Rows[i].Op], &doc.Rows[i])
}
want := map[string]int{
"ctor": 4, "to_messages": 19, "from_response": 17, "post": 4,
"chat": 12, "scalar": 16, "cloud": 11,
}
for op, n := range want {
if len(byOp[op]) != n {
t.Errorf("op %s: got %d rows want %d", op, len(byOp[op]), n)
}
}
for op, rows := range byOp {
t.Run(op, func(t *testing.T) {
for _, row := range rows {
if msg := replayRow(row); msg != "" {
t.Errorf("%s: %s", row.Name, msg)
}
}
})
}
}
func TestBatteryDeterministic(t *testing.T) {
raw, err := os.ReadFile("truth.json")
if err != nil {
t.Fatalf("read truth: %v", err)
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatalf("parse truth: %v", err)
}
names := map[string]bool{}
for _, row := range doc.Rows {
if names[row.Name] {
t.Fatalf("duplicate row name %q", row.Name)
}
names[row.Name] = true
}
}
+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.223.0"
__version__ = "0.239.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
+34
View File
@@ -5,10 +5,35 @@ episode → fact → observation → concept → belief → rule → skill.
Each promotion needs: evidence threshold, provenance, contradiction
handling, prediction/behavioural consequence, outcome-based revision.
Schema alone does not count — the ladder moves items or it doesn't.
Dichotomy wave #2479 tranche: the ladder kernel is ported to FULL Rust
(src/rust/abstraction_ladder, parity oracle frozen_al_truth.json). This
facade delegates to the Rust core via rune/al_loader when built; the
Python implementation below is the strangler fallback, pinned with
RUNE_AL_CORE=python.
"""
from __future__ import annotations
import os
def _core():
"""The Rust abstraction_ladder core when available (strangler: the
Python fallback is load-bearing). RUNE_AL_CORE=python pins the
fallback."""
if os.environ.get("RUNE_AL_CORE", "auto") == "python":
return None
try:
from rune import al_loader
except Exception: # noqa: BLE001 — any loader failure means fallback
return None
try:
return al_loader if al_loader.available() else None
except Exception: # noqa: BLE001 — availability probe must never crash
return None
STAGES = ("episode", "fact", "observation", "concept", "belief", "rule", "skill")
# Evidence threshold per promotion (sightings required)
@@ -25,6 +50,9 @@ THRESHOLDS = {
def next_stage(stage: str) -> str | None:
"""The stage after this one (None at the top)."""
core = _core()
if core is not None:
return core.next_stage(stage)
try:
i = STAGES.index(stage)
except ValueError:
@@ -36,6 +64,9 @@ def can_promote(item: dict) -> dict:
"""Can this item promote? Needs: evidence threshold met, provenance
present, no open contradictions, a stated consequence, and (for
belief+) a verified outcome."""
core = _core()
if core is not None:
return core.can_promote(item)
stage = item.get("stage", "episode")
nxt = next_stage(stage)
if nxt is None:
@@ -59,6 +90,9 @@ def can_promote(item: dict) -> dict:
def promote(item: dict) -> dict:
"""Promote one stage (or return the refusal)."""
core = _core()
if core is not None:
return core.promote(item)
verdict = can_promote(item)
if not verdict["promote"]:
return {"item": item, **verdict}
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""al_loader.py — ctypes loader for the Rust abstraction_ladder core
(dichotomy wave #2479 tranche). Mirrors lc_loader.py / broker_loader.py:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* al_next_stage({"stage"}) -> stage string or null
* al_can_promote({"item": {...}}) -> verdict doc
* al_promote({"item": {...}}) -> {"item": ..., ...verdict}
Pure kernel: no ops, no store seams, no op callback.
Free: capability_scoring's cs_free_string (shared process). NEVER re-export
a free symbol here. Override the .so with AL_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" / "abstraction_ladder"
LIB_PATH = Path(os.environ.get("AL_RUST_LIB",
_CRATE_DIR / "target" / "release" /
"libabstraction_ladder.so"))
_lib = None
SYMBOLS = ("al_next_stage", "al_can_promote", "al_promote")
class AbstractionLadderCoreUnavailable(FileNotFoundError):
"""The Rust abstraction_ladder core is not built. Callers fall back
to Python (strangler law; RUNE_AL_CORE=python pins the fallback)."""
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 AbstractionLadderCoreUnavailable(str(LIB_PATH))
lib = ctypes.CDLL(str(LIB_PATH))
for sym in SYMBOLS:
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p # char* — never default c_int
_lib = lib
return lib
def _free(ptr) -> None:
"""Free via capability_scoring's cs_free_string (same process)."""
try:
scoring = ctypes.CDLL(str(
_REPO / "src" / "rust" / "capability_scoring" / "target" /
"release" / "libcapability_scoring.so"))
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
scoring.cs_free_string(ptr)
except OSError:
pass # leak-on-fallback only if capability_scoring absent; bounded
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(ptr)
doc = json.loads(out)
if not doc.get("ok"):
raise RuntimeError(doc.get("err", f"{sym} failed"))
return doc["value"]
def next_stage(stage):
return _call("al_next_stage", {"stage": stage})
def can_promote(item: dict) -> dict:
return _call("al_can_promote", {"item": item})
def promote(item: dict) -> dict:
return _call("al_promote", {"item": item})
+39 -18
View File
@@ -17,23 +17,26 @@ reason; unrelated hardened confirmations are preserved.
Storage: <project_store>/behaviour_ledger.json (versioned document).
Phase G strangler slice 2 (port 4): the state machine (define / get /
transitions / selective invalidation / queues / summary) is served
IN-PROCESS by the Rust `evidence_provenance` cdylib through ep_loader.py
— parity 13/13 proven vs this module's Python vintage (full-scenario
doc parity, byte-equal error messages, save render indent=1 sort_keys).
File glue (load/save) stays Python; timestamps are pinned by this shim
(`now`) so the core stays deterministic. State constants and class API
unchanged (git history holds the original).
Dichotomy wave #2479: the state machine (define / get / transitions /
selective invalidation / queues / summary) is served by the FULL Rust
`behaviour_ledger` cdylib through bl_loader.py — the kernel is a pure
function of the parsed ledger doc, timestamps pass IN (`now`, no-clock
law), and parity is pinned by the frozen truth battery embedded in the
crate (frozen_bl_truth.json). RUNE_BL_CORE=python pins the retired
evidence_provenance shim path (ep_loader) for parity harnesses. File
glue (load/save) stays Python. State constants and class API unchanged
(git history holds the original).
"""
from __future__ import annotations
import json
import os
import sys
import time
from pathlib import Path
# ── Rust core wiring (slice-2 port-4 shim pattern) ──────────────────────────
# ── retired shim wiring (RUNE_BL_CORE=python pin; fallback when the
# wave core is not built) ────────────────────────────────────────────────
from rune import paths # noqa: E402 (#41 centralized paths)
_CRATE_DIR = str(paths.crate_dir("evidence_provenance")) # #41 centralized
if _CRATE_DIR not in sys.path:
@@ -63,6 +66,24 @@ STATES = (UNVALIDATED, IMPLEMENTED, AUTO_VERIFIED, REAL_WORLD_PASSED,
CONFIRMED_STATES = (AUTO_VERIFIED, REAL_WORLD_PASSED, USER_CONFIRMED)
def _kernel():
"""Dichotomy wave #2479: the FULL Rust behaviour_ledger core first
(RUNE_BL_CORE=python pins the retired evidence_provenance shim —
parity harnesses). The kernel owns the state machine (define / get /
transitions / selective invalidation / queues / summary); the file
glue and the class API stay here."""
if os.environ.get("RUNE_BL_CORE", "auto") == "python":
return ep_loader
try:
from rune import bl_loader
if bl_loader.available():
return bl_loader
except ImportError:
pass
return ep_loader
def _now() -> str:
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
@@ -93,7 +114,7 @@ class BehaviourLedger:
dependencies: list[str] | None = None,
automated_ref: str = "",
environment: str = "") -> dict:
self.doc, rec = ep_loader.bl_define(
self.doc, rec = _kernel().bl_define(
self.doc, behaviour_id=behaviour_id,
owning_project=owning_project,
intended_behaviour=intended_behaviour,
@@ -109,7 +130,7 @@ class BehaviourLedger:
return rec
def get(self, behaviour_id: str) -> dict | None:
return ep_loader.bl_get(self.doc, behaviour_id)
return _kernel().bl_get(self.doc, behaviour_id)
# -- state transitions (all recorded in history — rule 19G) -------------
@@ -118,7 +139,7 @@ class BehaviourLedger:
generation: str = "") -> dict:
"""Automated verification. GREEN NEVER IMPLIES USER_CONFIRMED
(rule 19W-22): the ceiling here is AUTO_VERIFIED."""
self.doc, rec = ep_loader.bl_record_auto(
self.doc, rec = _kernel().bl_record_auto(
self.doc, behaviour_id, passed=passed, detail=detail,
generation=generation, now=_now())
return rec
@@ -128,14 +149,14 @@ class BehaviourLedger:
confirmed_by: str = "user") -> dict:
"""Real-world/manual test result (testing mode). USER_CONFIRMED
requires an explicit confirmed_by='user' pass (rule 19M)."""
self.doc, rec = ep_loader.bl_record_real_world(
self.doc, rec = _kernel().bl_record_real_world(
self.doc, behaviour_id, passed=passed, detail=detail,
generation=generation, confirmed_by=confirmed_by, now=_now())
return rec
def accept_limitation(self, behaviour_id: str, *, note: str,
approved_by: str = "user") -> dict:
self.doc, rec = ep_loader.bl_accept_limitation(
self.doc, rec = _kernel().bl_accept_limitation(
self.doc, behaviour_id, note=note,
approved_by=approved_by, now=_now())
return rec
@@ -150,7 +171,7 @@ class BehaviourLedger:
structural dependencies intersect the changed closure. Every
invalidation carries a reason; unrelated confirmations stay.
Returns the invalidated records."""
self.doc, recs = ep_loader.bl_invalidate(
self.doc, recs = _kernel().bl_invalidate(
self.doc, changed_entities=sorted(changed_entities),
generation=generation, reasons=reasons, now=_now())
return recs
@@ -158,7 +179,7 @@ class BehaviourLedger:
def retest_history(self, behaviour_id: str) -> list[dict]:
"""Generation-aware history: all confirmations + invalidations
for one behaviour, oldest first (rule 19S-12)."""
return ep_loader.bl_retest_history(self.doc, behaviour_id)
return _kernel().bl_retest_history(self.doc, behaviour_id)
# -- queue (testing mode, 19L/M) ------------------------------------------
@@ -167,8 +188,8 @@ class BehaviourLedger:
"""Eligible testing items: anything not confirmed/accepted/n.a.
Dynamic — the queue re-derives from states after every fix
(rule 19M)."""
return ep_loader.bl_outstanding(
return _kernel().bl_outstanding(
self.doc, component=component or "", project=project or "")
def summary(self) -> dict:
return ep_loader.bl_summary(self.doc)
return _kernel().bl_summary(self.doc)
+176
View File
@@ -0,0 +1,176 @@
#!/usr/bin/env python3
"""ctypes loader for the FULL Rust behaviour_ledger core (dichotomy wave,
#2479 tranche). Mirrors broker_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* bl_define({doc, behaviour_id, owning_project, intended_behaviour,
origin_intent, pass_criteria, fail_criteria, procedure,
owner_component, dependencies, automated_ref, environment, now})
→ {doc, rec}
* bl_get({doc, behaviour_id}) → record | null
* bl_record_auto({doc, behaviour_id, passed, detail, generation, now})
→ {doc, rec}
* bl_record_real_world({doc, behaviour_id, passed, detail, generation,
confirmed_by, now}) → {doc, rec}
* bl_accept_limitation({doc, behaviour_id, note, approved_by, now})
→ {doc, rec}
* bl_invalidate({doc, changed_entities, generation, reasons, now})
→ {doc, recs}
* bl_retest_history({doc, behaviour_id}) → events (oldest-first)
* bl_outstanding({doc, component, project}) → queue
* bl_summary({doc}) → {total, states, outstanding}
Domain errors (unknown behaviour, duplicate define, …) surface as
{"ok": false, "err": <exact Python message>} and raise ValueError —
the same type the retired evidence_provenance shim raised.
No-clock law: `now` (UTC "%Y-%m-%dT%H:%M:%SZ") is always an argument —
the kernel never reads the wall clock.
Free: capability_scoring's cs_free_string (shared process). Override the
.so with BL_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" / "behaviour_ledger"
_LIB_PATH = Path(
os.environ.get("BL_RUST_LIB", _CRATE_DIR / "target" / "release" / "libbehaviour_ledger.so")
)
_lib = None
class BlCoreUnavailable(FileNotFoundError):
"""The Rust behaviour_ledger core is not built. Callers fall back."""
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 BlCoreUnavailable(
f"Rust behaviour_ledger core not built: {_LIB_PATH} missing — run "
f"`cargo build --release` in {_CRATE_DIR}"
)
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in (
"bl_define", "bl_get", "bl_record_auto", "bl_record_real_world",
"bl_accept_limitation", "bl_invalidate", "bl_retest_history",
"bl_outstanding", "bl_summary",
):
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p
_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 via any sibling free symbol already in-process (capability_scoring)
try:
scoring = ctypes.CDLL(str(
_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so"
))
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
scoring.cs_free_string(ptr)
except OSError:
pass # leak-on-fallback only if capability_scoring absent; bounded
doc = json.loads(out)
if not doc.get("ok"):
raise ValueError(doc.get("err", f"{sym} failed"))
return doc["value"]
def bl_define(doc: dict, *, behaviour_id: str, owning_project: str,
intended_behaviour: str, origin_intent: str = "",
pass_criteria: str = "", fail_criteria: str = "",
procedure: str = "", owner_component: str = "",
dependencies: list | None = None, automated_ref: str = "",
environment: str = "", now: str) -> tuple[dict, dict]:
"""define(): returns (new_doc, new_record)."""
v = _call("bl_define", {
"doc": doc, "behaviour_id": behaviour_id,
"owning_project": owning_project,
"intended_behaviour": intended_behaviour,
"origin_intent": origin_intent, "pass_criteria": pass_criteria,
"fail_criteria": fail_criteria, "procedure": procedure,
"owner_component": owner_component,
"dependencies": list(dependencies or []),
"automated_ref": automated_ref, "environment": environment,
"now": now,
})
return v["doc"], v["rec"]
def bl_record_auto(doc: dict, behaviour_id: str, *, passed: bool,
detail: str = "", generation: str = "",
now: str) -> tuple[dict, dict]:
"""record_auto_verification(): returns (new_doc, record)."""
v = _call("bl_record_auto", {
"doc": doc, "behaviour_id": behaviour_id, "passed": bool(passed),
"detail": detail, "generation": generation, "now": now,
})
return v["doc"], v["rec"]
def bl_record_real_world(doc: dict, behaviour_id: str, *, passed: bool,
detail: str = "", generation: str = "",
confirmed_by: str = "user",
now: str) -> tuple[dict, dict]:
"""record_real_world(): returns (new_doc, record)."""
v = _call("bl_record_real_world", {
"doc": doc, "behaviour_id": behaviour_id, "passed": bool(passed),
"detail": detail, "generation": generation,
"confirmed_by": confirmed_by, "now": now,
})
return v["doc"], v["rec"]
def bl_accept_limitation(doc: dict, behaviour_id: str, *, note: str,
approved_by: str = "user",
now: str) -> tuple[dict, dict]:
"""accept_limitation(): returns (new_doc, record)."""
v = _call("bl_accept_limitation", {
"doc": doc, "behaviour_id": behaviour_id, "note": note,
"approved_by": approved_by, "now": now,
})
return v["doc"], v["rec"]
def bl_invalidate(doc: dict, *, changed_entities: list,
generation: str = "", reasons: dict | None = None,
now: str) -> tuple[dict, list]:
"""invalidate_for_change(): returns (new_doc, invalidated records)."""
v = _call("bl_invalidate", {
"doc": doc, "changed_entities": list(changed_entities),
"generation": generation, "reasons": reasons or {}, "now": now,
})
return v["doc"], v["recs"]
def bl_get(doc: dict, behaviour_id: str) -> dict | None:
"""get(): record or None (normalized id)."""
return _call("bl_get", {"doc": doc, "behaviour_id": behaviour_id})
def bl_retest_history(doc: dict, behaviour_id: str) -> list:
"""retest_history(): events oldest-first (empty for unknown)."""
return _call("bl_retest_history", {"doc": doc,
"behaviour_id": behaviour_id})
def bl_outstanding(doc: dict, *, component: str = "",
project: str = "") -> list:
"""outstanding(): filtered queue, TEST_FAILED first."""
return _call("bl_outstanding", {"doc": doc, "component": component,
"project": project})
def bl_summary(doc: dict) -> dict:
"""summary(): {total, states, outstanding}."""
return _call("bl_summary", {"doc": doc})
+1 -1
View File
@@ -233,7 +233,7 @@ def main() -> int:
)
check(
"Rune minor version advertises provisioning and restore verification",
'__version__ = "0.223.0"' in rune_version,
'__version__ = "0.239.0"' in rune_version,
failures,
)
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
+38
View File
@@ -9,6 +9,13 @@ it belongs to — or proposes a new topic when nothing fits.
No preset categories anywhere: the seed set is empty, and every
category in the topology was earned by co-firing.
Dichotomy wave #2479 tranche (contract
BUILDER-2026-10-11T23:55:54Z-982aa3-factory-wave): the deterministic
topology kernel is ported to FULL Rust (src/rust/concept_topology,
parity oracle frozen_ct_truth.json). This facade delegates to the Rust
core via rune/ct_loader when built; the Python implementation below is
the strangler fallback, pinned with RUNE_CT_CORE=python.
"""
from __future__ import annotations
@@ -17,9 +24,28 @@ DECAY_PER_TICK = 0.95
CLUSTER_THRESHOLD = 2.0
def _core():
"""The Rust concept_topology core when available (strangler: Python
fallback is load-bearing). RUNE_CT_CORE=python pins the fallback."""
import os
if os.environ.get("RUNE_CT_CORE", "auto") == "python":
return None
try:
from rune import ct_loader
except Exception: # noqa: BLE001 — any loader failure means fallback, never a broken call
return None
try:
return ct_loader if ct_loader.available() else None
except Exception: # noqa: BLE001 — availability probe must never crash the fallback
return None
def cofire(topology: dict, topics: list) -> dict:
"""Record one co-firing event: every pair in `topics` gains an edge
(weight +1). Topics are created on first sight (no preset list)."""
core = _core()
if core is not None:
return core.cofire(topology, topics)
topology = dict(topology)
edges = dict(topology.get("edges", {}))
seen = dict(topology.get("seen", {}))
@@ -36,6 +62,9 @@ def cofire(topology: dict, topics: list) -> dict:
def decay(topology: dict) -> dict:
"""Decay all edges one tick (stale associations weaken)."""
core = _core()
if core is not None:
return core.decay(topology)
topology = dict(topology)
edges = {k: v * DECAY_PER_TICK for k, v in topology.get("edges", {}).items()
if v * DECAY_PER_TICK >= 0.5}
@@ -45,6 +74,9 @@ def decay(topology: dict) -> dict:
def clusters(topology: dict, threshold: float = CLUSTER_THRESHOLD) -> list:
"""Emergent clusters: connected components over edges >= threshold."""
core = _core()
if core is not None:
return core.clusters(topology, threshold)
edges = {k: v for k, v in topology.get("edges", {}).items()
if v >= threshold}
parent: dict = {}
@@ -73,6 +105,9 @@ def clusters(topology: dict, threshold: float = CLUSTER_THRESHOLD) -> list:
def suggest(topology: dict, fact_topics: list) -> dict:
"""Suggest the cluster a new fact belongs to — or a new topic."""
core = _core()
if core is not None:
return core.suggest(topology, fact_topics)
cls = clusters(topology)
best, best_overlap = None, 0
for c in cls:
@@ -87,4 +122,7 @@ def suggest(topology: dict, fact_topics: list) -> dict:
def new_topology() -> dict:
"""The empty seed: no preset categories."""
core = _core()
if core is not None:
return core.new_topology()
return {"edges": {}, "seen": {}}
+18 -1
View File
@@ -9,6 +9,8 @@ CANDIDATE/UNIT-PROVEN — existence is not trust (§15).
from __future__ import annotations
import os
from rune.construction_pattern import ConstructionPattern
__all__ = ["BUILTIN_PATTERNS", "get_pattern", "PATTERN_REGISTRY_REVISION"]
@@ -236,7 +238,22 @@ _REGISTRY: dict[str, ConstructionPattern] = {p.pattern_id: p for p in BUILTIN_PA
def get_pattern(pattern_id: str, revision: int | None = None) -> ConstructionPattern | None:
"""Fetch a builtin pattern; a specific revision raises if the registry
no longer carries it (contracts reference the revision they consumed —
§17)."""
§17).
Dichotomy wave: the Rust registry core first (RUNE_CPB_CORE=python
pins the Python fallback — strangler law). Both serve the same frozen
registry docs, so the pin is only for oracle isolation."""
if os.environ.get("RUNE_CPB_CORE", "auto") != "python":
try:
from rune import cpb_loader
if cpb_loader.available():
doc = cpb_loader.get_pattern(pattern_id, revision)
return ConstructionPattern.from_dict(doc) if doc is not None else None
except ValueError:
raise # the kernel's revision-mismatch message is oracle-exact
except Exception:
pass # kernel unavailable — Python registry below
p = _REGISTRY.get(pattern_id)
if p is None:
return None
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust construction_patterns_builtin registry core
(dichotomy wave, #2479 tranche). Mirrors cs_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* cpb_registry_revision({}) -> number
* cpb_patterns({}) -> [pattern doc, ...] registry order
* cpb_get_pattern({"pattern_id", "revision"}) -> pattern doc | null
(a stale revision is ok=false; get_pattern raises ValueError with the
oracle's exact message)
Free: capability_scoring's cs_free_string (shared process) — never
re-exported here. Override the .so with CPB_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" / "construction_patterns_builtin"
_LIB_PATH = Path(
os.environ.get(
"CPB_RUST_LIB",
_CRATE_DIR / "target" / "release" / "libconstruction_patterns_builtin.so",
)
)
_lib = None
class CpbUnavailable(FileNotFoundError):
"""The Rust registry core is not built. Callers fall back to the Python
registry — strangler law."""
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 CpbUnavailable(
f"Rust construction_patterns_builtin core not built: {_LIB_PATH} missing — "
f"run `cargo build --release` in {_CRATE_DIR}"
)
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in ("cpb_registry_revision", "cpb_patterns", "cpb_get_pattern"):
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_char_p]
fn.restype = ctypes.c_void_p # char* — never default c_int
# free comes from capability_scoring (same process, shared allocator law)
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()
fn = getattr(lib, sym)
raw = json.dumps(payload).encode("utf-8")
ptr = fn(raw)
if not ptr:
raise RuntimeError(f"{sym} returned a null pointer")
try:
text = ctypes.string_at(ptr).decode("utf-8")
finally:
lib._free(ptr)
env = json.loads(text)
if not env.get("ok"):
raise RuntimeError(f"{sym} core error: {env.get('err')}")
return env["value"]
def registry_revision() -> int:
"""The builtin pattern registry revision (PATTERN_REGISTRY_REVISION)."""
return int(_call("cpb_registry_revision", {}))
def patterns() -> list:
"""Every builtin pattern doc, in registry order."""
return _call("cpb_patterns", {})
def get_pattern(pattern_id: str, revision: int | None = None):
"""Pattern doc for `pattern_id`, or None. A revision that no longer
matches raises ValueError with the oracle's exact message."""
raw = json.dumps({"pattern_id": pattern_id, "revision": revision}).encode("utf-8")
lib = _load()
ptr = lib.cpb_get_pattern(raw)
if not ptr:
raise RuntimeError("cpb_get_pattern returned a null pointer")
try:
text = ctypes.string_at(ptr).decode("utf-8")
finally:
lib._free(ptr)
env = json.loads(text)
if not env.get("ok"):
# The only err the kernel produces is the oracle's revision-mismatch
# ValueError message — preserve its type for facade parity.
raise ValueError(env.get("err"))
return env["value"]
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""ct_loader.py — ctypes loader for the Rust concept_topology core
(dichotomy wave #2479 tranche, contract
BUILDER-2026-10-11T23:55:54Z-982aa3-factory-wave). Mirrors broker_loader.py:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* ct_cofire({"topology", "topics"}) -> topology dict
* ct_decay({"topology"}) -> topology dict
* ct_clusters({"topology", "threshold"?}) -> cluster list
* ct_suggest({"topology", "fact_topics"}) -> suggestion dict
* ct_new_topology({}) -> empty topology
Free: capability_scoring's cs_free_string (shared process). NEVER re-export
a free symbol here. Override the .so with CT_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" / "concept_topology"
_LIB_PATH = Path(
os.environ.get("CT_RUST_LIB", _CRATE_DIR / "target" / "release" / "libconcept_topology.so")
)
_lib = None
class ConceptTopologyUnavailable(FileNotFoundError):
"""The Rust topology core is not built. Callers fall back to Python
(strangler law; RUNE_CT_CORE=python pins the fallback)."""
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 ConceptTopologyUnavailable(str(_LIB_PATH))
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in ("ct_cofire", "ct_decay", "ct_clusters", "ct_suggest", "ct_new_topology"):
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p # char* — never default c_int
# free comes from capability_scoring (same process); load it if not yet resident
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"):
raise RuntimeError(doc.get("err", f"{sym} failed"))
return doc["value"]
def cofire(topology: dict, topics: list) -> dict:
return _call("ct_cofire", {"topology": topology, "topics": topics})
def decay(topology: dict) -> dict:
return _call("ct_decay", {"topology": topology})
def clusters(topology: dict, threshold: float | None = None) -> list:
payload = {"topology": topology}
if threshold is not None:
payload["threshold"] = threshold
return _call("ct_clusters", payload)
def suggest(topology: dict, fact_topics: list) -> dict:
return _call("ct_suggest", {"topology": topology, "fact_topics": fact_topics})
def new_topology() -> dict:
return _call("ct_new_topology", {})
+43 -2
View File
@@ -1,12 +1,17 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust drift_ledger core (dichotomy debt queue
batch 3, #2479 tranche). Mirrors broker_loader.py's contract:
batch 3 + #2479 dichotomy wave tranche). Mirrors broker_loader.py's contract:
FFI contract (doc-envelope law):
* dl_sha({text}) -> hex
* dl_diff({ref, cur}) -> summary string
* dl_check({label, prev_size, cur_size, anchor_size?, anchor_sha?, cur_sha?}) -> {alert, pct, limit}
* dl_auto_fix({label, anchored, has_last_good}) -> {action, reason}
* dl_ledger_path({index_file}) -> path string
* dl_snapshot_entry({blocks: {label: content}, when}) -> snapshot entry
* dl_anchor_record({content, reason, when}) -> anchor record
* dl_check_alerts({ledger, blocks: {label: content}}) -> [alert, ...]
* dl_fix_decision({ledger, label, cur_sha?}) -> {fixed, reason}
Override the .so with DL_RUST_LIB (a FILE path).
"""
@@ -39,7 +44,9 @@ def _load() -> ctypes.CDLL:
if not _LIB_PATH.is_file():
raise DriftLedgerUnavailable(str(_LIB_PATH))
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in ("dl_sha", "dl_diff", "dl_check", "dl_auto_fix"):
for sym in ("dl_sha", "dl_diff", "dl_check", "dl_auto_fix", "dl_ledger_path",
"dl_snapshot_entry", "dl_anchor_record", "dl_check_alerts",
"dl_fix_decision"):
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p
@@ -75,3 +82,37 @@ def sha(text: str) -> str:
def diff_blocks(ref: str, cur: str) -> str:
return _call("dl_diff", {"ref": ref, "cur": cur})
def check_block(label: str, prev_size: int, cur_size: int, anchor_size=None,
anchor_sha=None, cur_sha=None) -> dict:
return _call("dl_check", {"label": label, "prev_size": prev_size,
"cur_size": cur_size, "anchor_size": anchor_size,
"anchor_sha": anchor_sha, "cur_sha": cur_sha})
def auto_fix_decision(label: str, anchored: bool, has_last_good: bool) -> dict:
return _call("dl_auto_fix", {"label": label, "anchored": anchored,
"has_last_good": has_last_good})
def ledger_path_for(index_file: str) -> str:
return _call("dl_ledger_path", {"index_file": index_file})
def snapshot_entry(blocks: dict, when: str) -> dict:
return _call("dl_snapshot_entry", {"blocks": blocks, "when": when})
def anchor_record(content: str, reason: str, when: str) -> dict:
return _call("dl_anchor_record", {"content": content, "reason": reason,
"when": when})
def check_alerts(ledger: dict, blocks: dict) -> list:
return _call("dl_check_alerts", {"ledger": ledger, "blocks": blocks})
def fix_decision(ledger: dict, label: str, cur_sha=None) -> dict:
return _call("dl_fix_decision", {"ledger": ledger, "label": label,
"cur_sha": cur_sha})
+135 -58
View File
@@ -48,6 +48,13 @@ DB = memory_env.store_db()
def ledger_path():
# The ledger lives beside the index, so overriding INDEX_FILE (as the
# curator gate and the canary do for isolation) moves the ledger too.
if _rust_core():
try:
from rune import dl_loader
if dl_loader.available():
return dl_loader.ledger_path_for(INDEX_FILE)
except Exception:
pass # fallback below
return os.path.join(os.path.dirname(INDEX_FILE), "drift_ledger.json")
@@ -63,9 +70,15 @@ CONSTITUTION_LIMIT = 0.05 # 5% — anything more than a directive entry is drif
BLOCKS = ("constitution", "persona", "human", "operating", "project")
def _rust_core():
"""RUNE_DL_CORE pin law: 'python' forces the oracle path, anything else
(default 'auto') serves the Rust core when the cdylib is built."""
return os.environ.get("RUNE_DL_CORE", "auto") != "python"
def _sha(text):
# Dichotomy batch 3: Rust core first (RUNE_DL_CORE=python pins fallback).
if os.environ.get("RUNE_DL_CORE", "auto") != "python":
if _rust_core():
try:
from rune import dl_loader
if dl_loader.available():
@@ -155,51 +168,54 @@ def surface_alert(msg, label, changed=""):
logger.warning("Failed to write alert file: %s", e)
def snapshot():
ledger = load_ledger()
now = datetime.now(timezone.utc).isoformat(timespec="seconds")
def _snapshot_entry(blocks, now):
"""Dichotomy wave: Rust core first (RUNE_DL_CORE=python pins fallback)."""
if _rust_core():
try:
from rune import dl_loader
if dl_loader.available():
return dl_loader.snapshot_entry(blocks, now)
except Exception:
pass # fallback below
entry = {"when": now, "blocks": {}}
for label in BLOCKS:
raw = read_block(label)
raw = blocks[label]
entry["blocks"][label] = {
"sha": _sha(raw),
"size": len(raw),
"content": raw, # last-good content backup for diffing + retro-fix
}
ledger["last_snapshot"] = entry
save_ledger(ledger)
return entry
def anchor(label, reason):
"""Record that the current state of `label` is authorised (a user directive,
a confirmed review, a 3-sighting promotion). Subsequent drift is measured
from here — this is the re-anchor after legitimate change."""
ledger = load_ledger()
raw = read_block(label)
ledger.setdefault("anchors", {})[label] = {
def _anchor_record(raw, reason, now):
if _rust_core():
try:
from rune import dl_loader
if dl_loader.available():
return dl_loader.anchor_record(raw, reason, now)
except Exception:
pass # fallback below
return {
"sha": _sha(raw),
"size": len(raw),
"content": raw, # authorised content backup for root-cause diffing
"reason": reason,
"when": datetime.now(timezone.utc).isoformat(timespec="seconds"),
"when": now,
}
save_ledger(ledger)
return f"anchored {label}: {reason}"
def check(strict=False, autofix=True):
ledger = load_ledger()
snap = ledger.get("last_snapshot")
if not snap:
# No baseline yet — record one and treat as healthy (first contact).
snapshot()
print("no prior snapshot; baseline recorded")
return 0
def _check_alerts(ledger, now_blocks):
"""The check() alert loop: Rust core first, oracle loop as fallback."""
if _rust_core():
try:
from rune import dl_loader
if dl_loader.available():
return dl_loader.check_alerts(ledger, now_blocks)
except Exception:
pass # fallback below
alerts = []
now_blocks = {label: read_block(label) for label in BLOCKS}
prev = snap.get("blocks", {})
prev = (ledger.get("last_snapshot") or {}).get("blocks", {})
for label in BLOCKS:
cur = now_blocks[label]
cur_size = len(cur)
@@ -245,6 +261,41 @@ def check(strict=False, autofix=True):
"content_changed": content_changed,
}
)
return alerts
def snapshot():
ledger = load_ledger()
now = datetime.now(timezone.utc).isoformat(timespec="seconds")
entry = _snapshot_entry({label: read_block(label) for label in BLOCKS}, now)
ledger["last_snapshot"] = entry
save_ledger(ledger)
return entry
def anchor(label, reason):
"""Record that the current state of `label` is authorised (a user directive,
a confirmed review, a 3-sighting promotion). Subsequent drift is measured
from here — this is the re-anchor after legitimate change."""
ledger = load_ledger()
raw = read_block(label)
ledger.setdefault("anchors", {})[label] = _anchor_record(
raw, reason, datetime.now(timezone.utc).isoformat(timespec="seconds")
)
save_ledger(ledger)
return f"anchored {label}: {reason}"
def check(strict=False, autofix=True):
ledger = load_ledger()
snap = ledger.get("last_snapshot")
if not snap:
# No baseline yet — record one and treat as healthy (first contact).
snapshot()
print("no prior snapshot; baseline recorded")
return 0
alerts = _check_alerts(ledger, {label: read_block(label) for label in BLOCKS})
if alerts:
for a in alerts:
msg = (
@@ -287,27 +338,23 @@ def auto_fix(label, ledger, strict):
Returns {action, reason}.
"""
if label == "constitution":
return {"action": "alert-only", "reason": "constitution changes only by explicit directive; needs human review"}
if label in ledger.get("anchors", {}):
block_meta = ledger.get("last_snapshot", {}).get("blocks", {}).get(label)
anchored = label in ledger.get("anchors", {})
has_last_good = bool(block_meta and "content" in block_meta)
result = _auto_fix_decision(label, anchored, has_last_good)
if result["action"] == "re-anchored":
# Authorised growth: re-anchor to the current (approved) state.
raw = read_block(label)
ledger.setdefault("anchors", {})[label] = {
"sha": _sha(raw),
"size": len(raw),
"content": raw,
"reason": "auto re-anchor after authorised growth",
"when": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}
ledger.setdefault("anchors", {})[label] = _anchor_record(
raw, "auto re-anchor after authorised growth",
datetime.now(timezone.utc).isoformat(timespec="seconds"),
)
save_ledger(ledger)
return {"action": "re-anchored", "reason": "anchored block grew (authorised) — re-baselined"}
# Unauthorised drift: restore to last-good content (new schema — restore
# the store's always-on core entries for this topic).
block_meta = ledger.get("last_snapshot", {}).get("blocks", {}).get(label)
if block_meta and "content" in block_meta:
elif result["action"] == "restored":
# Unauthorised drift: restore to last-good content (new schema —
# restore the store's always-on core entries for this topic).
restore_topic(label, block_meta["content"])
return {"action": "restored", "reason": "unauthorised drift — restored to last-good content"}
return {"action": "alert-only", "reason": "no last-good content available to restore"}
return result
def diff_blocks(ref, cur):
@@ -379,6 +426,44 @@ def restore_topic(label, content):
logger.error("restore_topic('%s') failed: %s", label, e)
def _auto_fix_decision(label, anchored, has_last_good):
"""The auto-fix ROUTING decision (IO execution stays in auto_fix)."""
if _rust_core():
try:
from rune import dl_loader
if dl_loader.available():
return dl_loader.auto_fix_decision(label, anchored, has_last_good)
except Exception:
pass # fallback below
if label == "constitution":
return {"action": "alert-only", "reason": "constitution changes only by explicit directive; needs human review"}
if anchored:
return {"action": "re-anchored", "reason": "anchored block grew (authorised) — re-baselined"}
if has_last_good:
return {"action": "restored", "reason": "unauthorised drift — restored to last-good content"}
return {"action": "alert-only", "reason": "no last-good content available to restore"}
def _fix_decision(ledger, label, cur_sha):
"""The retroactive-fix gate decision (restore IO stays in fix)."""
if _rust_core():
try:
from rune import dl_loader
if dl_loader.available():
return dl_loader.fix_decision(ledger, label, cur_sha)
except Exception:
pass # fallback below
snap = ledger.get("last_snapshot", {})
block_meta = snap.get("blocks", {}).get(label)
if not block_meta or "content" not in block_meta:
return {"fixed": False, "reason": f"no last-good content for {label}"}
if label in ledger.get("anchors", {}):
return {"fixed": False, "reason": f"{label} is anchored (authorised); use anchor to re-baseline instead of fix"}
if cur_sha == block_meta.get("sha"):
return {"fixed": False, "reason": f"{label} already matches last-good"}
return {"fixed": True, "reason": f"{label} restored to last-good state"}
def fix(label):
"""RETROACTIVE FIX (#B): restore a drifted block to its last-good content.
@@ -390,20 +475,12 @@ def fix(label):
ledger = load_ledger()
snap = ledger.get("last_snapshot", {})
block_meta = snap.get("blocks", {}).get(label)
if not block_meta or "content" not in block_meta:
return {"fixed": False, "reason": f"no last-good content for {label}"}
# Never silently revert an anchored block — it was authorised.
if label in ledger.get("anchors", {}):
return {"fixed": False, "reason": f"{label} is anchored (authorised); use anchor to re-baseline instead of fix"}
good = block_meta["content"]
cur = read_block(label)
if _sha(cur) == block_meta["sha"]:
return {"fixed": False, "reason": f"{label} already matches last-good"}
# New schema: restore the store's always-on entries for the topic (the old
# markdown block files are retired).
restore_topic(label, good)
journal(f"{label} restored to last-good content (retroactive drift fix)")
return {"fixed": True, "reason": f"{label} restored to last-good state"}
result = _fix_decision(ledger, label, _sha(cur))
if result.get("fixed"):
restore_topic(label, block_meta["content"])
journal(f"{label} restored to last-good content (retroactive drift fix)")
return result
def handle_hook(event, data=None):
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust experience_encoding core (dichotomy wave
#2479 tranche). Mirrors broker_loader.py/le_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* ee_evaluate({event, existing_episodes, corpus_texts, lexicon_available})
-> {verdict, reason, provenance}
`lexicon_available` pins the oracle's flat `import lexicon_evolution`
environment seam (ImportError → conservative evidence admission); the
facade derives it from the live import and the frozen truth replays it.
Free: capability_scoring's cs_free_string (shared process). Override the
.so with EE_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" / "experience_encoding"
LIB_PATH = Path(
os.environ.get("EE_RUST_LIB",
_CRATE_DIR / "target" / "release" / "libexperience_encoding.so")
)
_lib = None
class ExperienceCoreUnavailable(FileNotFoundError):
"""The Rust experience_encoding 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 ExperienceCoreUnavailable(str(LIB_PATH))
lib = ctypes.CDLL(str(LIB_PATH))
getattr(lib, "ee_evaluate").argtypes = [ctypes.c_void_p]
getattr(lib, "ee_evaluate").restype = ctypes.c_void_p
# free comes from capability_scoring (same process) if loaded; else our own malloc path
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"):
raise RuntimeError(doc.get("err", f"{sym} failed"))
return doc["value"]
def evaluate(event, existing_episodes=None, corpus_texts=(),
lexicon_available=True) -> dict:
"""Rust core evaluate: returns {verdict, reason, provenance}."""
return _call("ee_evaluate", {
"event": event,
"existing_episodes": list(existing_episodes or []),
"corpus_texts": list(corpus_texts or ()),
"lexicon_available": lexicon_available,
})
+43
View File
@@ -13,14 +13,57 @@ becomes an episode. Event → evaluate → classify → attach provenance:
Admission uses the lexicon discipline (distinctive terms, not common
words) + grounding (traces to real material) — the same meta-judgement
as retrieval and evolution, so growth and admission speak one lexicon.
Dichotomy wave #2479: the admission decision runs through the FULL Rust
core (src/rust/experience_encoding) when built; RUNE_EE_CORE=python pins
the Python fallback — parity harnesses. The _py_ variant is the
load-bearing fallback + parity oracle; the ctypes seam is rune.ee_loader.
The oracle's flat `import lexicon_evolution` is an environment seam: when
the module is missing, the core is bypassed and the conservative evidence
fallback applies exactly as the oracle wrote it.
"""
from __future__ import annotations
import os
def _ee_core():
"""Dichotomy wave #2479: Rust core first (RUNE_EE_CORE=python pins the
Python fallback — parity harnesses). The kernel owns the entire
admission decision (empty gate, duplicate scan, lexical signal,
grounding, classification, provenance)."""
if os.environ.get("RUNE_EE_CORE", "auto") == "python":
return None
try:
from rune import ee_loader
except ImportError:
try:
import ee_loader
except ImportError:
return None
try:
return ee_loader if ee_loader.available() else None
except Exception:
return None
def evaluate(event: dict, existing_episodes: list | None = None,
corpus_texts: tuple = ()) -> dict:
"""Evaluate one event. Returns {verdict, reason, provenance}."""
core = _ee_core()
if core is not None:
try:
import lexicon_evolution # noqa: F401 (the oracle's seam)
except ImportError:
return _py_evaluate(event, existing_episodes, corpus_texts)
return core.evaluate(event, existing_episodes, corpus_texts)
return _py_evaluate(event, existing_episodes, corpus_texts)
def _py_evaluate(event: dict, existing_episodes: list | None = None,
corpus_texts: tuple = ()) -> dict:
"""Evaluate one event. Returns {verdict, reason, provenance}."""
existing_episodes = existing_episodes or []
text = (event.get("text") or "").strip()
source = event.get("source", "unknown")
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust grading core (dichotomy wave, #2479
tranche). Mirrors bh_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* gr_affirm_rule({fact}) -> the (possibly rewritten) rule string
* gr_grade_fact({fact, target, user_name}) -> the tier name
* gr_tier_of({grade}) -> the journal label (G0/G1/G2p/G2/P/G3/G4)
`user_name` is grading.py's import-time MEMORY_USER_NAME normalization
(strip().lower()) — the facade passes its module-level value so the kernel
stays pure. Free: capability_scoring's cs_free_string (shared process) —
NEVER re-export a free symbol from this crate.
Override the .so with GRADING_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" / "grading"
_LIB_PATH = Path(os.environ.get("GRADING_RUST_LIB", _CRATE_DIR / "target" / "release" / "libgrading.so"))
_lib = None
_SYMBOLS = (
"gr_affirm_rule",
"gr_grade_fact",
"gr_tier_of",
)
class GradingCoreUnavailable(FileNotFoundError):
"""The Rust grading core is not built. Callers fall back to Python."""
def available() -> bool:
return _LIB_PATH.is_file()
def core():
"""The shared kernel probe (strangler law): the delegation entry the
facade uses. RUNE_GR_CORE=python pins the fallback — THIS is
where the pin is honored, so every consumer gets it."""
if os.environ.get("RUNE_GR_CORE", "auto") == "python":
return None
try:
return _load()
except Exception: # noqa: BLE001 — build/loader failures mean fallback
return None
def _load() -> ctypes.CDLL:
global _lib
if _lib is not None:
return _lib
if not _LIB_PATH.is_file():
raise GradingCoreUnavailable(str(_LIB_PATH))
lib = ctypes.CDLL(str(_LIB_PATH))
# FFI segfault law: explicit argtypes/restype (c_void_p for returned
# char* — the default c_int truncates 64-bit pointers).
for sym in _SYMBOLS:
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p
_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(ptr)
doc = json.loads(out)
if not doc.get("ok"):
raise RuntimeError(doc.get("err", f"{sym} failed"))
return doc["value"]
def _free(ptr) -> None:
"""Doc-envelope free via capability_scoring's cs_free_string — NEVER
re-export a free symbol from this crate (duplicate symbols break the
cdylib link)."""
try:
scoring = ctypes.CDLL(str(
_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so"
))
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
scoring.cs_free_string.restype = None
scoring.cs_free_string(ptr)
except OSError:
pass
def affirm_rule(fact: str) -> str:
return _call("gr_affirm_rule", {"fact": fact})
def grade_fact(fact: str, target: str | None = None, user_name: str = "") -> str:
return _call("gr_grade_fact", {"fact": fact, "target": target, "user_name": user_name})
def tier_of(grade: str) -> str:
return _call("gr_tier_of", {"grade": grade})
+66 -6
View File
@@ -20,6 +20,28 @@ matched only via the optional MEMORY_USER_NAME env var.
import os
# ── Rust core wiring (dichotomy-wave doc-envelope pattern) ──────────────────
try:
from . import gr_loader
except ImportError: # direct script execution (curator `import grading`)
try:
import gr_loader # type: ignore[no-redef]
except ImportError: # noqa: S110 — no loader on path → Python oracle below
gr_loader = None # type: ignore[assignment]
def _gr_core():
"""The Rust grading-core probe (strangler law). RUNE_GR_CORE=python
pins the Python fallback — the pin is honored inside gr_loader.core
so every consumer gets it. Returns the loader MODULE (never the raw
CDLL)."""
if gr_loader is None:
return None
try:
return gr_loader if gr_loader.core() is not None else None
except Exception: # noqa: BLE001 — kernel failure → oracle below
return None
# --- Imperative / rule signals: a sentence instructing HOW to operate. ------
RULE_WEAK = [
"should", "prefer", "instead of", "lead with", "first", "before ",
@@ -100,8 +122,9 @@ AFFIRM_DICT = {
}
def affirm_rule(fact):
"""Rewrite a perceived demand/rule as a positive affirmation.
def _py_affirm_rule(fact):
"""Python fallback: rewrite a perceived demand/rule as a positive
affirmation.
Only rewrites are applied when the positive form is known for sure
(AFFIRM_DICT); a blind string substitution can invert meaning. Unknown
@@ -116,8 +139,23 @@ def affirm_rule(fact):
return f
def grade_fact(fact, target=None):
"""Return the tier (G0..G4) for a fact/rule based on type + language.
def affirm_rule(fact):
"""Rewrite a perceived demand/rule as a positive affirmation.
Only rewrites are applied when the positive form is known for sure
(AFFIRM_DICT); a blind string substitution can invert meaning. Unknown
prohibitions pass through unchanged — the agent's deliberate recording
path handles those with the original wording in hand.
"""
core = _gr_core()
if core is not None:
return core.affirm_rule(fact)
return _py_affirm_rule(fact)
def _py_grade_fact(fact, target=None, _user_name=""):
"""Python fallback: return the tier (G0..G4) for a fact/rule based on
type + language.
`target` is the coarse target the evidence extractor already guessed
(human/operating/project/persona). Grading refines where within the tiers
@@ -164,8 +202,22 @@ def grade_fact(fact, target=None):
return "hindsight"
def tier_of(grade):
"""Human label for a grade, for journaling / canary output."""
def grade_fact(fact, target=None):
"""Return the tier (G0..G4) for a fact/rule based on type + language.
`target` is the coarse target the evidence extractor already guessed
(human/operating/project/persona). Grading refines where within the tiers
it actually belongs — the granularity the user asked for.
"""
core = _gr_core()
if core is not None:
return core.grade_fact(fact, target, _user_name)
return _py_grade_fact(fact, target, _user_name)
def _py_tier_of(grade):
"""Python fallback: human label for a grade, for journaling / canary
output."""
return {
"constitution": "G0",
"operating": "G1",
@@ -178,6 +230,14 @@ def tier_of(grade):
}.get(grade, "G3")
def tier_of(grade):
"""Human label for a grade, for journaling / canary output."""
core = _gr_core()
if core is not None:
return core.tier_of(grade)
return _py_tier_of(grade)
if __name__ == "__main__":
# quick self-check
cases = [
+113 -5
View File
@@ -32,6 +32,24 @@ KINDS = {"message", "reply", "tool_call", "tool_result", "revision", "branch_hea
EDGE_TYPES = {"parent", "reply_to", "calls", "results_in", "revises", "supersedes", "branches_from"}
def _mg_core():
"""Dichotomy wave #2479: Rust core first (RUNE_MG_CORE=python pins the
Python fallback — parity harnesses). The kernel owns the graph state
machine (load parsing + schema gate, node construction, revise/branch
wiring, lineage/history walks, flat migration, cues); the uuid/clock
seams, the save FILE IO and the json.dumps renderings stay here."""
if os.environ.get("RUNE_MG_CORE", "auto") == "python":
return None
try:
from rune import mg_loader
if mg_loader.available():
return mg_loader
except ImportError:
pass
return None
def _now() -> str:
return datetime.now(timezone.utc).isoformat()
@@ -48,6 +66,18 @@ class MessageGraph:
# -- persistence ----------------------------------------------------
def _load(self) -> None:
core = _mg_core()
if core is not None:
text = None
if self.path.exists():
try:
text = self.path.read_text()
except OSError:
text = None
state = core.load(text)
self.nodes = state["nodes"]
self.order = state["order"]
return
if not self.path.exists():
return
try:
@@ -61,8 +91,13 @@ class MessageGraph:
self.order.append(n["id"])
def save(self) -> None:
core = _mg_core()
if core is not None:
doc = core.save({"nodes": self.nodes, "order": self.order})
else:
doc = {"schema": SCHEMA, "nodes": [self.nodes[i] for i in self.order]}
self.path.parent.mkdir(parents=True, exist_ok=True)
self.path.write_text(json.dumps({"schema": SCHEMA, "nodes": [self.nodes[i] for i in self.order]}, indent=2))
self.path.write_text(json.dumps(doc, indent=2))
# -- append ---------------------------------------------------------
def add(
@@ -73,6 +108,15 @@ class MessageGraph:
raise ValueError(f"unknown kind {kind!r}")
if edge not in EDGE_TYPES:
raise ValueError(f"unknown edge {edge!r}")
core = _mg_core()
if core is not None:
out = core.add(
{"nodes": self.nodes, "order": self.order},
uuid.uuid4().hex[:12], _now(), role, content, kind, parent, edge,
)
self.nodes = out["graph"]["nodes"]
self.order = out["graph"]["order"]
return out["node"]
node = {
"id": uuid.uuid4().hex[:12],
"kind": kind,
@@ -88,21 +132,49 @@ class MessageGraph:
return node
def tool_call(self, name: str, args: dict, parent: str | None = None) -> dict:
core = _mg_core()
if core is not None:
out = core.tool_call(
{"nodes": self.nodes, "order": self.order},
uuid.uuid4().hex[:12], _now(),
json.dumps({"call": name, "args": args}), parent,
)
self.nodes = out["graph"]["nodes"]
self.order = out["graph"]["order"]
return out["node"]
return self.add("tool", json.dumps({"call": name, "args": args}), kind="tool_call", parent=parent, edge="calls")
def tool_result(self, call_id: str, result: str) -> dict:
call = self.nodes.get(call_id)
if call is None:
if call_id not in self.nodes:
raise KeyError(f"no such call {call_id!r}")
core = _mg_core()
if core is not None:
out = core.tool_result(
{"nodes": self.nodes, "order": self.order},
uuid.uuid4().hex[:12], _now(), call_id, result,
)
self.nodes = out["graph"]["nodes"]
self.order = out["graph"]["order"]
return out["node"]
call = self.nodes[call_id]
return self.add("tool", result, kind="tool_result", parent=call_id, edge="results_in")
# -- revision / branch ----------------------------------------------
def revise(self, node_id: str, new_content: str) -> dict:
"""Regenerate an answer without losing provenance: the old node is
marked superseded; the revision carries the supersedes edge."""
old = self.nodes.get(node_id)
if old is None:
if node_id not in self.nodes:
raise KeyError(f"no such message {node_id!r}")
core = _mg_core()
if core is not None:
out = core.revise(
{"nodes": self.nodes, "order": self.order},
node_id, new_content, uuid.uuid4().hex[:12], _now(),
)
self.nodes = out["graph"]["nodes"]
self.order = out["graph"]["order"]
return out["node"]
old = self.nodes[node_id]
rev = self.add(old["role"], new_content, kind="revision", parent=old.get("parent"), edge="revises")
rev["edges"].append({"type": "supersedes", "to": node_id})
old["superseded_by"] = rev["id"]
@@ -112,6 +184,15 @@ class MessageGraph:
"""Start a new conversation branch off any message."""
if node_id not in self.nodes:
raise KeyError(f"no such message {node_id!r}")
core = _mg_core()
if core is not None:
out = core.branch(
{"nodes": self.nodes, "order": self.order},
node_id, uuid.uuid4().hex[:12], _now(),
)
self.nodes = out["graph"]["nodes"]
self.order = out["graph"]["order"]
return out["node"]
return self.add("system", f"branched from {node_id}", kind="branch_head", parent=node_id, edge="branches_from")
# -- lineage context -------------------------------------------------
@@ -123,6 +204,9 @@ class MessageGraph:
node_id = self.order[-1] if self.order else None
if node_id is None:
return []
core = _mg_core()
if core is not None:
return core.lineage({"nodes": self.nodes, "order": self.order}, node_id)
chain: list[dict] = []
seen = set()
cur = node_id
@@ -142,6 +226,9 @@ class MessageGraph:
def history(self, node_id: str) -> list[dict]:
"""Every revision of a message, oldest first (the provenance trail)."""
core = _mg_core()
if core is not None:
return core.history({"nodes": self.nodes, "order": self.order}, node_id)
trail = []
# find the root: walk supersedes edges backwards
cur = node_id
@@ -168,6 +255,24 @@ class MessageGraph:
"""Schema migration: the flat {role, content} transcript becomes a
chained graph (arrival order, parent links). Compatibility preserved —
no conversation is lost in the upgrade."""
core = _mg_core()
if core is not None:
g = cls.__new__(cls)
g.path = Path(path) if path else GRAPH_PATH
text = None
if g.path.exists():
try:
text = g.path.read_text()
except OSError:
text = None
state = core.migrate_flat(
text, list(transcript),
[uuid.uuid4().hex[:12] for _ in transcript],
[_now() for _ in transcript],
)
g.nodes = state["nodes"]
g.order = state["order"]
return g
g = cls(path=path)
for m in transcript:
g.add(m.get("role", "system"), m.get("content", ""), kind="message")
@@ -177,6 +282,9 @@ class MessageGraph:
@staticmethod
def cue(node: dict) -> str:
"""One quiet relationship line per node (no visual clutter)."""
core = _mg_core()
if core is not None:
return core.cue(node)
for e in node.get("edges", []):
if e["type"] == "supersedes":
return f"replaces {e['to'][:8]}"
+152
View File
@@ -0,0 +1,152 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust message_graph core (dichotomy wave, #2479
tranche). Mirrors broker_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* mg_load({"text": str|null}) -> graph wire {"nodes", "order"}
* mg_save({"graph"}) -> save doc {"schema", "nodes"}
* mg_add({graph, id, ts, role, content, kind, parent, edge}) -> {graph, node}
* mg_tool_call({graph, id, ts, content, parent}) -> {graph, node}
* mg_tool_result({graph, id, ts, call_id, result}) -> {graph, node}
* mg_revise({graph, node_id, new_content, id, ts}) -> {graph, node}
* mg_branch({graph, node_id, id, ts}) -> {graph, node}
* mg_lineage({graph, node_id}) -> [nodes]
* mg_history({graph, node_id}) -> [nodes]
* mg_migrate_flat({text, transcript, ids, tss}) -> graph wire
* mg_cue({"node"}) -> cue string
Graph wire: {"nodes": {id: node} (insertion-ordered), "order": [ids]}.
Errors ride the envelope typed — {"ok": false, "err": msg, "type":
"ValueError"|"KeyError"|"AttributeError"|"TypeError"} — and the loader
raises the MATCHING Python exception (the oracle raises those; the parity
harness compares exception types and messages, so the kernel's guard
clauses must round-trip as the same Python error the oracle raises).
Free: capability_scoring's cs_free_string (shared process). Override the
.so with MG_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" / "message_graph"
_LIB_PATH = Path(
os.environ.get("MG_RUST_LIB", _CRATE_DIR / "target" / "release" / "libmessage_graph.so")
)
_ERR_TYPES = {
"ValueError": ValueError,
"KeyError": KeyError,
"AttributeError": AttributeError,
"TypeError": TypeError,
}
_lib = None
class MessageGraphCoreUnavailable(FileNotFoundError):
"""The Rust message-graph 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 MessageGraphCoreUnavailable(str(_LIB_PATH))
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in ("mg_load", "mg_save", "mg_add", "mg_tool_call", "mg_tool_result",
"mg_revise", "mg_branch", "mg_lineage", "mg_history",
"mg_migrate_flat", "mg_cue"):
getattr(lib, sym).argtypes = [ctypes.c_void_p]
getattr(lib, sym).restype = ctypes.c_void_p
# free comes from capability_scoring (same process) if loaded; else our own malloc path
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 load(text: str | None) -> dict:
return _call("mg_load", {"text": text})
def save(graph: dict) -> dict:
return _call("mg_save", {"graph": graph})
def add(graph: dict, node_id: str, ts: str, role, content, kind: str,
parent: str | None, edge: str) -> dict:
return _call("mg_add", {"graph": graph, "id": node_id, "ts": ts, "role": role,
"content": content, "kind": kind, "parent": parent,
"edge": edge})
def tool_call(graph: dict, node_id: str, ts: str, content: str,
parent: str | None) -> dict:
return _call("mg_tool_call", {"graph": graph, "id": node_id, "ts": ts,
"content": content, "parent": parent})
def tool_result(graph: dict, node_id: str, ts: str, call_id: str, result) -> dict:
return _call("mg_tool_result", {"graph": graph, "id": node_id, "ts": ts,
"call_id": call_id, "result": result})
def revise(graph: dict, node_id: str, new_content, rev_id: str, ts: str) -> dict:
return _call("mg_revise", {"graph": graph, "node_id": node_id,
"new_content": new_content, "id": rev_id, "ts": ts})
def branch(graph: dict, node_id: str, head_id: str, ts: str) -> dict:
return _call("mg_branch", {"graph": graph, "node_id": node_id,
"id": head_id, "ts": ts})
def lineage(graph: dict, node_id: str | None) -> list:
return _call("mg_lineage", {"graph": graph, "node_id": node_id})
def history(graph: dict, node_id: str) -> list:
return _call("mg_history", {"graph": graph, "node_id": node_id})
def migrate_flat(text: str | None, transcript: list, ids: list, tss: list) -> dict:
return _call("mg_migrate_flat", {"text": text, "transcript": transcript,
"ids": ids, "tss": tss})
def cue(node: dict) -> str:
return _call("mg_cue", {"node": node})
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust mutation_guards core (dichotomy wave
#2479 tranche). Mirrors ee_loader.py/broker_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* mu_classify({target_kind, operation, scope, attributed, backed_up})
-> {verdict, reason, recovery}
* mu_guard({mutation}) -> {verdict, reason, recovery}
Free: capability_scoring's cs_free_string (shared process). Override the
.so with MU_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" / "mutation_guards"
LIB_PATH = Path(
os.environ.get("MU_RUST_LIB",
_CRATE_DIR / "target" / "release" / "libmutation_guards.so")
)
_lib = None
class MutationGuardsCoreUnavailable(FileNotFoundError):
"""The Rust mutation_guards 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 MutationGuardsCoreUnavailable(str(LIB_PATH))
lib = ctypes.CDLL(str(LIB_PATH))
for name in ("mu_classify", "mu_guard"):
fn = getattr(lib, name)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p
# free comes from capability_scoring (same process) if loaded; else our own malloc path
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"):
raise RuntimeError(doc.get("err", f"{sym} failed"))
return doc["value"]
def classify(target_kind, operation, scope="single",
attributed=True, backed_up=False) -> dict:
"""Rust core classify_mutation: returns {verdict, reason, recovery}."""
return _call("mu_classify", {
"target_kind": target_kind,
"operation": operation,
"scope": scope,
"attributed": attributed,
"backed_up": backed_up,
})
def guard(mutation: dict) -> dict:
"""Rust core guard_mutation: returns {verdict, reason, recovery}."""
return _call("mu_guard", {"mutation": mutation})
+69 -20
View File
@@ -17,34 +17,62 @@ Classification (deterministic, no model):
Recoverability: a CONFIRM/DENY verdict carries the recovery shape
(snapshot id required, or the reason none exists).
Dichotomy wave #2479: the classification runs through the FULL Rust core
(src/rust/mutation_guards) when built; RUNE_MG_CORE=python pins the
Python fallback — parity harnesses. The _py_ variants are the
load-bearing fallback + parity oracle; the ctypes seam is rune.mu_loader.
"""
from __future__ import annotations
# Target-kind severity (higher = more protected)
KIND_SEVERITY = {
"constitution": 3,
"identity": 3,
"persona": 3,
"skill": 2,
"belief": 2,
"operating": 1,
"fact": 1,
"archive": 0,
}
import os
# Operation severity
OP_SEVERITY = {
"delete": 3,
"overwrite": 2,
"append": 1,
"read": 0,
}
def _mg_core():
"""Dichotomy wave #2479: Rust core first (RUNE_MG_CORE=python pins the
Python fallback — parity harnesses). The kernel owns the entire
classification (severity tables, branch order, verdict + reason +
recovery shape)."""
if os.environ.get("RUNE_MG_CORE", "auto") == "python":
return None
try:
from rune import mu_loader
except ImportError:
try:
import mu_loader
except ImportError:
return None
try:
return mu_loader if mu_loader.available() else None
except Exception:
return None
def classify_mutation(target_kind: str, operation: str, scope: str = "single",
attributed: bool = True, backed_up: bool = False) -> dict:
"""Classify a proposed mutation. Returns {verdict, reason, recovery}."""
core = _mg_core()
if core is not None:
return core.classify(target_kind, operation, scope, attributed,
backed_up)
return _py_classify_mutation(target_kind, operation, scope, attributed,
backed_up)
def guard_mutation(mutation: dict) -> dict:
"""Guard one mutation dict {target_kind, operation, scope, attributed,
backed_up}. Returns the verdict dict (ALLOW/CONFIRM/DENY + reason)."""
core = _mg_core()
if core is not None:
return core.guard(mutation)
return _py_guard_mutation(mutation)
def _py_classify_mutation(target_kind: str, operation: str,
scope: str = "single", attributed: bool = True,
backed_up: bool = False) -> dict:
"""Classify a proposed mutation. Returns {verdict, reason, recovery}."""
kind_sev = KIND_SEVERITY.get(target_kind, 1)
op_sev = OP_SEVERITY.get(operation, 1)
scope_sev = {"whole": 2, "multi": 1, "single": 0}.get(scope, 0)
@@ -82,13 +110,34 @@ def classify_mutation(target_kind: str, operation: str, scope: str = "single",
}
def guard_mutation(mutation: dict) -> dict:
def _py_guard_mutation(mutation: dict) -> dict:
"""Guard one mutation dict {target_kind, operation, scope, attributed,
backed_up}. Returns the verdict dict (ALLOW/CONFIRM/DENY + reason)."""
return classify_mutation(
return _py_classify_mutation(
mutation.get("target_kind", "fact"),
mutation.get("operation", "append"),
mutation.get("scope", "single"),
mutation.get("attributed", True),
mutation.get("backed_up", False),
)
# Target-kind severity (higher = more protected)
KIND_SEVERITY = {
"constitution": 3,
"identity": 3,
"persona": 3,
"skill": 2,
"belief": 2,
"operating": 1,
"fact": 1,
"archive": 0,
}
# Operation severity
OP_SEVERITY = {
"delete": 3,
"overwrite": 2,
"append": 1,
"read": 0,
}
+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.223.0
Version: v0.239.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.223.0 | Textual TUI | {len(loaded)} native modules[/dim]",
f"[dim]Rune v0.239.0 | Textual TUI | {len(loaded)} native modules[/dim]",
)
# Auto-show night mode suggestion if evening/night hours
+47
View File
@@ -13,6 +13,14 @@ the gate suite green + no regression vs the baseline run.
Worktree isolation: proposals build on `git worktree` branches off the
live repo, so the candidate never touches the working tree until
promoted. Discard = remove worktree + delete branch (full cleanup).
Dichotomy wave #2479: the FULL Rust core (src/rust/self_modify via
smod_loader) owns the subprocess seam (_run: every git invocation and
gate command) and every proposal decision; this module remains the
parity oracle and the strangler fallback (RUNE_SMOD_CORE=python pins it
— the RUNE_SM_CORE pin belongs to shard_manager). Raise parity holds at
the type level: the loader re-raises FileNotFoundError /
TimeoutExpired / KeyError / TypeError exactly as the oracle would.
"""
from __future__ import annotations
@@ -24,6 +32,25 @@ import tempfile
import time
def _smod_core():
"""Dichotomy wave #2479: Rust core first (src/rust/self_modify via
smod_loader). RUNE_SMOD_CORE=python pins the Python fallback below —
it remains the parity oracle (strangler law). The RUNE_SM_CORE pin
belongs to shard_manager. The kernel owns the subprocess seam (_run:
every git invocation and gate command) and every proposal decision;
the wall clock enters as an argument (no-clock law)."""
if os.environ.get("RUNE_SMOD_CORE", "auto") == "python":
return None
try:
from rune import smod_loader
if smod_loader.available():
return smod_loader
except ImportError:
pass
return None
def _run(cmd, cwd, timeout=300):
r = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True,
timeout=timeout)
@@ -33,6 +60,11 @@ def _run(cmd, cwd, timeout=300):
def propose_change(repo: str, branch: str, base: str = "master") -> dict:
"""Create the isolated worktree + branch. Returns the proposal record
(with rollback_ref = the base HEAD — the recovery path)."""
core = _smod_core()
if core is not None:
return core.propose_change(
repo, branch, base,
time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()))
rc, out, err = _run(["git", "rev-parse", base], cwd=repo)
if rc != 0:
return {"proposed": False, "error": f"unknown base: {base}"}
@@ -49,6 +81,9 @@ def propose_change(repo: str, branch: str, base: str = "master") -> dict:
def run_gates(worktree: str, test_cmd: list | None = None) -> dict:
"""Run the gate suite inside the worktree. Returns {passed, output}."""
core = _smod_core()
if core is not None:
return core.run_gates(worktree, test_cmd)
cmd = test_cmd or ["python3", "-m", "pytest", "tests/", "-q", "-x"]
rc, out, err = _run(cmd, cwd=worktree, timeout=600)
return {"passed": rc == 0,
@@ -57,6 +92,9 @@ def run_gates(worktree: str, test_cmd: list | None = None) -> dict:
def compare_baseline(candidate_result: dict, baseline_result: dict) -> dict:
"""Compare candidate vs baseline gate runs. Regression blocks."""
core = _smod_core()
if core is not None:
return core.compare_baseline(candidate_result, baseline_result)
if not candidate_result.get("passed"):
return {"promote": False,
"reason": "candidate gates red — regression blocks promotion"}
@@ -69,6 +107,9 @@ def compare_baseline(candidate_result: dict, baseline_result: dict) -> dict:
def promote_or_discard(repo: str, proposal: dict, verdict: dict) -> dict:
"""Merge (promote) or delete (discard) the proposal branch."""
core = _smod_core()
if core is not None:
return core.promote_or_discard(repo, proposal, verdict)
branch = proposal["branch"]
wt = proposal["worktree"]
if verdict.get("promote"):
@@ -88,6 +129,9 @@ def promote_or_discard(repo: str, proposal: dict, verdict: dict) -> dict:
def rollback(repo: str, rollback_ref: str) -> dict:
"""Restore the pre-change ref. The recovery path every proposal carries."""
core = _smod_core()
if core is not None:
return core.rollback(repo, rollback_ref)
rc, _out, err = _run(["git", "reset", "--hard", rollback_ref], cwd=repo)
if rc != 0:
return {"rolled_back": False, "error": err[:200]}
@@ -96,6 +140,9 @@ def rollback(repo: str, rollback_ref: str) -> dict:
def monitor(proposal: dict) -> dict:
"""The post-promotion watch record: what to observe after promotion."""
core = _smod_core()
if core is not None:
return core.monitor(proposal)
return {"monitoring": True, "branch": proposal.get("branch", ""),
"rollback_ref": proposal.get("rollback_ref", ""),
"watch": ["gate suite green on next CI run",
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""sl_loader.py — ctypes loader for the Rust spine_language core
(dichotomy wave #2479 tranche). Mirrors broker_loader.py:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* sl_classification_sides({"root"}) -> measured map
* sl_place_languages({"root", "doc"}) -> {"doc", "placed"}
* sl_contract_discoveries({"root"}) -> discovery list
* sl_normalize_discovery_ids({"doc", "root"?/"discoveries"?}) -> {"doc", "renamed"}
* sl_apply_contract_discoveries({"doc", "root"?/"discoveries"?}) -> {"doc", "placed"}
`root` is the repo root the store reads hang off (the facade always sends
str(ROOT) — tests monkeypatch rune.spine_language.ROOT and it must be
honored). Free: capability_scoring's cs_free_string (shared process).
NEVER re-export a free symbol here. Override the .so with SL_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" / "spine_language"
_LIB_PATH = Path(
os.environ.get("SL_RUST_LIB", _CRATE_DIR / "target" / "release" / "libspine_language.so")
)
_SYMBOLS = (
"sl_classification_sides",
"sl_place_languages",
"sl_contract_discoveries",
"sl_normalize_discovery_ids",
"sl_apply_contract_discoveries",
)
_lib = None
class SpineLanguageUnavailable(FileNotFoundError):
"""The Rust spine_language core is not built. Callers fall back to
Python (strangler law; RUNE_SL_CORE=python pins the fallback)."""
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 SpineLanguageUnavailable(str(_LIB_PATH))
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in _SYMBOLS:
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p # char* — never default c_int
# free comes from capability_scoring (same process); load it if not yet resident
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"):
raise RuntimeError(doc.get("err", f"{sym} failed"))
return doc["value"]
def classification_sides(root: str) -> dict:
return _call("sl_classification_sides", {"root": root})
def place_languages(doc: dict, root: str) -> dict:
return _call("sl_place_languages", {"doc": doc, "root": root})
def contract_discoveries(root: str) -> list:
return _call("sl_contract_discoveries", {"root": root})
def normalize_discovery_ids(doc: dict, root: str, discoveries: list | None = None) -> dict:
payload = {"doc": doc, "root": root}
if discoveries is not None:
payload["discoveries"] = discoveries
return _call("sl_normalize_discovery_ids", payload)
def apply_contract_discoveries(doc: dict, root: str, discoveries: list | None = None) -> dict:
payload = {"doc": doc, "root": root}
if discoveries is not None:
payload["discoveries"] = discoveries
return _call("sl_apply_contract_discoveries", payload)
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust self_modify core (dichotomy wave, #2479
tranche). Mirrors broker_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* smod_propose_change({repo, branch, base, now}) -> proposal dict
* smod_run_gates({worktree, test_cmd|null}) -> {passed, output}
* smod_compare_baseline({candidate_result, baseline_result}) -> verdict
* smod_promote_or_discard({repo, proposal, verdict}) -> result dict
* smod_rollback({repo, rollback_ref}) -> result dict
* smod_monitor({proposal}) -> watch record
No-clock law: `now` (the oracle's
time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())) is always an
argument — the kernel never reads the wall clock.
Boundary: the subprocess seam (_run — every git invocation and gate
command) is owned by the kernel, exactly like the oracle's _run. The
facade keeps only the wall clock and the RUNE_SMOD_CORE pin.
Raise parity: the oracle RAISES (FileNotFoundError from a missing cwd /
binary, TimeoutExpired on gate timeouts, KeyError from a missing proposal
key, TypeError at the argv seam). The kernel answers with the envelope's
err half and this loader re-raises the ORACLE'S exception TYPE via
_raise_like_oracle — raise parity holds at the type level, not just
raise-vs-return.
Free: capability_scoring's cs_free_string (shared process) — this crate
deliberately exports NO free symbol of its own (duplicate exports break
the cdylib link when sibling crates share one process). Override the .so
with SMOD_RUST_LIB (a FILE path).
"""
from __future__ import annotations
import ctypes
import json
import os
import re
import subprocess
from pathlib import Path
_REPO = Path(__file__).resolve().parents[2]
_CRATE_DIR = _REPO / "src" / "rust" / "self_modify"
_LIB_PATH = Path(
os.environ.get("SMOD_RUST_LIB", _CRATE_DIR / "target" / "release" / "libself_modify.so")
)
_SYMBOLS = (
"smod_propose_change",
"smod_run_gates",
"smod_compare_baseline",
"smod_promote_or_discard",
"smod_rollback",
"smod_monitor",
)
_lib = None
class SelfModifyCoreUnavailable(FileNotFoundError):
"""The Rust self-modify 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 SelfModifyCoreUnavailable(
f"Rust self_modify core not built: {_LIB_PATH} missing — run "
f"`cargo build --release` in {_CRATE_DIR}"
)
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in _SYMBOLS:
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p
_lib = lib
return lib
def _raise_like_oracle(err: str, sym: str):
"""Re-raise the ORACLE's exception type, not a blanket RuntimeError.
The kernel's envelope err encodes enough to reconstruct the raise
parity the Python oracle has (subprocess FileNotFoundError /
PermissionError / TimeoutExpired, dict KeyError, argv TypeError):
* "[Errno N] …" → OSError subclass by errno (spawn seam)
* "Command '…' timed out after S seconds" → subprocess.TimeoutExpired
* "KeyError: 'k'" → KeyError(k)
* "TypeError: …" → TypeError
Anything else stays a RuntimeError.
"""
if err.startswith("[Errno "):
try:
errno = int(err[7:].split("]", 1)[0])
except ValueError:
raise RuntimeError(err) from None
if errno == 2:
raise FileNotFoundError(err)
if errno == 13:
raise PermissionError(err)
if errno == 20:
raise NotADirectoryError(err)
raise OSError(errno, err)
m = re.match(r"Command '(.*)' timed out after (\d+) seconds$", err)
if m:
raise subprocess.TimeoutExpired(m.group(1), float(m.group(2)))
if err.startswith("KeyError: "):
raise KeyError(err[len("KeyError: "):].strip("'"))
if err.startswith("TypeError: "):
raise TypeError(err[len("TypeError: "):])
raise RuntimeError(err)
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 via capability_scoring's cs_free_string (same process)
try:
scoring = ctypes.CDLL(str(
_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so"
))
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
scoring.cs_free_string(ptr)
except OSError:
pass # leak-on-fallback only if capability_scoring absent; bounded
doc = json.loads(out)
if not doc.get("ok"):
_raise_like_oracle(doc.get("err", f"{sym} failed"), sym)
return doc["value"]
def propose_change(repo: str, branch: str, base: str, now: str) -> dict:
return _call("smod_propose_change", {
"repo": repo, "branch": branch, "base": base, "now": now,
})
def run_gates(worktree: str, test_cmd: list | None) -> dict:
return _call("smod_run_gates", {
"worktree": worktree, "test_cmd": test_cmd,
})
def compare_baseline(candidate_result: dict, baseline_result: dict) -> dict:
return _call("smod_compare_baseline", {
"candidate_result": candidate_result,
"baseline_result": baseline_result,
})
def promote_or_discard(repo: str, proposal: dict, verdict: dict) -> dict:
return _call("smod_promote_or_discard", {
"repo": repo, "proposal": proposal, "verdict": verdict,
})
def rollback(repo: str, rollback_ref: str) -> dict:
return _call("smod_rollback", {
"repo": repo, "rollback_ref": rollback_ref,
})
def monitor(proposal: dict) -> dict:
return _call("smod_monitor", {"proposal": proposal})
+51
View File
@@ -13,6 +13,11 @@ Three transforms, applied to BOTH cores' output at build time:
Same dependency laws as spine.py: structural_store primitives only, no
I/O beyond the classification/scaffold store reads, deterministic.
Dichotomy wave #2479 tranche: the deterministic transform surface is
ported to FULL Rust (src/rust/spine_language). This facade delegates to
the Rust core via rune/sl_loader when built; the Python implementation
below is the strangler fallback, pinned with RUNE_SL_CORE=python.
"""
from __future__ import annotations
@@ -26,11 +31,31 @@ from rune.structural_store import (
ROOT = Path(__file__).resolve().parents[2]
SRC_DIR = ROOT / "src" / "rune"
def _core():
"""The Rust spine_language core when available (strangler: Python
fallback is load-bearing). RUNE_SL_CORE=python pins the fallback."""
import os
if os.environ.get("RUNE_SL_CORE", "auto") == "python":
return None
try:
from rune import sl_loader
except Exception: # noqa: BLE001 — any loader failure means fallback, never a broken call
return None
try:
return sl_loader if sl_loader.available() else None
except Exception: # noqa: BLE001 — availability probe must never crash the fallback
return None
def _classification_sides() -> dict:
"""The measured dichotomy map (W0): module stem → RUST|GO|TUI-LAST|...
Loaded once per _place_languages call, best-effort — a missing or
unreadable classification.json degrades to {}/unassessed verdicts,
never to invented facts."""
core = _core()
if core is not None:
return core.classification_sides(str(ROOT))
try:
data = json.loads((ROOT / "store" / "rebase" / "classification.json").read_text())
return {name: rec.get("side", "") for name, rec in data.items() if isinstance(rec, dict)}
@@ -44,6 +69,12 @@ def _place_languages(doc: dict) -> int:
kind; the exact same rule construction_decision.language_placement
implements (kept inline here to avoid a per-entity FFI crossing for
the spine's own 218 entities; the FFI path is used in contracts)."""
core = _core()
if core is not None:
result = core.place_languages(doc, str(ROOT))
doc.clear()
doc.update(result["doc"])
return result["placed"]
GO_HINTS = ("server", "transport", "dispatch", "entry", "bridge",
"hook_engine", "provider", "watch", "notif", "voice",
"gitea", "event_", "message_", "tool_exec", "work_manager")
@@ -134,6 +165,15 @@ def _normalize_discovery_ids(doc: dict) -> int:
module records produced by the earlier discovery projector; leave all
other open-world entity classes alone.
"""
core = _core()
if core is not None:
# The gather goes through the module-level seam (tests monkeypatch
# _contract_discoveries to isolate the doc from the live store).
result = core.normalize_discovery_ids(
doc, str(ROOT), discoveries=_contract_discoveries())
doc.clear()
doc.update(result["doc"])
return result["renamed"]
renamed = {}
observed = {
eid for _, entities in _contract_discoveries()
@@ -167,6 +207,13 @@ def _apply_contract_discoveries(doc: dict) -> int:
spine learns what construction actually built. Doc-level transform,
applied to BOTH cores' output. The CANDIDATE law holds: only CLOSED
contracts write back."""
core = _core()
if core is not None:
result = core.apply_contract_discoveries(
doc, str(ROOT), discoveries=_contract_discoveries())
doc.clear()
doc.update(result["doc"])
return result["placed"]
placed = 0
for cid, entities in _contract_discoveries():
for eid, kind, provenance in entities:
@@ -189,6 +236,10 @@ def _contract_discoveries():
"""Closed contracts' DISCOVERED entities (issue #2465 addition 2):
expected_new_observed + candidate corrections, gathered from the
scaffold store. Returns [(contract_id, [(entity_id, kind), ...])]."""
core = _core()
if core is not None:
raw = core.contract_discoveries(str(ROOT))
return [(cid, [tuple(item) for item in items]) for cid, items in raw]
try:
doc = json.loads((ROOT / "store" / "scaffold" / "current.json").read_text())
except (OSError, json.JSONDecodeError):
Loaded 100 of 229 files, more files were not shown because too many files have changed in this diff. Show more