One type authority
The language and database accept the same declared types.
- Type registry one approved list
- Trident parser language declarations
- MerDB parser column declarations
- Refusal unknown spelling stops
2027 · MerTek technology
Core system / System overview
Application rules with units, bounds and named failures
Trident is the language MTK applications are written in. Every value states its type, every loop its pass count and every enum branch its members, and the compiler checks all three. A program reaches files, the network and devices only through the grant its host builds at launch.
Worked example
Ridgeline Power runs 14 substations, and each transformer's gateway reports top-oil temperature. Oil above 85 °C for 90 seconds means the on-call crew gets paged. Readings arrive as loose documents, two gateways can report the same substation, and the alarm service may reach exactly one paging endpoint.
Write the limits with their units. Oil temperature is a measure, a value that carries its unit and family (85degC:temperature), and the hold time is a duration. If a gateway sends a reading in millimetres, the comparison faults with a dimension error instead of treating it as the number 85.
const measure oilLimit = 85degC:temperature
const duration holdFor = 90s
enum Severity { Watch = 1, Alarm = 2, Trip = 3 }
Severity classify(measure oil, duration over) {
if (oil > oilLimit && over >= holdFor) {
return Severity.Alarm
}
return Severity.Watch
}Admit each reading with expect. A gateway document is a chest, so its fields are only claims until something checks them. expect checks every named field and its complete type before it binds any of them. A reading that fails takes the else arm, and that arm must leave. The when names all three severities, so adding a fourth stops the build at every when that does not handle it.
text admit(chest reading) {
expect { text station, measure oil, duration over } = reading else {
t.log.warn("reading refused: station, oil or over is missing")
return "refused"
}
Severity level = classify(oil, over)
return when (level) {
is Severity.Watch { use "log" }
is Severity.Alarm { use "page" }
is Severity.Trip { use "page and shed load" }
}
}Scan the replay with a stated bound. After a 90-second link drop, a gateway replays up to 600 buffered readings. repeat fixes its pass count before the first pass, and Trident has no while, so the scan ends when the buffer does.
i32 firstHot(measure[] backlog) {
i32 found = -1
repeat (backlog.count(), i32 i = 0) {
measure reading = backlog[i]
if (reading > oilLimit) {
found = i
break
}
}
return found
}Page once per substation, under a deadline. The host delivers each alarm to the on event handler as one bounded call. lock? holds the key page:SS-07 under an exclusive policy, so the second gateway's alarm takes the else arm instead of paging again. The page itself runs in a task with a 2-second deadline; a timeout or a refused request runs the task's own else arm, and the handler carries on.
void pageCrew(text station) {
chest policy = t.sync.policy.exclusive(0ms, 10m, false)
lock? ("page:" + station, policy) {
task (duration = 2s) {
buffer body = t.buffer.fromText(`station={station} reason=oil`)
chest reply = t.net.http.post("https://pager.ridgeline.example", "/v1/pages", [], body)
} else (e) {
text why = e.message
t.log.error(`page for {station} failed: {why}`)
}
} else {
t.log.info("page already open for " + station)
}
}
on event("OilAlarm", chest alarm) {
expect { text station, measure oil, duration over } = alarm else {
return
}
if (classify(oil, over) == Severity.Alarm) {
pageCrew(station)
}
}Run it under a grant. Every library call goes through the grant the host builds at launch, and the application's _grant/grant.draft can only narrow it. The compiler derives every library the program calls, so a grant that revokes outbound HTTP stops the alarm service before main runs and names the call that needed it. The real service revokes only process launch.
$ cat oilalarms/_grant/grant.draft
u16 schemaVersion = 1
text[] revoke = ["t.net.http", "t.proc"]
$ tricli oilalarms
tricli: lend: revoked: t.net.http
tricli: a/ reaches t.net.http.post, and _grant/grant.draft revokes t.net.http
tricli: falseStart: grantThe service pages each substation's crew once, gives up on a page after 2 seconds, and logs every refused reading by name. The units, the branches and the loop bounds are checked when the program compiles, and the grant is checked before main runs. Nothing in the source assumes a unit, a loop end or a network route that it does not state.
System diagram
The node view and script view edit the same program. The branch, task budget, error path, and lock are visible before the workflow runs.
Inside the system
Follow the data, decisions, and access boundaries. Each diagram explains a concept used by applications.
The language and database accept the same declared types.
A call must pass each namespace and implementation boundary.
Temporary work is bounded and failure is catchable.
Both engines must produce the same typed result.
A failure carries a code, message, origin, and retry state.
Scenario choices stay visible; reusable compute stays shared.
Key parts
These are the main boundaries, inputs, outputs, and failure rules. The examples show a specific use of each part.
A binding states its type, and a computed value never converts on assignment. Units, time domains, currencies and nullability are part of the type, so the compiler checks them like any other mismatch.
i64 total = count refuses and names i64(count). A speed compared with a bare f64 refuses and names the call that strips the unit.
A loop states its pass count before it starts. A task states its deadline or memory budget, and a lock policy states how long to wait and how long to hold. Work that runs until someone stops it belongs to a session's frame calls, not to a loop.
The replay scan is repeat (backlog.count(), i32 i = 0), and the page runs under task (duration = 2s).
A program names libraries; the host decides which ones it lends. The compiler derives the list the program reaches, and the launch stops if the grant does not cover it. A library the grant leaves out is absent from the program's catalog, so a call to it refuses before any argument is evaluated.
Revoking t.net.http stops the alarm service at launch with a/ reaches t.net.http.post, and _grant/grant.draft revokes t.net.http.
Code
Each example is short and complete enough to read on its own. Hover a dotted word for its definition.
A literal reads at its destination's type, and a unit travels inside the value or the type. 1h30m prints as 90min because a duration keeps one exact amount.
const i32 stations = 14
const duration holdFor = 90s
text main(text[] argv) {
i64 readings = 0 # the literal reads at i64
f64<family=velocity> vibration = 4.2mm/s
measure oil = 91.5degC:temperature
duration handover = 1h30m # prints as 90min
return text(handover)
}
# i64 total = stations refuses and names i64(stations)
# duration billing = 1mo refuses: a month is a calendar span
foreach walks a snapshot of the array, and repeat takes its pass count first. A while refuses and names repeat, foreach or a session's frame as the spelling to use.
text main(text[] argv) {
text[] stations = ["SS-01", "SS-02", "SS-07"]
i32 checked = 0
foreach (station in stations) {
checked++
}
i32 attempts = 0
repeat (5, i32 attempt = 1) {
attempts = attempt
if (attempt == 3) { break }
}
return `{checked} stations, {attempts} attempts`
}
A when over an enum names every member and takes no else. Add Flood = 4 to Severity and this function stops compiling with "no arm names Flood".
enum Severity { Watch = 1, Alarm = 2, Trip = 3 }
text crewAction(Severity level) {
return when (level) {
is Severity.Watch { use "log only" }
is Severity.Alarm { use "page on-call crew" }
is Severity.Trip { use "page crew and shed load" }
}
}
text main(text[] argv) {
return crewAction(Severity.Trip)
}
A plain read of a missing field faults while the program runs: partial.oil raises absentField. expect turns the same gap into an ordinary branch the author wrote.
text describe(chest reading) {
expect { text station, measure oil } = reading else {
return "incomplete reading"
}
return station + " at " + text(oil)
}
text main(text[] argv) {
chest partial = { text station = "SS-09" }
return describe(partial)
}
A fault carries a reason, a message and the call chain. The handler reads the reason as a member of the system enum fault and decides whether to retry, resume or fail.
measure parseOil(text raw) {
if (raw == "") {
fail "empty oil reading"
}
return 0degC:temperature
}
text main(text[] argv) {
text trail = "start"
on error (caught) {
fault why = caught.reason
text message = caught.message
when unguarded (why) {
is language { trail = trail + " | " + message }
}
resume
}
measure oil = parseOil("")
return trail
}
A task runs its block in its own fault region. It may narrow the deadline, the scratch memory or the call depth it inherits, never widen them, and a fault inside runs its else arm once.
text main(text[] argv) {
text trail = "ok"
task (duration = 50ms) {
i32 zero = 0
i32 lost = 1 / zero
} else (raised) {
trail = "task faulted; the call goes on"
}
task (scratchBytes = 65536) {
text[] lines = ["SS-01 ok", "SS-07 hot"]
} else (raised) {
trail = "over budget"
}
return trail
}
The policy states how long to wait and how long the lease lasts. lock releases however its block ends. lock? never waits on a busy key: its else arm runs instead, with no fault.
text key = "ledger"
chest policy =
t.sync.policy.exclusive(0ms, 1s, false)
lock (key, policy) {
# held; released however it ends
}
lock? (key, policy) {
# held
} else {
# busy: no fault, this arm runs
}
Uses
Review questions
Which rules need units, time domains or currencies in their types
Which loops, calls and locks need written bounds
Which libraries each application should be lent, and which revoked