Commit is not a generation
Stored source becomes fieldable only after validation and publication.
- Source commit durable edit
- Validation parse + gates
- Generation published application
- Activation selected runtime state
2027 · MerTek technology
Engineering reference / System overview
Two update paths: live generations and staged nodes
MTK updates running software along two paths. Trident source and configuration changes publish a validated application generation that goes live between calls. Runtime and driver changes move through a staged node rollout, with every transition recorded and the previous generation kept for rollback.
Worked example
The Tulsa depot runs its readiness application on 140 nodes: handhelds, bench stations and two servers. On Tuesday the parts lead wants repair orders open more than 30 days to need a second approval. On Wednesday the platform team ships a runtime build with a barcode-scanner driver fix. The rule change must land in minutes; the driver fix must never take the whole floor down at once.
The engineer edits one module and checks it as a candidate. t.code.check runs the same compiler and verifier the host will run, so a candidate that fails here never becomes a generation.
chest candidate() {
chest row = {
text moduleId = "a.approval",
text source = "bool needsSecondApproval(u32 openDays) {\n return openDays > 30\n}\n"
}
chest[] modules = [row]
chest map = { u16 schemaVersion = 1, text entryModule = "a.approval" }
map.modules = modules
return map
}
text main(text[] argv) {
chest verdict = t.code.check(candidate())
chest[] faults = verdict.faults
if (faults.count() > 0) {
return "refused: " + text(faults.count()) + " faults"
}
return "ready to publish"
}The application asks its host for the replacement as a row in its result, never as a call. merge keeps every module the edit does not name, and adopt keeps live peer connections. The host compiles the whole program, installs it between calls, and keeps generation 184 as the rollback point.
chest replaceRequest(u64 generation, chest moduleMap) {
chest program = { text origin = "authored", u64 sequence = 0 }
chest? source = moduleMap
guid? connection = null
program.source = source
program.connection = connection
chest payload = { text compose = "merge", text connections = "adopt" }
payload.program = program
chest request = { text kind = "programReplace" }
request.generation = generation
request.payload = payload
return request
}The driver fix is a new runtime, so it takes the node path. Each node runs the release its tri.draft pins, and a runner of another version refuses at the argument stage. The runner also checks its own image bytes against the pinned digests. Five bench stations get the new pin first.
# The runtime release this node runs, and the runner images it accepts.
text release = "1.0.2"
sha256 releaseDigest = "a41c97e2b0d35f8861c4e2f09b7d3a6e5f1c8b2d4e6a7f9031c5b8d2e4f6a7b9"
sha256[] releaseRunners = ["0c9e4d1b7a3f5e2c8d6b4a1f9e7c5b3d2a4f6e8c1b3d5f7a9e2c4b6d8f1a3c5e"]A node installs a runtime as one complete generation and moves one pointer. The old generation stays on disk, so rollback moves the pointer back, and a running process keeps the generation it started with.
~/.mertek-trident-cache/
<sha256> # one blessed object: tricli, tri, or the launcher
releases/<sha256>/ # one release: its public manifest and its artifact set
generations/<sha256> # immutable descriptor: tricli, tri, launcher, release
pins/active # one atomic reference to a complete descriptor
trust/ # durable highest accepted signed-index generationBlue and green serve together. Pilot nodes on 1.0.2 and the other 135 on 1.0.1 share one wire. The handshake refuses only a different protocol identity, and each side reads any other difference as a fact. The application plan stays pinned by digest the whole time.
# The application plan this node runs, pinned by its digest.
# A substituted plan file refuses before any provider opens.
text plan = "/srv/depot/readiness/plan.chest"
sha256 planDigest = "5d1f0c3a9e8b7d6c5b4a3928170f6e5d4c3b2a19087f6e5d4c3b2a1908f7e6d5"Generation 185 went live at 12:06; a request in flight finished on 184, and the next one ran the new rule. The driver fix reached 5 nodes on Wednesday, 25 on Thursday and all 140 by Monday. When 9 handhelds of one model showed scan failures, the team paused the wave and moved them back to the previous runtime in one step.
System diagram
Application policy can move without replacing the compiled node. Runtime changes use staged rollout and rollback.
Inside the system
Follow the data, decisions, and access boundaries. Each diagram explains a concept used by applications.
Stored source becomes fieldable only after validation and publication.
A node update advances through durable states one transition at a time.
Current and incoming fleets serve together until the cutover is proven.
Both update paths begin from a recorded genesis version and configuration.
Key parts
These are the main boundaries, inputs, outputs, and failure rules. The examples show a specific use of each part.
Trident source and configuration change through a published generation: checked, compiled whole, then installed between calls. The retained state carries over, and the previous generation stays as the rollback point. A candidate that fails to compile changes nothing.
The second-approval threshold moves to 30 days on generation 185 while handhelds keep scanning.
A runtime or driver change installs as a complete node generation and widens in recorded waves: prepared, staged, serving, then expanded or rolled back. Each transition is a durable record, and rollback selects a retained generation instead of rebuilding one.
A scanner-driver fix reaches 5 bench stations, then 25 nodes, then 140. Every wave traces back to the depot's genesis baseline.
Old and new nodes serve together until the cutover is proven. The handshake refuses only a different protocol identity. Other differences are facts each side reads, per declared handler and per trove, never per build.
A handheld still on 1.0.1 keeps syncing with a server on 1.0.2, because every handler it calls kept its signature.
Code
Each example is short and complete enough to read on its own. Hover a dotted word for its definition.
A candidate that compares a u32 with 30.5 is refused before anything is published. t.code.faultText renders each fault exactly as the runner would, here an L.real refusal at line 2, column 23 of a.approval.
text report(chest candidate) {
chest verdict = t.code.check(candidate)
chest[] faults = verdict.faults
text out = ""
foreach (fault in faults) {
out = out + t.code.faultText(fault) + "\n"
}
return out
}
Compiling the same module map always gives the same digest. The generation a reviewer checked and the generation a node runs are provably the same program.
text digestOf(chest candidate) {
chest compiled = t.code.compile(candidate)
sha256 program = compiled.programDigest
text entry = compiled.entryModule
return entry + " " + text(program)
}
Under adopt, the new generation receives one peerAdopted event per live connection before any message reaches it. A replacement that declares no handler for a kept connection's events is refused at install, and the old generation keeps running.
chest main() {
chest state = {}
return state
}
on event("peerAdopted", const chest fact) {
text endpoint = fact.endpoint
t.log.info("generation 185 kept a connection on " + endpoint)
}
on start is the launch's vote: reaching the end of the block accepts the launch, and stop inside it refuses. on stop reads why the runtime ended, such as finished or stopped, and cannot veto it.
on start(const launch) {
on exit { t.log.info("start vote recorded") }
t.log.info("readiness node starting")
}
on stop(const details) {
text cause = details.cause
t.log.info("readiness node stopped: " + cause)
}
text main(text[] argv) {
return "serving"
}
A generation changes source and configuration. It never changes the application's id or its declared major, so every generation stays one application in the control plane's records.
# The application's identity, shared by every generation.
text id = "mertek.depotreadiness"
text name = "depotReadiness"
u32 major = 1
Uses
Review questions
Which changes are source and configuration, and which need a new runtime
How large the first wave is, and what stops a wave
How long old and new runtimes must share the wire