Language Maturity

Language Maturity
Section titled “Language Maturity”The rule: organic stuff is Python. Once it hardens, it rusts.
Every service in Sanctum starts its life as a Python script because Python is what you write while you’re still discovering what the service is. The curfew engine didn’t know it needed credits, or homework mode, or guest approvals, or a weekly digest, until the first time somebody’s kid found a loophole. Each of those features was a hot-fix-turned-design-decision, and doing that iteration in Rust — where every schema change costs an hour of borrow-checker yoga — would have meant shipping less, slower, and with fewer features your family actually needed.
But once a service has stopped discovering what it is, the game changes. Now the cost function flips: the rewards of Python (fast iteration) are irrelevant because nothing is iterating, and the costs of Python (no compile-time guarantees, requirements.txt drift, 400 MB of .venv, process that silently OOMs at 3 AM) are paid every single day. That’s the moment to port it to Rust, where the single-binary deploy, the compile-time catches, and the launchd-friendly lifecycle pay off forever afterward.
This page is the doctrine for that transition, plus the tool that tells us which service is where.
The Lifecycle
Section titled “The Lifecycle” organic ← Python, features │ still evolving ▼ hardening ← velocity slowing │ ▼ ready ← quiet for months, │ port scheduled ▼ graduated ← sanctum-rs/services/Services move left-to-right over time, usually slowly. A service can regress (a dormant service getting a new feature ticket moves from hardening back to organic), and that’s fine — the point of the doctrine is to not rewrite in Rust anything that’s still in motion.
Stage Definitions
Section titled “Stage Definitions”Each stage has a precise, testable definition. No vibes.
| Stage | Criterion | Verdict |
|---|---|---|
organic | ≥ 5 commits touching the main source file in the last 30 days | Keep iterating in Python |
hardening | 1–4 commits in the last 30 days, or activity in the last 90 days | Watch — shape still settling |
ready | 0 commits in the last 30 days and ≤ 2 commits in the last 90 days | Port to sanctum-rs/services/<name>/ |
porting | Rust source exists in sanctum-rs/services/, but the binary is not built or the deployed runtime is still the original (Python/Node) | Finish the build + cutover, then graduate |
graduated | Rust binary built and running as the deployed launchd unit on the target host | (terminal — precedent) |
skip | Criticality below medium | Stays Python forever — rewrite cost not justified |
The thresholds live in tools/rust_readiness.yaml and can be tuned per policy. They’re tuned to be conservative: “hardening” is the default verdict for anything that’s not firmly in either corner, which is the correct bias — do not rewrite what is still changing.
Graduation Criteria
Section titled “Graduation Criteria”Before a service actually gets ported, it must clear all four:
- Velocity floor. The
readysignal fromrust_readinessmust have held for at least 60 days. One quiet month can be a coincidence; two is a trend. - Criticality justifies the rewrite. Low-criticality scripts (a one-off backup utility, a fun demo) stay in Python. Rust buys reliability, and reliability has a price; spend it where it matters.
- Interface surface is documented. The HTTP routes, CLI flags, env vars, and on-disk state format must be written down (usually in this architecture section) before anyone opens a Rust editor. If you can’t describe it, you can’t port it.
- Behavioural parity tests exist. At minimum, a black-box HTTP/CLI test suite the Python version passes today — which the Rust version will also have to pass before it takes over the launchd slot.
Precedent — what has actually graduated
Section titled “Precedent — what has actually graduated”Two services are currently serving production traffic from a Rust binary: sanctum-watchdog and sanctum-proxy. Both are recent and both are worth being honest about.
sanctum-watchdog — Sanctum itself was first committed on 2026-03-14. A shell-script watchdog (~/.sanctum/scripts/watchdog.sh) appeared on 2026-03-20 as part of the Sanctum rebrand. The Rust implementation was imported into the sanctum-rs workspace on 2026-04-09 (commit 83a376a, “Import Mac Mini Sanctum Rust services” — meaning the Rust code already existed in another location before being consolidated). Within four days of the import, the binary absorbed the bandwidth monitor (84027b4) and had its self-health reporting normalised in stack checks (8a0ee66). The Rust process is the live sanctum-watchdog running on the host today.
sanctum-proxy — the cloud-tier model router on :4040. Live process is the Rust binary; deployed copy at ~/.sanctum/sanctum-proxy/sanctum-proxy was last replaced on 2026-04-18 during the smart-routing changes (the Brainy category, the general → council-mlx privacy reroute, the Opus-4.6 → 4.7 rename). Launchd dispatches via ~/.sanctum/scripts/proxy-launcher.sh.
The crucial observation isn’t a timeline — it’s that both rewrites were boring. No surprises, no redesign, no “while we’re at it” features. That’s what you want. If the Rust port is interesting, the service wasn’t ready.
Forward-looking: with Sanctum only weeks old, the doctrine on this page is mostly aspirational. The 30-day and 60-day windows in the readiness rules below assume a system mature enough to have 60 days of quiet for a service to point at. Most services here don’t yet. That’s a feature, not a bug — the rules will become biting as the codebase ages, which is exactly when you want them to bite.
The Current Registry
Section titled “The Current Registry”The source of truth is tools/rust_readiness.yaml. As of this writing:
| Service | Language | Criticality | Stage |
|---|---|---|---|
force-flow | Python | critical | hardening |
screen-time | Python | critical | organic |
memory-vault-mcp | Python | medium | hardening |
sanctum-watchdog | Rust | critical | graduated |
sanctum-proxy | Rust | critical | graduated |
sanctum-firewalla | Rust source / Node runtime | high | porting |
sanctum-tts | Rust source / Python runtime | medium | porting |
To get the live view, run the tool:
python3 tools/rust_readiness.py # pretty tablepython3 tools/rust_readiness.py --json # for the dashboardpython3 tools/rust_readiness.py --strict # exit 1 if any service is "ready" (CI gate)python3 tools/rust_readiness.py --find living-force # resolve an alias to its serviceServices can carry aliases in the registry to reconcile doctrine names with runtime names. The Living Force, for example, is documented on its own architecture page but executes as the Rust binary sanctum-watchdog — the alias means --find living-force lands exactly where it should, and the table shows the alias dimly under the service name so the connection is visible at a glance.
Tooling
Section titled “Tooling”tools/rust_readiness.py is a single-file Python script with no deps beyond PyYAML. It reads the registry, runs git log against each service’s source path in its declared repo, computes loc, and derives a stage per the policy in the YAML. Output is an aligned table when connected to a TTY (with stage colours) and JSON otherwise, so dashboards can ingest it directly.
The intention is for this to be:
- Cheap to run — no network calls, only local git and file reads. Takes under a second.
- Cheap to trust — the stage rule is three lines of code in
derive_stage(); there’s no ML, no heuristic stack, no per-service override magic beyond an explicitstage_override: graduatedfield for already-ported services. - Cheap to wire up —
--strictreturns exit 1 if any service isready, which means a nightly cron can page someone when a service quietly finishes cooking.
Future work: surface the table in the Holocron UI so the current state is visible at a glance, and emit a weekly “language maturity report” into Jocasta’s Monday digest summarising which services moved stages this week.
Why This Doctrine Exists At All
Section titled “Why This Doctrine Exists At All”Because the most tempting way to lose a weekend is to rewrite a working service in a language you like more. This doctrine is the speed bump between “I have a weekend” and “I have a production outage.” Every service that is still growing limbs is a service that isn’t ready. Every service that has stopped growing limbs is a service that will pay you back, for years, if you make it boring and ferric. The trap is “just one more turn” — one more refactor, one more Rust crate, one more abstraction that looks clean in the diff and breaks something invisible on the machine. The harness exists to tell you when the turn is actually free.
Python while the shape still moves. Rust once it stops. Watch the tool.