Changelog¶
Repository changelog rendered inside the searchable docs site.
For upgrade planning, read Version History,
then open the affected Install, Program, Run, Hardware, or Reference
page.
Repository Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
[Unreleased]¶
[0.33.0] - 2026-11-06¶
Added¶
- W010 recognizes a body-less library stub that declares it reads the host clock: the
{attribute 'reads_host_clock'}pragma (specification 07, "Declared Clock Sources") makes the stub's call sites warn, and the message names the declared source instead ofCURRENT_DT. The stub's own file stays silent, and the pragma-attribute parser now accepts CODESYS-style flags that carry no value (issue #264). - Under the CODESYS conversion profile, the round-2 conversion families of
DEV-036:
<ANY_INT>_TO_TIME(a millisecond count),REAL_TO_TIMEandLREAL_TO_TIME(milliseconds truncated toward zero, negative counts saturating toT#0ms),DWORD_TO_TOD(the ms-of-day magnitude modulo 24 h), and string-to-integer/bit-string conversions (typedSTRING_TO_*and overloadedTO_*) with the one CODESYS parse rule: leading spaces, an optional sign, a decimal or2#/8#/16#literal, trailing characters ignored, no number yielding0, out of range keeping the low-order bits. The values are measured on a live e!COCKPIT runtime and recorded in specification 07 section 2.10.
Fixed¶
- Under the CODESYS conversion rules,
TO_STRINGandTIME_TO_STRINGof aTIMEor anLTIMEgive the text the CODESYS runtime gives, measured on a WAGO PFC200: each unit that is not zero, as inT#1s500ms,T#2dandLTIME#1h2m3s4ms5us6ns, andT#0msfor no time. A compiled program refused to build them, and the evaluator wroteT#1500ms(specification 07 §2.7). - The status bar no longer says a runtime is connected while the side panel says "Not connected" for the same runtime. A runtime that answers without a debug session is "Not connected" on both until Connect attaches one, and a runtime that keeps debugging disabled is "Debugging disabled" in the side panel too, as in the status bar and Live Values (specification 25 §5.1, §6.9).
- Live Values now names the project's own runtime "This project's runtime", as the side panel and the
status bar do. Before, it called that runtime "Runtime at
" after a Start from Live Values or Devices & Connections, or after a Connect that kept debugging disabled (specification 25 §6.9).
[0.32.0] - 2026-11-06¶
Added¶
trust-dev agent serve-mcpserves the Agent API v1 as MCP tools over the platform's shared MCP connection layer (trust_lsp::mcp): a 1:1 tool mapping of the contract's methods (dots become underscores), advisory generated input schemas, contract errors as error tool results whose text carries the stable{code, message, data}object, sequential serving with one shared harness session per instance, and no configuration beyond the--project/--bundleflag. The binding is specified in specification 20 §8 anddocs/guides/AGENT_MCP_BINDING_V1.md.runtime.build,runtime.validate, andruntime.compile_reloadaccept an opt-inissues_limitcap, so large projects stay under agent hosts' message limits. An applied limit truncatesissues[]to the first entries of the deterministic order, bounds the failed-build error text and the compile_reload build-failureruntimeMessage, and addsissuesTruncatedandtotalIssues. An omitted limit preserves the whole-list responses.
Changed¶
- Devices & Connections: selecting a simulated plant opens it in the inspector, like every other runtime child, instead of opening its setup editor. The inspector shows the plant's exact ID, every state the runtime reports (not only the first), its model, its command and feedback mapping in order with live values when there are any, its parameters, and its saved configuration apart from the running one. Configure plant opens the setup editor, and Open in Control Studio is offered in both (specification 47 §5, §6.1).
- An HMI agent simulator journey reads its draft in the language server. A draft at another revision, or a program
that changed since the draft was composed, ends the journey before its first step with
CONFLICTin its answer, and a program that does not compile withCOMPILATION_FAILED; the journey no longer loads the project's render context first (specification 43, AT-08). - W010 tracks clock sources instead of declarations (issue #250). A TIME, LTIME, DATE, LDATE, TOD or DT declaration no longer warns. A call of CURRENT_DT() warns, and so does the call site of a function, method, function block or program whose body reaches one, directly or through the POUs it calls; the message names the source: "CURRENT_DT reads the host clock, which replay does not control". TIME() and what is built on it (T_PLC_MS, T_PLC_US, RTC_MS, RTC_2, the timers) stays warning-free, as does a value whose source cannot be traced, such as a body-less library stub (specification 28 section 5).
Removed¶
- The bytecode engine's built-in import contracts, with the stable error code
runtime_import_unsupported. A compiled program image imports nothing, because the program library is compiled into it, so a device checks no import contract; a deployment record that lists an import is refused as a record of another program (specification 34 §5.2.1).
Fixed¶
- On a Mac with Apple silicon, a compiled program could compute a wrong value or crash at random, more often the
busier the computer. Functions of the program library that a program links kept values in the processor register
x18, which macOS may overwrite when it switches threads; among them were the functions behind
CONCAT,FIND, numbers as text and the trigonometric functions. The library now leaves x18 alone, as the program's own code already did (specification 44 §4.4). - Under a CODESYS
vendor_profile,TO_STRINGof an enumerated value gives the value's name in a running program, as specification 07 says; it gave the number ('1'forColor#Green). - Under a CODESYS
vendor_profile,TIME_TO_<integer>,LTIME_TO_<integer>,TOD_TO_DWORD,LTOD_TO_DWORDandTO_STRINGof aBOOLbuild and run; the build refused them as not compiled yet. A duration's count that does not fit its integer keeps the low-order bits (TIME_TO_SINT(T#300ms)is44), as specification 07 says, where the runtime's evaluator stopped with an overflow. - Completion and hover know
DIFU,DIFD,TP_TIME,TON_TIMEandTOF_TIMEas standard function blocks. They show the standard documentation, and thestdlibsetting oftrust-lsp.tomlnow applies to them:nonehides them like every other standard block, andiecoffers the_TIMEtimers but not Mitsubishi'sDIFUandDIFD, which are not IEC names (specifications 08 and 14). - The HMI authoring MCP server (
trust-lsp hmi-agent) refused a tool call withoutarguments, which MCP allows for a tool that takes none. It now runs the call with no arguments (specification 43 §3.1). - When the simulated plants could not be read, for example after a session timeout or a plant file that changed between two reads, Devices & Connections dropped every plant from the canvas until the next good read. It now keeps the last saved plants, shown as Status unavailable without live values (specification 47 §20.6).
- Live Values shows a failed Start or Stop, a debug session that did not launch and an unreadable settings value as errors even when their text holds no word like "failed", and keeps the runtime's access reason in view over the active-force summary: each message's colour comes from where it comes from, never from its words (specification 25 §4.2).
- Live Values keeps the runtime's name and endpoint in its header when its debug session ends or another debug session becomes active, instead of showing an unnamed target; a lost-connection or debugging-disabled message goes once live values arrive again; and a release on a target that stays armed keeps saying so instead of disappearing after 5 seconds (specification 25 §4.2, §4.4).
- An HMI agent's cancellation that reaches VS Code before its tool call no longer gets lost: the call then starts cancelled and its tool answers at once, instead of running to its end (specification 43 §3.1).
- Under the CODESYS profile,
ADR(STRING[n])at the string's base now binds a plainPOINTER TO BYTEformal (or a byte-pointer variable target) with the DEV-039 pointer-binding warning instead of an error, matching the vendorMemCopycopy-loop idiom. Every other pointee mismatch keeps its error. - Clients that send a running runtime a new request as soon as one is answered no longer hold its cycles back. Between two cycles the scan thread serves the commands queued when it begins to serve them, at most 1,024, and a command queued meanwhile waits for the next boundary. Before, it served commands until the queue was empty, so a steady stream of requests, such as three readers of the program's variables on a busy Windows machine, could keep any cycle from running (specification 11 §6.9).
Security¶
- While the language server trace logs each message's parameters (
trust.languageServer.traceatverbose, ortrust-lsp.trace.serveratverbose, atcompactor in thejsonformat), the extension refuses every command that would carry a credential to the language server, before anything is sent, and says so once a minute with the setting to change: - an HMI deployment, a run target's connection and Live Values with their runtime token;
- Devices & Connections with its runtime token or a protocol form's password;
- the HMI users editor with the project's
runtime.toml; - a simulator journey's session and its cookie;
- a new simulator's control token. A simulator launch then stops instead of starting without a token.
Before, only a simulator journey was refused, and the others reached VS Code's log folder. A language-model tool
never sends or receives such a command. The language server names these credentials in its
initializeanswer (specification 25 §6.4).
[0.31.0] - 2026-11-05¶
Added¶
lsp.diagnosticsof the agent API takespageandpage_sizetogether to bound the listed issues; a page reportspage,pageSize,totalIssuesandtotalPages(specification 20).
Changed¶
- An HMI agent simulator journey no longer takes a method command (
invoke) as a step, stops before its next command when the target does not say it runs in simulation, and is refused while the language server trace logs each message's parameters (verbose, or the JSON trace format), which would log the named session's cookie. Changing the selected run target ends the simulator sessions (specification 43, AT-08). - A release publishes the packages that its pull request's rehearsal built and tested when the merge commit on
mainhas the rehearsed head's tree, as soon asmainhas the commit. Before,main's CI tested the same tree again for about two hours and the release built every package again for about one and a half (0.29.0 and 0.30.0). Each rehearsal keeps the release's files asrelease-bundle-v<version>-<tree>for 30 days; a promoted release runs the real-database matrix on its commit and checks every file against the rehearsal'sSHA256SUMSbefore it publishes. A pull request's or merge group's code never runs on the project's own runner, and a manual rehearsal runs the code of the ref a maintainer chooses. A merge commit with another tree is released aftermain's CI, building every package, as before.release-provenance.jsonrecords the released commit's tree, whether the packages were promoted, and the rehearsal run that built them (specification 24, "Release after main CI").
Fixed¶
- An HMI page whose control declares a
pulseortimeoutabove 600 seconds is refused by the page check (specification 39). Before, a time of years could panic the runtime's command handling and keep one of the followed-command slots for good, so that after 64 such commands no confirmation or remote pulse end was reported. A followed command now gives its slot back however its follow ends. trust-dev agent servereports a project's diagnostics as the language server does.lsp.diagnosticsandruntime.compile_reloadnow give the same findings astrust-lsp check, from the project'strust-lsp.toml: vendor profile, include paths, libraries and severity overrides. Before, they analyzed the project's folder under the IEC rules without its libraries, so a CODESYS project showed narrowing stores as errors and every symbol of a library elsewhere as undefined (#236). The agent's analysis writes no index cache and fetches no dependency, and apathorsources_rootthat leaves the project, or names none of its source files, is refused.- An inherited function-block member used as an array subscript no longer reports E101
"undefined constant". The index-bounds check folds only compile-time constants; for a
name it cannot fold it asked the lexical scope whether the index is a computed value,
and a member declared in a base function block (
fbChild EXTENDS fbBase, indexed by aVAR_INPUTof the base) resolved in neither, so a statement likegvlLog[uiEntityIdx] := 1on the derived block reported an undefined constant while the identical statement with a local member stayed silent. The guard now also asks the enclosing POU's EXTENDS chain — under both dialects, since function-block inheritance is IEC 61131-3 Ed.3 §6.6.3.4 — and a genuinely unknown name keeps E101 (specification 09 §3.8). - PLCopen import and export say that trust-runtime was not found only when Node cannot start it at all (
ENOENT). A launch failure whose text only said "not found", such as a trust-runtime the user may not run, was reported as a missing trust-runtime instead of with its own text (specification 25). - The debugger, the console (
trust-runtime ui) andtrust-dev ctlsend each control request to the runtime in one piece. They wrote the request's newline as a write of its own, which the TCP stack held until the runtime acknowledged the request: about 40 ms a request from Linux, and up to 200 ms from Windows, where every control connection is TCP. A debug stop makes several such requests (specification 11). - An HMI agent simulator journey no longer reports a step as observed when its readback path is one the running program does not have, which the runtime answers as unknown; the step now times out (specification 43, AT-08).
- An HMI agent simulator journey step that commands a control without a confirmation, such as a button without feedback, is observed once the runtime applies the command; it always timed out before (specification 43, AT-08).
- An HMI agent simulator journey step waits for its control's own confirmation time, so a control that confirms later than 5 seconds is no longer reported as timed out, and it ends with the runtime's own outcome and reason when the runtime reports the command timed out, refused or unknown (specification 43, AT-08).
- Devices & Connections shows the saved plants of a stopped runtime as
Configured, orDisabled, as specification 47 §5.2 defines a plant saved without runtime evidence. Before, every plant of a stopped runtime showedStatus unavailable, a state meant for a runtime whose live status cannot be read (specification 47 §20.6). - The runtime answers each control request at once. It wrote the newline that ends a reply as a write of its
own, and the TCP stack held that newline until the client acknowledged the reply: about 40 ms a request
from Linux, and up to 200 ms from Windows, where every control connection is TCP.
trust-runtime ctland the fleet status request send their request lines in one piece for the same reason (specification 11). - The HMI Builder's My equipment lists a project's own function blocks when a library folder holds the
project, as
Plant = { path = "../.." }holds each Plant example. Before, every file of such a project was taken for the library's, and the list was empty. A library inside the project still keeps its blocks out (specification 39 §21.9, by the deepest-folder rule of specification 14 §7.6).
[0.30.0] - 2026-11-03¶
Added¶
- The
VOIDreturn type of a function, under the CODESYS profile:FUNCTION MemCopy : VOIDdeclares a function without a result, as the vendor libraries declare it (WagoSysPlainMem.MemCopy). The function carries no return slot, a call captures no result, and the body may exit early with a value-lessRETURN;(specification 04 section 2,docs/IEC_DEVIATIONS.mdDEV-043).
Changed¶
- Library files no longer receive the unused-symbol warnings W001, W002, and W009, in the editor
and in
trust-lsp checkalike: a library is API surface that an application only uses, and it is judged in its own project, where its files are that project's own sources and every warning is on. For each file, the deepest folder that holds it among the project's own source folders (itsinclude_paths, or the project folder) and its library folders ([[libraries]],[project] library_paths, and resolved[dependencies]packages) decides, and an own folder wins over an equally deep library folder. A dependency outside the project folder, as in the OSCAT examples, is diagnosed with the project's settings. Every other diagnostic stays on for library files (specifications 14 §7.6 and §7.6.4, 28 §1). - Under the IEC rules, a function declared
: VOIDis now an error (E207): IEC 61131-3 has noVOIDdata type. Before, every profile accepted it. A project that uses it either selects the CODESYS profile (vendor_profile = "codesys"intrust-lsp.toml) or declares the function without a result type (FUNCTION LogMessage, specification 04). - An unused global constant now warns (W001) wherever it is declared, also at the top level of a file. Before, only one declared in a NAMESPACE, a CONFIGURATION or a RESOURCE warned (specification 28 §1, decided by the owner on 1 November 2026, as CODESYS's static analysis reports a project's unused global variables).
- A trend's Export saves the visible window as the runtime writes it from its trend history, as TwinCAT and CODESYS export from their recorders: a sample a second for the last hour, then each minute's lowest and highest value. Before, the page wrote the file from what it had drawn (specification 39 §5.9, decided by the owner on 1 November 2026).
Fixed¶
trust-lsp checkreports the diagnostics of the project'strust-lsp.toml, as the editor shows them: a broken library or dependency configuration (L001–L007) now fails the check. Before, the check looked only at the source files, so a CI gate passed a configuration the editor marked as an error (specification 14 §7.7, issue #213).- A function block invocation under the CODESYS profile binds positionally to its input and
in-out formals only, in declaration order; an output formal binds only by name with
=>, so vendor calls such asfb(1, 2)on a block with an output formal compile. Functions and methods keep the full positional association, and the default rules are unchanged (IEC_DEVIATIONS.mdDEV-044; specification 04, "Function Block Call (Table 42)"). - A global constant that a program uses through its
VAR_EXTERNALdeclaration is no longer reported as unused (W001).
[0.29.1] - 2026-11-03¶
Changed¶
- An HMI switch sends
togglewith the value it shows, and the runtime writes the opposite, so the value the operator saw decides. A switch, selector, mode selector or slider that shows no value is now refused by the runtime with its reason, which goes to the audit log like every other command; before, the page kept that refusal to itself (specification 39 §5.8, decided by the owner on 1 November 2026).
Fixed¶
- A local, a parameter or a member of a POU named like a global namespace or program now hides it in a dotted name,
as local names shadow global names (specification 09 §1.3). Before,
GVals.Memberread the namespace'sMembereven with a localGVals: a running program read the wrong variable, and the editor reported neither a wrong type nor the namespace member's unused warning (issue #203). - The side panel shows a project runtime that stops within two seconds of going online as not running. Before, it stayed shown as answering until something else refreshed the panel.
[0.29.0] - 2026-11-02¶
Changed¶
- Interface and abstract-method conformance diagnostics move from
E202to the new dedicated codeE208("must implement method/property", signature and visibility mismatches, missing abstract implementations). Type-used-as-value, access-specifier and initializer-constraint diagnostics keepE202, so per-code severity overrides no longer mask interface completeness regressions.
Fixed¶
- Saving a simulated plant whose actuator is on-off (
kind = "on-off") no longer deletes the actuator fromsimulation.toml. Before, reopening and saving such a plant, even unchanged, removed the whole actuator table. The plant editor now keeps the actuator's kind and its minimum on and off times, which it does not edit, and takes its own actuator fields from what you entered. ADDwith the duration first, such asADD(T#1H, startTime)for aTIME_OF_DAY, is now reported as an invalid argument where it is written. IEC 61131-3 Table 35 listsADDwith the time of day or date and time first, as the+operator already required. Before, the editor accepted it and the build then failed with a message that called the addition a comparison.- Devices & Connections shows a simulated plant as
Waiting for samplesuntil its simulation has advanced. Before, it showedRunning, with the plant's starting values as live values. - On a runtime that answers, a plant the runtime does not run is no longer
Status unavailable. A disabled plant showsDisabled, and a plant saved after the runtime started showsRestart required. - The canvas no longer hides every simulated plant when one saved plant lacks its model or name. Only that plant is left out.
- Passing a fixed-size array to an
ARRAY[*]parameter no longer reports an implicit conversion, in every parameter direction: the binding aliases the argument's storage for the call (varying arrays) and converts nothing. Element mismatches and concrete bound mismatches keep their errors (specification 28 §2, IEC deviations DEV-040). - A new state in the Statechart editor gets a name no other state has. Before, Add state named it after the number of states, so after a deletion it could repeat a name, and saving the chart kept only one of the two states, losing the other with its transitions. Saving now also refuses, with the reason, a chart in which two states share a name or more than one state is initial, instead of keeping one of them (specification 25 §11.4).
- Deleting an element in the Ladder editor takes it out of the rung's logic, as CODESYS and TwinCAT do: its wires go with it, a series element closes the gap and a parallel leg made of the element alone leaves the other legs (specification 25 §11.4). Before, the wires to a deleted element stayed in the file and kept the power from the node they ended at, so a coil behind a deleted contact never switched on, and the implicit local of the deleted contact's operand stayed declared.
- Programs that the analyzer accepts now also build in these forms, which the build refused with an error that named no line:
- an integer constant expression whose exact value fits its declared type folds exactly, so
LOG_LOCAL0 : INT := 16 * 8no longer overflows a narrow intermediate (IEC deviations DEV-038); - under the CODESYS profile, an empty named argument (
RST := ,,Q => )) omits the argument: an input keeps its stored value or takes its declared default, and an output stays unconnected (DEV-041); - a qualified enumeration member in a global initializer (
Namespace.EnumType.Member,EnumType.Member) and, under the CODESYS profile, a constant named through a library container its stub flattened (LogParameter.MAX_LOG_MSG_LENin a string capacity) (DEV-042); - a
TIMEvalue added to aTIME_OF_DAY(base + T#1H), which IEC 61131-3 Table 35 defines (item 2b). - Ladder's Find and replace and the Statechart's Add and Delete action mapping did nothing: they asked through browser dialogs, which VS Code's webviews ignore. They now ask in VS Code's input boxes and warnings, Escape cancelling, and the language server refuses a new action's name that is blank or already in use (specification 25 §11.4).
- When Live Values' I/O state request times out, the panel clears its values and says that it lost the connection to
the runtime. Before, the old values stayed under
I/O state request failed: A stale runtime or debug session blocked startup., a start's message; other failed requests could be told as a start's failure too, such as a missing runtime binary or a workspace that is not writable. A failed request now names its own reason, and the debug adapter'sno program runs: launch or attach firstasks to Start or Connect like any missing session (specification 25 §4.4). - A runtime that keeps debugging disabled keeps the name Connect gave it, such as
Cpu, in the status bar, in the side panel's Target and in HMI Live. Before, the status bar and the side panel named it by its address (127.0.0.1:<port>) and HMI Live by its whole endpoint (tcp://127.0.0.1:<port>). HMI Live now names every runtime as the status bar does, the project's own runtime asThis project's runtime(specification 25 §6.9, specification 39 §11 item 6). - Declaration sizes over flattened library containers: under the CODESYS
profile, a dotted size reference (
STRING(LogParameter.MAX_LOG_MSG_LEN),STRING(WagoTypesLog.LogParameter.MAX_LOG_MSG_LEN)) resolves when the named GVL level was flattened into namespace-level constants — the reference's last segment must match exactly one constant or initializer-backed declared value table-wide, in the string-capacity and array-bound contexts only. Ambiguous leaves and references rooted at a scope-local value stay rejected; bare names never take the tolerance, and ordinary initializers keep the strict rule (IEC_DEVIATIONS 2026-10-25). The analyzer and the runtime harness fold the same value, so documentation-faithful sizes (128/20/40-class stub constants) compile at their declared capacities instead of failing the compile. PLCopen import/export passes symbolic string lengths through verbatim (mirroring array dimensions) instead of failing POUs closed at import.
Security¶
- Deploy and Roll back in the HMI Builder follow no redirect from the runtime's web server, so the runtime's control token is sent only to that web server, as in Live mode (specification 39 §16.2 item 8). Before, the extension followed a redirect with the token.
[0.28.0] - 2026-11-01¶
Fixed¶
- The help under a hardware terminal that no PLC signal fits, in Devices & Connections, suggests an address the runtime
binds the terminal's type to:
%ID0for aTIME,%IW0for aWCHARand the byte address%IB0for aSTRING. Before, it suggested a long-word address such as%IL0, which the runtime refuses for these types, and it did the same for aDATEor a structure, which now get the advice without an example (HMI component catalogue §8.1). - Cancelling Identify while it waits for a loop in Auto to settle always stops before the loop is switched to Manual. Before, a Cancel that arrived during the wait's last reading could still let the switch happen; the mode was returned afterwards (specification 35, B3.4).
- Identify's wait for a loop in Auto to settle keeps the readings its window judges and fewer older ones than those, so a long wait no longer grows the engine's memory: a 24-hour campaign of a 16-member PID group held about 84 MiB (specification 35, B3.4).
- Identify's waits for a steady output and for a loop in Auto to settle say that they are measuring the rate until twenty readings measure it. Before, their first progress said "changing 0 °C/min" while the temperature rose (specification 35, B3.4).
- Implicit-conversion warning: a derived DUT converted to its own ancestor
(directly or transitively) no longer emits
ImplicitConversion— the conversion is a well-defined base-part-copy upcast that assignment compatibility already accepts. Conversions between DUTs without an ancestor relation keep the warning. - Unused-symbol analysis: input parameters of a method that IMPLEMENTS an
interface method no longer receive a false
W002unused-parameter warning — the implementing POU must carry the full interface signature even where its body ignores a pin. Methods outside every implemented interface keep the warning. - Unused-symbol analysis:
EXTENDSandIMPLEMENTStargets count as used — a function block referenced only as a base class (or a DUT/interface only inherited) no longer receives a falseW009unused warning, while the never-instantiated derived declaration still does. - Unused-symbol analysis: qualified value access chains (
GVals.Member, dotted declaration-size references) now mark the TERMINAL symbol used. The parser models these as nested field-access nodes with a plainNameterminal, which the unused-symbol collector never resolved — constants referenced only through qualified cross-file uses (expressions,STRING[GVals.Len]sizes) were falsely reported asW001unused constants. Genuinely unreferenced constants still warn. - PLCopen GVL import: members that a dropped same-named
constant="true"mirror list re-declares keep constant-ness — the merged list renders a separateVAR_GLOBAL CONSTANTsection for them (PLCO226info) instead of flattening them to plain variables, so mirror semantics match the vendor project (PLCOPEN_DECISIONS 2026-10-26). - Enumerated values convert to their declared integer base in assignments,
comparisons, call arguments, and CASE labels — including base-less
enums (
INTbase) — and dotted enum member access accepts any namespace depth (WagoTypesLog.eSysLogPriority.LOG_ERR), closing the E202 "cannot be used as a value" class on real CODESYS projects. Integer assignment into base-less ("closed") enums, cross-enum assignment, and unknown members stay rejected (IEC_DEVIATIONS 2026-10-26). - Qualified type references resolve case-insensitively through the
cross-file project type catalog: a consumer spelling a namespace-
qualified function-block type with different casing than the declaring
file (
WagoAppJSON.FB_JSON_Writer_02over a stub declaringFb_JSON_Writer_02) now resolves the type and can invoke the instance (fbInst(...)), instead of silently failing the type lookup and reporting "not callable" at the call site. ST identifiers are case-insensitive; the catalog now keys and matches like the symbol table. - Call binding:
VAR_IN_OUTstrings bind across declared capacities (same string family only), matching CODESYS and the vendor buffer idiom of aSTRING(1)formal whose real capacity arrives in a separate size argument (WagoAppString.StrAppend). The previous capacity-equality rule rejected vendor-library calls that compile clean in the vendor toolchain; seeIEC_DEVIATIONS.md(2026-10-28). - Integer/bit-string narrowing (assignments and call arguments) becomes a
warning instead of an error under
[project] vendor_profile = "codesys", matching the CODESYS compiler warning, andSIZEOFyields its natural unsigned magnitude (UDINT) there so vendorudiSizeparameters bind directly. Other profiles keep the error and the signed result; non-integer mismatches keep their errors under every profile; seeIEC_DEVIATIONS.md(2026-10-28). - Comparisons and bit-string arithmetic match CODESYS: under
[project] vendor_profile = "codesys"signed/unsigned integer pairs, integer/bit-string pairs, and two bit-string operands in+ - * /are admitted and evaluate through a common magnitude (the runtime promotes; bit-string results carry the wider width and wrap). MOD on bit-strings, mixed integer/bit-string arithmetic, integer/REAL accuracy-loss combinations, and all of these shapes under other profiles keep the strict IEC rejections (IEC_DEVIATIONS 2026-10-28). - Standard conversions, under a CODESYS
vendor_profile: vendor-extension entries match the CODESYS/TwinCAT toolchain (the IEC dialect keeps rejecting them) —TO_STRING/*_TO_STRINGover enumerated values (variant name),BOOL('TRUE'/'FALSE'),TIME/LTIME(T#/LT#text), and same-family strings (identity);TIME_TO_<int>/LTIME_TO_<int>andTOD_TO_DWORD/LTOD_TO_DWORDduration magnitudes. Implemented in the compiler accept table and the runtime conversion dispatch; seeIEC_DEVIATIONS.md(2026-10-28). - Standard conversion extension round:
REAL_TO_TIME/UINT_TO_TIME(the value is the millisecond magnitude of the duration),DWORD_TO_TOD(the raw ms-of-day magnitude), andSTRING/WSTRINGto bit-string targets parse the text as an unsigned integer literal and yield0when it is not one. Other integer-to-TIMEpairs, string-to-BOOL, and REAL-to-bit-string conversions stay rejected; implemented in the compiler accept table and the runtime conversion dispatch; seeIEC_DEVIATIONS.md(2026-10-28 extension round). - Pointer-target lenient bindings match CODESYS:
POINTER TO CHARandPOINTER TO BYTEpointees bind each other with a warning, andADR(STRING[n])binds against aPOINTER TO ARRAY OF BYTEformal (the vendor MemCopy and JSON-buffer idioms). All other pointee mismatches stay errors; seeIEC_DEVIATIONS.md(2026-10-29, DEV-039).
[0.27.0] - 2026-10-31¶
Changed¶
TON,TPandTOFstart timing on the call that samples the edge, as CODESYS and TwinCAT do: that call returnsETT#0s, and each later call adds the task time since the previous one. Before, the edge call already counted the time since the previous call, so a timer finished one task interval early: with a 10 ms task andPTT#30ms, aTON'sQrose at the second call after the edge instead of the third. Programs see their timers finish one task interval later than in 0.26.0 (specification 08 §5;docs/IEC_DECISIONS.md, 2026-10-30).- Lesson 58 (valve characterization) holds its last opening until 155.4 s, so its project test runs 155.5 s; its figure and steady values come from a new Control Studio recording.
- Releases follow
main's CI instead of a hand-pushed tag. When CI passes on a commit whose version has no complete release yet, the Release workflow builds and tests every target, tags that commit, publishes the GitHub release and the Marketplace packages, and verifies the tag, the release and the Marketplace listing of every target. A pull request that changes the version rehearses the whole release on its head, and CI's version guard no longer waits for a release (specification 24). - Pull requests run the documentation, capture and diagram checks whenever the whole change touches their paths, also above the 300 changed files that GitHub's path filters compare, and each such workflow ends with one conclusion that fails when any of its checks failed (specification 22).
- The documentation is checked against the product: the Structured Text in the README, the docs pages and the examples'
READMEs quotes its example's program or parses, the commands and settings the pages name exist in the VS Code
extension, no page names a retired command or the Runtime Panel, and every
trust-runtimecommand line they show parses. Fixed on the way: an OpenOT example that named a variable with the keywordACTION, four Structured Text examples that did not close their program, function or function block, three command names that had changed,ctl restartwithout its mode, a tutorial's run without its command, and 15 example and docs pages that sent readers to the Runtime Panel or toStructured Text:commands: they name Live Values,truST:commands and Devices & Connections, where the ADS commands went (specification 22). - Example READMEs are step-by-step walkthroughs: what you learn, what you need, numbered steps with the values a real run shows, what happened and why, things to try, and a reference (specification 25 §3.5); CI checks every converted README against that standard. The first is Control Studio: PID loop: it identifies the simulated heater, tunes a PI controller for a 12 s settling time, validates it on the program, applies and restores it, and explains the model, the PI law and the SIMC tuning with the numbers of the run. Its gallery description no longer promises the setup form.
- The README shows what truST does that it left out: code from CODESYS and TwinCAT, Siemens SCL and Mitsubishi GX Works3, process data in seven databases through OpenOT, tasks, RETAIN and the watchdog, the simulator's scenarios and faults, the browser IDE and other editors, the HMI Builder itself, and operating in production (audit log, fleet rollout, backup and offline installation). Its examples table starts with the tutorials and adds debugging, visual programs, tests and CI, and coming from another PLC; it says that Windows runs on x86-64 only, and its platform and specification notes are written for readers rather than auditors. The Marketplace page names the libraries and the vendor profiles.
- The documentation site shows the newest screenshots rendered on
mainwithout a commit of the images. Before the site is built, the screenshots of the newest passing Docs Captures run ofmainare laid over the images; only what the captures wrote is taken, never the hand-made pictures. The capture refresh pull request, which had to be merged into each release by hand, is gone (specification 22).
Fixed¶
- Identify waits until a loop it finds in Auto has settled before it switches the loop to Manual for a step: the measured value and the controller output must both be steady by the rule of the steady baseline (for a PID group, every member's measured value and output), and the wait shows its progress and can be cancelled. Before, an Identify started while the controller still moved its output (for example right after Restore) held that moving output, the plant settled open loop away from the setpoint, and Apply then waited in vain for that operating point while the controller held the setpoint (specification 35, B3.4).
- Identify returns a loop it switched to Manual for a step test only once the controller output has come back to its restored value. Before, the return could start while the output still moved there at its rate limit; the runtime then refused it ("stale program, binding, or expected values") and the loop stayed in Manual (specification 35, B3.4).
- Control Studio designs to the requirements you type before the first Tune. Before, the first change after Identify kept every requirement marked Automatic, so Tune replaced the typed values with Automatic's own while Validate checked the typed ones (specification 35, B4.3).
- Validate on a configured simulated plant starts the controller from its declared states at the operating point, as
Apply does, with a startup-done flag such as
Initializedset. Before, the program's startup code initialized the controller at the new setpoint, so a PID took the setpoint step without its proportional action, answered about 5 s late and failed requirements it meets (specification 35, B5.3). CTU,CTDandCTUDare the INT counters of IEC 61131-3 Table 45:CTU(PV := 10)compiles, and a plain counter given a DINT preset is refused when the program is compiled; it ran with a preset of 0. A DINT counter isCTU_DINT,CTD_DINTorCTUD_DINT(specification 08 §4).- A
TPpulse ignores a rising edge ofINwhile it runs, as IEC 61131-3 Figure 15 a and specification 08 have it; such an edge restarted the pulse and made it longer thanPT. - A statechart transition with
afterfires when its state has been active that long, counted from the scan that enters the state, and a timed self-transition starts its timer again. The generated companion reset every timer before running it in each scan, so such a transition never fired (specification 17 §3.2). - A library block inside a
RETAINinstance keeps its own state over a warm restart and a power loss: aTOF's running off-delay and aTP's running pulse go on, and a counter or anR_TRIG/F_TRIGwhose input is TRUE at the restart counts no new edge. They lost that state, so the delay or pulse ended at once and the counter counted one more (specification 10 §4.8; IEC 61131-3 §6.5.2.2).
[0.26.0] - 2026-10-30¶
Added¶
- VS Code's Target picker offers the workspace project's own runtime as
This project's runtimewhile it runs outside VS Code, for example started withtrust-runtime runin the project folder. Selecting it and pressing Connect connects with the endpoint and token thattrust-runtime ctl --project <dir> endpoint --jsonreports, and follows the runtime when it restarts on another port; when the runtime stops, the entry says it is not running. Before, the picker offered only the Simulator, and reaching such a runtime needed its endpoint typed in Devices & Connections (specification 25, §5.1). - A visual SFC's steps are read as IEC 61131-3 spells them (Table 54):
<step>.X, the step flag, and<step>.T, the time since the step was entered, which isT#0sin the scan that enters the step and keeps its value while the step is inactive, so a transition can wait onFill.T >= T#5s.<step>_activestays, and is all a step named with a keyword (On) has. The SFC editor's transition properties have a Priority field for a selection's order, the lowest number first (sfc-profile.md). [project] vendor_profileselects the language rules too, not only formatting, the standard library and the conversion rules:codesys,beckhoffandtwincatswitch on CODESYS's departures from IEC 61131-3 (recorded inIEC_DEVIATIONS.md, DEV-028 to DEV-032) in the editor,trust-lsp check, the debugger, the runtime's web IDE and the runtime build; every other profile keeps the IEC rules (specification 14).- The runtime build compiles the CODESYS constructs of PR #140 and runs them: a program reached by its name
(
Program.member,Program(...), through the program's instance of the same name), references read and written without^,REF=,EnumType.Memberin starting values, a structure thatEXTENDSanother (the base's fields first) and its copy to the base, function block members written from outside, and empty named bindings.
The CODESYS vendor extensions of PR #140; the ones that change what an IEC program means apply only under a CODESYS
vendor_profile:
- Resolve textual action call sites in the IDE (CODESYS vendor extension): bare
action_name()calls andProgramName.action_name()resolve to the action symbol, so go-to-definition, hover, find-references and rename include action call sites instead of silently dropping (or corrupting) them. - Tolerate identical duplicate declarations in PLCopen
globalVarslists (e!COCKPIT MixedAttrsVarList parity): repeats are dropped with aPLCO605info instead of skipping the whole list; conflicting redefinitions still reject the list (PLCO604). - Import CODESYS
pouType="action"PLCopen POUs:Parent.Actionnames merge into the parent POU as textualACTIONblocks (PLCO219info); body-less actions skip withPLCO204, and actions without parent linkage keep thePLCO203fail-closed skip. Textual actions nested under the parent POU in real e!COCKPIT exports (<pou><actions><action name="Plain">) merge the same way, where they were dropped silently. Actions go before the parent's end keyword written in any case, never into a comment; a parent whose source has no end keyword has its actions skipped withPLCO203instead of reported imported. - Import e!COCKPIT methods attached to PROGRAM POUs (before, only function-block methods were kept): the method body
merges into the program mirror, so self-calls like
StateToBool(...)resolve. - Read the canonical e!COCKPIT v3.9.2 array initializer form (
<initialValue><arrayValue><value><simpleValue …/>) in GVL imports, flattened to the ST bracket list; the boot tables vanished from the mirror before. - Namespace-wrap every imported
globalVarslist (not onlyqualified_onlyones), so qualified access resolves; unqualified access to a namespaced list needs aUSINGdirective (PLCOPEN_DECISIONS). - Resolve CODESYS qualified enum member access (
EnumType.Member, optionally namespace-qualified) in variable initializers, expressions and CASE labels (with the numeric values for the coverage checks), also for enums declared inside namespaces, instead of reporting an undefined identifier or a type used as a value. - Under a CODESYS
vendor_profile, accept an empty right-hand side on named call bindings (input :=andoutput =>with no target, at a comma or the closing parenthesis): the parameter is skipped, a dangling output is discarded;?=still needs a target (DEV-032). - Accept enum-typed array subscripts (enum values and enum-typed variables) as a CODESYS vendor extension, beside the IEC ANY_INT subscripts.
- Accept
RETAIN PERSISTENTin state-owning variable sections as a WAGO/CODESYS vendor extension (persistent implies retention);NON_RETAINcombinations and duplicate qualifiers remain errors. - Make textual actions callable by name (CODESYS vendor extension): bare
action_name()inside the owning POU andinstance.action_name(), and under a CODESYSvendor_profilePOU.action_name()(DEV-030); the calls take no arguments and give no value, and a variable of the same name still shadows the action outside call position. - Under a CODESYS
vendor_profile, function-block members default to Public: plainVAR,VAR_STAT, methods and properties are reachable through the instance name (fbInst.member) from other POUs; an explicit access specifier restricts them, andVAR_IN_OUT/VAR_TEMP/VAR_EXTERNALmembers stay restricted (DEV-028). Assignment through an input-parameter slot (fbInput.field := value) stays rejected. - Under a CODESYS
vendor_profile, a function block's or program's body may assign its ownVAR_INPUTmembers between calls. Function and method parameters stay call-local, and pointer or reference input slots keep the read-only-slot policy (DEV-029). - Under a CODESYS
vendor_profile, a program is reached by its name:Program.memberreads and writes across programs (including function-block instances),Program.action()runs its action andProgramName();runs its body; a bare program name in value position remains rejected (DEV-030). - Support CODESYS-style DUT inheritance (
TYPE Derived EXTENDS Baseon STRUCT types, vendor extension): inherited field access, derived-to-base assignment compatibility, duplicate-field and cycle diagnostics, and cross-file base resolution. - Under a CODESYS
vendor_profile, references are dereferenced without^: member access, indexing, arithmetic and assignment copy through a reference; assignment between two references still rebinds.POINTER TOkeeps requiring an explicit^and stays rejected in arithmetic (DEV-031). - Add the CODESYS-style
REF=reference-rebinding statement as a vendor extension:ref REF= variablerebinds to the variable's storage,ref1 REF= ref2to a compatible reference target, andref REF= NULL(or0) clears the reference; value and pointer targets are rejected. - Accept the CODESYS-style
REFERENCE TO Tspelling as an alias of the IECREF_TO Treference type, with the same declaration rules and meaning.
The CODESYS and e!COCKPIT dialect constructs of PR #141:
- Under a CODESYS
vendor_profile, aSTRING/WSTRINGcapacity or an array dimension bound may name a plain variable of any file of the project whose initializer is constant, also qualified (STRING(GVLMqtt.uiLenMsgState),ARRAY[0..(GVLMqtt.udiLenMsgBuffer - 1)]); the declared initial value fixes the size when the program is built. TEMP and interface-direction sections are excluded, chains through other mutable variables stay rejected, and no other constant-expression context changes (DEV-033). REF=binds strings of any declared size to each other and a derivedSTRUCTvariable behind a base-STRUCTreference; numeric widening still never aliases. Under a CODESYSvendor_profile,=and<>of a reference with a value or another reference compare the values they name; ordering comparisons still need an explicit^(DEV-031).- PLCopen import carries the CODESYS/e!COCKPIT dialect constructs that vendor projects rely on:
REFERENCE TO/POINTER TOderived names import as native pointer/reference declarations (including sized-string bases such asREFERENCE TO STRING(GVLMqtt.uiLenMsgState)),datatypeinheritanceemitsTYPE x EXTENDS y,pouinheritanceputsEXTENDS/IMPLEMENTSon FUNCTION_BLOCK declarations, project-level<Interface>objects import as declaration-onlyINTERFACEblocks,globalVarscontainer qualifiers (retain/persistent/constant) survive into the renderedVAR_GLOBALheader, e!COCKPITMixedAttrsVarListattribute views and identical whole-list duplicate copies merge into the primary list, and array dimension bounds pass through verbatim as constant expressions. The migration report counts imported interfaces inimported_interfaces. - Program-scoped
METHOD ... END_METHODdeclarations (e!COCKPIT dialect) are accepted and callable by bare name from the program body; calling them through the program's name needs a CODESYSvendor_profile(DEV-030). They run on the variables of the program instance they are called on, theirVAR_STATvariables kept in that instance. - Textual
ACTION ... END_ACTIONdeclarations of programs and function blocks build and run instead of being refused: an action runs on its owner's variables only when it is called, by its bare name in the owner's body or through a function block instance;RETURNends the action. Calling a program's action through the program's name needs a CODESYSvendor_profile(DEV-030). - Under a CODESYS
vendor_profile,ON,NULLandSETmay name variables, members, parameters and enum values (OSCATTIMER_EXT.ON,FF_DRE.SET, WAGOeChar.Null);NULLstays the reference literal in value position andONtheRESOURCE ... ONseparator (DEV-034). - The runtime harness compiler honors the
RETAIN PERSISTENTvariable section combination (persistent storage implies retention, either spelling order); duplicates,NON_RETAINcombinations and qualifiers beyond that pair stay rejected.
Changed¶
- The plant editor's Dynamics fields send what is typed to the runtime's
plant.edit(set_dynamics_text), which reads it into the draft; the extension no longer reads numbers and lists itself (specification 47 §20.1). - The SFC editor's Validate refuses a step named like a variable, an action or a timer the companion declares, and a transition priority that is not a whole number from 0; the first would give a companion that does not compile, the second was silently ignored.
- The sidebar's notice of a failed Connect offers
Open Devices & ConnectionsorSet auth tokenby the remedy that the language server names: the first when the runtime accepts no connection or does not answer, the second when it refuses the token by its code (specification 25 §5.1). A failed Update shows the debug adapter's summary of the reload (§6.3), and a malformed PLCopen import shows trust-runtime's message, which now says that the input XML is malformed (§11.3). The extension kept copies of these rules that read the failure's text. - Live Values shows the value type that the debug adapter names: the runtime's, else the one the row's direct address
names by its size, now also for a bit address without a size prefix such as
%I0.3(specification 25 §4.3). The panel kept its own copies of the address and truth-value rules.
Fixed¶
- The Target picker calls a remote runtime not reachable only when it failed to answer a probe. With the control endpoint switched off, in simulate mode or during a session no probe asks it, so it is Not connected and Connect, which probes first, stays enabled; Connect was disabled until the setting was switched on by hand (specification 25 §5.1).
- VS Code's status bar says what the side panel says of the selected runtime, and both notice within seconds when a runtime they are online with starts or stops outside VS Code. The status bar had words of its own: a managed runtime whose status was unavailable showed as stopped (specification 25, §5.1).
- A device set up with truST (setup, the web setup or the wizard) and a runtime created in the config UI keep their
RETAIN values across a restart and a power cycle: the
runtime.tomlthey write stores them inretain.bin, as a new project's does. They had[runtime.retain] mode = "none", so every start was cold (IEC 61131-3 §6.5.6.1). - A runtime whose project folder is too deep for its own control socket starts when
$XDG_RUNTIME_DIRis open to the user's group or belongs to another user: its socket goes into a privatetrust-runtime-<uid>folder in the temporary folder. It refused to start without an explicit endpoint (seen on a Raspberry Pi 5, where/run/user/1000has mode 0770). - A function block's statements after a
METHODorACTIONdeclaration run with the rest of its body; the build compiled only the statements before the first such declaration and left the rest out without a diagnostic. - Compile says that trust-runtime is missing only when the extension cannot start it; trust-runtime's own output with
the words
not foundwas reported as a missing trust-runtime. - A visual program imported from a folder whose name begins with the workspace folder's name, such as
/ws-otherbeside/ws, is copied into the workspace; it was opened where it was, as if it were inside the workspace. On Windows and macOS a program inside the workspace folder opens where it is also when its path spells the folder in other letter case, such asC:\WSforc:\ws, as VS Code compares paths there; it was copied. - PLCopen import puts a function block's methods before its end keyword written in any case, as IEC 61131-3 reads
keywords (§6.1.3): a function block closed with
end_function_blocklost its imported methods. - A DUT
EXTENDSclause whose base names no declared type is diagnosed (cannot resolve type '<base>',E102); the derived struct was accepted silently and inherited from nothing (PR #140). - A project constant whose evaluation fails is reported in the file that owns it: a library or stub file's failed constant no longer appears as a diagnostic under every other analyzed file's path (PR #140).
- The runtime's operator display at
/hmishows the session, the user and role with Log out or Log in, at the top bar's right end whenever the screen has a top bar. A top bar withoutuserleft it floating over the page's edge (specification 39, §4.8 item 8). - Control Studio's Identify returns a loop it found in Auto from the output the loop holds. It wrote the Manual command it had found back first, in Manual, which stepped a process that moved during identification to that old command before the Auto entry (lesson 30's plant went from 47.55 % to 75 %, while the controller held 67.55 %). The Auto entry sets the Manual command to the output it holds, as Apply's does (specification 35, C1). A loop found in Manual still gets its Manual command back.
[0.25.1] - 2026-10-29¶
Fixed¶
- Control Studio's Identify and Validate judge a step's response from the scan at which the runtime reports the step applied (specification 35, B3.4 step 4). Before, the response counted from the newest sample when the step was sent: when the start reached the runtime late, as on a busy computer at a high simulation speed, the samples recorded in between could end the step before it took effect, and the restoration then replaced the step: Validate refused the recording for want of excitation, and Identify's recording held no step or one cut short.
- On an ARM computer (a Mac with Apple silicon, ARM Linux) the runtime makes a compiled program it loads the instructions the CPU fetches before the program runs (specification 44 §10.3). Before, the CPU could run instructions its instruction cache still held for those addresses.
- The runtime's operator display at
/hmiloads its modules again (#134): 0.24.74 servedapp.jsbut answered 404 for every module, so the page stayed blank onDisconnected. The display of 0.25 (specification 39) serves every file its page names, and a test requests each one. - The documentation site publishes again. Since 0.25.0 its checks had stopped it: the link check read the HMI Builder page's shortcut Ctrl+[ as the start of a link, the control course's learning path and four other pages linked files outside the site by relative paths (they now link them on GitHub), and the HMI Builder's screenshots were missing from the capture inventory.
- Control Studio can read a loop's signals while the runtime starts. Before a program's first
complete scan the runtime refused
control.signals.inspectwith "current runtime scan unavailable", so a run or recording that began then failed at once. It now answers that the read waits for the scan, as annex E3 of the Control Studio protocol states, and Studio asks again. - On a computer, a restart begins the tasks' timing, and the cycle grid with it, when its first cycle starts, as a chip does (specification 10 §4.3). It began them when the restart ended, so the time between counted: after an automatic restart's backoff the first cycle ran the tasks, and a longer backoff counted activations as missed.
- A real-time (T0) shared-memory transport made without a configured root removes that root when it closes, and names it so that no later process opens it. Before, the root stayed in the temporary folder, and a later process that the operating system gave the same process ID opened its channels and went on from their old sequence.
- On Windows, a debug build of
trust-runtimelinks again (cargo build,cargo run -p trust-runtime): the runtime provides the critical section that the compiled cycle's libraries take. Since 0.25.0 nothing provided one outside a build of the whole workspace's tests, and Windows' linker refused the missing symbol.
[0.25.0] - 2026-10-28¶
Tagged but not published: its release run stopped before publishing, and 0.25.1 is the first published release of 0.25.
Added¶
Control.PIDhas two documented extensions (specification 45 §3.1): the simulation membersHeldMeasurement/MeasurementHeld, which a plant'spid:feedback writes before the scan so thatUpdatetakes the simulated measurement, and the observation membersModeValue/ModeObserved, the mode the lastUpdateran in, which Control Studio's PID writes check. A project that carries an older copy of the Control library gets a plant fault naming itspid:target until the copy is updated.- New control operation
comm.compare(Viewer) andtrust-runtime discover --compare --project <dir>compare a search with the project's Modbus TCP, EtherCAT, MQTT, OPC UA and ADS devices: they list the configured devices that were not found, the found devices that the project does not use, and the devices whose identity differs (an EtherCAT SubDevice in another position, a Modbus device with another product code). The Devices & Connections Discover pane marks each cardIn the project,Not in the projectorDiffers from the projectwith the facts that differ (On canvasis that mark for a TwinCAT device), and itsCompare with projectaction lists the comparison of a finished search. Amodbus-tcpconnection form now offers the optional expectedvendor_name,product_codeandrevision, so a Save keeps them. - New control operation
identify(Operator) andtrust-runtime identify --duration-s <n>: for 1 to 60 seconds a runtime blinks the output that[runtime.identify] outputnames (an I/O address such as%QX0.7, or a GPIO line such asgpio:17) at 2 Hz and reportsidentifyinginstatus. It works in every state of the resource: a running, paused, stopped or faulted runtime blinks, an output that[[io.safe_state]]names keeps its safe value while a stop or a fault is in force, and a restart that reloads a stopped program reads[runtime.identify]again. A project whose program uses that output is refused when it loads, and byvalidate,checkandide servetoo. Devices & Connections offersIdentifyon a runtime, for ten seconds, and shows the runtime's answer. A chip answersidentify_not_configureduntil its firmware drives an output. - Find a project's runtime with
trust-runtime ctl --project <dir> endpoint --json; every failure is a JSON error with a stable code, and VS Code shows its message.ctl, theuiconsole, debug attach and VS Code (Live Values, the runtime target, Start in online mode, Devices & Connections) ask again at each connection, so they follow a runtime that restarted on another port. A runtime that does not answer is reported as not running, and an endpoint file that you cannot read is skipped for the configured endpoint. Changingcontrol.auth_tokenwhile the runtime runs withdraws the generated token. Ctrl+C on Windows,ctl shutdown --exitand a config UI stop remove the endpoint file; a plainshutdownkeeps the runtime and its file. The web IDE settings offer no endpoint by default. An empty value or Reset removes the endpoint and the control and mesh tokens; Reset used to write empty tokens, which the runtime refused. mDNS announces a control endpoint only when another computer can reach it: a socket path or a loopback port is never announced.ctlwith aserial://endpoint or atcp://endpoint on another host takes the token from--token,TRUST_CTL_TOKENor the project'sruntime.toml, never from the endpoint file of the project's own runtime. Control Studio's command line without--endpointaskstrust-runtimefor the project's runtime in the same way, and takes the token it reports when neitherTRUST_CONTROL_TOKENnor--token-filegives one; before, it read only the endpoint written inruntime.toml. - Create a project folder with
trust-runtime new --parent <folder> --name <name>. It writes the files of a new truST project, prints the project and itssrc/Main.stwith--json, and refuses a folder that is not empty unless--forceis given, which overwrites only the scaffold files and keeps an existing.gitignore. Names that Windows cannot use as one folder name, such asC:xorCON, are refused on every platform. New Project in VS Code uses it and writes the same files as before, without the endpoint line. It now asks before it uses a folder that is not empty, also one without truST files, as specification 25 requires, and an empty or refused name gets the command's message. Every way of creating a project now lists/.trust-runtime/in its.gitignore. - Control Studio reattaches an interrupted operation when it reopens after VS Code restarted: it follows a
dispatched identification step to its end, completes a journal without one, and ends an identification whose
simulator restarted or reloaded its program since, saying so with both program revisions. The refusal
"This loop has an unfinished operation" now offers Resume; before, the loop stayed blocked with no way
out on the page (specification 35, A8.6; acceptance test, 26 October 2026).
trust-control-studio identify --resumemakes the same reattach. - Added: the
mqttdriver on a chip with a network: the ESP32-C6 runs the computer's MQTT 3.1.1 session, typed points, raw mode and Sparkplug B from one shared implementation, keeps the session alive between the scans and while the program is stopped, and ends it in order when the program is replaced. A broker can be named by a host name, looked up with the chip's name servers. The C6 keeps 16 received messages (1,024 payload bytes), packets of up to 1,024 bytes and 2,048 bytes waiting to be sent, and counts the driver's buffers when it loads the program. TLS and symbolicmappingsare refused on the C6 for now, with the reason. - Added: the
modbus-tcpdriver on a chip with a network: the ESP32-C6 reads and writes a Modbus TCP server over Wi-Fi with the computer's keys, checks and deadlines, from one shared implementation of the driver's exchange that builds every frame with thetrust-modbuscrate. - Added: the
analog,pwm,counterandcanI/O drivers, with the sameio.tomlkeys on a chip and on Linux (specification 11 §6.5): on the ESP32-C6 over its ADC, LEDC, PCNT and TWAI controllers, on Linux over IIO, sysfs PWM, the counter subsystem and SocketCAN. macOS and Windows refuse them with the reason.trust-runtimeon Linux now linkssocketcan. - Added:
trust-runtime buildalso writesbuild/install.<target>.bin, the container thattrust-runtime ctl installinstalls on a device, so a chip's program no longer needs a developer tool to pack it (specification 44 §4.1). - Added:
trust-runtime ctl capabilitiesprints what a runtime reports of its device: a chip's network and address, its firmware slots, its memory for programs and its last reset. - Added:
trust-runtime firmware install --reset-settingsalso erases a chip's device settings and pairings through its boot loader on USB, so a chip whose control token is lost boots without a token, without pairing tokens and without a network. - Added: a chip pairs clients as a computer does, with
pair.start,pair.claim,pair.listandpair.revoke(specification 11 §6.9.6). The ESP32-C6 keeps its pairings in two flash sectors at0x7F6000and0x7F7000, taken from its program store, whose slots are now 2,060 KiB at0x3F0000and0x5F3000: install the firmware again withtrust-runtime firmware install, then the program. A chip pairs only while it serves a network and its clock is set. - Added: a chip trusts the keys its
device.tomlnames under[signing] trusted_keys(specification 44 §5.11): it then runs only programs one of them signed, at an install, a restart and an online change, andstatusreportssignature: trusted. A computer's install also checks the signature against its owndevice.toml, where before only its start did. - Added: a chip on a network is found by the same discovery as every truST runtime (specification
34 PORT-06): it announces
_trust._plc._tcp.local.over mDNS with its runtime ID, version, platform and control endpoint when it takes an address, answers browses, and withdraws itself before a reset it makes. A chip whosedevice.tomlnames a time server sets its clock from it with SNTP once it has an address and every hour (TARGET-06);capabilitiesreports the server, the last correction and why a request took no time. - Added: a chip joins its network and serves its control connection over it (specification 34
PORT-06). An ESP32-C6 whose
device.tomlnames a Wi-Fi network joins it at boot, takes its address from DHCP or the settings, and joins again after a loss; with a control token in the new[control]section it serves the control protocol on TCP port 9901 ([control] port), sotrust-runtime ctl --endpoint tcp://<address>:9901 --token <token>reaches it as it reaches a computer, installs included. The token is then required on every link. One client holds the network connection at a time; a second is told that the device is busy (server_busy), and a tool connects again for about a second and a half, as a chip frees its connection a moment after a client closes it. The chip answers ping on its address, leaves its network before a reset it makes itself, and derives a WPA2 passphrase's key at boot, so that joining never stalls its program's scans.capabilitiesreports the chip's network, its address and the reason a radio did not start. The radio's code takes 80 KiB of the C6's program region, now 112 KiB; a chip without Wi-Fi settings starts no radio and keeps its heap whole. - Added: a chip keeps its own settings,
device.toml(specification 34 TARGET-02): its name, its Wi-Fi network and address, Bluetooth, its time server, the keys whose signed programs it trusts and the wiring values it cannot detect.trust-runtime ctl device-set <device.toml>stores them while the program is stopped,ctl device-getreads them back without the Wi-Fi password, andctl device-resetrestarts the chip with them. A setting the chip has no hardware for is refused with the reason. - Added: truST is installed on a chip and updated from the tools (specification 34 TARGET-04).
trust-runtime firmware install --chip esp32c6 <folder>writes the release's firmware through the chip's own boot loader over USB, andtrust-runtime ctl firmware <image>updates a chip that runs truST over its control connection: the image goes into the firmware slot that does not run, the chip checks it, restarts into it and runs it on trial until its boot is done; a firmware that does not finish its boot is rolled back by the boot loader, and the command says so.capabilitiesreports a chip's firmware slot, version and rollback. The ESP32-C6's flash layout changes for the second firmware slot (the program store is now two slots of 2,064 KiB), so a chip needs a freshfirmware installand its program installed again once. - Added:
--endpoint serial:///dev/ttyACM0(serial://COM5on Windows) reaches a chip over its USB port intrust-runtime ctl,ads serveranddiscover, andtrust-runtime ctl install <container> [--restart cold|warm]installs a program on a chip or a computer. - Added: the tools say which update is needed when a runtime runs another truST release
(specification 34 TARGET-05):
trust-runtime ctland the tools that open a device's link write, for example,warning: the runtime runs truST 0.30.9 and these tools are truST 0.31.0: update the runtime to truST 0.31.0, and the Devices panel names the update in the runtime's inspector. The older side is the one to update.ctlreads the version withstatusas request 0, so the action's request keeps id 1 on a socket, a serial port and a chip's network alike (specification 11, 6.8.1). - Added: a chip answers
config.getandconfig.setas trust-runtime does (specification 34 TARGET-07): the log level, the cycle watchdog, the fault policy, retain and control of its installed program, in trust-runtime's keys, by the same rules. A change is committed before the reply, the program takes a new watchdog, fault policy or retain save interval at its next boundary, the hardware watchdog follows a new cycle watchdog timeout, and a control token set this way is required from then on. A chip answered both withunsupported request. - Added: a
STRUCT OVERLAPmay hold structures, other overlaps and arrays, as IEC 61131-3 §6.4.4.7.1's own exampleCom_datadoes (specification 02 §3.6): each lies over the shared bytes in its own layout, its members and elements read and write those bytes, and a whole member is copied member by member. The build refused such a member with "which is no elementary value". A string stays refused, as IEC leaves the overlapping of strings out of the standard. - Added: a body may name a directly represented variable,
%QX0.2 := %IX0.1;, in a program, function block, function or method, as IEC 61131-3 §6.5.5.1 permits (specification 03 §5). The build refused it with "the name %IX0.1 is not compiled yet". Each address is a variable at that address, sampled, published and kept as a variable declaredATit is. - Added: the ESP32-C6 has a wall clock, so a program that calls
CURRENT_DTinstalls and runs there. The chip takes its time from the computer that talks to it:trust-runtimeandtrust-devsend the new requestclock.setwith their own clock whenever they open the chip's control link (specification 11, the runtime clock contract). The C6 counts the time with its crystal and keeps it across every reset but a loss of power; until a computer sets it, its clock counts from 1970-01-01 at power-up and replies carry no time. trust-runtime on a computer refusesclock.setwithclock_not_settable: its operating system holds its clock. - Added: a program type may have several instances in a configuration, each with its own
variables, task,
RETAINorNON_RETAIN,VAR_CONFIGvalues and access paths (IEC 61131-3 §6.8.2, Table 62 6a; specification 18 §2.4), where the build refused a second instance with "not supported yet". The connections of a program's inputs and outputs in the configuration,PROGRAM P1 WITH Fast : P(x1 := %IX1.1, x2 := w, out1 => %QW5)(Table 62 8a, 8b, 9a, 9b), now run: an input connected to a direct address reads it and an output writes it, and the others are made at every activation, inputs before the body and outputs after it. The build used to accept them and ignore them. A connection the program does not declare, an output connected with:=, an input with=>or a value of another type is refused with the reason. - Added: compiled programs run every construct the bytecode engine ran (specification 44 0.23):
EN/ENO, outputs given by position,RETURNwith a value,JMPand labels,VAR_STAT,UNION,STRUCT OVERLAP,POINTER TOandADR,?=,WSTRING,IS_VALID, edge-qualified inputs, function blocks run by a task, located structures and arrays, hierarchical addresses (%IX1.2.3),VAR_CONFIGlocations and initial values,VAR_ACCESSnames,CURRENT_DT,ARRAY[*]parameters and pointers withLOWER_BOUND/UPPER_BOUND, a function's own function block instance with its initial values, and a structure that refers to itself. A write to an element of anARRAY[*]input is refused at build: an input is the caller's array (specification 03 §6).DATE,TODandDTat a direct address are refused with the name of their long form. The kernel interface is version 5, so images built before must be rebuilt. - Added:
trust-runtime build --target <device or CPU>compiles a project into native machine code for that device, in a program image (build/program.<target>.tpi) with its memory figure inbuild.jsonand, with--emit-source, the generated Rust (specification 44). The reference plant programexamples/vat_pasteurizer(batch control with a PID loop, twelve alarms, status texts, a plant simulation) needs 18,978 bytes of RAM on the ESP32-C6 as a debug image (its memory figure) instead of about 160 KB, and on a computer it computes the same values as the runtime, bit for bit, scan after scan. Building needs the toolchain folder atTRUST_TOOLCHAIN. The build also writes the image's deployment record, andcargo xtask portability pack-projectpacks an image likeprogram.stbc. The ESP32-C6 firmware built withbuild-firmware esp32c6 <report> --nativeadmits the image where it lies in its program slot and runs its code from a 128 KiB region of its RAM, through the same program store, install,status,tasks.statsandio.readas today; the pasteurizer's control task takes about 0.06 ms there, 0.18 ms at worst over an hour, and its 86 KB image installs over USB. A compiled program keeps itsRETAINvariables across a power cut as an interpreted one does: the ESP32-C6 saves them into its checkpoint log in the same format, byte for byte, restores them at a warm start with the same conversions, and checks at an install that the new program accepts them. Online change and the debugger of a compiled program follow (specification 44 M3). - Added:
trust-runtime conformance --execution-backend nativeruns the conformance suite on compiled programs: each case is built for this computer and run by the kernel, and its trace is compared with the same expected artifacts (specification 44 §14.2). A project without aCONFIGURATIONnow compiles as the runtime runs it, eachPROGRAMas an instance of its name. - Added: the control request
cycle.stepruns one scan of a stopped program at a time the client gives, with the values it gives written before and the values it names read after, on a computer and on a chip alike (specification 11 §6.8.1). The scan touches no driver and starts no task, its fault is reported and not latched, and a cold restart in it loads a program installed since the last one.trust-runtime conformance --execution-backend native --device <port> --target esp32c6runs the conformance suite on a device with it: each case is compiled for the device, installed and stepped cycle by cycle, and its trace is compared with the same expected artifacts. A step'srunnames a program instance whose body runs once in place of the cycle, andtrust-dev test --execution-backend native --device <port> --target esp32c6runs a project's tests on a device with it: the project is compiled and installed once, and each test is one step that restarts the program cold and runs its test program (specification 22). The test image runs no test program by itself, so a device that restarts or resets with it installed runs no test. - Added: release images. A project whose
trust-lsp.tomlsays[build] debug = falseis compiled without debug hooks bytrust-runtime build --target,bench project --execution-backend nativeandtrust-dev test --execution-backend native; without the setting each builds a debug image, astrust-runtime builddid before, sotrust-dev testnow tests the kind of image the project deploys (its test images had no hooks). A release image says so in its header and in the build's report ("debug": false), still ends a loop that never ends at the statement budget, which then counts the passes through loops, and cannot be debugged (specification 44 §13.2). On an x86-64 computer the pasteurizer's control task takes 150 ns per activation as a release image instead of 221 to 231 ns, and its code is 21,184 bytes instead of 47,504. - Added: compiled programs may be recursive, as interpreted ones are: a function, a method or a
function block that calls itself, directly, through others or through an interface, runs within
a call depth that the kernel works out from the task's stack, the stack beyond the program's
bound in the bytes one turn of its largest cycle takes, which the build reports as
call_level. A recursion that does not end ends the scan with the error a call beyond the bytecode engine's call levels gives (call depth exceeded) instead of overrunning the stack (specification 44 §6.4, §8.5). The kernel interface is version 3: a kernel refuses an image of another version, so a chip's firmware and its programs are updated together. - Added: the debugger of compiled programs (specification 44 §13.2). A debug image names its POUs
and their frame variables in its symbol table and links each call's frame into the kernel's
context, so a breakpoint set by line (
breakpoints.set, resolved through the image's statement table asLINE_TABLEresolves it), a pause andstep_in,step_overandstep_outhold the scan at the statement, anddebug.stack,debug.scopesanddebug.variablesshow the call stack and the variables of the held scan: each frame's instance and frame variables, the globals, the retained variables and the instances, through the shared request service on a chip and a computer alike. Live Values queued while the scan is held reach it when the hold ends. A release image refuses the debugger withthe program was built without debug hooks ([build] debug = false). The kernel interface is version 4, so a chip's firmware and its programs are updated together. On the ESP32-C6 a breakpoint in a function holds the scan with the call stack of the function, its function block and the program, steps and resumes, over USB. Watch expressions, hover and the debug console'sevaluate, breakpoint conditions, hit counts and logpoints work on compiled programs too: VS Code's debugger compiles each expression with the project's types into the portable form of specification 11 §6.13.2, which the runtime evaluates in the held frame without parsing ST text, on a computer and a chip alike, and a logpoint's message appears in the debug console. An enumeration literal such asMode#Runtakes its number from the program the runtime runs. - Added: VS Code's simulator runs the program compiled for this computer where the truST toolchain
is installed (specification 44 NX-16): a launch builds the project as
trust-runtime builddoes, without touching the project'sprogram.tpiorprogram.stbc, runs it in the debug adapter on trust-runtime's own scheduler with theio.tomldrivers and a control server at the launch's endpoint, and debugs it as an attached runtime: breakpoints with their conditions, hit counts and logpoints, stepping, the call stack, watches, Live Values and the I/O panel.stopOnEntryholds the first statement, and "Reload" builds the project again and changes the running program online. Without the toolchain, or with a construct the native build does not compile yet, the simulator runs the bytecode engine and says why in the debug console;"executionBackend": "vm"in the launch configuration asks for it. - Added: program images are signed, and a runtime can run only the images of keys it trusts
(specification 44 §5.11).
trust-runtime buildsigns the image it writes with the key the project'strust-lsp.tomlnames in[signing] key, or else with your own key,signing-key.pemin truST's configuration folder (~/.config/truston Linux), which the first build creates, readable by you alone and never printed; the build's report names the signer's public key. A runtime whosedevice.tomlin that folder lists[signing] trusted_keysinstalls, starts and changes online only images one of those keys signed, and refuses an image changed after signing; without the list it checks no signature and runs unsigned images too, as before.statusreportssignature:noneortrusted. - Added: truST installs the compiler for compiled programs itself (specification 44 §4.6). The
first build that needs it downloads this release's toolchain bundle (about 220 MB, once) into
truST's data folder (
~/.local/share/trust/toolchains/on Linux), checks it against the release'sSHA256SUMSand its own manifest, and uses it from then on.trust-runtime toolchain statusnames the toolchain the tools use, andtrust-runtime toolchain install [--from <file>]installs it, also without internet from a bundle file. VS Code installs it before a debug launch with its progress shown, and has the commands "truST: Install Toolchain" and "truST: Install Toolchain from File".TRUST_TOOLCHAINstill names a development toolchain, and nothing is downloaded then. Each release publishes the bundle of every platform it builds the tools for. - Added: a runtime refuses a program image built with another release's toolchain, naming both
program libraries, and an image with an I/O binding outside its process image, each with its
reason (specification 44 §10.2); an image names the hash of its sources. The toolchain's library
folders name their release in a
LIBRARYfile: a toolchain folder of an earlier build needsscripts/native/build_toolchain_libraries.shagain. - Added: trust-runtime on Linux, Windows and macOS runs compiled programs (specification 44 §8.8):
trust-runtime buildwrites the project's program image for this computer,program.tpi, with its deployment record, when the Rust toolchain of truST is installed (TRUST_TOOLCHAIN; without it the build still writesprogram.stbcand says so, and--execution-backend vmasks for it), andrunandplayrunprogram.tpiwith the cycle a chip runs: theio.tomldrivers, the retain file the bytecode engine writes (each restores the other's), the watchdog and fault policies, Live Values, online change with an image,cycle.step,program.installof an image into the project or its program store, and the debugger with breakpoints, stepping and the call stack, answered by the same code as on a chip.statusreportsexecution_backend: "native". The HMI, the ADS and OPC UA servers and the historian show a compiled program's variables as they show an interpreted program's, the ADS and OPC UA clients bind their points to its globals, the MQTT tag mappings bind its program variables, and the OpenOT telemetry reads its producers.runtime.toml[resource] max_statements_per_scansets the statements one task activation may run (default 1,000,000). A build ofprogram.stbcremoves an earlierprogram.tpi, andtrust-runtime fleet startbuilds astrust-runtime builddoes, so a computer runs the program the last build wrote.trust-dev testruns a project's tests compiled where the toolchain is installed;--execution-backend bytecodekeeps the bytecode engine. A global declaredATa process-image address now compiles. A project with a construct the native build does not compile yet is built asprogram.stbcwith a note naming the construct, bytrust-runtime build,fleet startandtrust-dev test, so it runs as before.capabilitieson an x86-64 or AArch64 computer lists the image format44.1beside STBC 2.0 and 3.0. - Added: online change of a compiled program (specification 44 §12).
bytecode.reloadwith a program image changes the running program at its next boundary and keeps its values as an interpreted program's change keeps them: the process images by address, every variable by its name, a structure's or an instance's members by name, a changed number, text or enumeration value when its new type holds it, an array's elements at the indices both arrays have, and references pointed at the same variable or madeNULL, with theonline_change_valueevents; the tasks go on with their schedule and the outputs keep their levels. The device works out what to carry from the two images it holds, so no tool keeps a copy of what a device runs. On the ESP32-C6 the new image is uploaded into the program region, 192 KiB of the RAM the C6 executes that its heap keeps for programs, and runs there, since an online change writes no flash: an image with debug hooks changes online, and a change can follow a change (the reference pasteurizer, 90,200 bytes, changed three times in a row on the bench). - Added: Live Values reach compiled programs as they reach interpreted ones:
setandvar.forceof a variable by its name, its retain key or its instance, applied at the cycle start and a force held every scan untilvar.unforce;io.writeandio.forceof the input and marker images before the program samples them, and of the output image before the outputs reach the drivers (specification 44 §13.3). Before, each was refused with aWriteRejectedevent. - Added:
trust-runtime bench project --execution-backend nativeruns a project compiled for this computer on the kernel, cycle after cycle in logical time, and reports each task's time per activation and the image's code and memory figure (specification 44 §14.3). - Added: compiled programs take references (
REF_TO,REF(),^,NULL) andVAR_IN_OUTin function blocks, functions and methods, and the Siemens profile's#localnames. The PLCopen motion single-axis library and its demonstration, OSCAT'sTICKERand twelve example projects now compile, and each runs as the runtime runs it: every variable of every scan compared, 846,000 values over 30 seconds of the vat pasteurizer. - Added: compiled programs take classes, function blocks that extend others, interfaces and
properties (IEC 61131-3 §6.6.5 to §6.6.8): a call through an interface, a
VAR_IN_OUT, a reference orTHISreaches the method of the instance's class,SUPERthe base's, a property is read through itsGETand written through itsSET, and a method'sVAR_OUTPUTreaches the variable given with=>. The five PLCopen motion object-oriented examples compile and run as the runtime runs them, about 375,000 values over 500 scans each; the conformance suite passes 20 of its 21 cases on compiled programs. A function block that neither implements an interface nor takes part in inheritance keeps its size. - Added: compiled programs take the string functions and the characters of a string (
LEN,LEFT,RIGHT,MID,INSERT,DELETE,REPLACE,FIND,s[i]), the numerical functions,EXPT,ATAN2, the bit shifts,MUX,MOVE, the comparison and arithmetic functions in their function forms, the counters in all their count types,SR,RSand Mitsubishi'sDIFUandDIFD, each giving the runtime's results and faults on every input the comparison tried. The conformance suite passes all 21 cases on compiled programs, and the Mitsubishi example compiles. - Added: compiled programs take dates and times: the operators on
DATE,TIME_OF_DAY,DATE_AND_TIMEand their long forms, the date and time functions (CONCAT_DATE,SPLIT_DT,DAY_OF_WEEK,ADD_TOD_TIME,MUL_TIME, …), the conversions between the date and time types and their literals, each giving the runtime's results and faults on every input the comparison tried. A date and time range fault of a compiled program is reported as the runtime reports it, not as an overflow. - Added: compiled programs convert numbers to and from text as the runtime does (
REAL_TO_STRINGspells the shortest digits that read back,STRING_TO_INTtakes white space,_, bases and signs), numbers to and from characters, and BCD (USINT_TO_BCD_BYTE,BYTE_BCD_TO_USINT). - Added:
trust-dev test --execution-backend nativeruns a project'sTEST_PROGRAMs compiled for the computer, each after a cold restart, with the bytecode runner's results and the runtime's messages of failed assertions (ASSERT_EQUAL,ASSERT_NEAR,ASSERT_TRUE, …). - Added: compiled tests compare texts in
ASSERT_EQUALand the other comparisons, with the runtime's message of a failed one (expected 'N', actual 'NE'); a compiled method takes a function block instance as an input, the caller's instance, and a method that returns its own instance (SetVolume := THIS) chains (b.SetVolume(10).Build()); aRETAINvariable of a function block compiles and is kept as the runtime keeps it;trust-dev test --execution-backend nativerunsTEST_FUNCTION_BLOCKs too. The OSCAT examples' tests give the same results compiled and interpreted. - Added: every program has a memory figure, the bytes of memory it holds while it runs, which the
runtime computes when it prepares the program: the prepared program, its persistent variables, the
activation stack for its deepest call chain, the operand stack, the engine's own state and, when
the project keeps retained values, their capture. The runtime reserves exactly that when it builds
the program, and no scan adds to it.
statusreports it inmetrics.program_memory, andtrust-runtime buildprints it for the computer that builds (program_memorywith--ci). A chip measures the memory it has for programs when it boots, reports it incapabilitiesasprogram_memory_bytes, and refuses a program whose figure is more, naming both numbers: at its install, before any byte is written, at its load and at an online change. A computer has no maximum. - Added:
statusreports inmetrics.scan_allocationsthe allocator operations made during the scans since the program started, on every runtime:engine, those the engine made running the program, which is zero, andservices, those the services made in and around the scans. A chip counts them with its heap and a computer with the trust-runtime program's allocator; a runtime that does not count them reportsnull. A scan that faults, a call refused at the call-depth limit and an output publication that the drivers refuse allocate nothing either. - The ESP32-C6 changes its running program online.
bytecode.reload(Admin) works on a chip as on a computer: the change is applied at the program's next scan boundary, without a reset, the lines keep their pins and output levels, and a change that fails leaves the program running as it was. The chip and the computer build the new program with one replacement: it installs the next generation, so a reference taken before the change is refused as stale after it, and the retained values carry over, with the retained function-block instances and references when the new program declares the same retained storage. The new program is prepared in the heap that remains less a reserve: 8 KiB of working memory, and the length of the newest checkpoint's payload when the running program keeps retained values. A program that needs more, by any limit of its preparation, is refused withinsufficient memoryand its numbers, and not as a damaged program; the dry run ofprogram.installon a chip takes the same limits, so a program that cannot fit is refused instead of resetting the chip. - The ESP32-C6 loads a newly installed program in place.
restartof the stopped program drops the program, its pins and its memory, then reads, checks and prepares the newest installed container, claims its pins and starts it cold or warm, without a reset, so the USB connection stays up. A program that cannot start leaves the resource faulted with the reason, and the nextrestarttries again.program.activeanswers on a chip, reading only the headers and records of the two containers. A chip's transfer ids now carry a boot count that it keeps in flash, so an id never repeats across boots, a loss of power included. The count's two 4 KiB sectors are declared: the chip filechips/esp32c6.tomlnames them (boot_counter_aat0x9000,boot_counter_bat0xA000), the chip file reader refuses a counter sector that is not one erase unit or that overlaps another region, andpartitions.csvdeclares them as the data partitionsbootcnt_aandbootcnt_b, which the next firmware flash writes to the board. - New control operations to move and install a program. The transfer operations and
program.installwork the same on a computer and on a chip (specification 34 PORT-05 and §11.2).transfer.begin,transfer.part,transfer.status,transfer.getandtransfer.abort(Admin) move a program into the runtime in checked parts of 4,096 bytes, each part with its CRC-32 and the whole upload with its SHA-256;transfer.getreads a reply that a runtime holds whole back in parts.program.install(Admin) installs the finished upload as the program the runtime starts next. It works only while the program is stopped (sendshutdownfirst) and answersresource_not_runningotherwise. It checks the container, the deployment record and its admission, the retained values against the newest saved ones, and the program's preparation, in that order, and changes nothing when one of them refuses. The reply is{"status":"installed","sequence":n,"program_sha256":"...","bytes":n}.bytecode.reloadalso takestransferin place ofbytes; it is still an online change that writes nothing, so a stop and arestartstart the installed program again.program.active(Viewer) reports the installed program and the one before it, on a computer and on a chip. A chip answersprogram.installafter its whole commit: specification 11 §6.8.1 gives the time a client waits for that reply, worked out from the container's size. - The ESP32-C6 installs a program over its USB control link. Its
stbcflash region is two program slots of 3,056 KiB, andprogram.installwrites the slot that does not hold the newest program, one 4 KiB erase or one 256-byte page in each pass of the control loop, reads the bytes back, and commits the container with the last four bytes of its magic, so a power loss or a failed step leaves the previous program running. At power-on and after a reset the chip starts the committed container with the highest install sequence; a damaged newest container refuses the start with its reason, and the older one is not started in its place.capabilitiesreports one slot, 3,129,344 bytes, as the chip'sapplication_slot_bytes, and the chip refuses a program that does not fit it. The steps to install are infirmware/trust-esp32c6/README.md. - The ESP32-C6 runs truST as a runtime, first on the ESP32-C6-DevKitC-1
(
firmware/trust-esp32c6), with the same runtime code astrust-runtime. It starts the installed project at power-on, claims the GPIO pins the project'sio.tomlnames, and answers the control connection on its USB port astrust-runtimedoes:status,health,tasks.stats,events.tail,faults,pause,resume,restartandshutdown, and Live Values (read, write, force and release).shutdownstops the program and the chip stays reachable;restartstarts it again. After a panic or a hardware-watchdog reset the chip reports the cause as its last fault.cargo xtask portability pack-projectpacks a project for it. - New control operation
capabilities(Viewer) on every runtime: the device descriptor that program admission uses (bytecode versions, scheduling profiles, limits, the application slot, the event store's default and maximum) and the last reset; a computer answersnullwhere only a chip has a value. - RETAIN variables on the ESP32-C6: with
[runtime.retain] mode = "file"the chip saves them into its checkpoint flash atsave_interval_msand atshutdown, and power-on and a reset start the program warm with the newest saved values, so an interrupted save loses at most that save. Thepathkey has no meaning on a chip. Retained values the program no longer declares, or whose type changed, are reported in the event log at the start, as on a computer. [runtime.events] capacityinruntime.tomlsets how many events the event store keeps (1 to 1,000,000; 1,000 by default on a computer), as PLC vendors size their diagnostic buffer per device.statusreports it asevent_capacity, and a chip receives it in its deployment record.- Every runtime has a stable runtime ID: generated once, kept in the project's
.trust-runtime/runtime_id, the same after every restart and different for two runtimes on one computer.statusreports it asruntime_id, and the mDNS announcement carries it with the truST version, the platform and whether control needs a token. Announcing and browsing followdiscovery.interfaces, use the computer's real host name, and never announce a local socket or a web server that is off.trust-runtime ide serveand setup mode announce themselves too, and setup prints its page under the computer's own.localname. ads.import_symbols.removeremoves imported ADS tag points from a project'sads.tomland regenerates the generated ST, andtrust-runtime ads remove-symbolsruns the same function from the command line.trust-runtime comm testruns thecomm.testrequest's code from the command line.comm.applyanswers theinstance_idof the connection it wrote foradd,upsertandedit, and an ADS tag import enables[runtime.ads]in the project'sruntime.tomlitself, live and throughtrust-runtime ads import-symbols, keeping every other line and comment.trust-runtime discoveralso finds MQTT brokers and OPC UA servers by their mDNS announcements, and OPC UA servers through the local discovery server's FindServers, and confirms each with the same probe as a known address.trust-runtime discoverruns one search for truST runtimes, ADS, Modbus TCP, MQTT and OPC UA from this computer or inside a runtime, and for EtherCAT (after you confirm the scan) and GPIO inside a runtime. It shows one device per thing found, with every protocol on it, while the search runs. Runtimes serve the same search ascomm.search.start,comm.search.statusandcomm.search.cancel.- The search also lists the USB devices and serial ports of the computer it runs on,
without opening or writing any of them: an STM32 on its boot loader, the ESP32-C6,
ST-LINK probes, USB-serial adapters and truST runtimes on USB, each with the serial
ports behind it, and every serial port with the USB device behind it
(
trust-runtime discover --only usb,serial). - The STM32F407 is a supported chip, first on the STM32F407G-DISC1
(
firmware/trust-stm32f407). The STM32F4 adapter selects the chip with itsstm32f407Cargo feature, the default. On the F407 it measures the board's crystal before it starts the 168 MHz clock, and falls back to the internal oscillator when there is none. Pins are named likePD12and checked against the chip's pin table when the firmware is built. - Each supported chip is described once, in
chips/<chip>.toml: its identity, processor, clock limits, flash and RAM with the firmware's layout, interfaces and pins, and the runtime components its firmware holds (today the engine). The files exist for the STM32F407 and the ESP32-C6.cargo xtask portability build-firmwarenow takes the chip name (stm32f407,esp32c6) and builds that chip's one firmware from its file; a bench board's name is refused with its chip's name.inspectandverifystill take the bench board. - Every standard function and function block has a versioned contract that states the
work and allocation it charges, the faults it can raise and the capability it needs
(specification 34 §5.2.1). A program whose call or function-block declaration does
not match the contract is refused when it is loaded, with the code
runtime_import_unsupportedand a message that names the function, for exampleunsupported import 'TON': a parameter's type or direction differs from the contract. Before, such a program was also refused, but the message did not say which function. trust-runtime buildalso writesdeployment-record.binnext toprogram.stbc: the program's deployment record, with its identity, the limits it requests and the built-in imports it needs (specification 34 §5.1.1). The runtime checks a project's record when it loads it and runs the program with the record's limits; a project without a record keeps the default limits.--cireports the record's path asdeployment_record.- Control Studio keeps each experiment's trajectory, approval, stop reason, abort state and restoration, and the simulated plant's configuration, seed, events and actuator, in the study's recording files, and the JSON export writes them; a failed Validate requirement names its value and its limit.
- Control Studio has its own engine, the program
trust-control-studio, so it also runs without VS Code.trust-control-studio identify --project <folder> --loop <id>identifies a loop's plant against its configured runtime and prints the model, its equation and its key numbers (or, with--json, the engine's answer);--recording <id>fits a kept recording again,--resumefollows a journaled identification to its end, and--resetstarts a new study.openlists loops and stages, andcancelends a journaled operation through the runtime's restoration path. The control token comes fromTRUST_CONTROL_TOKENor--token-file, never from an argument. The first Ctrl-C cancels and waits until the runtime confirms the restoration; a second leaves the experiment to the runtime.trust-control-studio serveis the same engine for the extension, which uses it from a later release. trust-control-studio validate --project <folder> --loop <id>decides whether the tuned candidate may be applied, without writing to the running controller. When the identified model is still provisional, it first records an independent live check with a different excitation and compares the frozen model's prediction with it; a pass qualifies the model. It then runs the current and the candidate settings as the program's actual Structured Text in isolated simulations, against the project's configured simulated plant or the qualified model, for every scenario the requirements and the loop declare, and prints each requirement's result (Pass, Fail or Inconclusive) with its measured value, limit, unit and source, the overall result, and whether Apply is available. A run that does not complete keeps the runtime's reason; missing evidence never passes.trust-control-studio apply,restoreandkeepchange the running controller only after a review: without--reviewthey print what the confirmation would write and a review identity, and--review <identity>confirms exactly that review, refused when the runtime has changed since. Apply writes the validated candidate, enters Auto without a bump and observes the response; Restore returns the parameter record, the mode and the references Apply found, from the journaled checkpoint, also after an interrupted Apply; Keep in project writes the running values into the project's sources. An Apply cancelled with Ctrl-C exits with status 130 and keeps the receipt of its restoration.trust-control-studio setupdiscovers a loop's bindings in the compiled project and saves the loop file.trust-control-studio tune --project <folder> --loop <id>designs a controller candidate from the study's identified model, without touching the running controller: Automatic chooses the simplest structure that meets the requirements (and lists why the simpler ones were rejected);--method simc|amigowith--choice faster|balanced|more-robustand--structure p|pi|pd|pidcalculates that formula unchanged;--method relayapplies the relay rule to a model identified with a Feedback relay (Kp = 0.45·Ku, Ti = Pu/1.2 from the measured cycles); and--method manualwith--setting <symbol>=<value>predicts your own settings. It prints the structure and gains, the method with its closed-loop time constant, the reason, the gain, phase and delay margins (an unbounded margin as ∞), and the predicted settling time and overshoot;--jsongives the engine's answer. Pinned settings are kept through every design, and a law's measurement filter time is part of the candidate. The candidate is stored in the study and is no longer current once the model is replaced. SIMC also tunes a second-order model, through Skogestad's half rule, and Automatic places the closed-loop poles of a delay-free model when no formula response meets the requirements. The engine'scontrolStudio/previewpredicts entered settings at once and stores nothing.- Control Studio tunes long processes. Experiments may run for up to 24 h and execute at the loop task's executions on the runtime's own time, so a busy computer or a faster simulation does not change them. Identify holds its step until the response has settled. At the time limit it accepts a response that has reached 63 % of its fitted change. It reconnects to a running test after VS Code restarts. Recordings follow the process instead of the controller's task: about 200 samples per expected settling time, each the average of the executions in between, so a slow process no longer produces hundreds of thousands of samples or holds up VS Code. The controller still runs at every execution, and a value held during a sample, such as a fixed parameter, is recorded exactly. Fits use the samples' real times. Saving a study no longer holds up VS Code: each recorded result is checked once and then reused, instead of being checked and copied again four times on every save. Closing Control Studio or VS Code never stops a running test, identification or Apply's observation; reopening follows it at once and shows its progress and its result.
- Control Studio: Automatic identification chooses the step itself. A loop in Auto with a setpoint gets a setpoint step in Auto. A loop in Manual gets a plant-input step, sized from the measured noise and the limits. Automatic never switches a loop in Manual into Auto. Validate checks a provisional model with a second step of the opposite sign.
- Control Studio: the controller's direction is stated in words, for example "increase the output while the measurement is below the setpoint". A model whose gain contradicts it is refused with that explanation, and a cooling loop is tuned with negative gains, also by the relay rule. A slow, self-regulating process is no longer taken for an integrating one, and a response too small for the measured noise is refused with the noise named.
- VS Code: truST: Simulation Speed… chooses 1× to 100× and saves it in
simulation.toml. The status bar, the Home view, Control Studio and Devices & Connections show the speed, and say when the computer cannot keep it ("×6.8 of ×10"). - HMI: while the runtime runs a simulation, the operator screens show a SIMULATION badge with its speed, in all eleven languages.
- HMI Builder: the palette lists Equipment, Instruments, Controllers and Groups and sequences first; the inspector shows a device's tag, its hardware status with one button and its key settings with their real values and units. Create PLC code writes one declaration and one call per new device into two checksum-marked regions, refuses regions changed by hand and keeps the program's own code; Create HMI places the declared devices in rows of instruments, controllers and equipment. Edits are never dropped, the toolbar keeps one row, canvas keys act only while the canvas has the focus, a just-placed device shows its settings at once, and an attached level sensor touches its tank's wall.
- VS Code: the truST sidebar has an HMI destination, and truST: Open HMI opens the project's start page in the HMI Builder, or the new-screen dialog for a project without screens.
- HMI Builder: My equipment lists the project's own function blocks in the Components panel;
placing one writes its definition and an automatic panel (outputs as lamps and values, interface
methods as buttons,
VAR PUBLIC RETAINmembers as settings), drawn as the new plaindeviceuntil Shown as in the inspector gives it one of My Symbols or an imported SVG, each picture input driven by the member of its name or the one chosen. A block extending a Plant block takes that block's definition. Libraries ship their own symbols too. - HMI symbols: a part can also move to and fro (
stroke), travel by a displacement (travelwithmove) or show only while a number equals a value (equalswithvalue), for an INT state's pictures; the symbol editor offers all three. - HMI Builder: Languages… in the top bar and Screen settings choose the screens' languages as tick boxes of truST's eleven languages by their own names, with the default marked.
- HMI Builder: the inspector shows every setting of a piece of equipment, the key ones first and the others under More settings, so alarm limits, alarm texts, signal ranges and feedback flags can be set before the PLC code runs; the equipment definitions now offer the feedback flags (Has running feedback, Has position feedback, Double-acting and the like) as settings.
- HMI Builder: in Live mode the equipment's Settings dialog has Keep in project, which writes the values commissioned on the PLC into the project (plant.toml and the generated declaration).
- Control Studio reads loop signals inside function blocks and records: members a block inherits,
structure fields, and enumerations (as their number). A controller's own
StateValueand a transmitter's inheritedValuecan now be watched and recorded. - Control Studio step tests can change a program variable by name instead of a
%Maddress. The runtime writes it between scans, guards it and confirms its restoration, as it does for an address. - A simulated plant can give a
Control.PIDits measurement directly with apid:<instance>feedback target. Model checks then work however the program computes the measurement (transmitter scaling, filters, fault holds). - HMI Builder: a PID controller's Controlled equipment offers heaters and variable-speed drives
as well as valves; Create PLC code drives the heater's power (
EH101.AutoPower := TIC101_Output) or the drive's speed, by what the equipment is, or another controller's setpoint (a cascade). - New example
examples/room_heating: a hot water tank and a room, built in the HMI Builder, with both temperature loops written by Create PLC code as ordinaryControl.PIDcode and set up in Control Studio from the code; a start/stop sequence, protections, alarms, trends, settings and English, German and Swedish screens. - HMI: a loop controller shows its loop as on a P&ID: a dashed signal line from its measuring instrument and one to the equipment it acts on, routed around the other objects. The lines follow the controller's configured measurement and controlled equipment, so the picture never contradicts the PLC code.
- HMI Builder: Screen settings list the runtime display's users and add or remove one
(Add user…, also HMI: Add User in the command palette); the password is stored only as
the hash
trust-runtime hmi-passwordmakes. A new project no longer needs a hand-editedruntime.tomlbefore anyone can log in. - HMI Builder: a control whose variable the runtime's write policy does not cover offers
Allow writing, in the inspector's new Problems section and as a quick fix in the page file;
it adds the rule to
runtime.tomlwith the control's role and limits. Create HMI Settings Page adds the rules of its controls, so a new settings page works at once. - HMI Builder: Screen settings edit the top bar's title, and a new screen's title is the project folder's name in words ("Room heating", not "room-heating").
- Add opt-in compiler/HMI request timing logs for preprocessing, analysis, cache waits and settings-probe execution, without recording source text or PLC values.
- HMI agent authoring gains project/selection provenance, exact draft inspection, multi-page proposals, viewport images, additional synthetic states, explicit incomplete-design saving, and revision-bound visual review. Incomplete pages fail native deployment checks. The existing builder can explore a synthetic proposal without runtime writes and open a placed controller's configured Control Studio loop. HMI CI uses a shared parity/browser gate and installed-VSIX smoke.
- HMI preview manifests identify the compiled renderer, actual fonts, source/frame revisions and PNG content; native-scale crops retain exact full-page pixels.
- HMI agent previews now include project PNG/JPEG/SVG images and unsaved SVG edits, reject unsupported animated or externally dependent SVG resources, and prevent applying an image proposal after its reviewed asset content changes.
- HMI agent validation reports rotated page bounds, overlap regions, shared contrast warnings and projected touch-target sizes for requested display profiles, while keeping visual findings separate from engineering errors.
- HMI agent composition accepts explicit regions, grid spacing, fill direction and pinned equipment. Library queries expose versioned composition guidance and shared-renderer references for process, equipment, repeated-unit, investigation and settings pages.
- Add an in-development HMI authoring toolset for existing VS Code agents and scoped local MCP clients: project context, visual component browsing, isolated draft composition/refinement, validation, browser-rendered previews and reviewed editor application with Undo. Dirty ST and HMI documents are respected; changed program revisions block stale application. Committed outcomes survive late cancellation, and retries retain their original application receipts.
- HMI draft review shows a read-only before/after comparison in the existing builder, with object changes, findings and explicit Apply/Discard actions. Drafts can be rediscovered after restart, verify stored content hashes, and support inspection by page region. Undo clears diagnostics for a newly created page that it removes.
- Specify agent-driven HMI authoring: visual catalogue discovery, revisioned drafts, composition, rendered review, editor transactions and CI/release acceptance. Implementation is tracked separately; no direct Codex SDK integration is shipped by this specification.
- Runtime mesh publish lists can select nested program/equipment members and array elements through the shared member-path resolver, while retaining existing global-name behavior.
- Plant HMI showcase pages combine process views, placed equipment panels, live readings, trends and alarms. Pipes approach a tank inlet around its body, scaled faceplate animations stay aligned, and compact trend axes keep their labels separated.
- HMI bars, gauges and split bars support validated display limit bands and an atomic inspector editor. Values can inherit project units and decimal precision without writing overrides into their page files; explicit object settings remain available.
- Plant analog instruments and tanks expose an optional engineering unit. HMI value snapshots carry analog measurements' and tank levels' actual alarm thresholds and priorities, allowing readouts and range marks to follow program changes without changing the measurement. Alarm readings retain the unit captured when raised, and their value column accommodates unit text. Numeric entry inherits units without rounding the editable value or widening the control's write range.
- HMI Builder Live controls use a named HMI login with the runtime's existing roles, confirmations, outcomes and audit path. The operating view requires the saved project's deployed bundle; simulator/test/recording sources stay read-only. Leaving Live, editing HMI files, changing targets or disconnecting closes the connection without replaying commands.
- Alarm details offer Show equipment, which opens the equipment's page and briefly outlines it without acknowledging or writing. Missing equipment has an explanation; the labels are included in all built-in languages.
- HMI trends support a fixed range for each pen and independent automatic scales. The inspector applies both limits together; the axis follows the first visible pen in its colour. Existing shared ranges remain the fallback, and CSV exports keep original values.
- HMI symbol definitions can be read with their cleaned SVG and written as validated TOML through the language server, supporting visual editing without changing the original artwork. Open a definition with HMI Builder to select parts, edit typed animation rules, bindings and ports, and try preview values. The editor uses the existing truST theme and shared HMI drawing; invalid text keeps the last valid preview while visual edits are paused.
- HMI Builder files (
docs/specs/39-hmi-builder.md): the language server reads, checks and writes the new HMI pages (hmi/pages/*.toml) and screen layout (hmi/screen.toml) throughtrust-lsp.hmiFileParse,trust-lsp.hmiFileFormat(canonical TOML: fixed key order, sorted bindings, one value per line) andtrust-lsp.hmiFileCheck(every binding checked against the compiled program, with file, line and a plain message).trust-lsp.hmiBindingsalso returns the program's variables with their structure, array and function block members (variables). A project may set its own colours per theme in[colors.dark]and[colors.light]ofhmi/screen.toml; tokens it does not set keep the default palette. Pages can hold text, images fromhmi/assets/, values, gauges, bars, split bars, status lights, trends (with aspanin seconds), tables, panels, groups, lines and page buttons. - HMI Builder in VS Code: page files (
hmi/pages/*.toml) open in a drag-and-drop editor with Components, canvas and inspector. Add a component by clicking or dragging it onto the page, select, move with the mouse or the arrow keys on an 8 px grid (Alt places freely), resize by its handles, delete, and edit labels, positions, ranges and bindings in the inspector. Every change is a normal text edit of the TOML file, written by the language server's canonical writer, so undo, save and Git work as for any file; Open as text stays available. The canvas draws with the same drawing module and font as the runtime, in the project's screen size, theme and colours. - Variables: the Tags tab of the bottom panel lists the program's programs, function block instances and members with their types, searchable. Drag a variable onto the page to create a matching component, bound to it and labelled with its name (a boolean makes a status light, a structure or array a table, any other value a value card), or onto a binding in the inspector. Each binding's ⋯ button opens a picker offering only the variables the binding can have. A drop that cannot work says why, in the binding checks' words. The list is read again when a source file is saved, and shows why while the program does not compile. Configured program instances show the type their configuration declares.
- Panels: the Components panel and the inspector resize by their edge and collapse to a strip; the bottom panel resizes by its top edge. A narrow editor shows the side panels as strips. The arrangement is remembered for the user in every page.
- Unsaved builder edits come back after a VS Code restart, in the builder.
- Operating the runtime HMI: setpoint entries and buttons on
/hmipages send commands (POST /api/hmi/command) for a logged-in user. Users, session times and a deny-by-default write policy are set inruntime.tomlunder[runtime.web.hmi];trust-runtime hmi-passwordprints a user's stored password hash. A page's controls can only narrow the policy (role,min/max); the editor reports a control that asks for more. Each command is checked (session, page version, permission, limits, type, interlock), written at a cycle boundary, read back, and confirmed by its feedback or readback; a repeated command id runs once, a pulse ends by itself, an interlock refuses with its reason, and every outcome is written to the runtime's audit log at info level. The page shows the user and role, a login dialog, each command's outcome, and becomes read-only when the session ends; a command whose answer is lost is shown as unknown and never sent again. Page files gain the control keysrole,confirm,action,pulse,expectandtimeout, and controls the bindingsfeedback,interlockandinterlock-reason. - HMI screen layout: the runtime draws the top bar of
hmi/screen.toml(title, logo, page title, alarm counter, clock and the logged-in user) and the navigation as a side bar or tabs, built from the page list. Pages may setorder,icon,groupandhidden. Popups (hmi/popups/<name>.toml, with their ownsizeand optionalpinnable) open from an object's[object.click] popup, with their own values and commands. They are modal until pinned, and close with their button or Escape. Page buttons go to their page. Project images underhmi/assets/are served to the display. The editor opens popup files, edits a page's place in the navigation, a popup's size and pin, and an object's click target, and New HMI Screen creates a screen layout and a first page from three templates. Deployment checks popups and click targets. - HMI controls: switches, selectors, mode selectors and sliders work on
/hmipages. A switch writes the opposite of its value; a selector writes one of itsoptions(by name to an enumeration, by position to a number); a mode selector writes Manual, Auto or Track, for example to aControl.PIDMode; a slider writes where it is released, within its limits (default 0 to 100). These controls send nothing while their value is unknown or stale. The runtime writes and reads back enumerations by value name, and checks each value against the control's options and limits; the editor checks options against the bound enumeration. The inspector edits a control's role, confirmation question, timeout, button action, pulse and options. Every control takes a press anywhere on its box, and an applied or confirmed outcome fades after a few seconds. Clicking an object in the editor no longer snaps it onto the grid. - Deploying the runtime HMI:
POST /api/hmi/deploytakes an HMI bundle (every file underhmi/with its SHA-256 version), checks it against the running program and the write policy, and activates it in one step; a refused bundle leaves the running one untouched and answers with its problems (file, line and message).POST /api/hmi/rollbackrestores the previous bundle. Both need the runtime's administrator access or an Engineer's HMI session and are written to the audit log. Open pages are told to reload, and their commands are refused as out of date. The language server'strust-lsp.hmiBundlebuilds and checks the project's bundle. A program deployment (POST /api/deploy) that would break bindings of the active HMI is refused with the broken bindings listed, unless it carries an updated bundle (hmi) or accepts them (accept_broken_bindings); an object bound to a variable the running program does not have reads "Not in running program". - Alarm library (
libraries/alarm,docs/specs/40-alarm-library.md): alarms are function blocks in the program, following ISA-18.2 and EEMUA 191.Alarm.Alarmholds the lifecycle (active and unacknowledged, active and acknowledged, cleared and unacknowledged), message, priority (High, Medium, Low), on- and off-delays, latching, shelving, suppression by design, out of service and first out (FirstOutGroup).DigitalAlarm,LimitAlarm(with deadband; a value that is not finite counts as beyond the limit),DeviationAlarmandDiscrepancyAlarmextend it, and a project's own block can extend it too by callingEvaluate. - Runtime alarm service: the runtime finds every alarm instance of the running program (at any depth, customer blocks
included), reads them at each call of
Hmi.Displayand records each transition with its time, message, priority and, for an operator's action, the user; the latest 1 000 records are kept in memory.GET /api/hmi/alarmsreturns the alarm list (unacknowledged first, then by priority, then newest; shelved, suppressed and out-of-service alarms apart, with their counts) and the records; the value stream sends the list when it changes and each new record.POST /api/hmi/alarmacknowledges, shelves (1 s to 24 h), unshelves, resets or comments an alarm for an Operator, and takes it out of or back into service for an Engineer, as a command with confirmation and an audit record. - Alarms on the runtime display: with
alarms = truein the screen's[top_bar], the top bar counts the alarms that annunciate, in the mark and colour of the highest priority (High a red diamond, Medium an orange triangle, Low a yellow circle), shows the newest alarm and notes shelved, suppressed and out-of-service alarms; it blinks while an alarm is unacknowledged and stops while the data is stale. Thealarm-listcomponent has tabs for active, shelved, suppressed and out-of-service alarms, an Acknowledge button on unacknowledged rows, pages, and a dialog per alarm with the actions its state allows (shelve for 15 minutes to 24 hours, unshelve, reset, comment, and out of service for an Engineer). Theevent-logcomponent shows the alarm transitions and each command's final outcome, newest first. Objects bound to a function block instance show the alarms that instance holds. Projects may set the new colour tokensalarm-high,alarm-mediumandalarm-low. - With
[runtime.openot]enabled, the runtime publishes every alarm transition to the OpenOT ring as a condition record (ConditionActive,ConditionCleared,ConditionAcknowledged, shelving, suppression, service, comments and resets) whose identity is the alarm's instance path (SourcePath, and aConditionIdderived from it), with the user of an operator's action. The runtime keeps the alarm list and records itself, so the history is recorded with or without the web server. - Trends on the runtime display open with the last
spanseconds from the runtime's historian, when it is enabled (GET /api/hmi/history). - Renaming a variable, an instance, a structure field or a program now also renames it in the bindings of the HMI pages and popups, and Find All References lists those bindings. Only the renamed part of each path changes.
- HMI Builder: binding problems of page and popup files appear in the Problems view at their lines, checked when a file opens or changes and when a source file of the project is saved, and on the canvas as an outlined object with "!" whose problems show on hover.
- HMI Builder Live mode: the editor's Live switch shows the page with the values of the running simulator or runtime,
read every 250 ms through the new control request
hmi.read(Viewer, at most 1000 paths; answered at the program's next call ofHmi.Displaywith the values, the cycle and the paths the program lacks). Live mode observes: controls send nothing and editing is off. Without a running target it says so and every value is unknown. An online runtime that answers counts as running even without a debug session. - HMI Builder edits like a design tool (
docs/specs/39-hmi-builder.md§3.3 item 8): Shift-click, a box dragged on empty canvas, Ctrl+A and Escape select; Ctrl+C, Ctrl+X and Ctrl+V work across pages through the clipboard and Ctrl+D repeats the last step, a copied binding moving to the next number when that variable exists (TT101toTT102); the inspector aligns and distributes, and guides snap to other objects' edges and centres and show equal gaps while dragging; Ctrl+G groups, Ctrl+Shift+G ungroups and a double-click edits one member; Ctrl+] and Ctrl+[ (with Shift: to the front or the back) change the layer order. Pages and Layers tabs sit beside Components: add, rename, duplicate, reorder, hide and delete pages; the objects front to back, hidden in the editor or locked. Ctrl+wheel and pinch zoom at the pointer, Space-drag and middle-drag pan, Shift+1 fits, Shift+2 zooms to the selection, Shift+0 shows 100 %, and a page reopens at the zoom it was left at. A right-click anywhere on the canvas opens a menu with cut, copy, paste, duplicate, delete, group, layer order, align, lock and Open faceplate. While a run target is connected, the Tags panel and a binding's picker show each variable's current value. - Runtime display: a control the signed-in role may not use is dimmed under a lock naming the role that may (Operator
or Engineer, or Locked where the write policy lets no role write it), and a press says so instead of sending. The
page, popup and faceplate answers carry each control's lowest role as
roles. - Runtime display alarm list: columns for priority, time, tag, message, a limit alarm's value against its limit when it
was raised ("91.5 > 90.0"), and state, under headings that sort (and reverse on a second click); a row of chips to
search and to filter by priority, equipment and unacknowledged, saying how many rows it shows; and sound (EEMUA 191):
a High alarm sounds until acknowledged or silenced, a Medium alarm once, with Silence in the top bar. The alarm list
rows carry
raised: { value, limit, high }. The equipment example gains an Alarms page. - Runtime trend history, on by default: every value a trend of the active HMI bundle shows is kept in memory for 24
hours (a sample a second for the last hour, then each minute's lowest and highest value), read at each call of
Hmi.Displaywhether or not a screen is open, so a trend opens filled without enabling the historian./api/hmi/historyanswers from it and always says"enabled": true. - Runtime display trends: range chips for 5 minutes to 24 hours, filled from the runtime's history; a box dragged on the plot zooms into its period, the wheel zooms the time axis at the pointer and a drag along the time row pans, each pausing the live scroll until Back to live; a cursor line that reads each pen at its time; a legend entry hides or shows its pen; Export saves the visible window as CSV; Expand shows the trend over the page.
- Runtime display: a day and night switch in the top bar that any operator may use; each panel's browser remembers it, and the project's theme stays the default.
- Runtime display: on a touch screen a number entry opens a large keypad under its allowed range, with touch-sized Cancel and Send; a mouse or keyboard opens the entry as before.
- HMI faceplates on a page: an object of type
faceplateshows a faceplate bound to one instance, exactly as its popup does, under a slim header naming the instance, with its values on the page's stream and its controls' commands. The HMI Builder's Components panel has a Faceplates group: a faceplate placed from it, or from a symbol's "Place faceplate on page", offers only the instances it fits; a placement keeps its proportions when resized. The editor,trust-runtime hmi checkand deployments report a faceplate the project lacks and an instance it does not fit, andtrust-runtime hmi testdraws placements. The Equipment library ships cards for the pump, valve, motor, tank and PID loop, and the equipment example has a Cards page. - HMI visibility binding: every object takes
visible, a BOOL of the program; it is shown while TRUE, hidden while FALSE, and stays shown while the value is unknown. The inspector names it "Visible while" and sets it on every object of a selection or group. - Docs: an HMI Builder guide (Develop > Operator Screens): getting started, the main tasks with pictures, and every key.
trust-runtime hmi check [--project <dir>] [--json]: the HMI Builder's checks over every HMI file of a project for its own CI (files load, assets present, bindings resolve in the compiled program, controls within the write policy), printingfile:line: messageand exiting 1 on any problem.trust-runtime hmi test [--project <dir>] [--update] [--chromium <path>]: every HMI page drawn in headless Chromium with design samples, laid out as the runtime display lays it out, and compared with its approved screenshot inhmi/screens/; a changed or unapproved page fails and leaves<page>.actual.png,--updateapproves.- HMI library (
libraries/hmi) andHmi.Display: the screens are served where the program calls the block, in a normal task of its choice. After that task the runtime applies the commands the screens queued, copies the values they show and reads the alarms; the web server works from that copy, so no HMI work runs on the PLC thread between scans. A page gets its values right after each call, at most four times a second, and is told how often the calls come; a command the program has not taken within its time limit is withdrawn and never applied later. A project with screens callsHmi.Displayto show live values, and its alarms are read and recorded at that call. New HMI Screen adds the library, aScreensprogram calling it and its task (every 250 ms, priority 10), the HMI Builder warns at the top ofhmi/screen.tomlwhen no program calls it, and the examples with screens call it. - Values of a link without data: each
io.tomldriver declares the input bytes it fills (Modbus TCP points or block, MQTT input points or raw topic, EtherCAT modules, GPIO input lines). A page value located there (anAT %I…declaration or aVAR_CONFIGentry) shows as stale, with the link's time since its last data, while that link exchanges no data; a value the program computes is not marked. - HMI Builder Deploy: the editor's Deploy button saves the project's HMI files, checks the bundle, and sends it to the selected runtime's web server (found from the endpoints the runtime reports, with its control token). The editor check's or the runtime's problems stop it and are listed with file and line; the simulator has no web server, so Deploy says to use Live mode or start a runtime. After a deployment the editor offers Roll back.
- HMI Builder quality checks: the Problems view and the editor warn about touch targets below 44 × 44 px, numbers
bound to REAL or LREAL without a unit (
unit = ""says a number has none; the inspector offers No unit), objects that take bindings but have none, labels and texts cut to fit, and project colours that lower the contrast of the drawing's text or look like the warning or an alarm colour. Warnings never stop a deployment. - HMI Builder Colours: with nothing selected, the inspector lists the screen theme's colour tokens with a colour picker
each; a chosen colour is written to
[colors.<theme>]ofhmi/screen.toml, and Reset brings back the default. - New example
examples/hmi_tank_level: a tank level loop with the realControl.PID, Alarm library alarms (a high level, a high-high trip and a pump overload trip with first out, and an own level-rise detection suppressed by design in Auto), and screens built in the HMI Builder. It runs in the simulator and ontrust-runtime, and is in the example gallery. - New tutorial
examples/tutorials/24_hmi_alarms: declaring alarms and operating them in the HMI's runtime display — priorities, acknowledge, shelve, first out on a trip, equipment alarms and own detection. - HMI Builder: Add pen adds a variable to a trend as a pen named after its last part.
- HMI Builder: P&ID-based symbols for a pump, an on/off valve and a control valve, offered under Equipment. What moves in the plant moves in the symbol: a running pump's impeller turns, a valve's ball or plug stands where the valve is, and a closed valve is drawn filled. The tag and state word stand under the symbol, with fault, manual, out-of-service and stale signs beside them; nothing turns when the viewer asks for reduced motion.
- HMI: languages.
languagesinhmi/screen.tomlandhmi/texts/<tag>.tomlper language translate every text of a project, and every word truST shows can be changed. truST ships its own words in English, Chinese (Simplified), German, Spanish, Japanese, Portuguese (Brazil), French, Korean, Italian, Turkish and Swedish. The runtime display has a language button; the browser keeps the choice and?lang=fixes a panel to a language. Numbers follow the language. - HMI Builder: a language picker above the canvas shows the page in each of the project's languages; in another
language a click on a text edits its translation in place, written to
hmi/texts/<tag>.toml. The Texts tab lists every text in every language with the missing ones marked, and Longest lengthens every text by 40 % to find the objects too small for a translation. The Problems view checks text files and warns of every text cut in any language. - Language server:
trust-lsp.hmiTextsgives the editor a project's languages and texts;trust-lsp.hmiFileParseandtrust-lsp.hmiFileFormatread and writehmi/texts/<tag>.toml. - HMI: Chinese, Japanese and Korean show in truST's own fonts (Noto Sans, subset to GB 2312, JIS X 0208 and modern Hangul), each loaded only for a page that shows such text and drawn in that language's forms.
- Libraries: the Equipment library (specification 41): motors, fixed- and variable-speed pumps, on/off and control valves and tanks with Auto, Manual and Out of service, interlocks and permissives with reasons, feedback monitoring with latched faults, Alarm library alarms, counters and safe restart behaviour.
- HMI pipes (
[[pipe]]in page files): a pipe joins two ports of the symbols (pump, valves, junction), is routed around the objects with few bends and follows them when they move. A process line is solid, a signal line dashed. A pipe shows flow only from its ownflowbinding while it reports flow, backwards when negative; never from a pump's or a valve's state. The language server checks the ports, the line style, the bends and the flow binding. - HMI: the phase 2 process and motion equipment of the control course, as two-dimensional P&ID-style symbols with their moving parts: open, gravity-drained and cascaded tanks filled to their level, dosing pump, supply line, mixing point, variable-speed pump, control valve with positioner; temperature, level, flow, pressure, position and speed sensors and the redundant sensor pair; cold room, refrigeration unit, electric heater with its vessel, cooler, jacketed and coupled vessels, heat draw-off; servo motor with drive, shaft, drum with load, flexible coupling, encoder, rotary arm and axis position indicator; the network link with its connection, packet age, hold and fallback. The Components panel groups them under Liquid, Thermal, Motion, Instruments and Communication.
- HMI: an instrument attaches to the equipment or the pipe it measures (
attachin page files) and is joined to it by a line; the HMI Builder attaches it in the inspector or by dragging from the instrument's handle. - HMI Builder: the inspector's Preview shows the selected object in each of its states, including stale and unknown, without a running program; nothing is saved.
- HMI faceplates (
hmi/faceplates/*.toml,hmi/pairs/*.toml, specification 39 §8.7): faceplate files open in the HMI Builder with their description and fields; an object's click opens a faceplate of the project or its libraries, for the instance its bindings name;trust-lsp.hmiFaceplateslists them. A pair whose block lacks a member its faceplate needs, or holds the wrong kind of value, is an error in the pair file and at the block's name in its ST file. In the runtime display a faceplate is titled by the tag of the object that opens it. - Equipment library: faceplates and pairs for the pumps, valves, motor and tank, the PID loop and the cascade;
controller blocks
PIDLoopandCascadeLoopthat runControl.PIDfor an operator (specification 41 §6). The library now needs the Control library too. New symbols: motor and loop controller. - New example
examples/hmi_equipment: a pump, a control valve, a tank and a PID loop of the Equipment library on a simulated tank, each opening its faceplate. - Equipment library: the equipment of a PLC programmer's plants, built on two bases. Motors (
Fan,Agitator,Blower,Conveyor), drives (VFDMotor,VariableSpeedPump,VariableSpeedFan,DosingPump),ReversingMotor,TwoSpeedMotor,Heater; actuators (Cylinder,Damper,ModulatingDamper,MotorValve,AnalogOutput); measurements (AnalogInputwith temperature, level, pressure and analyzer inputs,FlowInput,WeighScale,DigitalInputwith level and pressure switches); groups (InterlockGroup,DutyGroup,Sequenceto extend with your steps,Counter). 23 faceplates and 37 pairs; a block that extends a base opens its base's faceplate and shows its own symbol in it. New symbols: fan, blower, agitator, conveyor, damper, cylinder, motor valve, weigh scale, analyzer, level switch and pressure switch. - Manuals for the Equipment, Alarm and Control libraries, under Develop › Libraries, like the OSCAT and PLCopen Motion manuals; a test compiles and runs every example in them.
- HMI Builder: Create HMI Pages from Program writes an overview page with a symbol for each instance whose block
has an equipment pair, bound to it and opening its faceplate; Add Missing Variables to Program declares and
calls in the program what a page drawn first binds, as one change shown for approval; Create HMI Settings
Page writes a page of entries for a program's retained variables, for engineers. Pairs name the member each
property of their symbol shows in a new
[bind]table, checked like their fields. - Communication link states for the program and the HMI (specification 42): a new Comm library with the
Comm.Linkblock, which the runtime fills at the start of every scan with its link's state (connected, degraded, failed, connecting, disabled or unknown), how long it has been in it, when data was last exchanged and the latest error, for everyio.tomldriver (modbus_tcp:0,mqtt:1, …), ADS client connection (ads:<connection>) and OPC UA client connection (opcua_client:<connection>). The block raises an alarm when its link has delivered no data for a set time (failed, misnamed or never connected), and opens the new link faceplate from thenetwork-linksymbol. Manual under Develop › Libraries. - Communication links can be switched off, tested and counted:
Comm.LinktakesEnable,TestandResetand reportsDisabled,Exchanges,Failures,ResponseTimeandTestResult. Anio.tomldriver's link switched off is neither read nor written from the next scan and raises no alarm; a test reads the device once without touching the process image and reports "answered in 12 ms" or the error. The link faceplate shows the counters and gives engineers Link on, Test link and Reset counters, within the runtime's write policy. Thenetwork-linksymbol takesout-of-service: a link switched off on purpose reads Off, not Lost. - Each link reports its protocol's own state as
Comm.Link.Detail, shown on the link faceplate: a Modbus TCP server and unit with its last exception reply by name, an MQTT broker and whether its session is connected or why not, what EtherCAT discovery found, a GPIO chip and its lines; an ADS client connection's summary and an OPC UA client connection's session detail. - HMI Builder Live mode: besides the run target, a page can show test values typed in the inspector for each binding,
or a recorded run: Record keeps the run target's updates and saves them under
hmi/recordings/, and the Recording source plays one back with Play and a timeline that can be scrubbed. Live mode's sources, recording and status stand on a bar of their own under the top bar. - Adaptive HMI display: on a screen smaller than the design size the runtime display shows a page at scale 1, so
texts and touch targets keep their sizes, and lays it out again at the screen's width, scrolling vertically. Panels
and groups, each display, control and text, and the process graphic move as blocks, a design's columns kept
together; a block too wide scales, never below 44 × 44 px controls, or, with
arrange = "row","column"or"grid"on a panel or group, rearranges its objects. The editor previews a page at 1920 × 1080, 1280 × 800, 1024 × 768 and on a tablet in landscape and portrait. - HMI components: vessel, heat exchanger, filter, cyclone, crusher and vibrating screen (Process), and frequency converter (VFD), drive, contactor and circuit breaker (Electrical), each with its states, words in all 11 languages and their own headings in the Components panel.
- My Symbols: a project's own SVG drawings become HMI symbols (
hmi/symbols/<name>.svgand.toml, placed asmy:<name>), their named parts turning, filling, showing values, blinking or changing colour with the bindings, in the editor, Live mode and the runtime display, with ports for pipes. The drawing is cleaned when read (scripts, event attributes, embedded objects and outside references are refused). HMI: Import Symbol copies an SVG and writes its definition; the Problems view and deployment check it. - P&ID view: P&IDs (
hmi/pid/<name>.toml) open in the HMI Builder in the HMI's own look, each symbol with its tag and without states, warned of only for cut texts (a hand valve needs no variable). A page that names its P&ID (pid = "<name>") shares its process objects with it: a change of their type, tag or bindings, an object added or deleted, and the pipes between them reach the other view at once; positions, displays and texts stay each view's own. Generate Process Screen from P&ID writes a linked page bound to the same instances. - HMI screen tests in CI (
scripts/hmi-screen-tests, HMI Screen Tests job): an operator journey through the tank-level example in the runtime, and the drawing compared with approved screenshots in both themes. - HMI Builder: a symbol shows its ports under the pointer; drag from a port to another to draw a pipe, or onto a pipe to branch into it through a new junction. Click a pipe to select it, set its line style and flow in the inspector, drag it to bend it (Straighten undoes that), and delete it with Delete. A valve or a junction dropped onto a pipe splits it into two pipes through it; deleting an object deletes its pipes.
- HMI Builder: the messages a program gives its alarms are listed in the Texts tab to translate like any other text;
trust-lsp.hmiBindingsgives them asalarms. - The examples whose old HMI was removed have screens again, built for the HMI Builder: plant_demo, web_ui_complete_project, hardware_8do, memory_marker_counter, network_canvas_demo, communication/ads_server_basic and tutorial 12.
- The runtime shows these pages read-only at
/hmi(the first page by name) and/hmi/<page>when the project hashmi/screen.toml; without it,/hmisays the project has no HMI. Pages are read when opened, and a file that does not load shows its errors with file and line. Values stream over/ws/hmi?page=<page>every 250 ms (or the scan period, if longer), including members of function block instances such asControl.PID, structure fields and array elements; a value whose program stops scanning or whose connection drops turns stale with its age. The page data and the stream need the Viewer role. The HMI ships the Inter font (SIL Open Font License). - CODESYS conversion profile (docs/specs/07-standard-functions.md 2.10): a project whose
trust-lsp.tomlsetsvendor_profiletocodesys,beckhoffortwincat(TwinCAT is built on CODESYS) runs its conversion functions with the CODESYS rules: they round half away from zero, keep the low-order bits of the target width instead of an overflow, convert REAL/LREAL to and from DWORD/LWORD as numbers, and make a bit stringTRUEwhen it is not zero.trust-runtime build/check/run,trust-dev test, debug sessions andtrust-harness --project DIRfollow the project's setting; atrust-harnessloadmay name its own"vendor_profile", and the Rust API hasCompileSession::with_conversion_profileandTestHarness::from_session.siemens,mitsubishiandgxworks3keep the IEC rules, which stay the default. Projects that already set one of the three CODESYS-family profiles for editing now also run with these rules. An unknownvendor_profileis refused by these tools and reported by the language server asC006. The CODESYS rules that conflict with IEC 61131-3 are recorded as DEV-024 to DEV-026. - Vendor-extension conversions used by CODESYS and TwinCAT projects compile and run
(docs/specs/07-standard-functions.md 2.9):
REAL/LREAL_TO_BYTE/WORD/LWORD,LREAL_TO_DWORD(numeric, likeREAL_TO_<unsigned>of the width),<number>_TO_BOOLandTO_BOOLof a number (value <> 0),BOOL_TO_REAL/LREAL. They were rejected with "cannot convert 'REAL' to 'BYTE'". The IEC conversions are unchanged. BYTE_TO_BOOL,WORD_TO_BOOL,DWORD_TO_BOOLandLWORD_TO_BOOL(andTO_BOOLof a bit string) are available and transfer the rightmost bit, as IEC 61131-3 Table 24 specifies:BYTE_TO_BOOL(16#80) = FALSE,BYTE_TO_BOOL(16#81) = TRUE.trust-lsp check [--project DIR] [--format text|json] [--file PATH]... [--deny-warnings]prints the diagnostics the editor shows for every source file of a project, without an LSP client (docs/specs/14-lsp.md §7.7): the project is loaded fromtrust-lsp.tomlexactly as the language server loads a workspace folder, paths are relative to the project in the platform's form, and the exit status is 0 without errors, 1 with errors (or warnings under--deny-warnings) and 2 for a usage or I/O problem, including a--filethat is not one of the project's source files.- Control Studio designs a trajectory feedforward (Specification 35 §17.16): for a PID whose program adds
Kv·ṙ + Ka·r̈of a planned trajectory, Tune designs the PID as forControl.PIDand setsKv = b/nandKa = a/nfrom a modeln/(a·s² + b·s), which for torque to angle are the friction B and the inertia J. The PID's margins and Auto-entry allowance apply, a gain outside its declared range is refused, and Tune names all five values. - Control Studio Requirements offers Measurement noise whenever the loop declares a measurement filter (a low-pass or notch) among its tuning components; that filter's validation needs the noise band.
- Control Studio designs P for a delayed integrating process (SIMC's Kp = 1/(K′(λ + L))) and checks it like PI; Apply on an integrating process starts from the level where it rests instead of waiting for the identification's level to return.
- Control Studio Requirements always offers an optional Input disturbance, a constant load added to the plant input in its own trial; Automatic tuning checks that load trial on the model for every family and refuses a design whose gains leave their declared range, naming the gain it needs.
- Control Studio identification fits an integrating process with its delay, so SIMC's integrating PI is available for an identified tank.
- Control Studio applies a static decoupler: a PID group whose program writes
u = D·vin Auto has Tune calculateD = K⁻¹·diag(K)and each PI through it, and Apply write the gains andDtogether, each controller starting at its share of the plant inputs (Specification 35 §17.15). The runtime's controller transfer gains thedecoupled-outputinitialization formula, and an isolated run records up to 64 channels (was 32), so a group's transfer check fits; the channel-sample budget is unchanged. - Control Studio setup form: Gain schedule sets up a
Control.PIDwhose program interpolates its gains between two to four operating points; it writes one loop per point, identified, tuned, validated and applied at that point with the schedule active (Specification 35 §17.14). - Control Studio setup form sets up LQG: the state-feedback structure's LQG design takes the program's estimates, their gains and observer gains, the noise variances and the model file the estimator implements (Specification 35 §17.1).
- Control Studio Tune shows a state-feedback candidate's gain, phase and delay margins and Ms for its loop broken at the plant input (Specification 35 §17.10).
- Control Studio setup form: Smith predictor sets up a
Control.PIDthat reads the measurement plus the program's predicted correction (Specification 35 §17.13). Tune writes the model's gain, time constant and delay into the program and designs the PI for the process without its delay, refusing a settling time no longer than the delay. Tune now shows the margins beside every candidate on the Tune page itself: the page rebuilt the candidate without them. - Control Studio setup form: Feedforward sets up a
Control.PIDwhose program adds a feedforward from a measured disturbance (Specification 35 §17.12). Identify fits one model from the controller output and the disturbance, Tune designs the PI and the feedforward gain, lead and lag from both paths and shows the predicted deviation with and without feedforward, Validate adds a measured-disturbance step, and Apply starts the feedforward state at the measured disturbance so the switch does not bump. The runtime's controller transfer gains thescaled-measurementinitialization formula. - Control Studio: a cascade set up in the form is one loop tuned together (Specification 35 §17.11): one identification of the plant input to both measurements, the inner controller designed first and the outer one through the predicted closed inner loop, one validation of both in Auto, and one Apply and Restore that switch both controllers together. The runtime's controller transfer switches additional controller modes at the same task boundary (
additional_modes). - Control Studio setup form: Other law sets up a law Studio has no design formula for (MPC, ratio, split range, override, gain schedule, …): Tune searches up to 64 combinations of the program's coefficient values on the actual ST and uses the best one that meets every requirement (Specification 35 §17.1). Family names read as words ("Paired PI", "State feedback").
- Control Studio setup form: Own P, PI or PID sets up a P, PI or PID law the program computes itself from its own gain variables (Specification 35 §17.1). Tune designs the gains to the requirements as for
Control.PID, offers only the families the law has, and checks them on the actual ST; validation starts the integral term and the derivative's previous value bumplessly. - Control Studio Tune shows every PID-family candidate's gain, phase and delay margin and peak sensitivity Ms (Specification 35 §17.10). Automatic tuning places the closed-loop poles for the declared settling band when no formula response settles in time, so tight bands on delay-free models are met instead of refused.
- Control Studio accepts loop signals without an I/O address: the controller output and setpoint may be program variables, and the measurement may be one the program only reads (Specification 35 §17.1). Plant simulation reads and writes such program variables by their symbolic path (Specification 46 §13), so validation runs on them. A measurement the program computes itself is refused with the reason.
- Control Studio: each member of a PID group has its own identification step, and Tune predicts every member's response with the current and tuned gains by running the program on the group model (as for switching, state-feedback and other project-owned controllers). Applying a cascade's inner loop is refused, before anything is written, while its outer controller is in Auto, and the runtime rejects the transfer if the outer controller switches to Auto before it takes effect. A loop whose gain stays below one now gets its predicted response, with an unbounded phase margin. Setup names the controller action by the direction the output moves.
- The examples gallery offers the five Control Studio examples (PID loop, cascade, interacting loops, ON/OFF control, state feedback), and the Control Studio guide links them.
- Tune shows the predicted response of an ON/OFF or state-feedback candidate and of the current settings, as it does for
Control.PID: it runs the program's own ST with both settings on the identified model in isolated simulation. Nothing is written to the running controller. - The setup form sets up an existing state-feedback law in the program, designed by LQR or pole placement: the instance that runs it, the plant input, the controlled output with its setpoint, and one to three further measured outputs, each with the variable that holds its gain, plus the reference gain. Studio identifies the plant with the measured outputs as the model's states, so every gain belongs to one measured signal.
examples/control-state-feedback-commissioningholds the second of two tanks in series with both levels fed back to the pump. - The setup form sets up an existing ON/OFF controller in the program: the instance that switches the output, its measurement, setpoint and output (switched between the output's minimum and maximum), Auto/Manual signal and manual output, the hysteresis and minimum ON and OFF times that Tune designs, and the retained output-on state and optional time in state. It also asks for the switched equipment's own minimum ON and OFF times, such as a compressor's restart protection; Tune never proposes shorter times.
examples/control-onoff-commissioningis a cold room held by a switching compressor. The step-by-step setup remains for P-only controllers and imported adapters. - Tune shows a PID group's result: every member's designed gains and, per member, its own path (gain, time constant, delay), the relative gain of the pairing and the gain the member has with the other loops closed. The setup form names a group's model channels after their signals, as Identify does, so a group set up in the form can be tuned from its identified model.
- The setup form offers PID group when a program has several
Control.PIDinstances: the included members, each filled in from its own calls, share one Auto/Manual signal and are written as one group loop that Tune designs from the group model. - Simulation plants may take a program variable in
%Mmemory as a command source, not only a%Qoutput (Specification 46 §13.1). Control Studio uses this to validate the outer loop of a cascade: its model is driven by the inner setpoint the outer controller writes. - Control Studio's SIMC rule tunes second-order models by Skogestad's half rule (τ = τ₁ + τ₂/2, θ = θ + τ₂/2) instead of asking for a first-order model; a second-order model with complex poles is still refused with the reason.
- PID groups can be tuned from their identified multivariable model (Specification 35 §17.9): each member's PI is designed by SIMC on its own path, reduced by the two-point method, for the gain it sees when the other loops are closed (Kᵢᵢ/λᵢᵢ). A pairing with a non-positive relative gain is refused with the pairing to use. A group declares this with the project seed
{ "kind": "decentralized-pid", "family": "PI" }. - Two example projects for Control Studio setup:
examples/control-cascade-commissioning(a jacketed vessel under a two-Control.PIDcascade) andexamples/control-two-zone-commissioning(two heated zones sharing a wall, oneControl.PIDeach). Their READMEs say how to set them up and in which order to tune. - Control Studio's Requirements editor has an optional Auto-entry tolerance in the controller's output unit, as the auto-commissioning specification requires; blank keeps the automatic allowance. A state-feedback or LQG controller with a noisy measurement can now be applied with a reviewed tolerance instead of the 1e-6 default.
- After a Feedback relay identification, Control Studio shows the measured relay cycle under the identified model (number of cycles, relay amplitude d, cycle amplitude a, period Pu and Ku = 4d/(πa)), or why the cycles are not usable. When the cycles qualify, Tune offers Relay rule (Ziegler–Nichols PI), which proposes Kp = 0.45·Ku and Ti = Pu/1.2 for validation. The relay results stay visible through Validate.
- Control Studio Validate can report command activity, the total movement of the applied command (Σ|Δu|, in percentage points for a % command), as a measured row, or check it against a declared maximum.
- Control Studio Tune offers Setpoint weight (β) beside the PI and PID gains when the controller law declares the
Control.PIDsetpoint-weight field; the predicted response and the validated and applied parameter record use the entered β, and calculated candidates keep β = 1. Dimensionless settings no longer show a stray "1" as their unit. - Control Studio Tune offers Measurement filter time (Tf) when the controller law declares the program's measurement filter; the predicted response and loop analysis include the filter lag, Tf is written with the gains, and calculated candidates bypass the filter (Tf = 0).
- Control Studio's Tune predicts the response of a supplied integrating plant, alone or behind a lag, such as a shaft driven by torque (θ/u = 1/(s(Js + B))), using the exact held-input step.
- Control Studio's Tune offers Derivative setpoint weight (γ) for PD and PID when the loop's law declares
derivativeSetpointWeightSymbol: γ = 0 differentiates the measurement, γ = 1 the error; calculated candidates use γ = 0, and the prediction includes the resulting derivative kick. - Instance arrays accept element initializers,
[(PT := T#100ms), (PT := T#300ms)], and repetitions written50((PT := T#100ms))or50(PT := T#100ms); an array type declared with an initializer gives it to every variable of the type, and a cold restart re-applies it. - Control Studio now checks supplied-model validation evidence and bound channel units before controller recommendation, selects PI when P cannot meet the declared gain bounds, rejects initial commands beyond the actuator range, and returns failures through the existing Find model, Tune, controller-choice or requirements controls. It also preserves declared pulse identification and uses a held-out multisine or PRBS frequency fit when requested. Explicit relay selection uses the feedback-relay acquisition route.
- Control Studio can commission a declared first-order PI controller in Auto through a typed retained-state adapter. It verifies the actual ST baseline, candidate and first-output continuity case, aligns integral and derivative history at the runtime task boundary, observes the applied response, and returns to Manual with the prior parameter record on rollback.
- Control Studio's automatic recommendation now lets a selected proportional controller continue through its own P tracking and Auto commissioning path when the declared loop has no PID retained-state adapter. PI and PID Auto commissioning still require their declared state-initialization adapter.
- Control Studio Test now offers automatic bounded excitation, derives a safe reviewed step from the live loop mode and command headroom, and carries independent Test-record identities through model validation. Manual-output tests can write a declared command address/type while retaining the actual MV/PV response for fitting.
- Control Studio Relay Test now qualifies the runtime's measured feedback cycles, then offers one explicit calculation of an unapplied PI proposal. It never substitutes a timed square wave, accepts gains from the webview, or applies a relay result automatically.
- Runtime controller transfers apply mode and state coherently at the owning task boundary, track fresh P-controller inputs, and retain observed adoption, cancellation and failure outcomes. Control Studio connects this to reviewed local-simulator Auto commissioning and guarded Manual rollback. Recorded responses, manual transfer tolerance and fresh recovery reviews survive saved studies; completed unconfirmed recovery is retained when its editor closes.
- Control Studio records declared symbolic controller modes with program identity and per-channel quality, protects active verification from model replacement, and clears incompatible manual gain pins when identification changes the model.
- Isolated Control Studio verification retains received sample prefixes after cancellation or wall-budget termination, with bounded capture and explicit failure status; cancelled records remain available to inspect, save and reopen.
- Connected Control Studio P workflows show current model, tuning and verification stage status, clear historical qualification on reopening, and use one Tune action.
- Control Studio carries declared and measured operating points into selected-model simulation, proportional tuning and matched actual-ST verification without adding controller bias. Automatic simulation identification reads the actual Manual operating command and checks its retained pre-excitation baseline.
- Control Studio reads a declared symbolic controller mode together with process signals from one runtime scan, and clears unavailable mode values.
- Opening a saved study during active work is rejected before replacing state; fresh verification resumes live readings and shows the unapplied candidate.
- Guided setup binds an existing proportional ST controller without invented plant coefficients, for model identification followed by tuning and verification.
- Control Studio retains completed identification trials when qualification or a later trial fails, and preserves earlier campaign evidence across retries.
- Failed Control Studio identification studies can be saved and reopened with their raw trial evidence; reopening does not authorize controller application.
- Control Studio uses the selected compatible supplied model for Routh analysis, and controller comparison prefers simpler sufficient designs over additional performance.
- Control Studio now retains declared unlimited-request and integral-state signals beside applied output for a completed saturation-return Test. It reports the actual clamp, integral change and release delay, then recommends anti-windup without resetting controller state or claiming additional actuator capacity.
- After a qualified saturation-return record, Control Studio can review one typed back-calculation candidate for the declared simulation loop, stage its complete parameter record, and restore the exact untracked baseline. Matched tracked-versus-untracked Test comparison remains in progress.
- Control Studio Routh–Hurwitz analysis displays declared characteristic polynomials, table calculations and analytical intervals for supported first- and second-order models, with explicit special-case limitations.
- Control Studio configures existing projects from compiled signal and controller metadata, discovers multiple loops, and rejects duplicate loop identities.
- Project-owned experiments can open every Control Studio workflow stage.
- Provided transfer-function models support offline named-parameter root-locus calculation, candidate selection and saved-result restoration, with explicit model provenance and approximation labels. New results use schema v4 while legacy v3 records remain readable.
- Show full objectives, expected observations and lesson-specific previews in the example gallery, and bundle first-action guidance with all six non-control starters. Preserve created projects when VS Code cannot switch folders and offer an explicit retry without copying or generating their files again.
- Replace the example gallery's filter-chip wall with compact hardware/category selectors and a result count. Isolate damaged lesson metadata and show its reason without offering a misleading Create action.
- Preserve learner-selected actuator and sensor layouts in the controllability and observability lessons, so their alternate comparison cases can actually run. Keep safe zero-flow restoration independent of those selections.
- Retain the state-feedback and observer lesson comparison controls instead of forcing the preferred controller path every scan; document separate fair runs and preserve existing safe current/torque restoration.
- Split optional plant settings into named actuator, signal conversion, realism and scenario tasks, with visible summaries that include disabled configured effects. Opening one task no longer exposes every engineering field at once.
- Show scenario event times in seconds with exact millisecond persistence; retain invalid edits and block Save instead of rounding or treating blanks as zero. Distinguish runnable simulator defaults from optional physical hardware on the six starter example cards.
- Return focus to example search after clearing an empty result, so the removed reset button does not leave keyboard focus lost.
- Reopen saved plant models that omit default starting values. Show their model-sized zero state without substituting a teaching template, and preserve the original omission when only the name changes.
- Show effective zero defaults for optional robot-joint friction and gravity parameters while preserving omitted values on save.
- Retain created lesson-project locations and offer Open existing project, without treating project creation as lesson completion.
- Label editor-polled Control Studio recordings as incomplete, retain observed scan gaps and dropped samples, and block identification from these partial recordings. Do not report editor refresh intervals as actual PLC cycle timing.
- Add an explicit optional temperature teaching example after selecting the first-order-with-delay plant model, with percent/Celsius assumptions and reversible physical parameter wording. Keep generic imported models generic.
- Add deterministic simulation plant models for controller verification,
including static, first-order, FOPDT, integrator, gravity-tank, robot-joint,
second-order, state-space, and piecewise-linear processes; scalar and MIMO
typed process-image bindings; enforced local-model validity bounds;
actuator nonidealities; seeded uncertainty and dropout faults; scheduled
plant events; bounded status/trace capture; atomic feedback publication;
failed-substep rollback; physical-I/O activation protection; and a native
closed-loop
Control.PIDperformance oracle. Saved simulated-plant bricks now use the canvas' standard confirmed Remove interaction and atomically remove only the selected plant and its owned events fromsimulation.toml. The VS Code workflow now has documentation-ready Plant Simulation and Control Studio journeys covering configuration, monitoring, bounded tests, identification, tuning, validation, Automatic Tune, recovery, and explicit physical-plant approval boundaries. - Add a runtime-bound heater step-identification lesson that teaches the real Control Studio Monitor, Test, Identify, Tune, and Validate workflow with a bounded manual-output experiment, independent validation data, explicit restoration, and an unapplied controller candidate. Control Studio now keeps signal choices across workflow navigation, shows retained recording events after reopening a result, frames identification fits in engineering units, and opens Bode plots on a useful dynamics and crossover range.
- Replace the Control Studio plot-tools disclosure with one direct, responsive analysis workspace for time response, identification, Bode, root-locus, Nyquist, Nichols, sensitivity, requirements, and typed MIMO evidence. Add unit-safe signal layouts, linked graphical candidate editing, bounded undo/redo and pinning, and evidence-bound chart-session restoration.
- Add the reusable
ControlStructured Text library with the specified combined-formPIDcontroller, independent first-/second-orderLowPassfilter, normative specification, native equation and seven-scenario conformance tests, and an executed closed-loop performance benchmark for tracking, robustness, sampling, disturbances, limits, noise, and stability margins across nominal, integrating, underdamped, delay-dominant, and nonlinear-actuator processes, plus open-loop instability, scheduled gain variation, severe deterministic scan jitter, and a coupled two-loop process; add a control-system engineering report and staged training curriculum that explains the design choices, results, tuning workflow, limitations, and improvement program. - Extend the existing Control Studio workflow with reproducible saved-run
revisions whose raw samples distinguish requested from applied manipulated
output without inventing unavailable requests, eighty-four executable
control-engineering lessons, and production-style lesson source organization
in which
Mainowns I/O, safety, and composition while substantial stateful algorithms use dedicated, pedagogically commented function blocks and typed records; independently qualified SIMC and AMIGO PI formulas, bounded P/PI/PD/PID candidate comparison, common performance and actuator metrics, deterministic rejection reasons, and an explicit unapplied recommendation and review flow. Relay experiments now switch from fresh measured process feedback and a retained hysteresis band instead of replaying a timed square wave, while preserving the existing bounded abort and restoration path. SIMC tuning now also handles eligible integrating models with rate-gain equations and ships an executable heater, buffer-tank, and dryer lesson that compares unapplied Fast, Balanced, and Robust candidates under gain and delay variation. The classical-analysis sequence now begins with an executable overhead-crane Routh-Hurwitz lesson that proves0 < K < 6and confirms stable, marginal, and unstable cases against three equivalent simulated plants and an independent numerical-root oracle. The following positioning-servo lesson now derives an uncompensated root locus, proves why gain alone misses the motion requirements, and validates a full-transfer-function lead design against an equivalent executable servo without current saturation. The flexible-drive lesson then measures low-, resonant-, and high-frequency response on three equivalent executable plants and compares unshaped and resonance-shaped Bode margins without silently applying either candidate. The delayed-conveyor-oven lesson follows with complete Nyquist contours, qualified encirclement counting, and time-domain confirmation of stable and unstable loops, including a long-delay case that wrapped phase misclassifies. The precision-drive lesson then evaluates loop, sensitivity, complementary- sensitivity, load-disturbance, and control-sensitivity paths on one common grid and confirms the shaped controller's robustness/noise improvements and slower load-recovery cost on equivalent executable motor copies. The sampled positioning lesson then proves the exact hold, sampling-zero, aliasing, and one-cycle-delay model in the z-plane, safely exposes unchanged gains becoming unstable at a coarse interval, and validates a corrected digital design. The following tuning-method lesson separates method eligibility from candidate feasibility and engineering preference by comparing qualified SIMC and AMIGO PI settings on equivalent heater and dryer plants, retaining AMIGO as explicitly unavailable for the integrating tank, and exposing time, actuator, margin, delay-margin, bandwidth, sensitivity, and Pareto evidence without applying or embedding a winner. The next lesson audits the complete exact-Simulation Automatic Tune route on a recirculating water heater, retaining the Test, Identify, Tune, and Validate decisions, failure and recovery boundaries, unchanged running controller, independent validation copy, and explicitly unapplied recommendation. The following conveyor-oven lesson now derives static and dynamic measured-speed feedforward from separate heater and disturbance paths, executes three equivalent ovens, qualifies settled versus not-settled results, and proves model-mismatch and invalid-signal fallback without removing PI trim or actuator constraints. The jacketed-reactor lesson then commissions the valve-facing temperature loop before closing the product-temperature loop, compares direct PI with fast and deliberately slow cascade structures on equivalent plants, and proves time-scale separation, earlier disturbance detection, tracking, and bounded valve authority from executable evidence. The following blending- skid lesson derives a wild-stream ratio station from material flow, compares it with identical composition-feedback trim on separate two-input plants, and proves prompt production-rate compensation, meter-bias recovery, and tracked low-flow or invalid-quality fallback. The split-range reactor lesson then compares explicit dead-zone and overlap failures with a mutually exclusive installed-gain-characterized heating/cooling mapper, including neutral crossover, utility-throughput, and authority-loss evidence. The compressor lesson then compares normal-only, untracked, and externally tracked high-selector override on equivalent plants, proving constraint priority, inactive-integral consequences, hysteretic takeover/release, and explicit invalid-margin fallback without claiming anti-surge protection. The gravity-tank lesson then derives and validates three local nonlinear operating regions, comparing a conservative fixed PI with atomic interpolation and hysteretically selected PIs whose compensated external tracking preserves live setpoint history and bumpless region transfer. The following long- conveyor lesson qualifies an ordinary PI boundary before applying Smith prediction, then proves nominal rise-time benefit, moderate model-mismatch degradation and deterministic measured-feedback fallback under severe mismatch. The following cooling-water flow-bench lesson characterizes a fixed-head equal-percentage valve approximation and then uses requested position, measured stem position, process flow, and repeated reversal evidence to distinguish healthy motion, command deadband, and mechanical stiction before recommending maintenance instead of an unjustified retune. The next binary-distillation lesson replaces a premature centralized-control summary with two independently restored open-loop interaction tests, derives all four signed steady-state gains and the Relative Gain Array from executed evidence, and selects the eligible diagonal pairing while leaving controller and decoupler design to the following lesson. That next lesson runs the same two PI tunings through direct, static-inverse, and proper dynamic cross-filter mappings, compares common servo and feed-disturbance records, and deliberately rejects the nominal dynamic design under declared cross-path mismatch when its small extra cross improvement does not justify its higher error. The following robustness lesson then evaluates nominal and stronger- interaction column models by singular direction and multivariable sensitivity, selects static decoupling over an equivalent centralized coordinator in the feasible envelope, and retains a jointly limited+2/+2request as explicitly infeasible instead of presenting reduced error as successful control. The following interconnected-tank lesson checks hardware feasibility before controller design: independently calculated controllability and observability ranks are matched to executed upstream- pump, downstream-bypass, downstream-sensor, and hidden-state records, while simulator truth channels remain explicitly unavailable as installed sensors. The next state-space lesson then combines a magnetic-bearing pole-placement bench with a loaded-conveyor-motor observer: executable evidence distinguishes a nonsettling position-only baseline from measured-state feedback, removes nominal zero-load speed bias, and deliberately shows unmodelled damping being absorbed into an incorrect physical-load estimate despite accurate speed. The weigh-belt lesson that follows implements an uncertainty-weighted scalar Kalman estimator beside a fixed filter, qualifies covariance and innovation calculations against executed noisy feeder records, isolates a paired load-cell-bias fault, and rejects invalid covariance through a finite fallback without exposing Simulation truth to the estimator. The active-suspension lesson then derives four independently qualified LQR designs from explicit state/effort costs, compares them with pole placement on common physical measures, and combines the balanced design with a position-only Kalman estimate. Executed nominal, doubled-noise, and model-mismatch records retain the force-energy, motion, estimator-error, and increased actuator-movement tradeoffs instead of presenting LQG as universally optimal. The two-zone building lesson then executes a three-step, 729-candidate finite-grid MPC on every interval, qualifies its first move and objective against an independent oracle, compares it with bounded PI on a common weather/occupancy/tariff schedule, and distinguishes completed infeasibility, invalid configuration, bounded fallback, and recovery without presenting a precomputed policy as an optimizer. The nonlinear inverted-pendulum lesson that follows replaces its former linear trace with the shipped sine-gravity plant, derives a bounded quadratic Lyapunov sublevel set, verifies eight certified trajectories against an independent Runge–Kutta oracle, and keeps recovered outside and saturated trajectories explicitly separate from certificate evidence. The gravity-loaded robot-joint lesson then compares fixed PID, gain-scheduled PID and feedback linearisation over common nominal and heavy-payload trajectories, qualifies local interpolation and nonlinear cancellation independently, preserves motor-command margin, exposes the predicted model-mismatch offset, and falls back deterministically when the cancellation model is ineligible. The polymer-extruder lesson then compares fixed, recipe-scheduled, and supervised adaptive PI on common drifting-process records, qualifies a bounded two-parameter recursive-least-squares estimator against an independent scan oracle, separates persistent excitation from explicit experiment authorization, freezes unsupported learning, applies scheduled fallback, and proves projection, corrupt-data safe output, and bounded recovery without hiding actuator or nonsettling costs. The flexible-wing ground-test lesson then rejects an unstable aggressive nominal controller and qualifies a sampled filtered-PD candidate over 27 structured uncertainty cases and a declared multiplicative residual envelope, retaining the sensitivity, robustness, actuator-effort, time-response, data-quality, and bounded-claim evidence instead of extrapolating it to flight certification. The remote pumping lesson then calculates delay margin and a conservative latency budget, compares arrival-order command retention with locally timed bounded ownership on paired header plants, and proves stale, reordered, poor-quality, unsynchronized, excessive-uncertainty and future-dated packet rejection with tracked local-PI fallback and explicitly approved recovery. The fault-aware pumping lesson then coordinates two fast pressure transmitters, slow reservoir level and independent drive feedback; qualifies model-assisted sensor isolation, justified single-sensor degradation, critical safe states, actuator mismatch, acknowledgement, healthy-time qualification and rate-limited approved restoration against one deterministic multirate fault record. The blinded steam-distribution casebook that follows keeps diagnosis outside the controller and distinguishes a qualified healthy baseline, poor tuning, valve stiction, transmitter drift, unavailable capacity, upstream disturbance, loop interaction, and model/schedule mismatch from synchronized requested-demand, measured-stem, reference-meter, disturbance, operating-region, and timing evidence before any retuning. The first integrated capstone then asks the learner to commission a walk-in cold room without a prescribed winner, comparing single-threshold, hysteresis-only, and hysteresis-plus-dwell candidates on the same noisy door- load record and validating measured compressor state, equipment-owned restart readiness, invalid-quality latching, manual recovery, and discrete output without hiding a continuous controller. The recirculating-water-heater capstone then joins physical modelling, independent FOPDT identification, minimum controller-structure selection, Robust and Balanced PI comparison, uncertainty and abnormal validation, and rollback planning into one executable commissioning decision while leaving the recommended revision explicitly unapplied. The flexible-indexing-table capstone then combines two-inertia mechanics, P/PD/PID structure selection, derivative filtering, root-locus and Bode/sensitivity qualification, exact sampled execution, encoder noise, torque limits and model variation; it retains the qualified10 msfiltered PID while rejecting unchanged delayed100 msexecution and leaves revision78unapplied. The jacketed-reactor capstone then commissions its utility-facing loop before production, combines cascade, qualified measured-load feedforward, mutually exclusive steam/cooling split range, tracked high-temperature override, modes and deterministic degraded handling, and compares the integrated architecture against an equivalent feedback-only reactor while leaving revision79unapplied. The gravity-tank capstone then derives and independently qualifies three local square-root-outflow models, measures the regional limitation of a conservative fixed PI, validates one atomically interpolated retained PI across the full level envelope, and proves schedule boundaries, requested/applied pump flow, tracking, degraded authority and rollback while leaving revision80unapplied. The conveyor-oven capstone then combines independently qualified FOPDT, Bode, complete-contour Nyquist and delay-margin evidence with measured- load feedforward, Smith prediction, model-mismatch fallback, modes and heater authority; executable evidence rejects the aggressive ordinary candidate, selects a6 sSmith design, and leaves revision81unapplied. The binary- distillation capstone then joins restored four-path identification, unused simultaneous validation, RGA pairing, static decoupling, singular-direction robustness, sensitivity, constraint feasibility, invalid-mapping fallback and operating modes into one architecture decision, selecting static decoupling for the normal envelope while leaving revision82unapplied. The two-zone building capstone then qualifies an additive thermal-load estimator, rejects cascade without a measured fast inner variable, and compares strong incremental PI with Balanced and actuator-conservative finite-grid MPC under the same forecasts and constraints. Executable evidence retains PI as the application recommendation because both predictive candidates exceed the declared actuator-movement ratio, while revision83remains unapplied. The independent capstone then provides an executable compressed-air reference without prescribing its winning family: it derives and validates receiver and compressor dynamics, screens simple and advanced architectures, proves PI stability and robustness, and selects PI plus qualified measured-demand feedforward from common normal, uncertainty, actuator and abnormal evidence while leaving revision84unapplied. - Add composable Smith prediction, PID gain scheduling, and static two-by-two decoupling to the Control library with native conformance tests and tuned heated-air-duct, gravity-tank, and climate-chamber projects. Organize all 35 control projects as a cumulative real-equipment curriculum. The added plant-modelling strand teaches response classification, conservation-law models, bounded identification, model-order selection, excitation and data quality, independent validation and residuals, local linearisation, frequency response, and MIMO gain/RGA analysis before P-to-PI offset removal, feedforward, cascade, bumpless handover, commissioning, ratio, and constraint override, while preserving stable gallery identities.
- Add the ESP32-C6-DevKitC-1 reference composition (
firmware/trust-esp32c6on the newtrust-platform-esp32adapter). It runs the same saved application bundle, oracle and trace grammar as the NUCLEO-F401RE on RISC-V with software floating point, and executes the interpreter's scan path from internal RAM: from flash, one task activation took 11.07 ms against the 10 ms period. On the owned board the bring-up passes the 101-row trace, numeric checks, GPIO safe outputs, depth-four calls and the MWDT0 watchdog reset, with a 1.97 ms slowest main scan and a 10,856-byte stack peak; production qualification remains separate.
Changed¶
- The VS Code extension carries the complete compiler toolchain of its platform:
rustc,rust-lld, the target libraries and the PLC program library (specification 44 §4.6). The first build installs it from the extension itself, with no download and no Rust installation, offline too; the bundle adds about 180 to 210 MB to the extension package, depending on the platform.trust-runtime toolchain installalso takes a bundle that lies beside the tools before it would download one. - The runtime display's number entries (setpoint, numeric input) send what the operator typed, and the runtime
reads it, with a comma or a point before the decimals, and checks its limits; a refused entry shows the
runtime's reason, such as
150 is above the highest allowed value 100(specification 39 §12.8). - The MCP configuration that HMI: Connect Agent Tools shows now starts the agent's HMI tools
with
trust-lsp hmi-agent <connection file>(specification 43 §3.1) instead of VS Code's Node runtime; replace an oldertrust_hmientry with it. - The simulated-plant editor in Devices & Connections takes its rules from the runtime (new plant
operations
plant.editandplant.signals, specification 47 §20.1), so the editor, the runtime and its validation agree. Visible changes: - an I/O driver named
sim,noopornoneno longer blocks plant signals (specification 46 §6.1); - a new plant suggests the first plant ID the project does not use (
new_plant,new_plant_2, …); - a saved scenario event with an unknown action is shown and flagged instead of silently dropped;
- piecewise-curve and state-space cell errors block Save like every other error;
- a draft the runtime's own validation refuses shows the runtime's reason and cannot be saved.
- An HMI command or alarm action whose text is longer than its target holds is refused with the
stable code
runtime_string_capacity_exceeded, never cut (specification 39 §12.8). - The HMI Builder's project rules moved into the language server (
trust-lsp.hmi…commands, specification 39 §17.3); the editor applies their answers. Visible changes: Allow writing and Add user… leave aruntime.tomlthat does not load, or that lists its write rules or users inline, as it is and say why instead of appending to it (§12.2, §12.8); Import Symbol refuses a file that is no XML drawing; a new page or panel in a project withouthmi/screen.tomlgets a 1920 × 1080 dark screen layout, and Create Pages from Program and Create HMI Settings Page create one that loads instead offormat = 1alone; renaming a page, panel or popup in the Explorer updates the links to it again (the files holding them were never found) and changes only the links to that kind of file; a title made from a file name never starts or ends with a space. - The HMI runtime display takes from the runtime what a session may do and when values are stale:
the login and session answers and the value stream's session carry
acts_asandmay_change_settings, and the stream's first message the stale limitstale_ms. The Settings dialog sends each entry as typed and the runtime reads and checks it, a decimal comma included (1,5), so its refusals, such asStart timeout: 700 is above the maximum 600 s, come from the runtime (specification 39 §11.1, §12.4, §21.4). - A program deployment through the web interface lists the HMI bindings it would break before it says that the resource must be stopped (specification 39 §16.4).
- The VS Code extension's project rules now live in the language server: the side panel's Start, Update and Debug
gates, the project that Compile checks, whether
trust-lsp.tomlcan be read, the simulation speed file, the Libraries panel and its quick fix, the namespace move's checks, the Testing view's tests and the examples gallery (specification 25, §1.1, §3.1, §6, §7, §11). What users see change: atrust-lsp.tomlshows Project needs repair exactly when the language server cannot load it (valid TOML such as[ project ]no longer does, a value of the wrong kind such asinclude_paths = "src"under[project]now does); a test inside aNAMESPACE, also a dottedNAMESPACE A.B, has its namespace-qualified name in the Testing view and intrust-dev test, so its result reaches it; Libraries lists and edits[dependencies.<name>]tables, adds a new entry right after the last one, removes an entry with the comment above it and offers View source only when the library has a source file; the namespace move refuses a path that is not identifiers joined by dots, or holds a keyword, before it starts. Start's default CONFIGURATION takes the[resource]name and cycle of the project'sruntime.toml, elseResand 10 ms (it was alwaysResand 100 ms, whichtrust-runtime checkrefused in a project whose resource has another name), and never overwrites an existing file; Start accepts a CONFIGURATION whose PROGRAM comes from a local library dependency or has a namespace-qualified name; a copied example's folder name follows the rule of a new project's name (specification 25 §3.1, §6.8, §11.6). - The rules of the Ladder, Blockly, Statechart and SFC editors moved from the
extension into the language server (
trust-lsp.visual*commands): the Structured Text companion and runtime entry, the file checks and formats, the Ladder edits, new-program templates and import checks. What changes for you: - A word, double-word or long-word address that a visual program names
(
%MW4,%ID0,%QL8) is typedWORD,DWORDorLWORDin the generated Structured Text, as its size is in IEC 61131-3; it wasINT,DINTorLINT. Declare a variable of the type you want at the address to compute with it. - A Ladder
DIVblock with a zero divisor faults like any Structured Text division; it used to write 0. - Ladder operands qualified with
LOCAL::,GLOBAL::,LOCAL.,GLOBAL.,L::orG::reach the variable of that scope; they were copied into the Structured Text, which then did not compile. A timer or counter first on its rung is no longer refused as "Undeclared ladder symbol 'TRUE'", and an address the runtime refuses (%IX0.9) is reported with its element. - New Ladder elements, rungs, branch nodes and wires get numbered ids
(
contact_1,rung_2) and new blocks numbered instances (T_1) instead of time stamps. - A Statechart name follows the rule of the other visual programs (letters, digits, hyphens and underscores). An imported Statechart or Blockly file whose name lacks the suffix of its kind gets it, so it opens in its editor, and a Blockly import checks that the file is a Blockly workspace.
- Generate in the Blockly and SFC editors writes the Structured Text
companion and runtime entry that run the program; it used to write a
separate
PROGRAMfile over the companion, and for an.sfcfile it overwrote the chart itself. SFC Preview ST only shows the companion, and SFC Validate shows what it finds. - Saving a statechart keeps its
id, and saving an SFC keeps its name, author, description and creation date; they used to be replaced. - The Blockly editor's Save writes a file its companion can be made of, a
Blockly chain that leads back to itself is reported instead of crashing the
generator, and Blockly and Statechart text uses the IEC escapes
$'and$$. - Statechart Validate checks what the companion needs (an
id, a state, an action type for each mapping in use), the action-mapping panel counts actions that transitions name, and an action writing an address the runtime refuses is reported instead of being skipped. - An
.sfcfile gets its Structured Text companion on save, like.sfc.json. - An SFC runs by IEC 61131-3 (
sfc-profile.md): a parallel split enters all its branches together and its join leaves them together, so charts with parallel branches no longer stay in their first step; transitions that can clear together do, and a numbered transition (priority) wins over the others of its step; the qualifiers S, R, L, D, P, SD, DS, SL and the new P1 and P0 work, with a duration typed asT#2s. An action runs one more scan after it ends, with<action>.QFALSE, as IEC actions do in CODESYS and TwinCAT: guard code that must only run while the step is active withIF <action>.Q THEN, or use P1. Validate, saving and Preview ST refuse a chart the companion cannot run (an action name that is not an identifier, a time qualifier without a duration, two initial steps in one sequence) instead of writing Structured Text that does not compile. The SFC examples run: their conditions such asT#1sand theirlogcalls are replaced. - The language server reads the Ladder number fields (timer and counter
presets, position) and the Statechart timer and mapping value as typed: a
blank or non-numeric Ladder number is refused with a message instead of
being stored as 0, a Statechart timer of
1e3is 1000 ms instead of 1, and a negative timer is refused instead of ignored. - Right-clicking a Ladder element or rung no longer replaces the editor with a script error.
- The editors' own simulators and runtime panels, which nothing could start any more, were removed; a visual program runs through its runtime entry from the truST sidebar and shows its values in Live Values.
- Projects no longer share one control socket. When
[runtime.control]omitsendpoint, the runtime listens on a socket of its own project, or on a loopback port with a generated token on Windows, and writes where it listens to.trust-runtime/control.json. On Windows the generated token goes into a per-user file under%LOCALAPPDATA%\trust\runtimes, never into the project folder. A runtime that cannot write the file still starts, logs a warning and reports it in its status. A runtime in a read-only or foreign project folder, which cannot keep its stable runtime ID in.trust-runtime/runtime_id, now starts too: it keeps the ID inruntimes/<hash>.idof truST's folder in your state folder, where it refused to start before. Project templates and examples no longer use/tmp/trust-runtime.sock, and the control course lessons, the commissioning examples and the HMI examples no longer commit atcp://endpoint and an example token, so new Windows projects, and every example that does not name its own socket, start on Windows. Eleven examples keep the Unix socket their instructions name and run on Linux and macOS only:ads_server_basic,mqtt_traffic_light,hardware_8do,plcopen_motion_single_axis_demo, the fiveplcopen_motion_oop_*examples, and tutorials 09 and 12. - Connect changes nothing until the runtime answers: a refused Connect (unreachable, missing or rejected token) leaves the target, the runtime settings and the status bar as they were, and the runtime's node in Devices & Connections keeps its place with the failure and a Set auth token action. After a successful Connect the side panel's Target, its picker and the status bar name the runtime. A runtime that answers with debugging disabled becomes the target, and Live Values says how to enable debugging (specification 25, §5.1).
- The rules of Devices & Connections moved from the extension into the language server
(
trust-lsp.devicesTopology,trust-lsp.devicesCanvasAct) and the runtime: the fleet view, its health roll-up and search, the forms' Apply, Test and Save, the ADS ports, tag imports and removals, Expose, EtherCAT channels and adding a runtime (specification 25, §5). What changes for you: - Connect existing runtime refuses an address that is neither
host:portnor atcp://orunix://endpoint, with the address help; it used to add the text to the fleet as an endpoint. - A runtime's own state outranks a connected connection, so a running simulator reads "Running".
- Test in an OPC UA client's edit form tests its first connection, as the summary's Test does; it used to send the whole list of connections, which the test could not read.
- After Save, the canvas selects the saved connection; it selected nothing, because it looked for the connection under an ID that no node carries. When the runtime names no connection, only a single matching connection of the protocol is selected; with several it used to pick the last one.
- While nothing reports the project's runtime, the canvas draws the simulator also beside other configured runtimes, the project's runtime in simulate mode shows the simulator's state, the search dims a local connection instead of relabelling it, a start failure is one fault instead of two, and a failed Connect to another runtime no longer marks the simulator.
trust-runtime comm discover --protocol ads --ads-port <port>, repeatable, names the ADS ports each find is checked on, and a live ADS browse that fails before the device's port answers returns an empty tree with the classified reason instead of a request error (specification 11).- Live Values starts a numeric row's write box empty, as the prepared value of a CODESYS or TwinCAT watch list; a REAL no longer fills the box with 17 cut-off digits, and the current value stays in the Value column (specification 25, §4.3).
- Control Studio's Bode plots draw the phase unwrapped, as MATLAB and python-control do: the Frequency tool, the
feedback result, the engineering plots and the measured response keep the phase continuous instead of jumping
between −180° and +180°, and a delay adds its −ω·L exactly, so
e^(−s)/(s + 1)reads −657.25° at 10 rad/s instead of 62.75°. The phase axis ticks every 45°, 90°, 180° or 360°, whichever gives at most eight steps, and the crossover markers sit on their curves. The margins are unchanged (numerical annex N4.3). - Control Studio names a plotted plant input "Output" on its axis, as in its legend, instead of "Applied input" (specification 35, B9.9). In the narrow loop setup form, each Change link stays at the start of its row.
- The HMI Builder, Control Studio, simulated plants and
Comm.Linkblocks run on the shared execution engine. The screens are served at the end of each scan in which the program calledHmi.Display, after that scan's outputs are published, instead of directly after the calling task, so a command reaches every task before any output it affects is published (specification 39, §18). Control Studio's task-boundary parameter and transfer commits, the link states and a simulated plant's controller measurement now pass through the engine's checked writes and service hooks instead of a copy of the program's storage. - The HMI's command, settings and one-off read queues hold at most 1,024 requests each; a request past the
bound is refused at once with
request queue full. - A production build (
trust-runtime build,run, a debug or Control Studio session) of a configured project leaves itsTEST_PROGRAMs unbound and does not instantiate them, so a runnable project such as a control course lesson keeps its tests beside its configuration; an unbound ordinaryPROGRAMis still refused (specification 22). - Changed: the computer's
mqttdriver runs on the shared implementation of the chips, so both apply spec 32 alike. Refusals readmqtt: <key>: <reason>; unknownsparkplugkeys, scaling on a Boolean point, topics that are not exact, a broker with user info or an unknown scheme, an emptyusernameand strings with U+0000 are refused, and bad TLS files are refused when the driver starts rather than at each connect. The connection opens when the driver starts; a read without a new message reportsmqtt input not freshat once; a write while the session starts waits at most 40 ms and reportsmqtt connecting; typed inputs write only their own bytes of the image, where they overwrote other drivers' bytes; a dropped driver sends DISCONNECT (and with Sparkplug B its NDEATH first); health names the transport's reason. - Changed: a chip's
modbus-tcpandmqttconnections send what their driver queued before they close, so a program's last bytes reach the server when the program is replaced, and a chip's refusal for want of memory after claiming the drivers also counts the drivers' buffers (... checkpoint buffer, debugger and the drivers' buffers need <k>). - Changed: the computer's
modbus-tcpdriver runs on the shared exchange of the chips, so both apply spec 11's rules alike. Every response is checked (function, byte count, write echo, MBAP protocol and length) and a failed check gives the connection up as a transport error; a response of another transaction is discarded and reading goes on; images larger than one request are split at the quantity limits; FC16's byte count no longer wraps above 254 bytes; an exception keeps the connection and stays anIoAddresserror naming the function and address; the reconnect backoff follows transport failures only and covers outputs too; health shows the failure's own text; input failures no longer leave a safe state unconfirmed; mapped reads leave unmapped bytes and the other bits of a byte alone. Refusals readmodbus-tcp: <key>: <reason>, unknown keys, port 0 andtimeout_ms = 0are refused, FC05 and FC15 (or FC06 and FC16) on one coil or register are refused as one table, and a validation no longer starts a worker. IPv6 servers ([addr]:port) stay supported. - Changed: pairing is the same code on every runtime. A claim at the limit of enabled tokens replies
at most 256 pairing tokens may be enabled; revoke one first, and a claim or revoke whose write fails replies with the write's error and changes no token, where both repliedinvalid or expired codeor claimed success before. A pairing file that cannot be read grants nothing and is no longer overwritten by the next claim. A runtime without random numbers refuses to pair instead of using predictable ones. The computer writespairings.jsonto a new file and renames it over the old one. - Changed: the deployment record is version 2: section 8 carries each I/O driver's
paramstable (specification 34 §5.1.1), from which a chip runs its drivers with the keys, defaults and checks of a computer. Version 1 records still read; a chip refuses their drivers other thangpiowithrebuild the project. - Changed: a program image is signed with Ed25519ph (RFC 8032), over the SHA-512 of the image with
the context
truST program image, where it was pure Ed25519 (specification 44 §5.11). A chip hashes an online change's image as its parts arrive, so that checking the signature no longer holds the running program's scans while the whole image is hashed. Images signed before this change are refused; build them again. The chips build their signature code for speed. - Changed: every runtime records each program's calls in
tasks.stats' top contributors, a chip as a computer; a forced marker reads its forced value between scans; a program loaded byCompiledRuntime::loadstarts at its starting values; an input of an enumeration type that holds no declared value of it is refused before the scan reads it; trust-hir refuses aVAR_ACCESSpath to aVAR_TEMP,VAR_EXTERNALorVAR_IN_OUTvariable and an enumeration literal that names no value of its type; aVAR_CONFIGentry that completesAT %Q*with an address of another area is refused at build (IEC 61131-3 §6.5.5.4). - Changed: a chip whose resource is Stopped writes an upload into its program store as the parts
arrive, instead of holding it in its heap, so a program as large as a program slot installs: on
the ESP32-C6 up to 3,056 KiB, where the heap held about 100 KB.
transfer.beginerases the first unit of the slot the install will commit, eachtransfer.partis answered once its bytes are written and read back, andprogram.installchecks the container in the slot and programs only its header, the magic last (specification 34 §5.1). An upload begun while the program runs is held in memory as before. A client waits up to 5 seconds for each begin and part reply. - Changed: the native build works out a program's starting values from its declarations itself and
no longer lowers the statements for the bytecode engine (specification 44 §3, §6.7), so it builds
what the bytecode build refuses: a constant array indexed by a variable,
DATE - DATE,SUPER(), aCHARabove 127, an enumeration value as a standard function's argument, and a function that calls itself by its bare name. Compiled programs also take a program instance declaredRETAINin its configuration, a call of an instance that is an element of an array (Pumps[2](...)), and anLWORDorULINTliteral aboveLINTin a statement. - Changed: a call beyond the call levels ends the scan with
call depth exceeded, in both engines, instead ofinvalid bytecode 'vm call stack overflow'. - Changed: compiled programs make their checks where they can fail and nowhere else, none dropped
(specification 44 §6.5, §6.6): an array element at a constant index inside the bounds is not
checked when the scan runs; a chain of real
+ - *on variables and literals checks its result at its last operation, which faults in the same cases and statement as a check of each; and a release image keeps aFORloop's counter in a register when nothing in the body but the loop can change it. On an x86-64 computer the pasteurizer's control task takes 140 to 151 ns per activation as a release image instead of 150 ns (RuSTy 140 to 141 ns), and its code is 20,188 bytes instead of 21,184. - Changed: a control request costs less on a chip: a request line is read straight into the request,
without first building a JSON value of the whole line (on the ESP32-C6 about 0.08 ms less per
request), and a chip answers
program.activefrom the identities it has read until an install writes its program store, instead of reading the store at every request (1.7 ms instead of 4.6 ms). The cycle percentiles thatstatusreports are read by selection from a copy on the stack, with no allocation per request; on the ESP32-C6 they still take 0.5 to 0.8 ms of astatus. Replies and refusals are unchanged. A chip still serves requests between cycles: on the ESP32-C6 a request takes about 0.9 ms,status4.7 ms andevents.tail7.4 ms, so a client that polls it delays the activations of a 1 ms task until milestone M5 serves requests in a context the tasks preempt (specification 34 STATE-08). - Changed:
shutdownstops a faulted resource as it stops a running one, on a chip and on a computer: the outputs take the safe state again, the retained values are saved, and the resource is Stopped, keeping its fault instatusuntil the next start. A program that faults at every start can therefore be stopped, replaced withprogram.installand started; before, a faulted resource stayed faulted and took no install. A process that exits after stopping a faulted resource still exits with status 1. The device link's stop no longer restarts a faulted program on its way to Stopped. - Changed: the stack a compiled program needs is the deepest chain of calls the build reads from the module, not the sum of every function's frame; calls through interfaces that come back to a function, as a decorator's do, count once for each instance.
- Changed:
status,health,tasks.stats,io.listandio.readwrite their replies as text without building a JSON value first, and a reply is written into its parts in blocks rather than piece by piece. On the ESP32-C6 astatusholds the scan loop about 6 ms instead of 9, anio.readabout 2 ms instead of 6. The replies are the same text as before. - Changed: the kernel interface of compiled programs is version 2: a kernel reads the real of
STRING_TO_REALwith the runtime's own reader for the program. An image built before must be rebuilt. - Changed: an install keeps one copy of the uploaded container while it commits, not two, so a chip installs a program twice as large from the same heap.
- Changed: a program takes less of a chip's memory. Each table of a prepared program holds exactly
its entries, and a chip boots the program it has installed where it lies in its flash, without
copying it into its heap: it reads the program's declarations and initializers there and keeps on
its heap only the part of each that a scan reads. On the ESP32-C6 the bench program
v1p, an 8,290-byte container, takes 15,112 bytes of heap instead of 19,052, at the same scan speed. A computer's program and the candidate of an online change are prepared from their bytes and take no more memory than before. An install writes the program slot that does not hold the running program, so a container that was installed but has not started yet is replaced by the next install. - Changed: scans are faster on every runtime, most on the chips. A program reads and writes its own and the global scalar variables, and the outputs of its timers and other standard function blocks, where they lie, without looking them up by name. A program body without calls to user POUs runs from compact code built when the program is loaded, which also calls the standard function blocks and converts values directly. A standard function block reaches its variables at places found when the program is loaded. Inputs and markers are sampled straight into their variables and outputs published straight from them, a chip decodes its I/O snapshot only when a request reads it, the debugger's statement hook runs only while a debugger is attached, and retained values are encoded only when a save is due. Results, charged work and faults are unchanged. A cycle reaches its programs at places found when the program is loaded, a call of a timer whose preset is a TIME or an LTIME also runs where it lies, and a scan with no queued writes or forces skips the Live Values queues. The ESP32-C6 reads its clock from the CPU's cycle counter between waits, and its RAM code list holds the code that every scan runs. It copies, fills and compares short values with its own routines in RAM instead of the ROM's, and reads its clock and its heap's operation count without a critical section. A BOOL, integer, bit string or character bound at its own width is sampled and published by copying its bytes. Compact code computes the arithmetic, comparisons and logic of integers and BOOLs on their stored values, without building general values, and a program body that keeps no variable in a call frame starts without one. A body whose operand kinds are all known when the program is loaded runs on the operands' bits, without building cells or values. Compact code converts an integer to another integer type, and a TIME to a DWORD, directly. The cycle, task and call statistics are kept in whole nanoseconds and turned into milliseconds when they are read, so a chip records a scan without software floating point. With debugging enabled, the debugger's statement hook is called only at statement starts instead of at every instruction. The ESP32-C6 gives 24 KiB of its heap to the code that runs from RAM, and 16,320 bytes more, which its heap takes back from the chip's LP SRAM as a third region that an allocation reaches only when the other two are full: its heap is 112 KiB (114,688 bytes, also the longest reply), of which a program can use 56,076 bytes (80,652 before). On the ESP32-C6 the scan of a 2-statement program went from 0.90 ms to 0.021 ms (its cycle from 2.9 ms to 0.12 ms), and that of a 15-statement program with a timer, an edge trigger and three conversions from 6.8 ms to 0.19 ms (its cycle from 9.9 ms to 0.31 ms).
- Changed: a chip's main stack decides how many call levels it admits, and its heap no longer
does: the ESP32-C6 admits 7 (its 78 KiB stack window less 2 KiB of headroom and the 34,008 bytes
its install takes, at 6,144 bytes a level, measured on the bench), and the STM32U5A9 the same until
its own bench. A program that needs more call levels is refused at its install, its load and its
online change with
insufficient stackand both numbers, instead ofinvalid bytecode 'vm call stack overflow'. A chip now takes trust-runtime's limits of constructed values, construction bytes and process image, which its memory figure checks; only its preparation bytes and validation scratch still scale with its heap. The C6 measures its whole stack (the 24 KiB window was filled by installing any program), and the U5A9's main stack is 78 KiB. - Changed: an online change keeps the value of every variable that the new program declares at the
same place and by the same name, retained or not, function-block members, timers' state and edge
memories included, as CODESYS and TwinCAT do. A variable whose declaration changed keeps what
fits: a value converted when the new type holds it (
INTtoDINT, a value inside a new subrange, an enumeration value by name), structure and function-block members by name, array elements by index; anything else starts with its initial value. References keep naming the same variable, or become NULL when it is gone. Before, every variable that was not retained took its initial value at an online change, and a retained value that did not fit its new declaration, or a retained function-block instance in a changed program, refused the change; no value refuses a change now. Each variable that did not keep its value as it was is recorded as anonline_change_valueevent (at most 32 per change, then their count). A change that recovers a faulted program keeps the value that caused the fault, so the new code must handle it. - Changed: an online change keeps the retained values the running program holds and applies no retain file or checkpoint over them, on a computer and on a chip, as CODESYS and TwinCAT do: the file holds older values and stays the source of a warm restart after a stop or a power loss. Before, a retained value that changed after the last save went back to the saved value, and a retain file that could not be read refused the change. A chip's change therefore keeps only its working memory free (8,192 bytes on the ESP32-C6); the check of an install still keeps room for the newest checkpoint.
- Changed: the tasks of a program that an online change installs continue the running program's schedule, so the first scan after the change runs the tasks that are due. Before, every task's interval started again at the change, so the first scan after it ran no task and the outputs that the program assigns showed their initial values for a cycle.
- Changed: a chip's checkpoint store reserves the capture of the retained values for the program's largest payload when the program loads, and grows it before an online change commits, so the first scans no longer allocate it. A warm start reads the newest checkpoint into the store's own buffer instead of a second one, which leaves the ESP32-C6 16,300 bytes more for programs.
- Changed: on a computer the runtime's default executor, the register tier, allocates nothing in a scan either, as the chips' executor does: texts, arrays, structures, enumerations and references are handled where they lie, and function results and inputs are copied as bytes. Programs read, write and are charged exactly as before. The profile's clone counters now count only the values actually cloned, so a program that compares or copies enumerations, texts or compound values shows fewer clones than before.
- Changed: every runtime, on a computer and on a chip, keeps a program's variables in flat memory
blocks (specification 34 MEM-12): each variable at its declared size and alignment, texts inline
and references in 16 bytes, in blocks reserved once when the program is built. What the tools
show, the debugger, Live Values and the variable reads, is read from those blocks. A program's
construction allowance now also covers the working memory its scans need, which is reserved when
the program is built: for example the buffer a character write edits a text in, sized for the
largest text the program declares, up to three bytes for each
WSTRINGcharacter. The value store the runtimes kept before is removed: the flat blocks are the only store, and a program reads, writes and is charged exactly as it was on the value store, which the tests keep as recorded facts. - Changed:
debug.variableslists a large array in pages on every runtime, as the Debug Adapter Protocol does: it takesfilter,startandcount, an array's entry reportsindexedVariablesand a structure'snamedVariables, and a request withoutstartandcountlists the first 100 elements. A chip lists an array of 300 structures page by page within its 256 variable handles, andtrust-debugpasses VS Code's paging on to an attached runtime. - Changed:
breakpoints.listentries now carryfile_id,path,lineandcolumnbesidestartandend, the shape a chip gives too (a chip has no source text, so it gives no byte offsets). - Changed:
shutdownon a computer now stops the PLC program and keepstrust-runtimerunning in the Stopped state, as it does on a chip, and as CODESYS and TwinCAT keep their runtime up while the application is stopped. The control server, the web UI and the other services stay up,statusreportsstate: stoppedandprogram_control: keeps_control, andrestart(Operator) starts the installed program again, loading the program and the resource configuration the project folder names now. The process ends on SIGINT or SIGTERM, or by the new explicit exit,shutdownwith{"exit": true}(Admin), whichtrust-runtime ctl shutdown --exitsends. Before,shutdownended the process. Scripts that waited for the process to exit aftershutdownmust send the exit;trust-runtime fleet runtime stopdoes.fleet runtime startnow starts the program of a runtime whose process answers while its program is stopped. - Changed: a program install now works only while the PLC program is stopped, on every runtime. The
web deployment (
POST /api/deploy) and the web rollback (POST /api/rollback) answerresource_not_running, asprogram.installdoes, while the program runs, and change nothing; stop the program withshutdownfirst, install, andrestart. The web IDE's Deploy button shows that refusal while the program runs. - Changed:
trust-runtime deploy, the web deployment and rollback, andprogram.installwrite a computer's project through a program store. The program is admitted (its record, its admission against the runtime and a dry-run preparation) before anything the runtime starts from changes, so a program the runtime cannot prepare is refused and the project is left as it was; before, it was written and failed at the next start. Each file is written through a temporary file and a rename.currentis replaced by one rename, andpreviousby a second, and a rollback does the same, so a stop between two steps leaves a project that starts. Every bundle holds its install sequence ininstall-sequence.trust-runtime deployremoves thebundles/<label>that an interrupted deployment left, where it refused the label; a label thatcurrentorpreviousstill names is refused as before. A web deployment of sources only now creates a new bundle with a new sequence. - Changed:
cargo xtask portability pack-projectwrites theTRSTA002container with install sequence 1, which is the containerprogram.installcommits into an empty store. The ESP32-C6 readsTRSTA002only: written into slot A at0x200000, a packed container is a committed program, and the first install after it takes slot B with sequence 2. ATRSTA001container that an earlierpack-projectwrote is not read any more: a chip that holds only that container refusesrestartwithno installed program: slot A holds a TRSTA001 container, which is not committed (only TRSTA002 is read), and the project is packed again. - The work and memory a program's copies cost are now counted by what the program copies, the same on every computer and chip (specification 34 TIME-08). Copying a number or other scalar costs no memory; a STRING costs its bytes, a WSTRING two bytes per character unit, and a structure or an array 16 bytes per field or element, and one work unit for each structure, array, field and element, however the runtime shares the value internally. Before, a scalar copy was counted as a full value (32 bytes on a chip), so a long loop on a chip could stop with a memory-limit fault although it allocated nothing; and the counts differed between 32- and 64-bit machines. A program near its work or memory limit may now be refused at a different point.
trust-runtime buildnow fills the deployment record's configuration section (specification 34 §5.1.1, section 7) from the project'sruntime.tomlandio.tomlwith the runtime's own parsers and checks, so a chip receives the resource, cycle, control, log, retain, watchdog, fault, task, I/O-driver and safe-state settings a computer reads from those files; a value the section cannot hold is refused with its file, key and value, and no record is written.- Preparation resolves every native call once, into a call plan: the formal index of each actual (named and positional), the presence pattern, the output descriptors, and the callee (a function's prepared POU, a function block's POU, a method through the prepared owner and method table, a built-in function block's kind and parameters). A scan reads the plan and resolves no argument names and builds no uppercase type-name key, and an interface attempt compares prepared POUs while still charging the type-name bytes TIME-08 prescribes. Every result, charge and refusal is unchanged.
- Construction sizes the engine's per-call reservations (the stack executor's operand,
frame and pending-call storage, the initializer stacks, the call-frame and instance
pre-reservation) to the program's static call depth instead of
max_call_depth, so a chip no longer has to reserve the whole ceiling. The validator walks every call target from every entry, counts a method and interface call over every implementing POU, and treats a nested instance declaration as the initialization it runs. A recursive program has no static depth and reserves formax_call_depth, exactly as before, and the admission ceiling and the runtime depth check are unchanged, so every result, charge and refusal stays the same. - A
STRINGwithout a length is nowSTRING[80], as in TwinCAT and CODESYS: it holds at most 80 bytes of UTF-8, and aWSTRINGwithout a length at most 80 UTF-16 code units. Before, it had no limit. A longer value is cut at assignment, never in the middle of a character, and an HMI write of a longer text to a program variable is refused withruntime_string_capacity_exceeded. DeclareSTRING[n]for a longer text; n is at most 65,535, and a larger length is a compile error.STRING[n]now holds n bytes rather than n characters, so a text with letters outside ASCII (such asä, two bytes) holds fewer than n characters;LENand indexing still count characters. A string function or conversion whose result would be longer than 65,535 now fails withruntime_string_capacity_exceeded. - The Devices & Connections ADS Browse pane's
Create routecreates the TwinCAT route through the runtime's guardedads.route_add, which takes the TwinCAT administrator login once and never stores or echoes it, and then Browse retries. The extension host no longer edits an ADS file itself: removing a tag runstrust-runtime ads remove-symbolsand shows its report. - The HMI's shipped symbols take their design size, label and ports from one table in the runtime (
symbol_geometry.json): the language server's pipe routes read it, and the editor's drawing reads the module generated from it, so both meet the same ports. - The HMI Builder's quality warnings (touch targets, missing units, unbound objects, cut texts, low contrast, colours that look
like an alarm's, and a program without
Hmi.Display) are decided by the language server (trust-lsp.hmiQuality); the editor reports only what its drawing measures. The warnings and their sentences are unchanged. - Control Studio's measured frequency response names its recording by what it recorded and when, for example "PRBS recorded
2026-10-22 14:05:31 UTC", instead of the study's
rec-…identifier. - Control Studio's panel in VS Code is now presentation only: every rule, check and decision runs in its Rust engine, and the TypeScript route and session are removed. A loop the engine does not run shows the engine's refusal and its next action. Export (JSON of the workflow result, CSV of the selected signal table, B10.5), the Channels… mapping, the loop monitor's live values and the target label, the study inputs, and the loop setup's step-by-step save now run through the engine; the HMI Builder opens an equipment's loops through it.
- The Tune page's initial-state response reads its Step and Duration in the engine: a time typed as
15 min,90 sor1 h 30 minis read as its seconds, and a text that is no time is refused with its sentence and the field marked, instead of silently taking the default. - Control Studio's Requirements… and experiment Limits… dialogs no longer check ranges themselves: the engine refuses a value outside its range, a reversed range, or a blank or unreadable number, with its sentence under the field that holds it. A PID group's Tune designs each member to its own output's requirements.
- Control Studio's verification scenarios are named and checked by the engine: a scenario added in the Requirements editor has no
name until the list is saved and then takes the first free
scenario-N, a timed change whose time is not a whole number of the loop's task periods is refused when the list changes (not when Validate runs), and the advanced fields are read by the engine as the text typed. The setup form's optional gain-schedule points and the PID group's decoupler matrix follow the engine's answer. - Control Studio's Apply, Restore and Keep follow the engine: it states which of them is offered after open and after each outcome, and how a review is confirmed (a press on a simulated target, an approval on a physical one). A confirmed Restore now ends its recovery and unlocks the page, a refused Apply, Restore or Keep keeps the engine's offers, and cancelling the approval of a recovery's Restore review frees the Review restore button again. The stage tabs enable what the engine's stage availability allows, and a finished project test that restored its declared values releases the experiment settings.
- Control Studio's experiment-settings tool runs on the engine again, for the course projects that declare their experiment's settings (lessons 21, 65, 71, 74, 75, 78 and 79): the settings are reviewed and applied within their bounds, frozen while a project test works on them, and released after its restoration is confirmed, also after an Abort or a failed test. The engine refuses an Identify start whose method the loop cannot run, with the Method list's own sentence; after a refused method choice the Method list shows the method the study still holds.
- Control Studio's project test that declares a baseline records from the start of its plan, so the recording times a lesson gives are the recording's own; a test that declares none still waits for a steady baseline first, so its fit has one. Every project test keeps each scan of its hold: a 30.1 s test keeps all 300 scans of its 30 s hold.
- Control Studio's project test of a loop that names a hold parameter (lesson 21) is reviewed and planned from the hold the runtime applies: the review shows the baseline, the hold and one restoration sample (35.1 s for a 30 s hold), and refuses a hold that cannot be read, lies outside its bounds or exceeds the declared maximum.
- Control Studio's Identify page takes the model's words from the engine: the Recorded-data fit starts from the engine's default
numbers (the first import no longer clears them), the model line names the model type, the gain, the time constants and the delay
as the engine words them (an integrator's rate gain per second,
°C), and a failed check keeps the evidence the engine sent with it. A plant handoff shows the plant's summary line from the runtime'splant.status. The simulation's speed (×10, or×9.4 of ×10when it falls behind) is the runtime's own words on every surface. - Every Control Studio structure now runs on its Rust engine, so the course's last lessons on the old route move over: state feedback, LQR and LQG design on a supplied state-space model (lessons 68, 69 and 72), with the closed-loop poles and the Riccati stages; the measured MIMO paths with the gain matrix, the relative gain array, the static decoupler and the pairing (lessons 61 and 62); and the finite search over a program's coefficients (lesson 89), whose trials run the program against the selected model and are ranked by the integral of squared error. The cascade, the Smith predictor, the gain schedule, the ON/OFF controller and the PID group with its static decoupler also tune, validate, apply and restore through the engine.
- Recorded-data fit, the manual identification of the Identify page, runs on the engine with its held-step settings and the sensor-lag comparison: the accepted model becomes the study's model that Tune designs on, and the chosen structure is now checked on the independent recording. A project test made while the identification waits for its independent recording keeps that recording for the check and leaves the model in place (lesson 26).
- Validate binds every input and output of the model through the loop's channels, so lesson 84's measured-disturbance feedforward validates its draw-off cases on the full two-input model.
- Loop setup is checked by the engine for every structure: the state-feedback, LQG and search-law setups and the five structures' setups are written by the engine, and each refusal is shown under the field it names.
- Loops whose program runs its own law now run on Control Studio's engine: a PID that the program computes itself
(
controller.projectwith a parallel PID seed) and the program's P controller. Tune designs the gains on the model, Validate runs the actual program, and Apply and Restore write and return the program's gain variables. - The course's remaining loops of these kinds now run on Control Studio's engine: the schema-1 observation loops (lessons 1, 2, 3 and 80), the loops with a declared analysis (lessons 44, 45, 47 and 48), whose gain chosen on the root locus or from the Routh interval is reviewed and written to the program's variable, the measured-disturbance feedforward (lesson 84), whose Tune designs the PI and the feedforward together and whose Validate also runs the measured disturbance, and the trajectory feedforward (lesson 90), whose Tune designs the PID with its rate and acceleration feedforward gains on an integrating model with a lag.
- Control Studio's Tune page analysis now runs in its engine for every loop: Bode, the measured frequency response, Nyquist and loop response, root locus, Routh–Hurwitz, the pole-zero map, lead/lag design, the initial-state response, controllability and observability, and the relative gain array. Long calculations show Abort. Reopening a loop shows the last tool with its inputs and calculates it again, and the root locus, Routh, feedback and measured-response results are kept in the loop's study.
- Loops without a controller law, such as the course's observation and open-loop lessons, now open on Control Studio's engine: Identify serves them, and Tune, Validate and Apply say that they do not apply.
- Control Studio's Open recording… (with any method except Recorded-data fit) and Open model… run in its engine. An opened file is kept as a recording of the study. Its channels can be chosen with their units, including archived channels that the loop does not bind. Two independent runs, from one file or from two files and from a simulation or a connected runtime, are fitted with independent validation. A supplied model becomes the study's model, which Tune designs on.
- Choosing an identification setting that Identify cannot use now names the rule it breaks, for example "Relay identification needs an explicitly declared process direction.", instead of a general refusal.
- Control Studio's panel in VS Code now runs on its engine,
trust-control-studio serve, so the panel and the command line decide everything the same way: identification, tuning, validation, Apply, Restore and Keep, which stages are available, and every refusal with its next action. Reopening a loop shows its identified model with the response plot, its candidate, its requirements (Automatic's values marked Automatic), its last current validation and Apply's offers, and it follows an operation that kept running while the panel was closed; Abort still stops it. Tune predicts the current settings beside the candidate when a runtime is connected. Validate also runs the Manual-to-Auto entry that Apply performs and needs it to pass; its table shows failures first and counts the passing range, state and transfer checks in one row. Reset clears the model, its plot and the tuning and validation results that depended on it. - Control Studio's Validate tests the candidate at the operating point Apply starts from: when the configured simulated plant cannot start there (for example after an Apply whose setpoint change stays in force), every case runs on the qualified model, and the result says why. Long Tune predictions keep each period's extremes on the chart instead of millions of points.
- The HMI agent's page authoring (composition, edits, drafts, reviews and requests) runs in the language server, written in Rust; the review's interactive exploration of the project's pages works again.
- The room heating example (
examples/room_heating) uses a small tank and room, so its slowest mode settles in minutes instead of hours. - Control Studio's engine identifies with one least-squares fit on every path: the delay and time constants are searched on grids scaled to the recording, with no fixed limit on the delay, so a slow process keeps its real dead time. A model with a clearly resolved delay is preferred to the same model without it. A pulse is fitted with its return to baseline. Before fitting, every recorded sample of the plant input and the measured output is checked against the loop's limits; a recording that left them is kept, not fitted.
- Control Studio pages are simpler:
- Each failure is one line with one button for what to do next (Limits…, Requirements…, Connect runtime…, Loop setup… and so on); clicking the line shows the details.
- Durations are entered and shown as "90 s", "15 min" or "2 h".
- Signals are named by their role (Measurement, Output, Setpoint) with the symbol on hover.
- The model shows as a formula with one line of its key numbers.
- Manual tuning previews the response while you type.
- The analysis tools are under Analyze….
- Requirements shows the main rows, with the test design under More….
- Apply sits directly under the results table.
- Loop setup shows what it found in the code as a short summary with Change links, and no longer asks for an identification step or duration. Only when the project runs its own experiment does it ask how long to record it.
- Control Studio: maximum overshoot is a requirement you can declare, in percent of the setpoint change. Limits nobody declared (a fixed 20 % overshoot, 2 % steady-state error, 6 dB gain margin and others) are no longer applied.
- Control Studio: when the target is simulated and the project's simulated plant drives the loop,
Validate runs against that plant, otherwise against the checked model. Apply on a loop in Auto
starts from wherever the controller's output is; a loop in Manual must hold the validated output
within the controller's response to the measured noise, no longer to within 0.0001 %. Entering
Auto allows the controller's response to the measured noise, from an identification in Auto as
well as in Manual, so a noisy loop is no longer reverted at the switch. Restore previous
settings returns a loop that was in Auto before Apply to Auto, also for
Control.PID; it used to leave it in Manual. Apply observes the response on the runtime's time and continues after a VS Code restart. Any measured output that leaves its permitted range restores the previous settings at once. A failed Apply leaves the loop in the mode it was found in. - Control Studio keeps each loop's study in the project, under
control-studio/<loop id>/: one study file and one file per recording. Studies kept by earlier versions move there on first open. - Runtime: live writes on a physical plant need approval, decided by the runtime from what it knows about its own I/O, never from a label in the request; controller rollback needs it too. A controller transfer is all-or-nothing: a failed check or a fault reverts it in the same scan, and BOOL values come back as BOOL. Measured values are checked with tolerance bands instead of exact comparison. Status requests never change state. A program reload or restart stops a running experiment safely, and only one live write runs at a time.
- Simulation: time advances by exactly one cycle per cycle, so a busy computer slows the
simulation instead of skipping cycles, and the runtime reports the speed it reached. The speed
(1× to 100×) can be changed while the simulation runs (
config.setsimulation.time_scale), also in the VS Code simulator, which now uses the project's speed. - Rebuild the Plant library and the HMI Builder around plain IEC 61131-3 objects. Devices are
commanded by mode: Auto by the program through
Auto…inputs, Manual by the operator through methods such asStart,Stop,Open,Close,SetSpeedandSetPosition(each returning whether it was accepted), Out of service by nobody; a mode change never moves anything. Take control, request identities, command contexts, registration and the settings provider are removed. Fail positions, always-on supervision, latched trips and drives starting at 0 % are part of every device; settings are ordinary RETAIN members described once in the component definition. Limit alarms useAlarm.LimitAlarm. A PID loop is no library block: Create PLC code writes it as ordinary code around the realControl.PID(see below), with negative gains for reverse action. - One HMI page format for the editor, the runtime,
hmi checkand the language server: equipment objects recordblock,instanceandfaceplate, panels live inhmi/panels, and docked panels are composed by the loader. Deploying sends the files as written. - HMI display: a control whose interlock permit reads FALSE is greyed with the permit's reason as its tooltip (Start and Stop in Auto read "Auto: switch to Manual"); a refused command names the reason alone, without an "interlocked:" prefix. Trends inside panels placed on a page keep their history, the PID panel shows the measurement and draws it in its trend, and a device's Reset is enabled only while it has a fault.
- HMI alarms: the runtime decides which actions an alarm offers each user and how it sounds, and refuses an action the alarm's state does not allow, with its reason. Acknowledging an acknowledged alarm, or shelving one that is not in the active list, is now refused.
- Compiler: the direct I/O warning (W011) concerns addresses declared inside function blocks, functions, methods and classes only; a program's I/O list no longer gets a warning per entry.
- Isolated Control Studio runs start from the running PLC's inputs, so blocks fed by inputs the run's plant does not write see real signals.
- PID loops are ordinary code: Create PLC code writes each loop as ST around the real
Control.PID(Configure, Initialize, Update), the way the control course does, instead of a Plant library block around it. Its values are variables beside the PID (TIC101_Setpoint,TIC101_Manual,TIC101_ManualOutput,TIC101_Output) with one parameter record (TIC101_Parameters) that the HMI's settings and Control Studio share; those the operator and the engineer set are retained. A bad measurement holds the output and the loop restarts from the next good one without a bump; Manual and Auto change without a bump. Create PLC code adds theUSINGdirectives and the write rules the loop's panel needs.FB_PIDLoop,FB_CascadeLoop,ILoop,E_LoopModeand the cascade panel are removed; a cascade is two linked loops. Component definitions may name variables beside the instance ({instance}_…), and a switch may name its two words (options = ["Auto", "Manual"]). - HMI: a loop controller shows its measurement in its setpoint's and its transmitter's unit and
decimals (
42.2 → 45.0 °C, before42.191 → 45.0 °C), and so do its panel and trend; the panel's output reads with one decimal. - Control Studio treats a connected runtime that reports its own simulation mode as a simulation: it identifies, tunes and validates against it without VS Code's local simulator, which cannot be operated from the HMI.
- HMI Builder: equipment is renamed in the inspector (its tag), which renames it in the program, the pages and plant.toml; new equipment gets the first free name per program instead of the same name twice; the Objects list names untagged equipment by its block; objects placed one after another step aside instead of piling up; a digital signal is drawn as an instrument bubble with its tag's letters (FS for FS-101); Create PLC code lists open connections in full.
- Devices & Connections: Connect hardware's help for a terminal without a fitting PLC signal
shows the declaration to add (for example
TT101_Raw AT %IL0 : LREAL;) instead of pointing to a device. - New projects: the runtime's web server is on for this computer (127.0.0.1:8080), so a deployed
HMI opens in a browser at once, and RETAIN values are kept in
retain.bin, so commissioned settings and operator setpoints survive a restart. - HMI: a switch's toggle keeps a control's usual size (at most 32 px high) in a taller object, in proportion to its label; the whole object still takes the click.
- HMI: a duration rounds its seconds to the object's decimals ("2 min 54 s" instead of "2 min 54.353 s"); without decimals it reads as before.
- Plant library: protection, interlock and permissive panels show only the conditions in use (with a
text, or unhealthy), from the new
InUse[1..8]output, instead of all eight rows. - Keep explicit synthetic alarm exploration usable without executing PLC alarm sources; label preview-only rows and use declaration checks for draft validation.
- Replace automatic HMI-to-PLC source synchronization with the explicit Create PLC code and Create HMI actions. Ordinary authoring reads declarations instead of compiling or executing PLC source.
- Prepare OpenOT compiler metadata once per operation and skip its semantic analysis for sources without OpenOT attributes; preserve ordinary compiler errors and telemetry output.
- Reduce cold HMI equipment discovery and settings loading work by resolving imported types at catalogued source locations and excluding executable bodies from declaration precollection.
- Agent previews embed the shipped multilingual fonts and isolate project symbol definitions. Library sheets include rendered object bounds and keep synthetic equipment visible.
- HMI Builder starts new page views at a readable 75% minimum with scrolling; Fit and saved zoom remain available. Design trends show identified sample history using the declared ranges; Live mode keeps actual history only. Alarm counters and disabled-control labels remain readable in both themes.
- The runtime display's value stream sends every value once, then only the values that changed, and the cycle count alone when nothing changed; the list of paths the program lacks comes when it changes. Trends still get a sample per cycle.
- Turning parts on the runtime display (pump impellers, fans, motor rotors) are drawn in a layer of their own above the page, which the browser turns by itself; a value update no longer restyles and repaints them. The display lays out a page once per screen size instead of at every update.
- HMI colours and text: the light theme takes deeper shades so every text and sign of the drawing has enough contrast (WCAG 2.1), and the dark theme's unknown grey is lighter. "Not in running program", a setpoint's limits and a refusal's reason are now 14 px. Short setpoint entries and numeric inputs draw one line without overlapping, a short value card cuts a long label instead of running it under the number, and a button's outcome sign no longer covers its text. New page buttons are 48 px high, new setpoint entries 240 × 96 and new numeric inputs 208 × 48.
- HMI Builder: the editor uses truST's shared webview tokens, so it matches the other truST editors and follows high contrast and forced colours.
- HMI: bars and split bars draw their label above the bars.
- HMI: text is measured with the widths of the font the HMI ships, so labels and words are placed where they really end.
- HMI: text objects, button labels and the state word under a symbol are cut with "…" when they do not fit; signs never are.
- Examples: the tank example, tutorial 12 and plant_demo show their pumps and valves with the P&ID symbols; the alarm tutorial's pictures are renewed.
- HMI: every component cuts a label, title, text or tag that does not fit with "…", ending before its state chips.
- HMI: a mode selector offers the modes of the enumeration it is bound to and shows them as words ("Out of service"); a symbol with a click opens it anywhere in its box; in a faceplate only the symbol carries the alarm mark.
- HMI: a table shows a
BOOLas a lit or empty mark, field names as words ("First out"), texts whole and from the left, and leaves out the empty rows at the end of an array of structures with their count. A duration reads as "1 h 2 min 3.5 s" instead ofT#1h2m3s500ms. A faceplate's values take the unit of the object that opens it. - HMI: the runtime display changes a page in place where its drawing changed instead of rebuilding it on every update; on a Raspberry Pi 5 a page of 30 turning pumps and 270 changing values went from 27 to 47 frames per second, and turning symbols no longer restart their animation on each update.
- Replace the experimental direct-model HMI draft command with tools for the customer’s existing agent; the extension does not require a separate language-model provider.
- Editor and project diagnostics are faster on large projects: a file's analysis no longer rescans every project
file for global constants and
VAR_CONFIGentries, reuses the project symbol merge it already computed, and imports other files' symbols without re-indexing the whole table per import (measured on a 400-file project: diagnostics after an edit in 0.14 s instead of 1.8 s). The reported diagnostics are the same; symbol-table walks now run in declaration order, so lists built from them, such as unused-symbol warnings, quick fixes and the runtime web IDE's symbol search, follow the declaration order, and the errors for incomplete addresses without aVAR_CONFIGmapping are listed in declaration order (they followed hash order).scripts/perf/project_scaling.pymeasures project-size scaling over LSP. - Control course: all 89 lessons follow the lesson template and the lesson map (
docs/specs/control-course-lesson-template.md,docs/specs/control-course-lesson-map.md). Each lesson starts from the previous lesson's result and explains its new idea in four steps: a plain definition, what it means in words (with a comparison a PLC engineer knows), the formula with every symbol explained, and a worked example on the lesson's own plant. It ends with Check yourself (three questions with answers) and shows one figure drawn from its own Control Studio run, with numbered marks that answer its predictions. Every number comes from a real run or is labelled as calculated. Lessons that introduce a plant show it in Devices & Connections; lessons 10 and 45 change the plant there (a sensor bias, a starting level) instead of insimulation.toml. Thirteen lessons explain a new mathematical tool in a Math you need box. Control Studio now does the design in the lessons whose map says so: lead/lag design (56), the cascade (63), the Smith predictor (67), the PID group (71) and corner-case validation (86, 89). Trajectory feedforward is now lesson 61, directly after lesson 60 on the same axis, and feedforward control lesson 62. Lesson 22 designs from a saved heater model so its result is repeatable. Lesson 40 gained its missing debug configuration. Stale values were corrected against the recorded runs: for example, lesson 25 now quotes lesson 20's measured 4.42 s ultimate period, and lesson 50 its recorded lowest velocity. - Control Studio Validate names an error measured from a reference other than the setpoint after that reference, for example "Maximum error from planned angle", instead of calling it a steady-state error.
- Control Studio's Manual tuning names a project controller's gains as its Automatic result does, instead of showing their raw conventions.
- Control Studio says why P is refused: unavailable on a delayed process, with the gain its offset would need, or unable to damp a shaft by the settling time; these refusals point to choosing another controller.
- Control Studio Requirements refuses a validation duration shorter than the latest check of a declared scenario and names the duration it needs; Validate refuses such a campaign the same way instead of failing with a generic sample-window error.
- Control Studio's Automatic tuning designs PI, PD and PID to the requirements, as it already did for P. It uses the response nearest the declared one (More robust, Balanced, Faster) whose predicted response settles within the settling band by the settling time and stays within the allowed error; when none can, Tune names the requirement the fastest response misses and by how much. It previously always used the declared response, so a PI could miss the settling time that Validate then failed. The PID example's README follows the current workflow with measured results.
- Apply waits, bounded and cancellable, for the process to return to the operating point the settings were validated at, showing the measurement's validated and current value, and then continues on its own. It asked the user to press Apply again. A manual output that differs from the validated point is refused at once with the value to return it to, because the process cannot settle by itself; Abort ends the wait without writing anything.
- Control Studio names a PID group member's gains as it names a single PID's ("P1.Zone1 · Proportional gain (Kp)"), shows each member's path gain and its gain with the other loops closed in their unit (for example °C/%), and names a group's one-setpoint validation runs after the setpoint they change ("P1.Zone1C setpoint alone").
- The setup form names a controller variable it cannot offer because it has no I/O address, and how to map it (
AT %I,%Qor%MinVAR_CONFIG), instead of leaving the field empty without a reason. - The setup form fills in each controller from its own compiled calls when the program has several PIDs: choosing a controller brings its parameter record, setpoint, measurement, manual output and output, and both controllers of a recognised cascade are filled in. Fields the user has chosen are kept. Previously only a program with a single PID was prefilled.
- Control Studio's setup form asks for the identification duration next to the identification step, instead of writing a fixed 60 s experiment into every loop. The duration depends on the process: a cascade's inner loop is identified over a few seconds from its fast response, a slow process over minutes.
- Control Studio's Configure control loop opens one form instead of a chain of prompts. The form is prefilled from the compiled program: the language server now reports how controllers are called (
controlSymbols.links), so aControl.PID's parameter record, setpoint, measurement, manual output and output come from its ownConfigure/Updatecalls, and a cascade (one PID's output feeding another's setpoint) is recognised and saved as two linked loops. The form asks only for what the program cannot say (units, ranges, the Auto/Manual signal, the identification step, and the startup-done flag when the program writes its own parameters) and writes the complete loop definition, including the seven retainedControl.PIDstates. Loop discovery now compiles projects with their declared library dependencies; previously every project that used a vendored library failed setup with "cannot resolve type 'Control.PID'". - Control Studio's additional-lag warning speaks of the process and the sensor instead of water, the unreachable code of the earlier Studio panel (71 files) is removed, the Routh–Hurwitz details show stability boundaries as decimals with the exact values on hover, and calculation tables use the shared table style.
- Tune explains a requested test change beyond the identified range: it names the range the model was measured in and the largest change it supports, instead of "Tune again".
- Control Studio webview tests that had to be enabled by hand now run in the VS Code suite (29 more rendered journeys); a rendered run in which no test passes fails.
- The control course was reviewed as a sequence. Lessons 11–13 and 15–17 are rewritten in the course's concise format with recorded results: Lesson 11 teaches the integral time T_i = K_p/K_i and keeps a Ki = 0.6 candidate recorded in Control Studio, and Lesson 21 compares that candidate. Lessons 5–10 have concise procedures. Derivation steps that were skipped are shown (complex roots and overshoot in Lesson 33, critical damping in 34, the PID characteristic equation in 35, transform pairs in 44, determinants in 70–71, the Riccati cost in 73 and others), terms are defined before use, wrong lesson references are corrected, and a corrupted formula in Lesson 77 is repaired. The gallery texts of the lessons follow their READMEs again, without the spaces an earlier conversion had removed.
- Tune shows why it is unavailable beside the Tune controller button when the ready model cannot be tuned by the selected controller. A recovered failed Apply now says "previous controller parameters restored" for every controller type, not "PID".
- Control Studio writes powers of s as superscripts (6s² + 5s + 1) in model equations and Routh texts, and analysis tables align their headers with their columns and separate the columns.
- Control Studio refuses a validation run that exceeds the runtime's recording limits before starting it and names the limit: at most 32 recorded channels and 250,000 channel samples per run. The runtime's own refusal also names the exceeded limit instead of "execution exceeds cycle, sample or declaration bounds".
- Control Studio's Tune page names the recommended controller configuration as soon as a model is ready ("P1.Controller · PI — recommended") and keeps it marked when another configuration is chosen. Its predicted response compares the tuned controller with the one running in the project (Tuned / Current), and units read with a multiplication dot, such as %/(°C·s).
- Control Studio refuses to tune a model without measurement evidence or independent validation and now says so, with "Identify model" as the next step instead of "Tune again". This applies alike to supplied models and to models returned by Identify.
- Control Studio Validate shows the control requirements and any failed or inconclusive check as rows, and summarizes the passing generated range, rate, controller-state, unchanged-parameter and transfer checks in one row. Requirements declared by a lesson or project scenario always keep their own rows. A refused Apply no longer reports the completed transfer check as missing.
- Lesson 19 (model-based PI tuning) now uses Control Studio's Tune, Validate and Apply with the real
Control.PID: its program keeps applied gains and the operator setpoint, designs its startup gains by coefficient matching, and declares the setpoint-peak and 30 kW load trials for validation. - Control Studio writes °C instead of degC in relay results and in the recorded-error and settling failure messages.
- The Feedback relay test measures the process noise during its baseline and switches just above it (mean ± twice the noise half-range) instead of at ±0.5 % of the measurement range, so the relay cycle meets the small-hysteresis assumption of Ku ≈ 4d/(πa).
- Validate's Auto-entry check allows the controller's proportional response to the measured process noise (twice Kp times the noise half-range from the model's recording) unless a transfer tolerance is reviewed; noise-free plants keep the exact check.
- Lesson 20 (relay tuning) now runs Control Studio's bounded Feedback relay test (d = 10 points) on the same heater and real
Control.PIDas Lesson 19, derives the relay-rule PI candidate in Tune, validates it against the Lesson 19 setpoint and load trials, and applies it. - The Validate results table is taller, so a complete results list fits without an inner scroll bar.
- Lesson 21 (tuning comparison) validates the manual, model-based and relay PI candidates on the same heater in Control Studio, with the same setpoint and load trials, settling within ±0.1 °C and command activity, and selects by the declared rule.
- A project step test records the hold the learner applied. When a loop names its hold parameter (
experiment.holdParameter), Review project test reads the applied value and plans the baseline, the whole hold and one restoration sample (35.1 s for a 30 s hold) instead of a fixed maximum. - Lesson 22 (setpoint weighting) runs the Lesson 21 controller (Kp 13, Ki 3.5) on the same noisy heater with the real
Control.PID, compares β = 1, 0.5 and the calculated 0.3 in Control Studio and explains why the integral's growth during the delivery delay still saturates the heater at β = 0.5. - Lesson 23 (measurement filtering) filters the heater measurement before the real
Control.PID, compares Tf = 0, 1 and 2 s on setpoint, load and noise trials in Control Studio (93 % less noise-driven command movement at 1 s; 2 s no longer settles), and starts the filter from the fresh measurement at Auto entry so Apply stays exactly bumpless. - Lesson 24 (sampling and timing) updates the heater's real
Control.PIDon the measured time between acquisitions, rejects intervals that are not positive or longer than half the loop's 4.46 s ultimate period (holding the command and restarting the filter), and compares normal, 1 s, late and wrongly timed updates under the Lesson 23 load requirements in Control Studio. Its first scan no longer divides by a zero interval when Control Studio's verification starts from preset controller states. - Lesson 25 (controller initialization) replaces the booster-pump example with the heater and its real
Control.PID: every retained value (outputs, setpoint and measurement references, rates, filter and timing) is initialized from the running heater at startup or once per request, automatic control waits for that initialization, Manual no longer updates the controller, and Control Studio compares an initialized start with one whose stored values are 0 before Apply initializes the controller at the Auto-entry boundary. - Lesson 26 (bumpless transfer) replaces the booster-pump example with the heater: while Manual commands the heater the real
Control.PIDtracks the applied command, setpoint and measurement (I = u_a − P), and the transfer scan reconstructs the manual command with no integral step. Control Studio shows a 60 % manual hold switched to Auto without a jump (60 % → 60 %, recovery settled in 18.7 s) next to the untracked jump to 87.7 %. - Lesson 27 (parameter updates) replaces the booster-pump example with the heater: the program adopts a changed parameter record together with its filter time only as a whole and only if it is valid (an 80 %/20 % limit pair or a negative filter time changes nothing),
Control.PID's incremental form keeps the command across a gain change (I re-based by P_old − P_new), and Control Studio refuses Kp 150 before validating and applying a Kp 10 / Ki 2.7 set. - Lesson 28 (measurement validity) replaces the booster-pump example with the heater: a sensor section supplies the reading with a quality flag and its acquisition age, readings with bad quality, outside 0–80 °C or older than 0.5 s latch a fault that removes heat in the same scan, freezes the controller and filter and clears the Auto request and manual demand, and recovery needs an acknowledgement, three consecutive fresh valid acquisitions and a new Auto request. Control Studio validates an invalid-value and a stale-sensor trial (0 % from detection, recovered within 0.034 °C).
- Lesson 29 (heater commissioning) replaces the four-heater capstone with the complete Lesson 28 heater controller: a capacity check from the energy balance (75, 90, 90 and 105 % holding commands; 37 °C at full power under 30 kW), a commissioning record declared before any result, and one Control Studio trial per requirement (setpoint, load, startup, manual transfer, saturation, capacity, stale sensor) before the Lesson 21–23 controller replaces the introductory PI through one parameter update.
- Lesson 30 (actuator tracking) replaces the software-only rate example with a hot-water valve whose stem moves at 0.5 %/s and publishes its measured position: the program distinguishes requested, bounded and measured position and tracks the measured stem in the integral (I_next = I* + Δt/T_t (x − u_r)). Control Studio compares it with bounded-target tracking (peak 37.03 °C and 25.4 s settling versus 37.74 °C with the request at 100 %).
- Control Studio records a declared actuator's measured position as the applied input and the manipulated variable as the requested output, so Identify fits the plant from what the actuator reached and plots the actual stem ramp, named after the actuator signal, instead of an ideal request step.
- Lesson 31 (valve characteristics) replaces the valve-bench example with the Lesson 30 hot-water valve in two pressure arrangements (regulated 100 kPa and a fixed 128.125 kPa supply with pipe loss 12.5 q²) driven by the same stem: a project-scripted staircase in Control Studio records stem, flows and pressures, and the program keeps only steady points to derive the installed slopes (0.020 versus 0.015 kg/(s·point)) and the heat at stated temperatures.
- Lesson 32 (valve stiction) replaces the flow-loop example with two hot-water valves probed from Control Studio: one ignores request changes below a 1-point command deadband, the other accepts every request but holds its stem until the positioner force F = k_s (u_t − x) exceeds 50 N and stops where it falls to 20 N. The recording shows the unchanged accepted target, the 55 N breakaway at 80.5 %, the stop at 78.5 % and the path-dependent 74 % after a reversal.
- Control Studio's response chart changes an auxiliary signal's line style after every three colours, as the live monitor plot does, so two displayed signals never draw with the same stroke.
- Lesson 33 (position control) replaces the lesson-function torque calculation with
Control.PIDin its P configuration on a simulated rotary axis (J = 1 kg·m², B = 1 N·m·s/rad, ±20 N·m): the program latches the velocity at the first arrival at a new target and removes torque outside ±2 rad or ±10 rad/s. Control Studio identifies the axis from a supplied state-space model, predicts and validates Kp 4 and 16 against the declared peak, late-error and torque limits (overshoot 0.46 and 0.72 rad, arrival at 1.27 and 3.29 rad/s), and Automatic tuning reports that P cannot provide the damping. - Control Studio names a failed operation by its page ("Tuning failed: …", "Identification failed: …") instead of its internal action.
- Lesson 34 (PD control) replaces the two lesson-function shafts with the Lesson 33 axis under
Control.PIDin its PD configuration (Kp 16, Kd 7 = 2√(J·Kp) − B, derivative on the measured angle). Control Studio compares P with the program's PD, shows the derivative kick of differentiating the error (366 N·m requested, 0.02 s at the 20 N·m limit), validates and applies the critically damped PD (no overshoot, 1.52 s), and Automatic tuning proposes Kp 1.56, Kd 1, which meets the 4 s requirement with a tenth of the torque. - Lesson 35 (PID load rejection) replaces the self-scripted trial program with the Lesson 34 axis lifting a 2 kg weight on a 0.1 m drum (τL = 1.962 N·m, a second plant input) under
Control.PIDin its PID configuration (Kp 16, Ki 12, Kd 7, β 0.3) with ±0.001 rad angle noise. Control Studio shows PD holding the load 0.124 rad low, PID holding it within 0.0015 rad, β = 1 overshooting the unloaded step by 0.23 rad and β = 0.3 passing every requirement and being applied. - Control Studio's Auto-entry check now allows the full controller response to measurement noise, including derivative (Kd·4n/Δt) and integral (Ki·Δt·n) terms, and, when no identification recording measured the noise (for example with a supplied model), measures it with a one-second resting run of the transfer trial's plant.
- Lesson 36 (derivative filtering) replaces the self-scripted two-filter program with the Lesson 35 controller plus a first-order measurement filter on the angle (α = Δt/(Tf + Δt)), so
Control.PIDdifferentiates the filtered angle and D uses the filtered velocity. Control Studio compares Tf 0 (183 N·m torque activity per step), 0.5 s (0.27 rad overshoot from late braking) and 0.1 s (29 N·m, unchanged step), and applies Tf = 0.1 s. - Lesson 37 (discrete PID) replaces the hand-written positional controller that drove the axis with
Control.PID(Kp 16, Ki 4, Kd 7, the Lesson 36 filter) and records the positional reading of every update: error, P, trial integral, D, request and the retained integral. A project experiment in Control Studio couples the drum load and steps the target to 1.5 rad; the recorded updates, including the clamp at 20 N·m with tracking, follow the discrete order exactly. - Lesson 38 (positional and incremental PID) replaces the positioning-table program, whose incremental path was a P-only shadow calculation, with the Lesson 37 axis under
Control.PID(incremental with Ki > 0) and derives the increment Δu = Kp·Δe + Ki·e·Δt − Kd·Δω_f + γ from two consecutive positional requests. From the same inputs the program runs a positional form with its tracking, Manual (I = u_a − P − D) and gain-revision rules, a secondControl.PIDwith Tt = Δt that keeps the applied torque, and the positional form without the gain rule. A project experiment in Control Studio clamps the Auto-entry request at 20 N·m and revises Kp from 16 to 24: the positional torque equalsControl.PID's at every update, while keeping the applied torque leaves a constant 4.49 N·m difference after the clamp and skipping the gain rule a 0.72 N·m one. - Lesson 39 (servo commissioning) replaces the flexible indexing-table capstone with the drum axis of Lessons 33–38 and one integrated program: encoder validity with a latched fault (zero torque, then acknowledgement, three fresh readings and a new Auto request), whole-set parameter updates, one torque writer, initialization, bumpless transfer and the filtered
Control.PID. The filter now runs in every mode, because restarting it at the Auto entry made the incremental D brake a falling load the wrong way (−5.5 N·m) after a fault. Control Studio validates the Lesson 36 candidate against declared positioning, load, torque-limit, startup, transfer and measurement-fault trials and applies it. - Lesson 40 (volume balance) replaces the net-flow program, which wrote the balance into a single-input integrator, with a tank plant that has separate inlet and outlet pump inputs (A = 0.5 m², ±0.0005 m level noise) and a model level the program advances with the balance from the measured level. A Control Studio project experiment raises the inflow to 0.012 m³/s for 10 s: the level reaches 0.2896 m against the model's 0.2902 m and then holds 0.29 m with equal flows.
- Lesson 41 (gravity outflow) replaces the program that calculated the outflow from the measured level with two models advanced from the applied inflow: the gravity balance A·dh/dt = q_in − c·√h, which stops at a negative level instead of repairing it, and Lesson 40's constant outflow. In a 200 s Control Studio experiment at 0.011 m³/s the tank settles at the predicted (0.011/0.020)² = 0.3025 m, the gravity model follows within 0.0009 m, and the constant-outflow model ends 0.35 m too high.
- Lesson 42 (linearization) replaces the program that simulated its own tangent against unused tanks with two identical gravity tanks stepped to 0.011 and 0.015 m³/s from the same state, each with a tangent model built at 0.25 m (g = 0.020 m²/s, K = 50 s/m², τ = 25 s). The Control Studio recording shows the small step within 0.0028 m of its tangent and the large step's error growing to 0.061 m, in line with the exact equilibrium error v²/c².
- Lesson 43 (gain scheduling) replaces the hand-written scheduled tank controller with a
LevelControllerblock aroundControl.PID(Kp = A/λ, Ki fixed at the 0.25 m design level or scheduled as c/(2λ√h) from the measured level; the retained request keeps the holding flow when Ki changes). Six tanks compare fixed and scheduled gains at 0.09, 0.25 and 0.49 m in Control Studio: the fixed loop needs 48 s at 0.09 m against the 30 s requirement, the scheduled loop 23–24 s at every level. - Lesson 44 (Laplace transforms) replaces the gravity-tank program, which did not match the lesson's heater, with the heater under
Control.PIDP control (Kp 5) and two predictions of a 35 → 36 °C step: the transfer function H(s) = 0.2·Kp/(5s + 1 + 0.2·Kp) without the delivery delay, and the same loop recomputed with the 1 s delay. In Control Studio the heater settles at the predicted 35.50 °C with 77.50 %; the delay-free rise is off by up to 0.157 °C, while the recomputed loop follows within 0.021 °C. - Lesson 45 (poles and zeros) replaces four abstract response models with the lesson's heater and shaft: the heater returns from 36 to 35 °C in open loop and the shaft steps to 1 rad under
Control.PIDP control (Kp 4). The program calculates the poles from the physical values (−0.2 s⁻¹; −0.5 ± j1.936 s⁻¹) and writes their predicted responses. In Control Studio the water follows its pole within the ±0.02 °C noise and the shaft swings every 3.24 s as predicted; its 0.016 rad larger swings match the pole shift of the 20 ms torque hold (σ = −0.48). - Lesson 46 (characteristic equations) replaces the crane stability project, which belonged to Lesson 48, with the shaft under
Control.PIDPD control (Kp 16, Kd 7) knocked by a 10 N·m, 0.1 s external torque. The program forms s² + 8s + 16 = (s + 4)² and, for the sampled law (P 10 ms and D 20 ms late), 0.86s² + 7.84s + 16 = 0, and predicts the motion after the knock from both. In Control Studio the shaft returns without passing its target; (s + 4)² is up to 0.0096 rad off, the sampled-law roots −3.08 and −6.03 s⁻¹ follow within 0.0012 rad. The heater's PI loop with its integral is worked as a calculation with both modes. - Control Studio's root locus on Tune draws the pole paths across the varied gain's declared range, labels the inspected gains, marks the selected candidate and names the fixed parameters. It previously showed only unconnected points for the inspected gains, with the selected value in the caption.
- Lesson 47 (root locus) declares the shaft with its braking connected, G₇(s) = 1/[s(s + 8)], with Kp varied and Kd = 7 fixed, and runs the chosen gain and an alternative on two identical shafts. In Control Studio Kp = 16 settles within ±0.02 rad in 1.46 s, Kp = 12 needs 2.16 s and Kp = 20 settles in 1.04 s with its first torque at the drive's 20 N·m limit.
- Control Studio's Routh–Hurwitz result names the method that completed a table with a row of zeros (the auxiliary polynomial, not ε), and its calculation details list the first-column signs of the table they come from together with every special case, such as the origin pole and the replaced row.
- Lesson 48 (Routh–Hurwitz stability) is shortened around the specified cases Kp = 1, 0 and −1: the table, the interval Kp > 0 with the excluded boundary, and the three recorded shaft trials.
- Lesson 49 (periodic excitation) replaces the flexible-drive multisine, which did not match the lesson's heater, with three copies of the heater driven by
PeriodicExcitation: u = 75 + 5·sin(ω·t) % for five periods at 0.1, 0.2 and 0.4 rad/s. In Control Studio the first cycle carries the start (mean up to 35.15 °C), and cycles 3–5 repeat about 35.000 °C with half swings of 0.91, 0.72 and 0.46 °C. - Lesson 50 (frequency response) replaces the flexible-drive project with the heater driven by Lesson 49's sine at 0.2 rad/s and a supplied heater model. Control Studio measures M = 0.1416 °C/point and φ = −57.07° against the model's 0.1414 and −56.46°.
- Lesson 51 (sensitivity functions) replaces the precision-drive project with two PI-controlled heaters (Kp 5, Ki 1): one receives a 10 kW sinusoidal heat load, the other 0.05 °C of sinusoidal measurement noise in its reading. In Control Studio the load moves the water by 0.559 °C and the noise moves it by 0.040 °C and the command by 0.28 %, as |S| = |T| = 0.790 and |CS| = 5.59 at 0.2 rad/s predict.
- Lesson 52 (loop shaping) replaces the single-trial heater program with nine heaters: the PI with the lead element (1 + Tz·s)/(1 + 2s) for Tz = 2 (F = 1), 3 and 10 s, each on a target step, a 30 kW load and 0.05 °C of noise, started together by Control Studio. The lesson calculates the crossover, phase margin and noise-to-command gain of the complete loop. In Control Studio only Tz = 3 s meets every requirement: the baseline lets the load pull the water to 33.75 °C, and Tz = 10 s moves the command by 1.41 % under the noise and saturates on the step.
- Lesson 53 (Nyquist stability) replaces the conveyor-oven program, which did not match its heater text, with two heaters under
Control.PIDP control (Kp 10 %/°C about 75 %) with 1 s and 8 s delivery delays. The lesson derives Z = P + N from the angle of 1 + L and calculates the critical delay of 6.05 s. In Control Studio the 1 s loop counts N = 0 and settles; the 8 s loop counts N = 2, Z = 2, and its recorded oscillation grows 1.44 times per 22.6 s period. - Lesson 54 (sampled-data stability) replaces the positioning-table program, which did not match its shaft text, with two shafts under the same PD (Kp 16, Kd 7) with one update of computation delay, updated every 0.02 s and every 0.6 s, and simulated in 10 ms steps for both. The lesson derives the exact held-torque update and the characteristic polynomial in z. Control Studio's pole maps of the two sampled loops show all poles inside the unit circle for 0.02 s and a pair at |z| = 2.41 for 0.6 s. The recording shows the 0.02 s shaft settling at the 0.01 rad target without overshoot and the 0.6 s shaft growing until the abort at 2.82 s.
- Lesson 55 (feedforward control) replaces the conveyor-oven program, which did not match its heater text, with three PI heaters (
Control.PID, Kp 5, Ki 1) under the same 30 kW extraction. The first runs PI alone; the second adds the feedforward D_m/2 throughControl.PID'sFeedForwardinput; the third takes it from a 36 kW estimate. The lesson derives the ideal feedforward −G_D/G_u = 0.5·e^(+s), which is not realizable, and the unavoidable 0.5438 °C drop. In Control Studio both feedforward heaters fall only to 34.456 °C, against 33.75 °C for PI alone; with the wrong estimate PI trims its part to 72 %. - Lesson 56 (cascade control) replaces the reactor program, which did not match its jacketed-vessel text, with five jacketed vessels. A single-loop PI and a cascade each run the 35 → 37 °C setpoint step and a 30 kW jacket loss, and one cascade runs its inner loop alone. The outer
Control.PIDoutputs the jacket setpoint in °C and the inner one the heater in %, with model-based gains 0.5/0.12 and 5/10 against the single loop's 2.5/0.1. In Control Studio both pass the setpoint test. On the jacket loss, the cascade keeps the vessel above 34.80 °C while the single loop falls to 31.74 °C. The inner loop reaches 63.2 % in 0.5 s, against 9.7 s for the vessel. - Lesson 57 (ratio control) replaces the blending-skid program, which did not match its dosing text, with two calibrated additive pumps under ratio stations (R = 0.1,
Control.PIDPI 10/10 from the model-based rule). One carrier steps from 100 to 150 L/min, the other stops. Below 5 L/min of carrier the station commands 0 % in the same update, evaluates no ratio and sums the tail volume. In Control Studio the ratio is back within ±0.005 after 0.9 s, and the stopped pump still delivers 0.175 L, below the 0.20 L limit. - Lesson 58 (split-range control) replaces the reactor program, which did not match its text, with three jacketed vessels from Lesson 56 fitted with a cooler. The split-range cascade's inner
Control.PIDrequests net heat, limited to −100…200 kW, which is then split into heater Q/2 and cooler −Q, never both. With 60 kW entering the jacket, Control Studio shows the split range handing over to the cooler in one update and holding 35.40 °C at 30 % cooler, while the heater-only cascade lets the vessel rise to 48.3 °C. - Lesson 59 (override control) replaces its reactor program with three heater-only jacketed vessels under a 42 °C jacket limit: the vessel PI alone, and an override with and without tracking. The override low-selects between a vessel PI and a jacket-limit PI (setpoint 41.8 °C), and the controller not selected runs in
Control.PIDTrack mode. In Control Studio only the tracked override keeps the jacket at or below 42 °C (41.83 °C). It holds the vessel at the feasible 38.16 °C for an infeasible 40 °C request, and gives the heater back in the same update when 37 °C is requested. Without tracking the jacket reaches 43.17 °C and the vessel stays stuck for 19.8 s. - Lesson 60 (Smith prediction) replaces its program with four heaters. A fast PI (λ = 1 s) runs on the measurement and inside a
SmithPipredictor fed with its delay-free and delayed heater models; the same predictor and the slow PI then run on heaters whose delay is 1.5 s. In Control Studio, with the matching model the predictor's residual stays below 5·10⁻¹⁰ K: the step reaches 63 % at 2.0 s with no overshoot, against 56 % overshoot on the measurement. It rejects a 10 kW load no better. With the 1.5 s heater it overshoots by 17 %, while the slow PI does not. - Lesson 61 (vessel commissioning) replaces its single-vessel trial selector with five jacketed vessels and one
ReactorTrainblock per vessel. The block owns the heater, tracks with the controllers not in charge, keeps the transfer update as a tracking update, and latches zero heat on an invalid reading. The heat balance rules out cooling, and no jacket limit is specified, so the lesson compares the single loop and the cascade on the same requirements. In Control Studio both meet the 35 → 37 °C step; on the 30 kW jacket loss the single loop falls to 31.74 °C, while the cascade holds 34.80 °C. The recommended cascade then passes the lifecycle case: 15.000 % on the transfer update and 15.144 % on the next, and 0 % in the update of a 0 °C sensor reading, still latched after the reading recovers. Lessons 53–61 also replace the lesson metadata's baseline and change summaries left over from the programs these lessons replaced. - Lesson 62 (multivariable process paths) replaces its distillation-column program with the README's two vessels coupled through an 8 kW/°C wall. One recording steps heater 1 and then heater 2 with the other fixed, and
CoupledVesselModeladvances the supplied balances from the old temperatures. Control Studio's Measured MIMO paths reads the four paths from settled windows: gains 0.1380, 0.0607, 0.0611 and 0.1376 °C per point against K's 0.1385 and 0.0615, all rises within 0.005 °C of the prediction. - Lesson 63 (relative gain array) replaces its distillation-column program with two pairs of the coupled vessels: isolated steps on one, and on the other the same steps with the other heater corrected by −(4/9)·5 points so that its vessel ends where it started. In Control Studio the compensated gain is 0.1109 against the isolated 0.1380 °C/point, a ratio of 1.245 against 81/65 = 1.246, and Measured MIMO paths gives the array 1.2426/−0.2426: the diagonal pairing is chosen. The fixed correction holds the other vessel only at equilibrium; it dips to 34.906 °C at 3.0 s.
- Control Studio's Measured MIMO paths shows each matrix with one precision, so a relative gain row visibly sums to one (1.2426 and −0.2426 instead of 1.24 and −0.2426), and the relative gain array and decoupler tables use the gain matrix's table layout.
- Lesson 64 (decentralized PI) replaces its distillation-column program with four pairs of the coupled vessels, one
Control.PIDPI per vessel, on both target directions and a 10 kW load on each vessel. The gains are Control Studio's declared experiment settings, so each gain set is its own run. With Kp 5 and Ki 1 every changed- and loaded-vessel requirement passes, but the unchanged vessel strays +0.284 °C, beyond ±0.2 °C. The bounded retunes (Kp 6: +0.272 °C; Ki 1.2: +0.288 °C) do not help, so the lesson keeps Kp 5 and Ki 1 for static decoupling. - Lesson 65 (static decoupling) replaces its distillation-column program with six pairs of the coupled vessels: paired PI on the vessel-1 target and load, and
DecoupledPion all four events.DecoupledPiuses the same gains through u = 75 + M·v, M = [[1, −4/9], [−4/9, 1]]: virtual integrals start at zero, the bias is added after the map, and physical limiting is tracked through M⁻¹. In Control Studio the map lowers heater 2 to 70.47 % in the first update and keeps the unchanged vessel within −0.152 °C, against +0.284 °C paired, so decoupled PI meets every Lesson 64 requirement. Under the loads it moves the other vessel more, −0.155 against −0.068 °C. - Lesson 66 (multivariable design selection) replaces its distillation-column program with three sets of the coupled vessels, with walls of 6, 8 and 10 kW/°C. The declared experiment setting Coupling Case selects which set a run exercises. Both architectures run in one block,
TwoVesselPi, with cross gain 0 for paired PI and 4/9 for Lesson 65's decoupler, frozen for 8 kW/°C. In Control Studio the decoupler passes all 12 of its cases, both directions and both loads at each coupling, with the unchanged vessel at −0.179/−0.152/−0.131 °C. Paired PI fails the unchanged-vessel limit in all six target cases (+0.233/+0.284/+0.328 °C), so the decoupler is chosen for the tested couplings. - Lesson 67 (state-space models) replaces its single-cascade program with two copies of the two-tank gravity cascade and
CascadeModel, the README's local model advanced by one explicit one-second step from both old states. The lesson first calculates the supplied model's Initial-state response in Control Studio: from tank 1 at 0.27 m, tank 2 rises on the stored water to 0.007358 m above rest at 25 s. It then records both tanks through a ±0.001 m³/s pump change, with both model levels within 0.00057 m of the tanks while the pump is changed. A level outside 0.05–0.45 m or a clock interval outside 1 ± 0.05 s latches zero flow. - Control Studio's Initial-state response draws Total as the wide process-value line, with Zero-state input and Initial condition over it as dashed and dotted auxiliary lines, so a part that equals the total stays visible; before, all three used the chart's default colours and the total was faint. Its calculation table shows each column in one notation (0.000769 instead of 7.69e-4 above 0.001477).
- Lesson 68 (controllability) replaces its program with Lesson 67's cascade and model, steered by a pulse pair designed from the model: 0.011 m³/s for 20 s, then 0.009 m³/s for the 11 s after which the model returns tank 1 to rest. Control Studio's Controllability and observability shows rank 2 of 2 with [B, AB] = [2, −0.08; 0, 0.08]. In the recording, tank 1 is back at 0.2501 m with tank 2 at 0.2603 m at 31 s. Tank 2 then drains, because no constant flow holds unequal levels.
- Control Studio's Controllability and observability reports each rank out of the state count. Under Calculation details it lists the declared state, input and output order, the matrices A, B, C, D, [B, AB, …] and [C; CA; …] with one precision per matrix, and what the ranks do not establish. Before, the second matrix column appeared in exponent notation (−8.00e-2 for −0.08), and the order and the scope were missing.
- Lesson 69 (observability) replaces its single non-equilibrium cascade with two copies of Lesson 67's cascade, both driven by Lesson 68's pulse pair. Tank 1 is reconstructed from tank 2's readings alone as y + 25·Δy, from exact readings on one copy and on the other from readings 5 mm off in alternate directions. The true tank 1 level is read only to score the reconstruction. In Control Studio the observability rank is 2 of 2 with [C; CA] = [0, 1; 0.04, −0.04]. The exact readings reconstruct tank 1 within 1.3 mm (RMS 0.00047 m); the noisy ones are off by 0.255 m RMS, since each 5 mm error enters through a derivative multiplied by 25 s.
- Lesson 70 (state feedback) replaces its magnetic-bearing project with two copies of the two-tank cascade. It is the first lesson to use Control Studio's state-feedback design end to end. The supplied model declares only the physical cascade; Studio derives the local model with both levels measured. State-space design places z = e^(−0.08) and e^(−0.12) and calculates K = [0.0562, 0.0369] m²/s and the reference gain 0.1131. Validate runs the actual program against the nonlinear cascade (settling 33 s and 25 s), and Apply commissions Auto. After a 0.01 m³ slug into tank 1, tank 2 rises 3.2 mm with feedback against 7.3 mm without, and both levels are within 0.35 mm of rest 50 s later.
- Lesson 71 (state observers) replaces its magnetic-bearing project with Lesson 67's cascade and four
TankObservers. It uses L = [0.48, 0.28] s⁻¹ for the error poles −0.16 and −0.20 s⁻¹ and the stronger L = [2.52, 0.64] s⁻¹, each fed exact and ±5 mm tank-2 readings, after a slug that the observers do not see. Control Studio checks the supplied error model: Poles and zeros shows −0.16 and −0.20 s⁻¹, and Initial-state response shows the predicted decay. The taught gain estimates tank 1 within 2 mm from 22 s with exact readings and from 30 s with noisy ones (RMS 1.43 mm, against 255 mm for Lesson 69's differentiation). The stronger gain converges by 12 s but carries 9.2 mm RMS of noise. - Lesson 72 (Kalman filtering) replaces its weigh-feeder project with Lesson 67's cascade and a two-state Kalman filter of tank 1 from tank 2's reading, which carries 5 mm of pseudo-random noise restarted with each experiment. The filter calculates its gain from the model's and the reading's uncertainty every second and updates its covariance in Joseph form. It also reports the RMS error it claims, next to the actual RMS error scored against the true level. With the true reading uncertainty, tank 1 is estimated within 2 mm from 25 s after the unseen slug, with an RMS error of 0.23 mm against a claimed 0.54 mm. Assuming 0.25 mm raises the gain on tank 1 to 19.5 and gives 1.01 mm while the filter claims 0.34 mm. Assuming 100 mm gives a smooth but late estimate (1.87 mm).
- Lesson 73 (LQR control) replaces its suspension project with Lesson 70's cascade under a gain designed by Control Studio's finite-horizon LQR. The design uses 20 steps, Q = diag(400, 400) with the same terminal weight, and R = 40000 or 160000. The program adds the level cost 400(x1² + x2²) and the effort cost 40000(q − 0.010)² of the applied flow for 60 s after a slug, the same scales for both designs, and records the largest level deviation 50–60 s after it. K0 = [0.0784, 0.0245] m²/s keeps tank 2 within +3.0 mm (level 0.489, effort 0.288); four times the effort weight gives [0.0344, 0.0098], +4.4 mm (0.913, 0.100). Both meet the 2 mm late requirement, and each design has the lower cost by its own weights.
- Lesson 74 (LQG control) replaces its suspension project with two cascades under the same gains from Control Studio's LQG design. The design supplies a one-second model that measures only tank 2, and gives K = [0.0787, 0.0239] m²/s with a steady Kalman gain L. One cascade measures both levels; the other feeds the gain with its estimate, predicting from the last applied command and correcting with the reading. During the project experiment both read tank 2 with the same 5 mm noise, and after a slug the estimator is not told about, the full-state loop stays within 0.58 mm 50–60 s later. With the model's error alone as process covariance, L = [0.0023, 0.0068]: the estimate misses the slug and the loop is 5.3 mm high. Allowing tank-1 upsets (L = [0.239, 0.106]) brings it to 1.7 mm.
- Lesson 75 (model predictive control) replaces its building-zone project with two of Lesson 13's heaters that share a 100-point supply. Every second the program predicts both heaters three seconds ahead for the 231 command pairs on a 5-point grid within the supply. It rejects plans that leave 26–33 °C, scores squared temperature errors plus a 0.002 move weight, and applies the first move of the cheapest plan; the cost splits into one part per heater, which keeps the search to 42 predictions. Raising heater 1 from 28 to 31 °C, the first plan borrows supply from heater 2 (70/30), and both targets are met within 8 s at 55/40. With 30 kW more taken from heater 2 the targets need 110 points: the plans use the whole supply, 50/50, and both heaters settle 1 °C low. The old test that loaded the building project is retired.
- Lesson 76 (Lyapunov stability) gets a program and a matching plant: an upright arm (J = 1 kg·m², B = 1 N·m·s/rad, G = 4 N·m, ±20 N·m) under τ = −16φ − 7ω, with V = ½Jω² + ½Kpφ² + G(cos φ − 1) and its certificate V ≤ 0.1 J published every 20 ms. Before, the project had no program, a mismatched arm simulation and a library copy. A Control Studio experiment setting chooses the start angle; the experiment holds the arm there with gravity compensation and then releases it. From 0.1 rad the state stays inside the certificate, V never increases, and the torque peaks at 1.6 N·m. From 0.2 rad, outside the proven region, the arm also returns and enters the region after 0.46 s: unproved, not unstable.
- Lesson 77 (feedback linearization) replaces its industrial-joint project with Lesson 76's upright arm under τ = 16(r − φ) − 7ω − Ĝ·sin φ, with the controller's gravity coefficient Ĝ as a Control Studio experiment setting. For a 0.1 rad target, no compensation rests at 0.1332 rad and misses both the 0.12 rad peak and 0.01 rad error requirements. Ĝ = 3.5 N·m rests at 0.1032 rad, and the nominal Ĝ = 4 N·m holds 0.1000 rad with gravity's own −0.399 N·m.
- Lesson 78 (adaptive control) replaces its extruder project with two of Lesson 13's heaters whose heat loss has dropped to 8 kW/°C, each held by the real
Control.PID(heater B in Manual during the estimation windows). From one-second heat balances the program estimates the heat loss by least squares, Ĥ = Σxy/Σx², over two 30 s windows. It accepts a window only for 7–13 kW/°C, at least 1 °C of temperature span, residuals within 5 kW and agreeing halves, and then sets Ki = Ĥ/(aλ). The steady window is rejected even though it gives 8.0 kW/°C, because it cannot separate the change from an unknown extraction. The step window is accepted and sets Ki to 0.4. On a 3 °C step the revised heater follows the λ = 10 s design (63 % at 9.9 s, no overshoot), while the old gains rise faster and overshoot slightly. - Lesson 79 (robust control) replaces its flexible-wing project with eighteen of Lesson 13's heaters spanning heat loss 8–12 kW/°C and delivery delay 0.8–1.2 s, each held by its own
Control.PIDin an array ofHeaterCaseblocks. A capacity check shows that holding 38 °C would need 108 % at 12 kW/°C, so the requirements use a 35 → 36 °C step and a 10 kW extraction. The heat loss varies gain and time constant together, and a small-gain bound, W·|T0| ≤ 0.254, proves stability for every heater in the range with Kp = 2.5 and Ki = 0.5. In Control Studio that candidate passes all eighteen cases; Kp = 30 and Ki = 6, tuned for the nominal heater, settles faster there but leaves the 8 and 10 kW/°C heaters with 1.2 s delay oscillating (7 of 9 step cases). - Lesson 80 (networked control) replaces its pumping-header project with six of Lesson 13's heaters, each under a remote
Control.PIDwhose commands travel as packets over a simulated network, with a localControl.PIDas fallback. The reusableCommandReceiveraccepts a packet only if it belongs to the current connection, has a newer sequence number and is 0–200 ms old; it holds the last command for at most 300 ms and then hands over to the local PI, which has tracked the applied command. Remote control returns only after an acknowledgement, three fresh packets and a remote command within 0.1 point of the applied one. In Control Studio a dropped, a reordered, a late and an old-session packet are each held or rejected by their own check, an outage switches to local control at 5.4 s, and the acknowledged heater returns to remote control at 8.2 s; the transfers change the command by at most 0.026 points, and all six heaters meet the step requirement. - Lesson 81 (fault-aware control) replaces its pumping project with six of Lesson 13's heaters, each measured by two temperature sensors and held by a
Control.PID. The reusableSensorSelectoraccepts a reading only if it is 0–80 °C and at most 0.5 s old, uses sensor A when valid and B otherwise, latches zero heat when two fresh readings differ by more than 0.4 °C for 0.2 s or no reading is valid, and restarts only after three fresh agreeing readings and a new Auto request; each change of sensor re-initializesControl.PIDwith the applied command so the command does not jump. In Control Studio a stale sensor is used until its reading is older than 0.5 s, an out-of-range sensor is replaced at once, a sustained disagreement latches zero heat at 5.2 s while two short ones do not, losing both sensors latches, and the restored heater restarts at 12.0 s from zero with only the integral step. - Lesson 82 (independent design) replaces its compressed-air project with a matched comparison on twelve of Lesson 13's heaters: PI against PI with feedforward of a measured 10 kW draw-off, each at heat losses of 8, 10 and 12 kW/°C with a setpoint step and a draw-off, against requirements and a decision rule declared before the test. A feasibility check shows that every requirement needs at most 96 %. Both candidates meet the setpoint requirement; only the feedforward candidate keeps the draw-off within 0.3 °C (about 0.18 °C, the fall during the 1 s delivery delay, at every heat loss), so the declared rule recommends it.
- While running with debug control attached, the debug snapshot shows the last completed cycle, including its last statement; a pause, step or breakpoint still snapshots the exact storage at the stop.
- Lesson 14 (identification experiments) uses that recording: its record ends just after the step returns, for any permitted hold. The lesson explains the steady start, step size and hold through the drift, uncertainty and first-order response calculations, with the recorded results.
- Control Studio's recorded-fit comparison now exposes the existing model-error and uncertainty evidence in collapsed details, retains the recommended structure with one primary action, and restores its model family and measurement settings when the editor reopens. Lessons16–17 use the three-page controls without changing their teaching objectives.
- Control Studio retains project-experiment recordings and signal selections on reopen, shows selected live setpoints and diagnostics with stable colours, and frames constant measurements without exaggerating numerical roundoff.
- Keep experiment review text compact and use shared numeric controls for declared settings. Save native recordings through the response context menu or command palette.
- Allow the identification-experiment lesson's complete 40-second hold after its five-second baseline; retain the actual applied-input record for subsequent identification.
- Adapt control-course experiment instructions to the existing three-page Control Studio while preserving the taught concepts and declared requirements.
- Tune automatically fills missing test settings from the selected plant model, recording and operating limits. Keep declared/manual requirements and pinned controller settings; label generated criteria in the optional editor and validation results and retain them across reopening.
- Control Studio Identify now performs one live experiment and retains its provisional model and measured response. Tune stops with the candidate; explicit Validate acquires independent data, checks the frozen model and executes the controller campaign before enabling Apply.
- Control Studio Identify records and excites the connected running plant in real time, using native experiment restoration and per-scan recording. Reset and reopening resume live selected signals. Independent live records retain their own provenance.
- Control Studio: replace the old multi-mode workspace with the agreed Identify, Tune and Validate pages, sharing the existing workflow/runtime services. Keep exact controller bindings, recorded-data identification, manual/pinned settings, channel limits and requirements, and guarded Apply/Restore in that flow. Fit explicitly selected recording channels and retain them after reopening; identify every declared PID-group member and require a supported complete parameter/state transaction before group tuning or application. Restore contextual project experiments, manual recorded-data fitting, supplied model import, analysis tools and scenario editing within those same pages. Retain measured frequency/MIMO results, exact Routh regions, initial-state responses and finite-horizon Riccati stages; preserve declared state and switching-controller pins during retuning.
- Refine Control Studio into one readable direct analysis surface with coordinated Bode magnitude and phase navigation, exact graphical gain entry and live margins, full-width plots below compact live values, visible wrapped controls at 200 percent zoom, restrained VS Code-themed chart chrome, export of the currently visible plot, and pixel-verified MIMO rendering evidence.
- Expose Control Studio as a permanent truST sidebar destination immediately
below Devices & Connections. The destination is visibly disabled with a
reason until a project is open, while configured simulated plants retain
their direct context-preserving Control Studio handoff. Runnable projects
can provide an explicit
control-loop.json; the water-heater proportional lesson now uses it so the sidebar opens its live SP, PV, and MV instead of an unbound workspace. - Define the Control Studio chart workspace as a large, discoverable engineering surface with visible plot and signal selection, unit-safe multi-channel lanes, explicit advanced-plot prerequisites, MIMO matrix/RGA/ singular-value views, professional time/frequency/stability plots, expanded mode, direct graphical controller tuning, linked candidate analysis, graphical requirements, pinned design comparison, evidence-bound session restoration, uncertainty envelopes, undo/redo, and precise responsive and accessibility acceptance criteria.
- Render lesson equations with VS Code and GitHub's native Markdown math, including conventional fractions, indices, vectors, and matrices, while preserving the curriculum's words-before-symbols and accessible prose rules.
- Replace the draft control-engineering curriculum specification and its premature implementation checklist with an 84-lesson causal learning program and specification-derived implementation checklist. The program teaches required theory before each real-equipment practical, carries one observed limitation into the next technique, and progresses through production PID, modelling, identification, classical analysis, industrial structures, MIMO, state-space, predictive, nonlinear, adaptive, robust, networked, diagnostic, and capstone work. The reviewed water-heater proportional-control lesson now defines the reusable course-wide conformance pattern: words and physical meaning before notation, executable predictions, actual Structured Text, precisely defined and validity-qualified measures, retained evidence, and a causal handoff from one lesson's observed limitation to the next.
- Make
examples/the single maintained source for bundled projects, generate the VS Code example package from its canonical catalog, and replace the flat control list with 84 ordered lessons plus topic links that make the rest of the tutorial and example catalog discoverable. The control examples README now provides the complete linked course map, stating the real equipment, theory, Control Studio stages, and practical skill for every lesson. Every canonical lesson directory now begins with its zero-padded course number and lives in one of 14 consecutive learner modules, so filesystem browsing preserves the same learning order as Control Studio. Separate README indexes let engineers find lessons by technique, plant behavior, and workflow without imposing a second technical taxonomy on the folder tree. Canonical bundled projects now use an explicit non-secret local example token so every shippedruntime.tomlpasses the runtime configuration validator.
Removed¶
- Removed: the bytecode engine. Every runtime now runs the project compiled into a program image
(specification 44 §15):
trust-runtime buildwritesprogram.tpi, andrun,play,fleet start,conformance,bench project,trust-dev testand the VS Code simulator run it. The STBC format with its encoder, loaders, validator and virtual machine is deleted, and so are the--execution-backendoptions ofrun,play,build,conformance,benchandtrust-dev testand the debug launch'sexecutionBackend, which entries below still name. A project that holds only an oldprogram.stbcis refused with "rebuild the project";runtime.execution_backendinruntime.tomlstill loads withvmornativeand selects nothing. Retain files written before restore unchanged.trust-bundle-genwritesprogram.tpi,trust-runtime checkno longer reports a bytecode size, andcargo xtask portabilityloses its STBC actions (pack,verify,footprint,compare-benchmarks). The STM32F407 firmware and its adapter are deleted with the engine they ran: the chips are the ESP32-C6 and the STM32U5A9. The engine's error codes, which nothing reports any more, leave the stable vocabulary:bytecode_*, everyvm_*code butvm_call_stack_overflow,runtime_invalid_bytecode,runtime_invalid_bytecode_metadata,runtime_unsupported_bytecode_version,runtime_condition_not_bool,runtime_case_selector_type,runtime_invalid_control_flow,runtime_type_nesting_exceeds,runtime_undefined_task,runtime_undefined_label,runtime_invalid_task_single,runtime_constant_write,runtime_staging_violation,runtime_visibility_violation,runtime_program_root_replacementandruntime_preparation_limit. - The NUCLEO-F401RE firmware is retired; the STM32 chip is the STM32F407. The STM32F4
adapter has no
stm32f401feature, andcargo xtask portabilityno longer acceptsnucleo-f401re. - HMI Builder: Live mode always shows the real run target; its test values and recordings are removed.
- HMI:
symbols = "rendered"inhmi/screen.tomlis an error; the symbols are two-dimensional. - Control Studio no longer contains built-in physical model families: the thermal-balance and gravity-tank models, their identification, the step-by-step setup's unit-based choice of them ("Thermal energy balance" for W and °C, "Gravity-tank volume balance" for m³/s and m) and their native simulation are removed. Models are transfer functions or state-space models; a physical plant is supplied as one, identified from recordings or simulated by the project's plant. Lessons 68 and 69 supply the equivalent linearized two-tank state-space model.
- The legacy HMI is removed; the HMI Builder (
docs/specs/39-hmi-builder.md) replaces it. Removed: the/hmipage with its assets, export (/hmi/export.json) and live socket (/ws/hmi); thehmi.*control operations; thetrust-runtime hmicommand; thehmi/pages thattrust-runtime runwrote into projects; the language server'strust-lsp.hmiInitcommand and HMI diagnostics; the VS Code HMI preview, its commands, thetrust.hmi.refreshIntervalMssetting and the HMI language-model tools; and the legacy HMI examples, tutorials and documentation.trust-lsp.hmiBindingsstill returns the process image and control symbols but no longerprogramsandglobals;statusno longer reportshmi_read_only,config.getno longer reportshmi.read_only, and the access capabilities no longer includehmi. Aruntime.tomlthat still contains[runtime.hmi_persistence]is now rejected: delete that section. Existinghmi/folders are no longer read or changed.
Fixed¶
- The cycle grid keeps the tasks' deadlines on computers and chips alike (specification 10 §4.3): it begins where the tasks' timing begins, also when the first cycle starts late; a cycle that starts a whole cycle interval or more late, because the operating system delayed its wake or the cycle before it ran too long, skips the boundaries it missed and stays on the grid; and a pause keeps the grid. Before, the next cycle could start at once, or the grid could lie off the tasks' deadlines, so that a cycle found no task due and still counted as a scan: a loaded computer counted scans that ran no task.
- The language server's HMI and Control Studio commands work in a project opened through a
symbolic link, as a project under macOS's
/varor/tmpis, or through a short name, as Windows' temporary folder is: unsaved editor text is used, the project's programs are no longer taken for a library's, and edits name the file the editor has open. The debugger, too, names a stopped program's file as the editor opened it (specification 13, "Stack Trace and Navigation"). - An alarm acknowledgement, shelving, reset or service change from an HMI page is recorded with the operator's name also when the scan applies it at once, and an action the runtime refuses no longer names the operator for a later change the program makes (specification 40, §5.1).
- Control Studio's Identify returns a loop it found in Auto to Auto also while the plant still moves. The runtime's check of the first output after the transfer allows the controller's response to the measurement's change before the entry, which the runtime reads itself, never from the controller's own state (specification 35, A6.4; protocol annex E6.9). Before, a busy computer that delayed the return by a few task periods made the runtime refuse it and leave the loop in Manual.
- On Windows, starting a project's runtime from Devices & Connections finishes again: the started
runtime no longer holds the output pipes of the command that started it, which kept the command
waiting for as long as the runtime ran. Control Studio's message when trust-runtime cannot be
started keeps its advice to pass
--endpoint, and Keep in project names an unsaved file as the editor does. - An HMI page left open no longer keeps a record of every command it sends. It remembers the newest 1,024 commands and alarm actions for their later outcomes (specification 39, §12.5).
- Control Studio's isolated runs and searches (validate, test runs, finite search) work on macOS and Windows. They ran only on Linux, which limits a worker's address space; on macOS and Windows the trust-runtime and trust-debug allocators now hold each worker to the same 1 GiB (protocol annex E8.1).
- Stopping or stepping in a compiled program shows the program's file again. The debugger named the file by its path relative to the project, which VS Code could not open ("Could not load source 'src/Main.st': unsupported command"); it now names the project's file, for stack frames and logpoints (specification 13, "Stack Trace and Navigation").
- A return to Auto is no longer refused when the plant moves while the transfer waits for its task: the runtime now takes the error the controller enters Auto with in the scan of the entry and adds its integral step to the first-output allowance (specification 35 A6.4, protocol annex E6.9). Before, Control Studio fixed that error from a status read earlier, so Identify's return to Auto after a step, Apply's Auto entry and Restore could be refused with "first controller output … violates reviewed transfer tolerance" on a fast plant.
- A project opened through a symbolic link works like any other: a breakpoint set in a file named
through the link reaches the program (the debug adapter resolves the path as the build did,
instead of comparing its text), and the HMI editor's library check no longer fails when the
temporary folder lies behind a link, as it does on macOS (
/var→/private/var). - Debugging a compiled program writes variables again: Set Value in the Variables view and an
assignment in the Debug Console change a variable, a structure member or an array element while
the debugger holds the scan (the new
debug.setrequest) and between scans (set); Live Values writes, forces and releases by path (global:Main.Pumps[2].Speed) with typed IEC literals (T#5s,REAL#2.5, an enumeration's value); the debug adapter repeats a write that meets a busy runtime instead of the extension; and a write to an I/O address outside the program's process image is refused instead of growing the image (specifications 11, 13 and 44). - I/O addresses are written the same way everywhere, in the form the address parser reads
(specification 10 §9.2): Live Values and the fleet now show an unresolved wildcard as
%Q*instead of%QX*, which could not be sent back; the fleet, setup and debugger replies keep a hierarchical address's whole path (%QW3.4.5, not%QW3), so the debugger finds such an I/O row by its name; and a refusal names aSTRINGbinding by its declared address (%QB24). - HMI method commands and equipment settings work on a runtime started with
trust-runtime run, whether debugging is on or off: they are checked against the running program's own tables, so a command that names only some of a method's inputs leaves the others at the values the interface declares (specification 39 §21.2). Before, the runtime had no program description to check them against and refused them. - Alarm comments take 120 characters in any language:
Request.Commentof the alarm library is aWSTRING[120], and the HMI writesWSTRINGpoints (specification 40 §4). A comment of 61 "ä" was refused before. - Discover truST runtimes listens for its whole mDNS browse window, 2,000 ms by default, instead of ending at the first quiet 100 ms, so runtimes that answer a little later are found (specification 11).
- A new Ladder, SFC, Statechart or Blockly program opens in its visual editor, with its Structured Text
companion beside it, instead of as JSON text or only the companion; a
.sfc.jsonopened as text also opens in the SFC editor. The new-Ladder template is valid schema version 2 (specification 25, §11.4). - HMI Live mode on the simulator says where alarms show instead of showing zero alarm counts, and its read-only explanation names the simulator (specification 39, §11).
- Choosing another run target in the side panel or with Set as run target ends the session with the previous one; the status bar, Live Values and HMI Live follow the side panel's Target instead of staying on the runtime that was connected before (specification 25, §5.1).
- Devices & Connections gives a connected runtime's node its endpoint, so it offers Disconnect and Set as run target for that runtime; before, Connect was disabled on it and Set as run target selected the Simulator. A running simulator is drawn live: its node green ("Running"), and its simulated I/O and plants with their live state, because the VS Code simulator now reports its I/O drivers' health and its simulation's active configuration revision as the runtime binary does (specification 47, §20.4). A card name such as "Simulated I/O" wraps instead of ending in an ellipsis.
- A runtime that answers Connect with debugging disabled reads "debugging disabled" in the status bar and in Live Values' state, instead of "connected" in one and "Not connected" in the other (specification 25, §4.4).
- The status bar says "Simulator ×10 paused" while the program is held at a breakpoint, as the runtime reports it, instead of "running".
- Completion lists a namespace as a namespace (LSP Module kind), not as a variable.
- A missing expression (
Level := ;) gives one error; the statement keeps its;, so no second "expected ';'" follows. - The alarm list's Value column never reads "2.5 > 2.5": when the tag's decimals round a reading onto its limit, it shows more decimals ("2.53 > 2.50"), and its sign compares the two numbers shown (specification 39, §5.9).
- High contrast themes mark the active part of the HMI Builder's Build/Live switch.
- Create project opens its name box with a selected suggestion instead of "Project name is required." before anything is typed.
trust-runtimecommands run with--jsonreport an error without the command-line tip, so VS Code no longer suggeststrust-runtime wizardafter a failed PLCopen export (specification 11, 6.8.1d).- Function-block and class instances follow IEC 61131-3 §6.5.6 over a warm restart, an online change and a
power loss, on every runtime, embedded or hosted: an instance declared RETAIN, alone or as an element of an
instance array, keeps its whole tree, including an unqualified nested instance such as a
CTUorTONinside it; a variable or nested instance its type declares NON_RETAIN starts over, also in a RETAIN global after a warm restart of the running runtime; a variable its type declares RETAIN is kept in every instance (@program-instance/...and@global-instance/...in the retain store, specification 10 §4.8). - A variable bound to a marker (
%M) starts from its initialized value after construction and a cold restart, and from its retained value after a warm restart or a retain-store load, instead of from a zero or pre-restart image (specification 11 §6.5). - A call on an element of a function-block instance array (
Pumps[2](Start := TRUE)), an instance array's element initializers ([(PT := T#100ms), 2((PT := T#50ms))]) and a named instance-array type's initializer (TYPE TONS : ARRAY[1..50] OF TON := [50(PT := T#100ms)]) are accepted by the bytecode producer and validator again. - Fixed: installing a program again on a chip no longer fails for memory. The chip takes its
checkpoint buffer once at boot and lends it to each program that keeps retained values, where
each start asked the heap for 16,300 bytes in one block, which a heap fragmented by the programs
before had lost; and a dropped
modbus-tcpormqttdriver closes its connection in order and gives its socket back before the next program claims its drivers. - Fixed: the
mqttdriver's Sparkplug B profile follows Sparkplug 3.0's rules for an edge node: a clean session; the NDEATH will at QoS 1 and without a sequence number;bdSeqanInt64that counts the connections frombirth_death_seq(now 0 to 255) and wraps after 255; the NCMD subscription before the NBIRTH; aNode Control/Rebirthmetric in the NBIRTH, whose metrics carry the newest image's values or null; and a rebirth when a host asks for one in NCMD. A broker's delivery at QoS 1 is acknowledged, and an emptyclient_idis allowed with Sparkplug, as its session is clean. - Fixed: a chip counts what a program keeps beside its memory figure: a debug program's buffer of
debugger stops at its install and load, and its I/O drivers' blocks when they are claimed, so a
program the heap cannot hold is refused with the numbers instead of running out of heap at its
start; a chip whose heap has no block for the checkpoint buffer refuses the start instead of
panicking.
capabilitiesreports a chip's heap (capacity_bytes,used_bytes,peak_bytes), and a panic for want of heap names the request and the free bytes. - Fixed: a project's
io.tomlcan no longer claim the ESP32-C6's flash pins, GPIO24 to GPIO30; a line there would have taken the flash the chip runs from. - Fixed:
statuson a chip reportedsignature: nonewhile the chip checked signatures against the keys of its device settings. - Fixed:
config.setrefuses alog.levelthat is not one oferror,warn,warning,info,debugandtrace, and trust-runtime writes its log lines by a level changed withconfig.setat once; it took any text and kept logging by the level it started with. - Fixed: a chip whose program hung or panicked comes back faulted, as trust-runtime's resource
stays faulted (specification 34 TARGET-08). After the hardware watchdog's reset the program is
Faulted with
watchdog timeoutwhileruntime.watchdog.hang_actionisfault, the default, and runs again withabort; after a panic it is Faulted withresource panic '<location>'. The fault is logged, the safe outputs follow the watchdog's action or the fault policy, and arestartruns the program. The chip ran the program again after either reset, so a program that hung or panicked did so again in a loop of resets without saying so. A program whose load ended the boot is not loaded again at the next boot, which comes back faulted in the same way; arestartloads it again. - Fixed: a program's memory figure counts what its load keeps on a chip, the cycle's copies of the process images and a debug image's line table (specification 44 §10.4), so a chip refuses at its install a program it could not load. With Wi-Fi, the ESP32-C6 had admitted the reference debug image, whose load then ran out of memory. The C6's Wi-Fi driver takes 56 KiB of its heap, its measured peak with a fifth more, instead of 64 KiB.
- Fixed: the ESP32-C6 holds a reply up to its whole heap, 180,160 bytes; the capacity left out the main heap's 64 KiB since the bytecode engine's RAM code was freed. The STM32U5A9 holds a reply longer than the 8,192-byte frame in parts, as the C6 and trust-runtime do, up to its 1 MiB heap (specification 34 PORT-05); it refused such a reply.
- Fixed: the variables of function block and class instances are retained as IEC 61131-3
§6.5.6.2 says, by the bytecode engine and by compiled programs: a member declared
VAR RETAINorVAR PERSISTENT, and each member without a qualifier of its own in an instance declaredRETAIN, keeps its value at a warm restart and is written to the retain checkpoint under its path (@program/Main/Line.Count,Plant.Pumps[2].Hours); a standard function block keeps its inputs and outputs and starts its own state again (specification 11 §6.7). Before, such members started from their initial values. A compiled program's image is format version 2, whose retain records name an area and an offset; a kernel refuses an image of format 1, so programs are built again. - Fixed: the OSCAT library's PLC timers wrap as upstream's do and no block faults when they
wrap.
T_PLC_US, the milliseconds times 1000, overflowed after 71.6 minutes of uptime, so every program withCTRL_PID,FT_PT1orFT_DERIVstopped with an overflow then;T_PLC_MSoverflowed after 49.7 days; and the 57 blocks that time with them subtracted two readings in checked arithmetic, so each faulted when its timer wrapped, or at 2^31 when it took the timer as DINT.T_PLC_MSandT_PLC_USnow wrap modulo 2^32, the newT_PLC_ELAPSED(NOW, LAST)gives the time between two readings across a wrap, and every block uses it (specification 31 §2.1).CYCLE_TIMEalso measures its least cycle again: upstream starts it at the largest 32-bit time, which the port had written as -1 ms.D_TRIGgives the difference of a falling input modulo 2^32 as upstream does instead of faulting, andDT_SIMUno longer faults on a first call long after the start, where it scaled the whole uptime bySPEED(after 24.9 days at 2.0). - Fixed: the OSCAT library took a
DWORDas a number throughDWORD_TO_REALandREAL_TO_DWORD, which IEC 61131-3 Table 25 makes binary transfers, in 25 places:FT_PT2divided by zero on its second scan,FT_PT1,FLOW_METER,M_TX,HEAT_METERandFT_Profileoverflowed once time passed,RDMandRDMDWfailed when the PLC timer's bits formed no number, andMULTIME,AIN,DT_SIMU,FILTER_DWandFILTER_MAV_DWgave wrong values above 2^23. They now convert throughUDINT(specification 31 §2.2). The boiler-room example's gas meter reading is fixed the same way. - Fixed: in the bytecode engine,
THISin a base's method reached throughSUPERis the own instance, soTHIS.m()there runs the instance's override (IEC 61131-3 §6.6.5.6, §6.6.5.7), andSUPERthere names the base of that method's class. Before, the method ran on the base part andTHIS.m()ran the base'sm; compiled programs already followed the standard. - Fixed: in the bytecode engine, a call through an interface that names no instance ends the scan
with
null reference dereference, as compiled programs report it (specification 10), instead oftype mismatch. - Fixed: a call of a derived function block takes the inputs and outputs it inherits as arguments
(IEC 61131-3 §6.6.7.2.7), two interfaces of one type compare with
=and<>(§6.6.6.5.1), a derived instance that adds no in-out of its own is taken for a base'sVAR_IN_OUT(§6.6.8.3), and aVAR_EXTERNALinside aNAMESPACEfinds aVAR_GLOBALdeclared outside it (§6.9). Before, the front end refused each of them (E204, E201, E205, E101). - Fixed: an integer literal for a
BYTE,WORD,DWORDorLWORDtakes the unsigned range of its width (IEC 61131-3 §6.3.2, Table 5):DWORD := 4294967295andLWORD := 16#FFFF_FFFF_FFFF_FFFFcompile,BYTE := 256is refused. Before, a bit string took theDINTrange and a typedLWORDliteral aboveLINTwas refused. - Fixed: VS Code's debugger, attached to a running runtime, answered every watch and
evaluatewith an error, because it read the runtime's answer in the debug adapter protocol's spelling; and it set a breakpoint with a condition, a hit count or a log message as a plain breakpoint, which stopped every time. Now the answer is read as the runtime sends it, a runtime on the bytecode engine gets no breakpoint whose options it cannot honor (the editor shows it unverified with the reason), and every breakpoint is answered in the order the editor asked. - Fixed: Live Values
set,var.force,io.writeandio.forceof an integer on a compiled program: the integer takes the declared type of its variable, or of the variable bound at its address, as it does on an interpreted program; it was refused as a type mismatch unless the variable was aLINT. - Fixed: VS Code's debugger, attached to a running runtime, keeps the breakpoints the editor already had: they arrive while the attach waits for the configuration, and the adapter now sends them once it connects and reports each one verified, or why not. Before, they never reached the runtime, so an attached session stopped only at breakpoints set after it started.
- Fixed: debugging on a chip: a chip that starts without a program and then loads one keeps the
stops that
debug.stopsreports, so the debugger sees its program stop (it saw none); the control link answers the debug operations of a compiled program (unsupported requestbefore);io.readshows a forced output with its forced value; and a compiled image names its source files by their path in the project (src/main.st), so a debugger on another computer finds them. - Fixed: a typed literal finds its type through a namespace that a
USINGdirective names (PIDMode#Auto, IEC 61131-3 §6.9.4). Before, the front end refused it (E102). - Fixed: after a
restart, a start aftershutdownor a recovery from a fault, trust-runtime on a computer went on with the cycle grid of the program before, so its next cycle could come less than an interval after the restart, when the restarted tasks were not yet due, and their first activation came an interval late; the grid now begins at the restart's first cycle, as on a chip (specification 10 §4.3). A chip's recovery from a fault byrestartnow begins the grid at its first scan too, instead of at the poll of the faulted wait. - Fixed: the ESP32-C6 no longer ends a reply with a full 64-byte USB packet. The host passes on a
transfer's bytes only after a shorter packet, so a reply whose length was a multiple of 64 bytes
(a 1,984-byte
status, for example) kept its last 64 bytes in the host until the next request, and the client timed out; the last 64 bytes now go out as 63 and 1. - Fixed: a compiled function's result, outputs and locals and a function block's
VAR_TEMPstarted each call at zero, not at their declared initial value or their type's default (an enumeration's first value, a subrange's lower bound, a structure's members' initial values), and an input a call left out took zero instead of its default; initial values of a function's locals and outputs did not compile. - Fixed: a compiled program refused
T0 := -273.15for aREAL: a literal with a sign takes the type of where it goes; aCASEon aSTRINGor aREAL, which the language allows, did not compile; and a text made of literals only (CONCAT('N', 'E'),'AB' > 'B', aCASElabel) gave a module with writable data, which the build refused. - Fixed: a compiled program for a computer whose code copied, filled or compared a large block,
such as a long
STRINGor a large array, did not link: the module had nomemcpyand called the memory functions through a global offset table it may not have. - Fixed:
ABS(-0.0)of a compiled program kept the minus sign of zero; it is0.0, as in the runtime. - Fixed: a compiled program bound a standard function's formal arguments by position, so
LIMIT(MX := 10, IN := x, MN := 0)took 10 as the minimum. - Fixed: a compiled method that returned a structure, a string or an interface received its result place where its instance belonged; methods returning one did not compile.
- Fixed: a compiled program gave two arrays of one storage one type, so
ARRAY[1..3] OF INTwas indexed by the bounds of anARRAY[0..2] OF INTdeclared before it, and an array of one enumeration took the values of another of the same base. - Fixed: a warm restart of a compiled program kept a retained function block instance or reference, which the runtime gives its initial value again (specification 10).
- Fixed: a compiled program's variable located at
%Mtook the marker image's 0 at its first scan instead of its initial value, and a restart cleared the process images; the variable's value is now published into the marker image at every start, and the images are carried over a restart, as specification 11 §6.7 says. - Fixed: a periodic task lost activations. The resource paced each cycle one interval after the previous cycle had started, so every late wake added to the next interval and a 10 ms task lost about one activation in 200 cycles on the ESP32-C6, counted as overruns. Cycles now keep a fixed grid of the cycle interval on computers and chips (specification 10 §4.3): a late wake no longer moves the next cycle, and a cycle longer than its interval starts the next one at once.
- Fixed: an ESP32-C6 could accept a program whose memory figure fitted the memory it reported for programs and then reset at every boot: its control loop, built after the program, needed blocks (its event store alone is 19,296 bytes) that the program's blocks had left no room for, though the free bytes were enough. The chip now builds its control loop before it loads the installed program, and a program that cannot start leaves the chip Stopped with the reason.
- Fixed: a chip builds the default of a global or a program variable that is an array, a structure or
a text in the variable's own bytes. Before, it built the default as a value of every element on the
heap at once, 32 bytes per element on the ESP32-C6, so an array of a few thousand elements could not
be loaded there, and an online change to a program with an array of 1,000 elements was refused with
arithmetic overflow. - Fixed: on the ESP32-C6 a force that names an instance by its identity (
var.forcewithinstance:<id>:<name>) is applied after a restart. Every restart installs the next generation of the program's state, but the loop's Live Values kept the generation of the program's first start, so the scan refused such a force as stale and withdrew it with awrite_rejectedevent. The loop now gives its Live Values the program's generation after arestart, the recovery of a faulted resource, the automatic restart of the fault policy and the start of a stopped program, and when it takes a program that started warm, at boot or when it loads one in place. - Fixed: the ESP32-C6 holds a reply longer than its 8,192-byte frame, up to the 114,688 bytes of its
heap, as a computer does, so
trust-runtime ctl events --limit 200over USB is no longer refused withreply longer than 8192 bytes. A reply the heap has no room for is refused withno room for a reply longer than <n> bytes, and never resets the chip. - Fixed: on the ESP32-C6 a firmware panic drove every configured output low before the chip
reset, whatever
[[io.safe_state]]named. It now drives each output to its safe value, as the STM32U5A9 and the stop and fault paths do. - A stop request (control
shutdown, SIGINT, SIGTERM) now ends a runtime whose scan the debugger holds, at a breakpoint or after a debug-mode pause. It releases the hold, the suspended scan runs to its end, and the ordinary stop path follows: stop outputs, retained-value save, exit. Before, the process waited for aresumeand a service manager's SIGTERM timed out. - A variable located at
%Mnow starts with its declared initial value (IEC 61131-3 §6.5.6): at the first start, after a cold restart, after a warm restart when it isNON_RETAINor unqualified, and after an online change. ARETAINone keeps its retained value. Before, the first scan copied the zeroed or carried marker image over the initialized variable. A variable that is bound to%Mafter the start (a checked binding update) now keeps its current value in the same way. - A runtime no longer faults with
Overflowafter about four billion calls since its last restart (about a day at 50 calls a millisecond), nor after as many function-block or class instances declared inside functions and methods. - A reference taken before a warm or cold restart or an online change no longer reaches
the new program's variables: a write or read through it is refused with
runtime_reference_lifetime, and a queued debugger or HMI write that named an instance before the change is reported aswrite_rejectedinstead of changing the instance that now has that number. Engineering aliases, I/O bindings and ADS and OPC UA client points follow the restart. For Rust embedders,ValueRefcarries agenerationand itsoffsetis au32; see the embedding migration guide (specification 34 §11.3). - Control Studio's Apply of a PID group waits until every member's output has settled at the validated operating point, and compares every member's plant input in Manual; it compared the primary member alone.
- The VS Code simulator's control server takes the program's sources again on every reload, so Validate's isolated runs, a finite search and breakpoints use the program as edited; they kept the sources the simulator started with.
- Control Studio starts its engine again on a request a minute after a failed start, so an engine installed or repaired meanwhile is used without reloading the window.
- Control Studio shows the block of an Apply's checkpoint at once when an Apply or a Restore fails after its first write (the failed update carries the stages), and an engine operation that ends before the panel follows it still reaches the page with its final answer.
- Control Studio's experiment-settings and declared-analysis parameter tools write to a physical target: a review of such a write shows an approval box, and the write carries the engineer's approval (it was always sent unapproved, so the engine refused it).
- Control Studio's resumed identification (
trust-control-studio identify --resume) answers the kept recording's samples and the restoration its experiment confirmed, as a fresh identification does. - Control Studio's Tune page shows the analysis it restores after a reopen as the chosen one: Analyze… offers its Close, and the Tune button names the analysis's action.
- Control Studio no longer shows a settings change that the engine refuses during a running operation as the end of that operation: the refusal shows on its editor's page, and the running work keeps its Abort.
- Control Studio's engine refuses Apply's review and confirmation for the validation the newest Apply already applied, with Validate as the next action, as the page's withheld offer always said; a request from the command line could apply it again.
- Control Studio's Identify no longer refuses a loop with "the loop's Auto/Manual mode is unavailable" when another reader, such as the panel's live values, had just changed the runtime's signal inspection: the engine waits for the runtime's next scan, up to twenty task periods of 50–100 ms, before it counts a run's or a review's sample as unavailable.
- The VS Code simulator's control status reports the tasks of the loaded program and the cycle its runner takes, also after a reload. It reported a fixed 10 ms cycle, so Control Studio sized the recordings and Apply observations of a loop on a background task in the simulator for 10 ms executions instead of the task's own period.
- Control Studio reviews a project test on any runtime: on a stopped Simulator the review answers with a Start simulator button, and a physical target is reviewed and run without a Simulator start first (the page used to start the Simulator before every first review, so a physical target could not review its project test).
- Control Studio's Restore on a runtime that was restarted or reloaded since Apply says that nothing could be restored, keeps the Apply's checkpoint as history and offers Validate again, instead of leaving the loop blocked. After any Restore, Apply is offered again only once a new validation passes, also when the panel is reopened. While a checkpoint waits for its Restore, Identify, Tune, Validate, Reset and the imports are refused with the reason, also after the panel is reopened.
- Control Studio's loop setup form sends every number as it was typed, and the engine refuses one it cannot read under its field
(a browser number field used to blank text such as
1,5before the engine saw it). - Control Studio's loop discovery no longer refuses a loop that declares an addressed BOOL signal, such as the course's experiment activation, fault and state indications: the compiled process image now lists BOOL bits beside the numeric variables, so the setup page and the Tune page's controller list find these loops.
- A project test on the engine now shows "Project test completed. Outputs restored." and its recorded signals in the chart, and a refusal shows its whole text. A project test of a loop whose signals are all addressed no longer ends with "Identification requires the complete recording from the connected runtime.".
- The Tune page's long analyses no longer lose their result when the calculation ends before the page has subscribed to it.
- Control Studio's Apply no longer starts when the process has only touched its settling band. It waits until every measured output has stayed inside its band for a settle dwell: the longer of twenty samples at the loop's task period and a tenth of the expected settling time (B6.2, B3.4).
- Control Studio's Apply observation no longer fails a long test with "The recorded response does not cover the reviewed duration.". Its recording is planned longer than the test duration by the target change's allowance and three record periods (B6.3, A4.2), so a response that starts up to one and a half record periods after the change still covers the whole reviewed duration.
- Control Studio's Identify waits for a steady start as long as two measuring windows of the expected settling time, so a slowly settling process is no longer refused as "still changing"; its window always holds at least 20 samples; a closed-loop setpoint step ends early only when the plant input has settled too; and a loop found in Auto gets its setpoint back after the experiment, also when the program sets the setpoint from the measurement while in Manual.
- Runtime: an isolated test run or search now validates a candidate in a program that sets its own parameter record during its first execution, as a program that designs or loads its startup gains does (the first scan in which its task runs). The candidate is written again at the task boundary after that scan, as Apply writes a record into a running program, and must hold from there; before, such a program's candidate was always refused as "not adopted". A program that overwrites the record later is still refused.
- Runtime: an experiment on an addressed signal with a declared type now reports its last applied
trajectory point (
last_applied, Control Studio runtime protocol E5.6). It stayed empty because the value read back was compared with the value in the process image's raw form. - Runtime: an I/O driver named
noneno longer makes simulation mode refuse to start. - Runtime:
trust-runtime ctl statusworks against a runtime that reports no fault; it failed with "status result 'fault' must be a string". - Control Studio: loop setup bounds a
Control.PID's rate limits widely enough for the Plant library's "no rate limit" value (±10⁹), so loops built with the Plant library validate and apply. - RETAIN members of function block instances, also inherited and nested ones, survive a warm restart and a power loss; an instance initialiser may set an inherited member, also of a function block declared in another file or a library (such initialisers were refused as unknown fields).
- HMI runtime: a pulse always ends, replay records are counted per user, every HMI write route checks the request's origin, and the active bundle is read once per activation.
- HMI alarms: an alarm is listed once, where it is declared, not again under each block that refers to its instrument through an interface; the same holds for displays and link blocks.
- HMI display: a setpoint entry's number no longer reaches into its label; Plant panels keep
every label and button text whole and show one message line, the devices' new
ReasonText(why the device is blocked, else why a command would be refused), with no empty line; Out of service and Return to service are lit only while they can act; a refusal's notice and mark go once a later command is accepted. - VS Code: Initialize truST here makes a project of an empty folder, or one with other files,
without asking; it asks only before replacing a project's own files. Adding the Plant library
now declares it in
trust-lsp.tomlwhen that file is not open (it was copied but not declared), and leaves the library's example projects out of the project. A new HMI screen's files are written at once; its first page no longer shows an unsaved, empty file's problem. Create PLC code saves the program it edits and writes the files it creates with their text, so closing VS Code afterwards no longer loses the code or leaves an emptyscreens.stthat blocked the next run (an empty one left by an earlier version is taken over). - HMI Builder: renaming equipment in the program (Rename Symbol) also renames the instance its page objects show, and their tag when it is the instance's name; before, the object kept pointing at the old instance. Create PLC code also accepts a rename that moves a call to another place in the name order, instead of refusing the region as changed by hand.
- HMI Builder: Apply connections and the inspector's equipment changes save the project design
(
plant.toml) and the files they create; before, the change stayed unsaved in a file without an editor and was lost when VS Code closed. - HMI Builder: a terminal needed only with feedback that the block leaves off by default (a
variable-speed fan's speed feedback) is optional, so the fan shows Connected instead of Partly
connected and Create PLC code no longer lists it as missing. Connecting such a terminal, a
control valve's position feedback for example, switches its feedback on in the declaration
(
HasFeedback := TRUE); before, the valve ignored the connected feedback. - HMI Builder: a process symbol without bindings, such as a tank or a hand valve drawn on a page, is a drawing and is no longer warned of as unbound.
- Create PLC code no longer adds a second
USINGfor a namespace whose directive follows a comment at the top of the file. - Control Studio: identification can switch a loop to Manual whose code copies the output into
the manual output in Auto. The switch was refused with "transfer values changed before task
boundary", because that manual output moves every scan; the runtime now accepts a state the
transfer declares as set by the program, and Manual starts from the held output. A loop whose
output has no I/O address (the program hands it on, as in
EH101.AutoPower := TIC101_Output) is no longer refused with "automatic tracking needs a mapped committed output": the transfer holds the output's value at the task boundary. Returning such a loop to Auto after identification allows the PID's own first step (its integral step on the error identification made and its response to the measured noise) instead of failing the whole identification with "violates reviewed transfer tolerance", and it no longer writes back the manual output from before identification, which only held the output of that moment. - Control Studio records a loop at its own task's executions instead of every resource cycle. A
100 ms loop in a 10 ms resource filled the 12000-sample recording in 120 s, so a 600 s
identification lost its step and failed with "Identification samples have gaps or inconsistent
timing"; it now holds 1200 s.
control.recording.starttakes an optionaltask. - Control Studio experiments run on the runtime's clock: in a simulation at 10x a 600 s identification excites the process for 600 s of process time (60 s of wall time) instead of 6000 s. Restoration and staleness deadlines stay on the wall clock.
- Control Studio's model check accepts a live plant's recording: it required samples on an exact time grid, which only a simulator gives, and reported "The independent recording has missing or irregular scans" for every live loop whose task runs every 100 or 110 ms. It now compares on an even grid at the recording's median spacing; missing samples and gaps are still refused.
- Runtime:
trust-runtime checkandvalidatereport a runtime.toml resource name the program's configuration does not declare ("runtime.toml names resource 'Simulator', but the program's configuration declares resource 'MainRes'; use the same name in both"); before, both said the project was ok and onlyrunrefused it. - Examples: every example's runtime.toml now names the resource its program's configuration
declares, so each starts with
trust-runtime run; 88 examples (the OSCAT, communication and starter examples among them) named a resource their program lacks. The ADS server example appears in TwinCAT under its resource name,CommRes. - New projects: the runtime starts as written. The scaffold no longer declares an empty mesh
auth_token, which the runtime refuses ("runtime.mesh.auth_token must not be empty"), and its runtime.toml names the configuration's resource (MainRes) instead of one the program lacks ("invalid bytecode metadata 'resource 'Simulator''"). - HMI: the labels and buttons of the panels a project shows, its own and its libraries' (a placed sequence panel, an equipment popup), are in the Texts table and switch with the display's language; a panel placed on a page no longer stays in English.
- HMI: an
activeoralarmpart of a project's own symbol drawn as a line is painted on its line; it no longer fills the outline solid. An active part also glows softly, as a lit status does (a heater element while it heats). - HMI:
currentColorin a project's own symbol (My Symbols) is the built-in symbols' line colour, so such a drawing follows the dark and light themes instead of drawing black. - Plant library: the sequence panel shows the state, the step name and the step time in rows wide enough for them ("Heat the room", "28 min 1 s"); they were cut or ran into each other.
- HMI Builder: the inspector's Add pen field empties once the pen is added; leaving the field no longer adds the same pen a second time.
- HMI Builder: a trend pen added in the inspector is named as its legend should read: the tag for a transmitter's value ("TT101"), the tag and member for another member ("TIC101 setpoint"), instead of the variable's last part ("Value", "Value2").
- HMI: pipes are drawn over layout panels, so a panel used as an area of the plant (a room) no longer hides the pipes that cross it; they stay under the equipment symbols.
- Devices & Connections: a simulated plant's input can read a memory value (
%M), such as a window opening the HMI sets, as the simulation specification allows; before, only%Qoutputs were offered and a memory address was refused. - Libraries: the empty page names Plant too, and the descriptions of the libraries to add wrap inside their cards instead of running into the next one.
- Devices & Connections: Connect hardware from the HMI Builder opens the equipment's connections instead of a blank canvas (the canvas is now bundled with the automatic JSX runtime).
- Compiler: passing an instance to an interface it implements, such as a transmitter to a PID
loop's
Measurement, no longer warns of an implicit conversion; only elementary conversions do. - HMI display: the equipment Settings dialog speaks the screen's language, and after Apply its Cancel reads Close until the next edit; a panel's trend takes the opening object's unit pen by pen, so a controller's output no longer reads in the measurement's unit, and a trend's pens of one unit share their scale, so a setpoint is read against its measurement. The runtime keeps the history of the trends inside panels for each instance a page opens them for, so a panel's trend opens filled.
- Restore declaration-only HMI field diagnostics and source locations, and patch the shell-quote development dependency.
- Preserve FB input/output/in-out members, inherited ones included, in declaration-only HMI metadata.
- Instrument OpenOT declarations using parsed token boundaries so single-line programs and keyword text in comments/strings retain their telemetry.
- HMI repair: keep project inspection off the language-server transport task, show readable PLC command refusals and name equipment popups from their selected PLC instance.
- Keep live panel controls attached during value updates, and install bundled library files without opening every source as a dirty editor. Reuse bounded pristine LSP compilations for unchanged source text, invalidating edits and discarding any runtime borrowed mutably.
- Synthetic HMI exploration resolves implicit equipment faceplate instances using the editor's existing binding rules, including alarm-to-equipment navigation. Configured-project alarm discovery now executes its first due preview scan instead of returning an empty alarm list.
- Keep narrow bar labels separate from stale ages. Ordinary value updates retain SVG text nodes and start bounded painting in the current drawing turn. Agent validation identifies uneven spacing and misaligned rows of repeated equipment cards.
- VS Code agent draft inspection shares the authoring store used by composition and MCP. Stale value ages refresh immediately even when ordinary value painting is deferred.
- Keep equipment-alarm badges clear of trend range, export and expand controls.
- HMI peer discovery includes runtimes that were already online at startup. Ordinary value cards coalesce whole-card paints across animation frames while urgent state/control changes remain immediate. Browser performance evidence measures completion of all updated values.
- HMI draft application can recover from definite pre-commit failures, while unknown editor outcomes prevent duplicate application even with another request key. Clean cached editor models no longer resurrect deleted source files.
- Binding checks: a write binding to a
VAR_GLOBAL CONSTANTis refused ("is a constant and cannot be written"), as for other constants. - Deploying the HMI version that is already active no longer replaces the kept previous bundle with a copy of it, so Roll back still goes back after Deploy is pressed twice.
- Runtime: a function block's inputs and outputs declared
RETAINorNON_RETAINkeep or reset their values over a warm restart as declared (IEC 61131-3 §6.5.6.2); before, they followed the instance's declaration. - Fixed:
Control.SmithPredictorFOPDT.Initializeignored the initialProcessValue; it now sets the initial correction from it, andUpdatereports the latest applied correction also when no model interval runs (specification 37 §2.3). - Fixed: the runtime display did not send the values that pipes'
flowbindings name, so pipes never showed flow. - Fixed: the runtime display's top bar and side bar were 1 and 21 px larger than their 56 and 232 px, so the Operator panel template's page did not fit a 1920 × 1080 screen exactly.
- Fixed: an enumeration value was reported ambiguous because another namespace that the code neither is in nor uses had a value of that name, so a project with the Equipment and Control libraries did not compile. The pair link check now finds members a block inherits.
- HMI: with a top bar that does not show the user, the session stands under the bar instead of covering its end.
- HMI Builder: a saved change to
hmi/screen.tomlreaches the open builder at once. - VS Code's simulator now runs projects that use libraries through
[dependencies]intrust-lsp.toml, astrust-runtime builddoes; before, their types did not resolve. - A program instance named in a configuration (
PROGRAM Tank WITH Fast : TankLevelControl) is now known by its program's type, so renaming the program's variables also renames them in the HMI bindings. - Function block inheritance: a call of a derived function block binds the inputs and outputs it inherits
(
MyAlarm(Message := 'Tank high', Priority := AlarmPriority#High)), which the compiler refused as unknown parameters. A base named with its namespace (EXTENDS Alarm.Alarm) is no longer dropped when the program is compiled for the runtime. HMI paths and the editor's variable list reach inherited members. - Warm restart: a function block instance declared
RETAINkeeps its variables' values, except those its type declaresNON_RETAIN(IEC 61131-3 §6.5.6.2); before, it started over. The retain file written for a power loss does not hold instance values yet. - Runtime CASE ranges (
1..5:) match unsigned integer and bit-string selectors. Range bounds were compared asLINT, so aUSINT,UDINT,BYTEorWORDselector aborted the cycle withTypeMismatch(bytecode VM) orCaseSelectorType(interpreter). Bounds are now constants of the selector type. A character string label keeps its full text: it is not cut to the length of aSTRING[n]/WSTRING[n]selector, so a longer label never matches. - Project-import name collisions (
duplicate imported declaration) are reported at a declaration in the file itself, or else at the first reference to the name in that file. They were reported in every project file at the offset of the declaring file, so one duplicate showed up once per file at unrelated text. A global or POU that reuses the name of a standard function block (TON,R_TRIG, ...) is reported at its own declaration and at the uses of the name in other files, with a message naming the standard function block, instead of at the start of every other file and with a "previously declared here" link to nowhere. - Untyped integer literals are contextualized to bit-string targets:
b := 16#FF,w := 65535,d AND 1,d = 0andCASE b OF 16#FF:are accepted forBYTE/WORD/DWORD/LWORDwhen the value is representable, as they already were for integer targets and bit-string initializers. The runtime gives such a literal the full range of its target, also in declarations and array and structure initializers, sod := 16#FFFF_FFFFandd : DWORD := 16#FFFF_FFFFno longer fail to compile with "integer literal out of range". Runtime CASE labels are evaluated in the selector type, so an untyped label matches a bit-string selector. - The integer literals
0and1are Boolean values where aBOOLis expected, as IEC 61131-3 §6.3.2 specifies:x := 1,F(flag := 1),x = 1,x AND 1, a structure or array default such asactive : BOOL := 1andBOOL#1are accepted and run, whilex := 2,x := INT#1andx := 1 + 0stay errors. - Control Studio's measured frequency response lists and plots its tones in ascending frequency with the phase unwrapped, as on a model's Bode plot; a resonance's phase turn was shown as a jump from −177° to +171°.
- Control Studio Automatic tuning starts from the More robust response also when every requirement is entered; before, a fully entered task started from Balanced while generated criteria started from More robust, so the same requirements could design different controllers.
- Control Studio: the Auto-entry allowance of a program's own P, PI or PID covers the measured noise through its gains, as for
Control.PID; a noisy measurement no longer fails the transfer check at a near-zero tolerance. - Control Studio: when Automatic tuning cannot meet the requirements, the next step it names is revising the requirements, and the Tune page no longer shows the previous candidate beside the refusal.
- Control Studio setup form offers as Auto/Manual signal only BOOLs without an I/O address, which Apply can switch, and says why when a program has none; previously an addressed mode was accepted and Apply refused it after identification, tuning and validation. Tune's peak sensitivity Ms is the peak over every frequency: a strictly proper loop's sensitivity tends to one beyond the plotted range, so Ms is never shown below one for it.
- Control Studio stays responsive during long studies: the host no longer resends every recorded identification run with each status message, sends the model-check recording only with results, and posts Apply's waiting progress only when the reading changes. A study with several long recordings could freeze the page for seconds at a time.
- Apply observes its test duration in the runtime's own time, bounded to max(3 × the duration, the duration + 60 s) of wall-clock time. It waited a fixed wall-clock time, so a runtime running behind real time (under load, or on a slower controller) was stopped early and a correct controller failed with "The recorded response does not cover the reviewed duration".
control.transfer.statusnow reportsruntime_time_ns, the runtime clock of the scan it reports. - Apply of a state-feedback (LQR or pole-placement) law no longer fails its Auto entry when the process rests inside the settling band but not exactly at the operating point: without a reviewed Auto-entry tolerance, the first output may differ by the law's response to that accepted deviation, Σ|K|·band plus the reference gain's share (Specification 35 §12).
- Validate lists each check once: an ON/OFF controller's switching levels and dwell times are no longer repeated by its on-off output's identical checks, and the dwell rows read "Minimum ON time" and "Minimum OFF time". Result labels show program symbols exactly as declared ("P1.Tank2SetpointM", not "P1.Tank2Setpoint M").
- Control Studio setup records a program instance's program type (
MainforPROGRAM P1 WITH MainTask : Main) in the loop file; it recorded the instance name. - Control Studio validates a cascade's outer loop on its identified model, which contains the closed inner loop, instead of on the project's plant: that run started the program from its declared values, with the inner controller in Manual, so the outer loop could never pass.
- A PID loop's bias is bounded as an offset of up to the output span either way. It was bounded to the output range, so a cascade's outer controller, whose output is a temperature setpoint, could not be validated with its zero bias.
- A variable mapped to
%Mmemory (for example withVAR_CONFIG … AT %ML16) starts from its declared orVAR_CONFIGinitial value at a cold start, and from its retained value at a warm restart, as IEC 61131-3 §6.5.1 and §6.5.6.1 require. Previously the first cycle replaced it with the zero memory image, and a cold restart kept the image from before the restart, so programs had to set such variables in startup code. - The setup form asks for the startup-done flag whenever the program itself writes a value that validation starts from (the setpoint, a manual output, the Auto/Manual signal or the parameter record) or calls the PID's
Initialize, for a single loop and for both loops of a cascade. The language server reports every symbol a program assigns (controlSymbols.writes). Previously only parameter-record writes were found, and the inner loop of a cascade whose startup code reset the manual outputs was validated with a setpoint step that never reached the process. - Control Studio keeps the validation results visible after a failed Apply. A rejected Auto entry that recovered previously replaced the passing rows with "Inconclusive · Scenario did not complete." for every validated scenario, and the failure and its recovery now read as two sentences.
- Control Studio's response chart keeps each row's axis name within its row. When a name such as "P1.ObservedKp (percentage-point/°C)" is wider than a short row, its unit moves to a second line; previously the names of stacked rows ran into each other.
- Control Studio's Identify waits, bounded and abortable, until the measured output is steady before it excites the plant, as the step-identification workflow requires. Pressing Identify plant again right after an experiment previously stepped a plant that was still falling, so the fit failed or its operating point was off.
- Identify with the Automatic method fits the simple models to a closed-loop setpoint step, as it already did for step, relay, PRBS and multisine tests; the fit now follows the experiment that actually ran instead of the requested method.
- Validate's Auto-entry check starts the controller where the project's plant actually rests, measured by holding it at rest, instead of at the fitted model's operating point. A small offset in the fitted operating point previously failed the exact transfer check.
- When Apply is refused because the process has not yet settled at the validated operating point, Apply stays available for the "Apply again" the message asks for.
- Measured MIMO path analysis rejects a baseline or settled window that contains the input step instead of averaging both command levels, and explains every rejection in plain words.
- Tune accepts a supplied model on a loop that declares no identification experiment.
- The runtime scheduler keeps its cycles on the cycle-interval grid: a late wake-up no longer delays every later cycle, and a stall longer than one interval moves the grid instead of replaying the missed cycles. With 0.5 ms of wake-up latency, 100 cycles of 10 ms previously ended 50 ms late.
- Control Studio Apply recovers when the controller's first Auto output breaks the reviewed transfer tolerance. The runtime now fails that transfer, publishes the output held at the transfer instead of the rejected one, and keeps running, so Studio's Manual recovery and the restoration of the previous gains complete. Previously the rejection faulted and halted the runtime, and recovery ended with "The parameter-operation identity remains unresolved".
- Control Studio Apply now accepts a process that has settled at the validated operating point within the requirement settling band, instead of demanding an exact match that a live or noisy process never reaches. When the process has not settled, Apply names the measured and validated values and offers "Apply again" rather than "Tune again".
- Control loops treat a signal declared without a unit as dimensionless, so identification and validation recordings no longer fail with "Invalid run channels or sample structure".
- After Apply, Control Studio keeps the study draft instead of warning "Could not retain the workflow draft": a commissioning recording may include the loop's additional signals, and only duplicate channels are rejected.
- Control Studio keeps large studies in workspace storage by storing each recorded run once and its samples column-wise, instead of reporting "Study evidence is invalid" after several validations.
- Control Studio identifies a relay, PRBS or multisine recording by first fitting the simple models (static, integrating, first-order and first-order with delay), simulated under the recorded input from the baseline before the first change, and keeps the simplest adequate one. Relay recordings of delayed processes no longer fail Identify with "No identifiable adequate state model"; they give the same delayed first-order model as a step test.
- The Requirements editor limits scenario event times by the validation duration currently entered. It previously used the duration present when the dialog opened and, with none entered, rejected every event after time zero ("Value must be 0.").
- Control Studio's manual Tune message for an out-of-range value keeps the field's symbol as labelled ("Enter proportional gain (Kp) between 0 and 100." instead of "(kp)").
- Control Studio no longer blocks the editor for several seconds when a controller is tuned again after a validation: the retained draft is not read back and decoded again after each store, and evidence is compared through a single-pass canonical text.
- Control Studio's charts label a fitted axis at its regular ticks only, so the padded ends (such as "1.58" above "1.5") no longer crowd them.
- Control Studio retains the workflow draft once after a burst of settings edits instead of for every edit, so typing controller settings after a validation no longer blocks the editor; results and resets are still retained at once.
- Control Studio's response plot on Identify, Tune and Validate shows hovered values rounded like its other charts (1.9 instead of 1.9017502305516345) and keeps the tooltip inside the plot.
- Control Studio runs a loop's declared verification scenario on the project's plant even when its id matches a generated model case such as
load-rejection; such a trial previously ran silently on the supplied model, without the plant's other inputs. - The debug-session simulator that Control Studio runs keeps its cycles on the task-period grid: a late wake-up no longer delays every later cycle, and a stall longer than one period is skipped rather than replayed. Simulated time previously fell about 2.5 % behind wall time even when every cycle fitted its period, so Apply on a 20 ms loop failed with "The recorded response does not cover the reviewed duration".
- Control Studio draws the applied output in its response plots as steps, held from one update to the next, instead of joining samples with ramps that misplace a command step by a whole update.
- Control Studio's measured frequency response works on a completed project experiment: the recording is offered to the analysis, a first scan without a cycle time no longer disqualifies it, the fit per segment is exact for a sine about any operating point (it read 0.46 instead of 0.14 °C/point before), and leftover samples no longer reject the record. Model Bode plots keep the phase continuous instead of wrapping it past ±180°.
- Control Studio's Loop response sensitivity view draws S, T and CS with their own theme colours and strokes; CS was a dim default colour on dark themes.
- Control Studio's Nyquist view states the count as the criterion does: P open-loop right-half-plane poles, N clockwise encirclements of −1 and Z = P + N. It previously printed a counterclockwise count without P or Z. The plot keeps equal units on both axes, so circles stay round and distances to −1 read true. Arrows show the direction of both frequency branches, and −1 is marked.
- Control Studio's pole-zero map keeps equal units on both axes, like the Nyquist view: the unit circle of a sampled model stays a circle, and a pole's distance from it reads true.
- Control Studio's Measured MIMO paths summary shows channel units in the course notation (°C instead of degC), and the pairing line no longer touches the Static decoupler disclosure.
- Control Studio's control requirements, and the requirements it proposes automatically, now apply only to outputs with a setpoint. A model that also measures an auxiliary output, such as the upstream tank used by state feedback, no longer receives requirements for it. Before, those requirements made the loop invalid, and Tune controller stayed disabled without an explanation.
- Control Studio's finite-horizon Riccati stages, the Routh table's entries, the pole view's cancelled factor and allowed-level limits now show each list or matrix with one precision. Before, the digits followed each entry's position in its row, so equal values read differently (14.5 beside 14) and small gains appeared in exponent notation (7.84e-2).
- Control Studio's state-space design fields show a draft error only under the field it concerns, such as Terminal weights, and clear it once the design reads. Before, one missing entry put the same message under every filled field, and the messages stayed after the design became valid.
- Control Studio writes channel counts in the singular where needed ("1 input, 2 outputs") and shows a dimensionless tuned value without a unit, for example an estimator gain of 0.2393 instead of "0.2393 1".
- Arrays of function block and class instances work as IEC 61131-3 Table 11 and the truST data-type specification describe:
Timers : ARRAY[1..2] OF TON;creates one independent instance per element, including each element's nested instances, in programs, function blocks, methods, named array types and globals.Timers[i](IN := x)calls an element,Timers[i].Qreads it, and restart re-creates every element. The runtime previously refused such declarations with "init failed ...: type mismatch" and could not compile a call through a subscript. - A function-block input may be assigned through its instance before the call,
Delay.IN := Start; Delay();, as IEC 61131-3 Figure 13 and the statement specification allow; the checker previously rejected it with E301. A function block still may not assign its own inputs. - A warm restart no longer restores the instance identity of a
RETAINglobal function block, class or instance array: the instances are re-created, as the runtime specification requires, instead of pointing at unrelated instances after the restart. - Keep chart tick labels distinct when small temperature changes occur around a nonzero operating point.
- Keep rejected independent model predictions inspectable after save/reopen without allowing acceptance. Preserve the model overlay when restoring hidden experiment signal selections.
- Keep Lesson18 on the same native heater used for identification, and use the permitted40s estimation hold for its independent validation handoff. Preserve the lesson's fixed error and timing requirements.
- Recognize a rate-limited step's rising increments as part of the same step, preserving the existing fitter's estimation of gain, dynamics and delay.
- Identify plant executes a fresh experiment for the chosen method instead of silently refitting a previous method's recording. Reset clears the working model, chart and dependent results, including the saved draft, while keeping configured signals/limits and the running controller unchanged.
- Keep large identified model equations scrollable above the experiment plot; explain clipped or rate-limited relay records without reporting missing cycles.
- Show actual identification inputs alongside measured outputs and model predictions on aligned time axes. Display returned experiment records before fitting completes and retain the matching input trace when reopening results.
- Keep identification on its model and response for review. Identify plant keeps its label; Reset chart clears the display without starting identification or deleting the model. Use the Tune tab to navigate; ask for missing requirements only when tuning is requested.
- Prepare isolated Control Studio bytecode before starting PLC scan deadlines, so compilation cannot consume the first scan budget on slower hosts.
- Show selected plant signals and their current readings in Identify before a model is acquired. Signal dropdowns apply checkbox choices immediately and close on outside click, without Done/Cancel buttons.
- Preserve scenario values when adding or renaming signals.
- Run the model-validation heater/probe through native plant simulation and
preserve its five-second holdout baseline. Model-based PI tuning now executes
the library
Control.PID; source-selected trials run the selected command/load. - Make the identifiability lesson reuse the saved model comparison and explain practical versus structural ambiguity with runnable ST calculations. Its heater project reads the noisy probe from native simulation through PLC input.
- Make the model-structure lesson follow the existing Control Studio import, comparison and Save/Open controls. Include runnable ST calculations for its two-state update and worked prediction, with an explained update-order exercise.
- Import the original first-order estimation run and its settings when a saved study also contains later validation data. Expose the additional-lag search grid and model-error limits; preserve the original reference and unfinished field values across Save/Open.
- Keep identification fitting and saved-study calculations responsive in the editor. Cancel retains the recording and confirms the outcome; edits and newer Open requests discard obsolete calculations. Preserve current field errors when an older settings acknowledgement arrives.
- Fit an additional physical lag on an explicit held-step parameter grid while preserving the first-order reference and recording. Keep model-adequacy limits separate from measurement-consistent uncertainty, retain ambiguous time-constant assignments, and reopen the provisional comparison through the existing identification session. The model-structure lesson now reads water and probe temperatures from the native plant through ordinary PLC inputs.
- Recognize
TEST_PROGRAMandTEST_FUNCTION_BLOCKas test-runner entry points in unused-POU analysis, preserving warnings for unused production declarations and unused variables inside tests. - Retain identification previews, measurement settings and both original estimation/validation recordings across editor restarts. Recheck accepted models on restore. Changing identification inputs clears the selected model and dependent tuning state, including the current saved draft. Preserve unfinished field edits and imported recordings in the current working draft without adding saved history revisions. Rejected saved results leave the current result identity and exported contents unchanged.
- Add measured held-step analysis in Identify with qualified baseline/final windows, explicit FOPDT search, measurement-consistent parameter ranges and grid refinement. Preserve estimation parameters when checking a distinct validation step; reject bad, clipped, saturated and inconsistent-unit data. Detect sustained onset in either response direction, explain missing input steps and require complete baseline and held-step window coverage. The lesson gain helper rejects zero input change through ENO without a division-by-zero fault; its adaptation and abort instructions match the project. Identify names and focuses invalid settings, explains rejected windows and points opened recordings to Validate. Short and zoomed editor layouts keep settings reachable; narrow plots retain readable reference labels. Zoom and Fit clear the previous inspection so its values cannot describe another window.
- Preserve native acquisition identities through recording and saved results, so an imported estimation run can use the next native Test for validation. Check the selected recording rather than an older Test with the same role. Include the five-second baseline in the step-identification lesson's Test.
- Distinguish the last Control Studio Test outcome from the current runtime and a newly reviewed plan, while retaining recordings and explicit recovery needs. Model and tuning updates preserve the Test-owned status and its accessible label.
- Put the selected course lesson's name and project action before expandable technical details. Preserve full teaching text, remove identical repeated introductions and retain the PID acronym in module titles. Copied lessons also adapt descriptive next-lesson links to the current catalogue instead of leaving links to sibling folders that were not copied. The step-identification lesson explains EN/ENO without a reference link to repository files absent from the copied project.
- Render authored lesson instructions, tables and equations in the example gallery. Open referenced bundled files as read-only source previews while keeping the selected lesson available in the same editor group.
- Keep native ST assertions out of Simulator source selection in the remaining
control-course projects while preserving their test bodies and discovery.
Filtered
trust-dev testruns now register all test declarations for compilation and execute only matching cases, including configured projects. - Import saved Control Studio recordings for identification while retaining their original acquisition identity through Save/Open. Keep source controller and design state out of the import, and exclude the step-identification lesson's native assertions from Simulator using the existing runtime-ignore marker. Show the recording before fitting, keep one primary next action and one historical source badge, and return explicit Monitor navigation to live data.
- Accept the documented manual PI Tune-to-Test lesson route when building the course catalog, while still rejecting unknown and duplicate stages. Align the Routh–Hurwitz lesson title and next-lesson link with the approved course.
- Keep identification experiment settings in Test, lock them during review/run, and record the declared baseline before enabling the existing project. Preserve newer I/O observations per debug session when delayed events arrive.
- Label paired process signals with their actual names on Control Studio axes, including model/measurement and heater/loss comparisons in Energy balance. Keep unselected diagnostics in Signals, and allow recorded channel selection directly in Validate without leaving the saved or retained response.
- Show applied input and measured output consistently for open-loop experiments, without a fictitious setpoint or undeclared controller mode. Validate displays the retained native recording before Save/Open while Monitor keeps live data. Keep padded temperature-axis endpoints from repeating rounded tick labels.
- Refresh Control Studio observations after a confirmed simulator restart and start a fresh live response window while preserving saved recordings. Share the coherent signal selection with recording and identification. Reject new project-owned Tests while primary signals have forcing or bad quality.
- Keep aborted Test recordings available for save/open/export while identification remains disabled for unqualified data. Preserve project-owned activation, stop and restoration details, including Boolean applied values, in saved results.
- Give manual controller parameters labelled numeric fields, bounded change reviews and observed rollback values. Keep model/search tools and raw JSON expandable, and show measured response while tuning without a fitted model.
- Keep recording-only Control Studio validation focused on the measured response, save/open/export actions and review readiness. Opening saved revisions replaces previous plot evidence and validation status without changing the controller.
- Name isolated diagnostic axes after their selected signal and draw an explicit explanation when a saved controller candidate has no stored comparison.
- Keep arithmetic roundoff in constant recorded signals from appearing as a large oscillation when Control Studio fits its axes; preserve raw samples.
- Run the heater setpoint/load lesson through the existing Control.PID and one native temperature sensor, with separate heater/load delays and applied-output feedback. Preserve both experiments and their original response requirements.
- Bind manual PI tuning to the actual Control.PID parameter record and native heater inputs. Preserve one-gain changes, accepted values and independent target/load trials across explicit restart and reapplication.
- Reuse that PID and native heater in Loop Diagnosis, with separate measured and reference inputs, a native sensor-bias experiment and aligned request/ applied-output diagnostics.
- Omit a repeated controller type from Control Studio setup choices when it duplicates the instance label; preserve compiled identities and custom choices.
- Align live Control Studio event markers with actual time and plot boundaries, including zoom and resize; show source, time and meaning on hover. Use Ready to apply / Ready to restore for the corresponding open change review.
- Add Back and four named phases to native Control Studio setup. Preserve compatible answers, revalidate changed choices, and prevent stale asynchronous validation from accepting an edited value.
- Hide Control Studio figure tools on Signals and Events. Expose the Response sample table with selected channels, engineering units and individual quality.
- Keep live time-axis labels separated near the latest observation without trimming the recorded response.
- Keep the existing Abort test action visible across Control Studio modes, Signals and plot expansion until the native experiment stops.
- Show the active Test recording and progress consistently; prevent manual recording controls and late notices from taking over its acquisition.
- Simplify lesson-owned Test review to duration and a compact exact plan; omit fixed controls, unused excitation seeds and internal Boolean units. Remove unsupported Automatic Tune actions and collapse empty header space.
- Keep the Signals list on the opened recording's values, units and quality; preserve offline readings and return to current values when live observation resumes.
- Draw selected controller diagnostics in live and reopened Response plots, preserve their samples and quality in exports, and separate incompatible units into aligned lanes. Restore the visible signal checkboxes with saved selection and show requests above the physical actuator limit without clipping.
- Keep multi-unit chart labels readable in compact Test views, contain long review values, and draw theme-derived measurement strokes at full opacity.
- Label controller-state axes by their actual role and keep diagnostic trace colours/styles distinct from primary SP/PV/MV when selecting signals.
- Run the heater P and PI lessons through the actual Control.PID block and keep the library quick-start example's history between scans. Add a read-only state snapshot for diagnostics without changing the controller equations.
- Use Test's automatic recording in the heater lesson instructions and explain how to save and reopen the measured response.
- Keep the manual Control Studio response, compact live values and Test form from overlapping; show a saved recording without requiring a tuned candidate.
- Give reviewed tests one confirmation action and a return to editing, keep pending starts locked, and restore controls for the next experiment.
- Preserve imported-run identity labels when reopening an evaluation and keep optional run-import fields behind an expandable control.
- Expose independent state-space input delays in the plant editor, preserving channel values and enforcing the native history bound before Save.
- Keep identified-model predictions bound to their model and output when guided setup assigns explicit channel names.
- Reject applied-actuator feedback mappings that overlap coupling or physics outputs, and run the windup lessons through the real PID and actuator readback.
- Keep independent model-fit evidence attached to its selected model, retain completed Tune/Verify results on screen, and use consistent response colors and compact parameter comparisons throughout Control Studio.
- Map the actual
Control.PIDseven-state initialization through the shared Control Studio verification and commissioning workflow. - Show builtin tuning controls only for adapters that use them and keep hidden gain fields from blocking project-owned tuning.
- Disable empty Monitor plot actions until samples arrive while preserving inspection of retained samples after disconnection.
- Hide controller removal when no controller is attached and clear its previous file selection on removal.
- Let engineers close an unapplied controller review with Close or Escape without applying or discarding the candidate.
- Honor a specific proportional gain when P is the only available adapter, including its declared parameter bounds, under automatic family selection.
- Preserve a newly selected Control Studio stage when a delayed opening-state response arrives.
- Keep empty advanced-workspace charts inside their allocated space so model import and validation controls remain visible.
- Keep supplied-model analysis disabled after a rejected import when no valid model has been selected.
- Bind declared-project controller tuning to the runtime's project identity, avoiding a false program-change rejection on the first tuning attempt.
- Keep unconfirmed Manual transactions on the recovery route, lock dependent inputs, and unlock a new study after reviewed runtime replacement.
- Navigate directly to the earliest missing workflow prerequisite.
- Keep supported controller predictions visible beyond the initial frequency range, use the task's physical coordinates and sampling period, and remove the extra prediction delay and invented current-controller baseline.
- Show one verification retry action when a failed run has no response samples.
- Connect pinned controller comparisons to their cached prediction plots, retain unavailable pins across context changes, and support keyboard design history.
- Preserve commissioning recording channel roles so SP/PV/MV curves render with their original units even when the recorded requirements only contain output bounds.
- Keep automatic model previews separate from saved analysis studies and format displayed search scores without meaningless floating-point digits.
- Control Studio replaces stale model plots on import, keeps unvalidated models inspectable without enabling Tune, and uses the actual engineering units in selected-sample announcements.
- Guided tuning distinguishes Automatic rule selection from explicit SIMC/AMIGO choices and hides response preferences for rules that do not use them.
- Manual/imported PID inputs report validation errors before submission; plot exports use the native Save dialog and report the actual saved filename or cancellation. Reviewed numerical settings use shared engineering precision.
- Validate the final part of an identified model's independent response as well as its average error, preventing an integrating approximation from hiding a first-order process's final-response mismatch.
- Reject blank required Control Studio tuning goals and gains, and show manual tuning errors instead of leaving the candidate in a generating state.
- Remove duplicate headings and introductory copy from Control Studio; keep manual tools and scenario editing inside Settings.
- Classical tuning accepts the exact held-input equivalent of supported scalar sampled models while actual-ST verification retains the original discrete plant.
- Relay qualification excludes recording startup and incomplete initial periods; switching between loops keeps each loop's task and draft in its own session.
- Updated the locked
js-yamldependency to 4.3.2 to address its dependency audit finding. - Control Studio keeps supplied-model imports in the main workflow, shows task requirements before tuning, and preserves one primary next action after edits and reopen. High-contrast primary buttons retain a visible outline.
- Recorded response plots resolve SP/PV/MV by requirement channel identity, keeping labels, units and output limits correct when observations are reordered.
- Auto commissioning retains native recordings and completed checkpoints on close. Matching saved receipts restore their plots without restoring Apply approval; lost acknowledgements reconcile the original parameter operation.
- Control Studio shares relay transitions and transfer-function realization between existing and new paths, and centralizes P/PID adapter operations. Explicit state, ON/OFF and project-owned controller mappings now use shared tuning, initialization and commissioning paths. Binary identification preserves actuator output levels and minimum dwell; switching commissioning evaluates observed levels and completed dwell intervals. Native tuning retains its runs and stops if the loaded program changes. Guided ON/OFF setup maps the existing mode, parameters and retained states from compiled symbols. Isolated operations save their identity before Start and restore it on reopen; Manual application recovery compares the exact reviewed record and revision history. The connected Main workflow now retains editable drafts automatically, restores recovery ownership before permitting new operations, and includes declared noise, load, interaction, switching and state-recovery scenarios. Model-derived settings check coordinate units; verification respects model validity ranges as well as equipment limits.
- Added exact delayed-integrator simulation for selected-model actual-ST verification, with bounded input history and reset support.
- Control Studio connects integrating models to the shared P/PI design paths and considers PD candidates using their calculated steady-error limits.
- Control Studio rejects nonrepeatable relay amplitudes and periods before offering tuning settings.
- Control Studio supports explicitly choosing P on a typed PID controller, zeroing I/D gains through verification and commissioning.
- Closing Control Studio during a standard Manual parameter apply or rollback now preserves only the native transaction identity and rechecks the matching runtime status after reopening. It never replays the write or retains a closed review token or client.
- Control Studio manual recording now retains the original recording owner when Start acknowledgement is lost, retries Stop on that owner, preserves returned runtime samples, and labels unconfirmed captures as unqualified evidence.
- Start Lesson 1's door disturbance with its experiment, so reading the instructions while the simulator runs no longer consumes the scenario.
- Begin targeted repairs to the 82-lesson control curriculum, preserving the P-to-PI progression and separating prose, executable-project and rendered workflow verification. Individual lesson acceptance remains in progress.
- Control Studio can now start and safely restore project-scripted curriculum experiments through one typed Boolean lesson authority without injecting a duplicate process excitation.
- Add lesson-specific Control Studio instructions and process-image mapping repairs. Their individual execution and rendered acceptance are tracked separately; this is not a claim that the entire course is verified.
- Replace raw starting-state arrays with labelled controls for first-order, integrator, gravity-tank, second-order and robot-joint models, retaining invalid-input protection and exact values.
- Render every robot-joint and state-space plant connection with its own named picker; preserve neighboring channel bindings and conversions when one changes.
- Route starter and lesson opening through one project-scoped pending destination, suppress superseded instructions, and offer recovery or an explicit retry when the selected source file or lesson cannot open.
- Show field-specific plant actuator defaults instead of labelling every empty value “Unbounded”; distinguish a zero slew rate from no slew limit.
- Order control lesson modules by their declared lesson sequence and show noncontiguous lesson ranges without implying missing lessons are included.
- Default omitted FOPDT delay history to its input operating point as specified, preserving explicitly configured zero history and deliberate non-equilibrium starts.
- Preserve simulated-plant validity limits when editing names or other exposed settings, carrying unedited configuration through the canvas draft and save candidate without resurrecting explicitly cleared editable effects.
- Apply plant command uncertainty independently to the held PLC command on every simulation substep, avoiding compounded offsets or gains within one PLC scan while preserving raw-command trace identity and deterministic replay.
- Resolve the configured project loop when Open Control Studio is invoked without arguments or from the sidebar, offer a labelled choice for multiple projects, and provide repair/example actions for invalid or missing loop configuration while preserving explicitly scoped plant/loop handoffs.
- Correct rejected Control Studio bindings in 24 control lessons and their bundled copies. Project-owned controllers declare their actual design owner; open-loop comparisons retain reference signals without inventing a setpoint or weakening unit and address validation.
- Keep the real Control Studio response chart visible at a usable height, use the declared manipulated-variable engineering range, disable plot and workflow actions until their evidence exists, prevent limit labels from covering traces, and treat an empty saved-result history as a neutral state.
- Resolve typed enum literals through the active namespace and
USINGscope, allowing imported declarations such asPIDMode#Manualto type-check across files without allowing a same-named variable to hide the literal's type. - A
POINTER TO ARRAY[*] OF Tagain accepts the address of a concreteARRAY[a..b] OF Tof the same rank, so the OSCAT array helpers andCRC_GENrun on the shared engine; a different element type or rank is still a type mismatch.
Security¶
- Security: tokio 1.54.1 (RUSTSEC-2026-0386), rustls-webpki 0.103.16 (RUSTSEC-2026-0382), webpki-roots 1.0.11 (RUSTSEC-2026-0381) and ctutils 0.4.6 (RUSTSEC-2026-0385).
- Security: the VS Code extension takes undici 7.30.1 through Blockly's jsdom (GHSA-4qv8-2p9j-2685,
GHSA-f72v-6w45-q472, GHSA-xjj6-f33q-929c), and its development tools serialize-javascript 7.1.4 and, through an
overridesentry for the packaging tool's Azure sign-in,@azure/msal-node7.1.1 (GHSA-xcx6-jx8p-x3h5), so thatnpm auditreports no vulnerability. - Security: the VS Code extension takes
markdown-it15.0.3, which fixes two quadratic-time denial-of-service defects in rendering (GHSA-vfgh-jcmw-g37q, GHSA-gfr2-rv44-r23c). - Security: the HMI's file routes (screens, panels, pairs, texts, symbols and images) read only regular
files inside the project's
hmi/folder; a link, a linked folder or anio.tomlis never read, so no HMI route can disclose the I/O configuration's credentials. - Security: a web deployment (
/api/deploy) is now checked against the signature policy of the project's installedruntime.toml. Before, the check read[runtime.deploy] require_signedandkeyring_pathfrom theruntime.tomlthe deployment itself carried, so a deployment could turn the requirement off, or name a keyring of its own, and install an unsigned program. - The web IDE's
/api/discoveryand/api/probeneed the same web login as the other routes, and/api/probetakes its target and credentials in a POST body instead of the URL.config.getandconfig.setcoverdiscovery.host_group;comm.capabilitiesreports discovery and mesh peer health; a runtime listening on a wildcard address reports its real network addresses instead of127.0.0.1. - Runtime mesh now enforces configured peer tokens and TLS using the runtime's certificate material. A requested TLS connection cannot silently start as plaintext; wrong or missing peer credentials are rejected.
- Security: dependencies updated for new advisories: rustls 0.23.45 (RUSTSEC-2026-0285), salsa 0.28.5 (RUSTSEC-2026-0308; the compiler's value-returning queries keep returning values), event-listener 5.4.2 (RUSTSEC-2026-0221), ratatui 0.30.2 with lru 0.18 (RUSTSEC-2026-0253), non-yanked spin 0.9.9 and 0.10.1, and the VS Code extension's
brace-expansion2.1.7 and 5.0.12 (GHSA-q2hr-2g5m-vwhr, GHSA-qhr7-859c-m2p7, GHSA-6j4f-fj2g-mc7p). The seven tracked advisory exceptions were re-reviewed (no upstream fix for opcua 0.12, tiny_http 0.12 or zenoh'spaste) and renewed until 2026-12-29.
[0.24.75] - 2026-10-16¶
Added¶
- Hosted hang supervision. With the watchdog enabled, an independent supervisor
thread watches each cycle's deadline, so a driver or native call that never
returns now faults the resource (
WatchdogTimeout) within two check intervals of its deadline instead of stalling it silently.runtime.watchdog.hang_actionselects what follows:fault(default) keeps the process serving the control plane, andabortexits the process with status 70 so a service manager can restart it. truST cannot drive outputs while the scan thread is blocked, so a hang relies on the I/O devices' communication-loss behaviour. trust-runtime ctl eventsandctl faults(optional--limit N) print recent runtime events and faults, so a rejected write'swrite_rejectedevent is visible from the command line.- Integration tests build and validate every runnable example project and check that every example, including the OSCAT test projects, configures a resource its sources declare. Compile-fail documentation tests now prove the exact compiler error that rejects each forbidden API use (CI runs them with error-code checking).
DebugRuntimeContext::newbuilds a debugger evaluation context over storage the caller owns.- Use the STBC 3.0 storage ABI for new applications: all temporal types occupy eight bytes consistently on every platform, preserving signed temporal ranges. Existing 1.1/2.0 artifacts keep their original sizes and offsets; explicit legacy imports identify their ABI, and unrepresentable physical writes reject atomically. The checked DWORD TIME millisecond transport encoding remains unchanged.
- Add an owned shared-core controller facade for hosted compositions alongside the borrowed MCU facade. Both use operation-scoped metadata/service borrows, checked named value writes and transactional restart with one mutable state.
- Prepare dynamic-reference candidate indexes without losing alias-conflict, invocation-lifetime or budget checks. Shared static-reference reads and writes reuse their checked target instead of resolving instance ownership twice.
- Separate declaration lowering into a non-executable hosted builder. Debug expression parsing keeps anonymous types in scratch registries, including retained watch expressions, without extending installed executable types.
- Project live VM callers and locals into detached debugger snapshots. Hosted watch and breakpoint conditions receive the snapshot evaluation context; observer failures follow shared frame cleanup and cycle fault handling.
- Route ordinary hosted construction and test-harness execution through STBC 3.0 and the shared engine; keep legacy 1.1 production explicitly selected. Installed hosted values and image writes use checked owner APIs. Rejected queued writes report their application failure without turning an invalid request into a PLC fault.
- Add explicit STBC 2.0 authoring and portable byte-level validation for declared storage, construction roots, compiled initializer recipes and access bindings. Legacy 1.1 remains available as an explicit compatibility producer. The source-free core now prepares and executes 2.0 through the shared bytecode dispatcher, including compiled initializers, typed storage, timers, process images and nominal-deadline task scheduling. Warm restart remaps retained objects transactionally; failed initialization or output copy-back does not publish partial replacement state or output destinations. Hardware execution and timing qualification remain separate bring-up work. Prepared declaration indexes and destination-only output journals keep execution costs independent of unrelated application storage. Hosted reference checks borrow paths without allocation. Runtime protection, profile admission and decoder-limit failures now have distinct stable diagnostic codes. Generic CTU/CTD/CTUD construction preserves their unbound integer state until the native call selects its concrete integer width. Initializer admission restricts writes to proven staging storage; 2.0 authoring shares equivalent default recipes, omits unused standard block templates and rejects unknown configuration/access path prefixes. The opt-in 2.0 producer supports staged member overrides on function-static FB instances at initial construction and after restart; legacy 1.x acceptance is unchanged.
Changed¶
- Share compact, lossless floating-point text codecs across runtime platforms, preserving both conversion directions and existing diagnostic spelling.
- Count the work limit the way a PLC program sees it: one unit per executed instruction, plus per-element work when arrays, references or text are copied. Lookups, safety checks and other engine bookkeeping no longer count, and passing an array as VAR_IN_OUT copies nothing, as IEC passes it by reference. Each program or task function-block invocation in a scan has its own 1,000,000-unit allowance again, so the OpenOT telemetry and database examples, OSCAT OOP and PLCopen Motion libraries run under the default limits. The stack and register executors charge the same total. Program construction and restart get their own allowance from the preparation profile (64 Mi units by default on hosted runtimes).
- Hosted execution prepares lookup indexes and frame plans once and moves owned call values instead of copying them; lifetime checks, transactional outputs and failure cleanup are unchanged.
- Calls do less work. Literal and plain scalar defaults are written straight into the new frame, and on hosted runtimes a function, function block or method that calls no other user POU runs directly inside the calling instruction (not while debugging). Results, faults and work-limit charges are unchanged; such calls no longer reserve call-handler memory.
- Move the M2E runner, trace records, bundle decoding and heap/stack measurement from the F401 firmware into the shared
trust-board-fixturecrate used by both board compositions; F401 code grows by 256 bytes.xtask portability build-firmware,inspectandverifynow take the board name (nucleo-f401reoresp32c6-devkitc-1). trust-runtime validateresolves the configured resource exactly asplaydoes, so a resource name the program does not declare now fails validation instead of passing and then failing at start-up.ctl setandctl var.forcetake an integer in the target variable's declared type, asio-writedoes:set global:Counter 41storesINT41 into anINTglobal; a value the type cannot take is rejected without a fault.- Pending I/O writes, variable writes, debugger expression writes and forces are
bounded at 1,024 each per runtime, for control, ADS and debugger requests alike;
a request beyond the bound is refused with
request queue full. A force whose value is rejected at the scan boundary is removed from the force set instead of being retried each scan. - The MQTT driver reports dropped incoming messages: after its 256-message queue
overflows it stays
degradedwithincoming queue overflow: <n> messages dropped. Safe-state confirmation uses the driver's output path, so an input overflow does not unconfirm a safe stop. - MQTT input freshness no longer affects the output path. An input read checks
for new messages without waiting, stale input reports its condition under the
on_errorpolicy without starting reconnect backoff, and outputs keep publishing. The driver's reported health combines its input and output paths; safe-state confirmation uses the output path only, so a stop caused by stale input can still be confirmed. - The control server's event store keeps the newest 1,000 events, the largest
events.tail/faultslimit (it kept 200). A debug control without an event sender buffers at most 1,024 events and counts the ones it drops (runtime_events_dropped). VariableStorage::create_instance, which panics when instance identities run out, is test support only; production construction usestry_create_instance.- POUs that contain a FOR loop now run on the register path and Tier-1. The FOR loop's zero-step guard was the one opcode the register path could not lower, so every such POU fell back to the slower stack path.
MUL_TIME,MUL_LTIME,DIV_TIMEandDIV_LTIMEcompute exactly as the*and/duration operators they name (IEC 61131-3 Table 35): integer factors scale exactly and a real result truncates toward zero. They used to round ties to even in binary64, which also lost nanoseconds for integer factors on durations above about 104 days. A non-finite factor isOverflowon both paths.LIMITfollows IEC 61131-3 Table 32,MIN(MAX(IN, MN), MX): with inverted limits (MN > MX) it returnsMX.- Numeric text conversions accept
_only between two digits, and a sign only on the digits ('1__0','_1','2#+-1'and'+2#1'are rejected). - An untyped integer literal takes the type of its destination: an assignment,
a variable, array element or structure field initializer, a call argument,
RETURN, or the other operand of a comparison. Every integer type accepts its whole range as a literal:ULINTup to 18446744073709551615,UDINTandLINTvalues above 2147483647, and typed minimums such asLINT#-9223372036854775808. A literal outside the destination's range or subrange is an analysis error, and subrange bounds take the subrange's base type, so aLINTsubrange can have bounds beyond theDINTrange. - Untyped literal arithmetic in a declaration's initializer takes the declared
type, as it already did in assignments:
x : LREAL := 5 / 2is now 2.5 (it was 2.0). TIMEandLTIMEliterals are computed exactly, so the largest duration (T#9223372036s854ms775us807ns) is accepted, andREAL#literals round once to the nearestREALinstead of rounding throughLREAL.- Tier-1 telemetry drops the deoptimization counters (Tier-1 never deoptimizes), and the register profile counts interpreted block entries.
- An oversized bytecode container is always reported as an invalid header, even when the host raises the preparation work budget, and the decoder names "REF_TABLE entries" in its resource-limit error.
DebugRuntimeContextkeeps its PLC storage private.- Record the shared hosted runtime's accepted Linux call-workload performance trade-off: a measured 16.4% median overhead against the modernization baseline, with unchanged functions, safety checks and separate hardware timing requirements.
Removed¶
Runtime::clear_debug_control. It had no caller, and dropping the debug handle released every active force although it claimed to leave the installed state unchanged; detaching a debugger keeps forces (specification 11 §6.9).
Fixed¶
- OPC UA startup now waits until this process's own server has bound its listen address, so another process holding the port can no longer pass for a started server on platforms where the check before startup does not see it (seen on macOS). A bind that fails during startup reports the same "listen address … is unavailable" error as that check.
trust-dev ctlandtrust-runtime uikeep the project's control token when--endpointis given, astrust-runtime ctldoes; they used to drop it.TP: on the call where a pulse ends withINFALSE,ETisPT; the next call withINFALSE resets it to zero. It used to reset on the same call, unlike the timing diagram andTOF.SRandRSaccept the IEC 61131-3 long input names (SET1/RESETandSET/RESET1, Table 43 rows 1b and 2b). Such calls compiled but faulted at run time.- Restart requests that arrive before the resource has taken the previous one coalesce into one restart, and a cold request takes precedence: a later warm request no longer silently replaces a pending cold restart.
- A bytecode reload that times out before the resource takes it is withdrawn and never applied. Before, it could still commit after its caller had been told it timed out.
- The resource command queue holds at most 1,024 commands. Further requests,
including
pause,resume,bytecode.reload,ads.statusand scheduler changes throughconfig.set, are refused withresource_busyinstead of growing memory without bound, andconfig.setapplies its scheduler policy changes as one step, so a refused update changes nothing. - A fault that halts the resource no longer ends the runtime process. The
resource stays faulted with its safe state applied,
status, the web UI, HMI and protocol servers keep working, andrestart(warm or cold) or an online change recovers it after the configured initialization. Before, the resource thread ended, arestartrequest was acknowledged but never applied, and the process exited with status 0. The process now exits with status 1 when it ends with the resource faulted. - Control
restartand online change on a stopped resource, or on one whose scan thread did not return, returnresource_not_runninginstead of reporting that a restart was queued. - 83 example projects named a resource in
runtime.tomlthat their ST did not declare; their RESOURCE declarations now match. For the seven communication examples andnetwork_canvas_demothis meanstrust-runtime playstarts them again; the other 75 are OSCAT test projects, run withtrust-runtime test. The ADS server example's resource istruST_ADS(a hyphen is not valid in an IEC name) and the EtherCAT example'sEtherCAT_demo. Five PLCopen motion examples without anio.tomlgot a simulated driver so they validate and run. - Hosted instance creation reports
Overflowinstead of panicking when instance identities are exhausted. CONCAT_TOD,CONCAT_LTOD,CONCAT_DTandCONCAT_LDTreject a component outside its range instead of carrying it into the next field (CONCAT_TOD(0, 75, 0, 0)was 01:15:00).SHL,SHR,ROLandRORhandle shift counts over the full 64-bit range: a count of 2^32 or more no longer wraps (SHL(BYTE#1, LINT#4294967296)wasBYTE#1).MID,DELETEandREPLACEwith a length past the end clamp to the end instead of overflowing (a panic in debug builds), and string positions above 2^32 give the same result on 32-bit targets as on 64-bit ones.TIME_TO_DWORDand aTIMEwritten to aDWORDprocess-image slot reject a negative duration instead of storing 0.TON,TOFandTPsaturate their elapsed time atPTacross a clock span wider than theTIMErange; such a span panicked in debug builds and added nothing in release builds.LTIME,LDATE,LTODandLDTvariables can be bound to long-word direct addresses (%IL,%QL,%ML) and carry their exact 64-bit value.DATE,TODandDTare refused at direct addresses with an error that names the long forms; the harness used to size them asDWORD.- Durations in the runtime configuration,
config.set, simulation settings, conformance cases and the test-harness protocol that exceed the representable range (about 292 years) are refused; they used to wrap to negative durations in release builds. - Control
pauseandresumeon a faulted or stopped resource return the error coderesource_not_runningand change nothing; they used to replypausedorrunningalthough the resource was not scanning. - The OPC UA server refuses to start, with a clear error, when its listen address is already in use. Before, its server thread failed silently while startup reported success, because the readiness check reached whichever process held the port.
- The release-candidate CI watcher no longer reports a run as complete before GitHub has registered any check for the new head.
ctl io-writeandio-forceof an integer no longer fault and halt the PLC (issue #135). An integer value takes the declared type of itsINT,DINT,WORDor other integer or bit-string destination when it fits; a value that does not fit is rejected with awrite_rejectedevent and the PLC keeps running.ctl step-over,step-inandstep-outare rejected with a clear error unless the debugger holds the scan (a breakpoint or a debug-mode pause), instead of silently stalling the scan after a productionpause(issue #136). An attached debugger still steps from a breakpoint.resumealways releases a debugger hold, andstatusreports the hold instead ofrunning.- Corrected
trust-runtime ctlcommands in the documentation: the operator guide and the safety commissioning tutorial now userestart cold(the CLI has no--modeflag), the cloud troubleshooting guide and profile cookbook useconfig-get, the operator guide describes the actualstatusline and points tostatsfor cycle timing, and the memory marker tutorial shows theIntvalues thatio-readprints. Tests now run these documented commands against a real runtime. - Charge standard-library text work. CONCAT, REPLACE, MID and the other text and
conversion functions charge 1 work unit per UTF-8 byte of their text arguments
before they run, and their text result as a new value. A program that keeps
doubling an unbounded
STRINGnow stops with the work or allocation limit fault instead of growing until the host runs out of memory. - Charge reference growth independently of internal buffer sizes, and stop counting a call's rollback journal against the allocation limit, as the work cost model states.
- The STBC 3.0 checksum covers the whole file, including the header. A file whose version or flags were changed is rejected instead of being read with other storage widths.
- A stalled health reader can no longer hold up the control cycle: health publication skips a cycle instead of waiting for the reader.
- Debugger I/O views keep the declared enumeration type name for unresolved and failed entries, and remote sessions no longer uppercase it.
- Writing an unknown
VAR_ACCESSname reportsruntime_undefined_variableinstead ofruntime_invalid_alias, as specification 10 states. A read-only alias still reportsruntime_invalid_alias. - Preserve contextual legacy imports whose POU index omits unused standard function blocks, and keep absolute structure-field I/O addresses distinct from relative storage offsets during source-free encoding.
- Use the default compiler outlining pass count for F401 firmware; reject the extra rerun that produced an invalid Thumb return sequence and a startup HardFault.
- Preserve physical structure backing through constant defaults, initializer coercion and STRN v3 durable RETAIN storage, retaining v1/v2 readers; reject contradictory alias constants before installation. Keep optimized remainder and partial-budget fault behavior aligned with the shared bytecode executor, and reserve reusable register frame payloads.
- Propagate shared-engine cycle limits through hosted management and I/O snapshots; preserve legacy supplied program instances, installed I/O rebinding and anonymous debugger expression types across the hosted compatibility adapters.
- Security: update
rustlsto 0.23.46 (RUSTSEC-2026-0346 to 0349: TLS secrets in client sessionDebugoutput, data sent to the outer server after an ECH rejection, up to 1 GiB allocated for certificate decompression before authentication, and a post-quantum key-exchange downgrade). Thetiny_httpHTTPS server keepsrustls0.20.9 under an owned exception until 18 November 2026: the downgrade concerns TLS clients, andrustls0.20 offers no post-quantum group.
Security¶
- The Unix control socket accepts connections only once it is owner-only (0600): it is bound, restricted and only then made to listen, without changing the process umask that every thread shares. It used to listen first and be narrowed afterwards, so another local user connecting in that moment got the token-less Unix Admin role. An existing file at the socket path that is not a socket is refused instead of deleted.
- trust-lsp inline runtime values send the editor's control token only to the
editor's own endpoint; an endpoint configured in the repository's
trust-lsp.tomlreceives only the token configured next to it.
[0.24.74] - 2026-10-11¶
Changed¶
- Modernize the supported build baseline to Rust 1.99 (edition 2024, resolver 3) and Node 24.21.0; refresh dependency families while retaining compatible LSP and editor API pairs. Software and full-function F401 fixture qualification pass; production and ESP32 hardware qualification remain separate.
- Test configuration no longer mutates process-wide environment variables; verification, register executor, OPC UA and persistence tests supply values directly through their owning configuration boundaries.
Fixed¶
- Isolate the real OpenOT PLC scan-overhead regression from concurrent functional tests and compare paired scans with alternating order, preserving the existing threshold and checking that the real persistence worker commits and drains.
- Route required real-database qualification through the Rust generated-environment decoder and clear inherited database settings before launching the gate, so stale parent values cannot satisfy missing generated inputs.
- Reject source-freeze manifests inside the source checkout, including symlink aliases, and preserve existing manifests rather than overwriting them. Source identity also checks working-tree file kinds, link targets and Unix executable bits; older byte-only snapshots remain historical evidence.
- Bound rendered-test browser startup and lifetime with Rust-owned subprocess cleanup and visible browser/version diagnostics; install selected CI tools and locked frontend prerequisites before their gates.
- Make PostgreSQL and TimescaleDB release-runner TLS mounts accessible to the service UID under restrictive host umasks, while keeping server keys private.
[0.24.73] - 2026-10-11¶
Added¶
- Add the NUCLEO-F401RE reference composition for the shared runtime, with bounded memory, separately installed STBC artifacts, and remote GPIO/watchdog evidence tooling. The full-function F401 bring-up passes saved-artifact traces, numeric checks, GPIO safe outputs, depth-four calls and independent-watchdog reset; production qualification remains separate.
Changed¶
- Use compact live activation storage and portable instance storage without changing PLC identities or functions. Portable field lookup reuses existing hash indexes; output journals keep ordered destination-only rollback. Charge batch retirement while preserving cleanup on exhausted execution budgets. Frame storage includes initializer staging in addition to admitted call depth. F401 fixture bring-up is verified; production storage and timing qualification remain separate.
- Reduce shared-runtime code duplication in numeric conversions, assertion comparison, bounded sorting and engine identity maps while preserving every PLC function and diagnostic. F401 placement uses immutable sector-0 space with separate load-region checks and a 16 KiB upper-region margin requirement; F401 fixture measurements meet the flash and stack margins; production qualification remains separate.
- Reduce portable preparation overhead with shared static standard-library metadata, indexed preparation tables and shared bounded sorting. Byte-input preparation avoids serialization, and embedded CRC uses a compact implementation with identical results. Portable preparation retains structured diagnostic context without eager formatting; hosted messages and fault codes remain. Default-library lookup returns a borrowed metadata view. F401 code uses the 448 KiB L2 region; two reserved checkpoint sectors do not yet provide persistence. All IEC functions and admission checks remain.
Fixed¶
- Preserve source-free restart initialization of direct input, output and marker image addresses. Warm and cold restart carry previous images into staging before ordered configuration actions, keeping unconfigured values and transactional failure behavior.
- Support system Bash 3.2 in the shared dependency-audit wrapper, including empty exception lists, and preserve native path separators in firmware remapping.
- Execute compiled PLC call chains through explicit shared VM continuations to bound native dispatcher nesting; stop F401 bring-up when measured stack headroom is insufficient.
- Keep deadline expiry latched through portable execution cleanup and build bulk input rollback snapshots without quadratic insertion work; preserve ordered writes and alias rollback.
[0.24.72] - 2026-10-10¶
Changed¶
- Share STBC 1.x decoding, validation, metadata extraction and serialization with the portable runtime core, preserving hosted bytecode APIs.
Fixed¶
- Preserve function-block instance ownership when a body contains a NULL literal, so instance-field accesses bind to the invoked instance.
- Bound bytecode validation scratch storage and work across instructions and metadata, retaining stack states only at control-flow block entries. Reject section payloads that overlap container headers/tables, misaligned headers, raw record discriminant mismatches and encoder count/extent overflow before preparing executable state.
- Reject overflowing byte-reader lengths and section/POU extents and relative jump targets before slicing or advancing the reader.
[0.24.71] - 2026-10-10¶
Changed¶
- Route core exponentiation, duration truncation and hosted numerical functions through libm 0.2.16 while retaining their promotion, narrowing and fault policies. Add isolated F401/C6 core build gates and explicit portable ordered-map hashing; this establishes library portability, not qualified board support.
Fixed¶
- Confirm managed fleet shutdown across control-connection teardown without mistaking interrupted status probes for a failed shutdown or a confirmed stop. Preserve authentication/protocol errors and retain the PID until stop is confirmed.
- Publish the runtime portability specification in the public reference and preserve warm release-build artifacts when the target filesystem already meets the existing 80 GiB capacity requirement.
- Keep the unsupported Git-marker regression executable with long temporary directory paths on Unix hosts.
- Preserve numeric operand types when compiling TIME/LTIME scaling, including duration aliases and nested scalar expressions.
- Preserve nominal periodic-task deadlines when the task interval is not a multiple of the resource cycle. For exact 10 ms samples, a 25 ms task now activates at 30, 50, 80 and 100 ms instead of every 30 ms; missed activations remain dropped and counted. These describe the logical scheduling rule, not measured hosted wake timing.
[0.24.70] - 2026-10-10¶
Changed¶
- Let release validation honor the builder's configured Cargo job limit instead of forcing a single concurrent build job.
Fixed¶
- Bind release validation capacity checks to the selected generated-target filesystem; support the mounted builder volume with the same leases, ownership checks and 80 GiB minimum, avoiding root-disk exhaustion during cold workspace tests.
- Remove deleted project sources from the language server consistently across directory notifications, reindexing and concurrent document close/index operations, preventing stale duplicate declarations when creating a new project. Retain registered file identity after text eviction so deletion and rename also work through filesystem aliases on macOS and Windows.
- Protect I/O credentials in IDE, configuration-text and remote proxy reads;
preserve stored secrets (including a literal
<redacted>) on unchanged saves, and prevent generic IDE symlink/rename aliases from bypassing redaction. Retained MQTT passwords are bound to their broker and authentication context; changing that context requires explicit replacement credentials. - Use the Rust 1.95-compatible atomic update API without deprecated calls on current stable Rust, and update concurrently to remove the shell-quote command-injection advisory.
- Security: update
rustlsto 0.23.45 (RUSTSEC-2026-0285, TLS 1.3 handshake messages accepted across encryption levels) andsalsato 0.28.5 (RUSTSEC-2026-0308, use-after-free in interned values and cached query results). The two queries whose results salsa now returns by reference are read by value, unchanged in behaviour. The VS Code extension'sbrace-expansion(2.1.7, 5.0.12) andjs-yaml(4.3.2) move past two high-severity CPU denial-of-service advisories. - Security:
GET /api/io/configanswered without the web authentication in the runtime's own web server, and every I/O configuration read returned driver credentials such as an MQTT password. The read now needs the Viewer role in every server mode, and passwords, tokens, secrets and private keys come back as<redacted>; saving a configuration as it was read keeps the stored credential (specification 11, §6.9.7).
[0.24.69] - 2026-09-04¶
Fixed¶
- Resolve symbolic MQTT tag mappings when a project is launched in the VS Code simulator, so mapped outputs connect and publish instead of leaving the debugger with an empty process image.
[0.24.68] - 2026-09-02¶
Added¶
- Expose typed logging objects with descriptive names such as
logged_values,alarm_history,message_log,state_history, andaudit_log; normal reports no longer require OpenOT field arrays or JSON paths, while canonical JSON remains internal recovery authority. Every backend and the InfluxDB spool initialize the same first schema generation, 1, directly. Because database persistence has not previously shipped, incompatible pre-release development schemas fail closed without migration, backfill, reconstruction, or automatic repair. Runtime status reports this shared contract asschema_generation, not a backend-specific version.
Fixed¶
- Preserve typed database transport failures through OpenOT schema initialization, commit, and maintenance so bounded reconnect still applies, while deterministic projection and storage failures fault immediately instead of repeatedly reopening the selected backend.
- Freeze bounded OpenOT shutdown drains from a fresh non-consuming producer head so records published after an older buffered poll are still committed, and restrict real-database runner teardown to exact-labelled Docker resources even when a matching temporary-state marker exists.
- Keep OpenOT source-head and pending-byte status observable while an initial remote database connection is unavailable, and run the same complete Architecture Safety gate before push and in GitHub CI. Protect active shared-builder Cargo targets with stable external leases so concurrent cleanup cannot delete exact-candidate test executables without leaking those leases into helper daemons or detached children, and preserve target compiler discovery instead of injecting host C/C++ compilers into Windows cross-target checks.
- Keep SQL Server logged-value statements below its 2,100-parameter limit, constrain the MySQL/MariaDB schema marker to its single valid key, replace the yanked MySQL client patch release, and run the same supply-chain policy in exact pre-push validation and GitHub CI.
- Keep PLC startup independent of remote logging availability, preserve cumulative status across reconnects, drain bounded shutdown work, and report the actual opened backend schema without exposing secrets.
- Make InfluxDB delivery bounded and continuous under sustained input, retain the complete typed/domain/provenance model, and require authenticated, CA-verified real-product release proof.
- Publish an InfluxDB spool commit before surfacing later reconciliation failures, preserve detailed reconciliation counters through backend selection, and reject compiled-out TOML backends synchronously at startup.
- Reject missing or empty selected database environment variables before worker startup, retry only explicitly classified remote-open reachability failures, and fault permanent schema or storage-open errors immediately.
- Reject missing database CA files before starting the persistence worker, preserve complete loss/unresolved provenance in SQL Server combined commits, and require capture-lifecycle parity for every Docs Captures workflow path.
- Provision every real logging database from repository-owned, digest-pinned runner scripts with ephemeral credentials, authenticated readiness checks, symlink-safe container/network/anonymous-volume teardown, and retained exact-candidate evidence.
- Preserve previous-value and audit fields in every relational
logged_valuesrow, reapply SQLite/Influx spool durability settings on every connection, and reject generation-1 databases whose actual columns, constraints, foreign keys, or indexes differ from the schema created by truST. - Keep initial InfluxDB transport outages in the bounded reconnect lifecycle, reclaim exact-labelled real-database runner resources after interrupted jobs even when temporary state was lost, and make exact-candidate Clippy use the same warning-deny command as GitHub CI.
[0.24.67] - 2026-08-30¶
Added¶
- Persist canonical OpenOT event, loss, and placeholder documents outside the PLC scan to a TOML-selected SQLite, PostgreSQL, TimescaleDB, MySQL/MariaDB, SQL Server, or InfluxDB 3 backend, with atomic durable checkpoints, idempotent replay, TLS/secret boundaries, InfluxDB outage spooling, runtime status, complete examples, and real-vendor conformance coverage.
Fixed¶
- Make every OpenOT database example portable to Windows and Linux by using a
TCP runtime control endpoint with an explicit local-only example token instead
of a Unix-only
/tmpsocket.
[0.24.65] - 2026-08-29¶
Fixed¶
- Keep real-Mosquitto conformance compatible with broker versions whose help command prints a usable version and then exits nonzero, so optional version reporting cannot override passing protocol assertions.
- Run exact-candidate VS Code browser and Extension Host checks with a unique,
short-lived
/tmpdirectory so long validated Cargo target paths cannot break Unix-domain process sockets. - Report prunable candidate worktree registrations as cleanup targets instead of misclassifying their missing directories as dirty worktrees.
- Give capture lifecycle assertions enough time to observe the launcher's bounded TERM-to-KILL cleanup and process reaping path.
- Keep direction-specific MQTT mappings isolated: write-only drivers no longer
clear shared input bytes, and read-only mappings no longer publish the raw
output image to the default topic. An explicitly empty
mappingslist also disables both raw directions instead of restoring the default topics. - Give MQTT one authoritative communication specification instead of mixing broker and wire behavior into the runtime-engine contract, and make the changed-file verifier reject MQTT production changes without that owning specification and a native executable test.
- Preserve IEC enum identity across MQTT tag mappings while using declared numeric member values on the wire, including alias and direct-binding cases, reject undeclared inbound values atomically, and retain the declared enum name in control snapshots for resolved, error, and unresolved points.
- Let
trust-runtime ctl statusrender a healthy runtime's JSON-null fault asnone, matching the live control response contract. - Make reused runtime communications conformance output directories inspect only the current case invocation instead of accepting stale passing lines.
- Require the runtime communications conformance gate to execute the shipped MQTT traffic-light mapping against a real Mosquitto broker and client.
[0.24.64] - 2026-08-29¶
Fixed¶
- Keep native Unix control-endpoint tests portable under long temporary paths, and make automated docs captures reap their owned process sessions and code-server container after success, failure, or interruption.
- Keep the Web IDE Hardware tab responsive for ordinary communication graphs by coalescing repeated hydration relayout requests into one bounded animation frame and one post-hydration resize/fit pass.
- Make post-merge release verification audit the exact candidate branch and validation worktrees, block dirty or divergent state, and report explicit cleanup targets before a release handoff can be completed.
- Reconcile staged checksum paths with GitHub's flat release-asset namespace while rejecting ambiguous basenames and unsafe manifest paths.
- Keep exact-candidate remote validation aligned with CI by disabling inherited Rust compiler caches and serializing the cold all-tests build to bound disk use.
- Fail exact-candidate preparation before creating build artifacts unless the remote builder has at least 80 GiB free, reflecting the measured size of a cold uncached all-tests target.
- Keep workspace integration-test binary discovery compilable under cold all-target check and clippy runs, and enforce that portability contract in CI.
[0.24.63] - 2026-08-27¶
Added¶
- Support MQTT tag mappings that bind fully qualified scalar program variables
directly to broker topics with explicit PLC-relative
readandwritedirections.
Fixed¶
- Make the runtime communications conformance gate reject zero-test filters and align its development and WAN profile cases with the shipped policy.
- Make the Neovim and Zed editor smoke gate fail when an exact Rust filter silently selects zero tests.
- Make tutorial 09 a complete standalone simulation project so Compile and the Runtime Panel resolve its source and configuration instead of falling back to the repository root.
- Bundle
trust-devin every release VSIX besidetrust-runtimeso native ST tests use the matching workbench binary and always return their JSON result. - Reject unknown MQTT
io.paramsfields instead of silently falling back to default topics, and let write-only tag mappings run without requiring an inboundtrust/io/insnapshot.
[0.24.62] - 2026-08-24¶
Added¶
- The VS Code extension test harness now supports opt-in
TRUST_VSCODE_TEST_GREPfiltering and optional per-test JSON output while preserving the unfilterednpm testdefault. - verification: added direct cargo-fuzz targets for HIR lowering, PLCopen XML, bytecode containers, runtime configuration, LSP incremental edits, and HMI payloads; added direct runtime-anomaly tests; and resolved six obsolete test skips, including retirement of a capture for a runtime command that is intentionally hidden from the command palette.
- trust-runtime: added focused automatic-restart storage tests alongside the existing runtime panic-containment and bounded Modbus slow-device tests.
Changed¶
- Specify runtime clock dispatch name/error identity and deterministic LSP diagnostic-override collision precedence, including canonical-key priority and alias-only lexical ordering.
- The parser example-corpus test now fails when the checked-in example tree is missing and parses every checked-in example instead of silently skipping the suite.
- Specify the nine shipped tutorial files as executable compile contracts and lock the blinker, traffic-light, and motor-starter examples to their reviewed clean-cycle timing and I/O sequences.
- Specify the native browser-analysis and WebAssembly JSON adapter boundary for native-result parity, opaque virtual document identity, full-set replacement, and explicit JSON success and failure projections.
- Specify the runtime variable-storage contract for instance-field resolution, declared layouts, reference-helper equivalence, cache invalidation, copy-on-write nested updates, and checked extreme array bounds.
- Specify fail-closed host lvalue writes, bounded helper capabilities, host-stage POU defaults and write-through behavior, IEC array repetition, representative integer-base exponentiation and Boolean comparison results.
- Specify retain codec compatibility, filesystem transaction visibility, retry semantics, atomic migration, and typed migration/orphan events.
- Specify mesh envelope metadata, subscription grammar, typed numeric decoding, bounded malformed-input handling, readiness, liveliness, QoS, and snapshot failure contracts without overstating TLS or query/reply proof.
- Specify the ADS AMS/TCP prefix, frame-length cap, header/payload correlation, canonical serialization, and response endpoint/correlation contract.
- Specify per-cycle watchdog deadline arming/restoration and bounded automatic restart backoff without treating host-runtime policy as an IEC deviation.
- Align runtime scheduling documentation on priority, due-time, stable-index, registered-high SINGLE, and background-program execution order.
- Specify ADS notification cadence, on-change coalescing, watch-length source sampling, invalidation, and representable FILETIME conversion.
- Specify ADS service-port routing, router/system compatibility responses, notification receiver binding, and bind-conflict startup failure.
- Specify deterministic ADS server symbol assignment, duplicate rejection, flag preservation, serialization identity, and local version progression.
- Specify deterministic simulation-model activation, coupling and physics scan boundaries, conflict rejection, replay, scripted faults, and scaled-clock watchdog isolation.
- Specify the
trust-st-complete-v1PLCopen profile, ST/data-type interchange, fail-closed import diagnostics, migration and CODESYS metadata, reviewed vendor shims, and Allen-Bradley/Siemens adapter artifact contracts. - vscode/docs: documented the shipped product contract for the truST sidebar,
Live Values, Devices & Connections, examples, libraries, HMI, and visual
editors; public guides now use the current Compile, Live Values, and direct
+ Addworkflows instead of retired Runtime Panel, Check, and Edit-mode wording. - trust-runtime: documented the stable signed-integer materialization policy:
representable
SINT/INT/DINT/LINTresults retain their exact value and destination tag, while range overflow fails before storage instead of wrapping, saturating, truncating, or substituting a value.
Fixed¶
- ci/trust-runtime: The managed-fleet CLI lifecycle integration test now uses file-backed child output capture and bounded command execution, preventing a background Windows runtime from retaining a capture pipe and deadlocking output collection after the fleet command exits.
- trust-runtime: ADS import defaults and runtime test fixtures now preserve
portable TOML paths on Windows, normalize platform-native source-list paths
in build and check assertions, and keep absolute and escaping sidecar paths fail-closed;
CURRENT_DTboundary tests no longer depend on hostSystemTimerepresentations; deployment pointer replacement now removes its Windows directory-symlink backup correctly. - trust-plcopen: Exported source-map paths now use portable
/separators on Windows as well as Unix hosts, keeping project-relative artifact identities stable for automation consumers. - trust-runtime: HMI asset requests now reject uppercase
.SVGspellings consistently on case-insensitive filesystems, preserving the documented lowercase-only asset policy. - ci/trust-runtime: The OpenOT fenced cross-process capstone uses one configurable 120-second startup and completion budget, avoiding false failures on slower hosts without relaxing its delivery or loss assertions.
- ci/trust-runtime: The focused OSCAT aggregate-project harness now permits a five-minute child-test budget on slower hosts while retaining periodic progress reporting and the same pass/fail oracle.
- trust-dev: Generated API documentation now keeps source identities relative to the selected project on macOS and Windows as well as Linux, instead of leaking a platform-specific absolute temporary path.
- ci/docs: Browser capture navigation now retries a transient editor-server startup stall within the same rendered journey, preventing a healthy capture run from being reported as a flaky documentation failure.
- trust-dev: Agent workspace read/write responses now use portable
/-separated relative paths on every supported host, keeping JSON-RPC consumers and cross-platform automation stable. - trust-runtime: Hardware settings controls remain clickable and render the selected project's configured drivers before editor analysis is ready.
- trust-runtime: Hardware settings now keep the driver panel reachable in the scrollable settings surface, and control authentication tokens use masked password input instead of being displayed as plain text.
- ci/vscode: the exact-SHA release-candidate guard now runs Extension Host
tests under Xvfb, and rendered Live Values layout proof discovers installed
Chromium and Google Chrome executables without a builder-specific override.
Exact-candidate checks use a fast report-boundary smoke, disable incremental
build duplication, and safely reclaim only the configured generated target
between Clippy and
test-all; exhaustive verification-tooling fixtures remain scheduled/manual maintenance. - Reject ADS client connections that declare no points and make the communication authoring schema explain that activation requires at least one connection with a selected point.
- Write the correlated runtime control acknowledgement before applying an accepted shutdown stop signal, preventing managed fleet stop from losing its response while the runtime process exits under load.
- Update the VS Code dependency lock to patched
brace-expansionandjs-yamlreleases after their high-severity denial-of-service advisories. - Keep malformed numeric candidates such as a real literal with a trailing dot as one fail-closed lexer error instead of publishing a valid numeric prefix.
- Preserve the stable
E106invalid-identifier diagnostic when malformed identifier candidates are rejected directly by the lexer, instead of degrading the editor diagnostic to a generic parser error. - Keep function-block parameter defaults in instance storage while encoding
recursive function/method call-local
INandOUTdefaults, including fixed and wildcard arrays, structs, unions, nested strings, and NULL references. Constant validation now shares one bounded recursion policy and rejects malformed aggregate frames and non-NULL reference identities, while supplied interface and object-instance formals no longer fail bytecode publication merely because no portable implicit constant exists. - Publish uniquely resolved cross-file enum values into their enclosing scope so later program initializers can use them, while preserving ambiguity for colliding variants from distinct enum types.
- Preserve enum identity for unqualified
CASElabels in bytecode lowering so both VM execution paths select the matching branch without weakening same-named constant shadowing. - Keep same-named fleet runtimes, endpoints, configured mesh peers, and links distinct across hosts and containers when Devices & Connections displays or combines legacy schema-v2/v3/v4 snapshots, including singleton topology, while failing closed only for ambiguous references and preserving unrelated topology.
- Clamp LSP telemetry duration milliseconds at
u64::MAXinstead of wrapping an overflowing duration into a small, misleading latency value. - Document the external-diagnostic JSON input contract, including trimmed, case-insensitive string severities and numeric LSP severities.
- Reject mutable BOOL, REAL, STRING, and aggregate-member dependencies in explicit variable initializers instead of applying the constant-expression rule only to direct integer initializers.
- Preserve the IEC reference-declaration exception so
REF_TOvariables may initialize fromREF(...)of eligible existing storage while retaining the canonical REF lvalue, type, visibility, and lifetime diagnostics. - Publish stable
C001-C005LSP configuration diagnostics for unreadable or malformed files, unknown standard-library profiles and workspace visibility, invalid positive bounds, and ignored invalid severity overrides; E303/E304 explainers now link to their actual IEC array/range clauses. - Keep project source discovery inside the supported
.st/.pouset after resolving contained symbolic links, while retaining deterministic canonical identity for valid contained aliases. - Preserve runtime link health detail in Devices & Connections and render degraded/error links with distinct warning/error tones plus an accessible hover/focus explanation.
- Release tag and version-evidence guards now fail closed unless an annotated tag resolves to the exact release commit, CI and Release runs match the exact event, branch, and SHA with usable URLs, API collections are well formed, successful API responses are top-level objects with stable endpoint/status diagnostics instead of tracebacks, and published asset names are unique.
- HIR temporal arithmetic now implements the complete documented IEC 61131-3 Table 35 operand/result matrix and rejects swapped, cross-family, and other unlisted temporal combinations at the operator boundary.
- Invalid user-defined type formations now remain internal diagnostic-recovery records instead of becoming publicly discoverable types, while valid sibling declarations continue to publish normally.
CURRENT_DT()now has an explicit UTC host-clock contract and preserves the full millisecondDTtick range instead of overflowing when the intermediate Unix timestamp exceeds signed nanoseconds.- OpenOT runtime conformance tests now carry the complete pinned authoring and
value-sampling Structured Text fixture closure, so those tests execute
independently of a sibling
open-ot-refcheckout instead of failing at the frozen-fixture boundary. trust-dev testnow compiles projects that contain no test POU before reporting an empty run, so compile-negative fixtures cannot become false successes merely by using ordinaryPROGRAMdeclarations.- HIR and runtime timer registration now support the IEC 61131-3 Table 46
TP_TIME,TON_TIME, andTOF_TIMEnames with fixed TIMEPT/ETsignatures and the same scan-step behavior as their overloaded base forms. - Bytecode
FORloops now derive their internal zero sentinel from the evaluated step value, so valid unsigned control variables and steps no longer fail strict numeric comparison with an unrelatedLINTsentinel. trust-runtime playnow creates a project only for ordinary project absence; current-directory and system-I/O configuration failures remain visible instead of being treated as a first-run setup request.- ADS onboarding sum-up reads now reject unknown handles and incomplete, duplicate, unknown, non-good, valueless, or descriptor-incompatible results before projecting any wire value.
- HIR now rejects an explicit integer variable initializer that depends on an ordinary mutable variable, while preserving literal and declared-constant integer expressions required by IEC 61131-3 Section 6.5.1.3.
- File-backed retain snapshots now persist
PROGRAMVAR RETAINvalues under collision-free qualified identities, restore the saved value on warm restart, and remain unloaded on cold restart; the reliability test now distinguishes a real store reload from unchanged live memory. - Debug breakpoints now emit at most one stop per breakpoint identity within a runtime scan, preventing overlapping debug-map entries from repeatedly stopping the same scan while preserving breakpoint re-hits on later scans.
- Runtime bytecode documentation now matches the executable STBC 1.1 opcode set and specifies source-to-section, resource/process-image, variable metadata, and validation contracts used by the existing encoder and runtime.
- trust-debug now rejects
stackTracerequests for thread IDs that are absent from the current DAP thread projection instead of returning a synthetic frame for another thread; debug evaluation documentation now matches the shipped side-effect-free standard-function whitelist. - trust-runtime-core and trust-hir now reject explicitly typed mixed numeric
operations when the operands have no accuracy-preserving common type instead
of silently widening values such as
ULINTtoREAL; representable untyped numeric literals retain their valid contextual operand or overloaded-argument type. - OSCAT: decimal rounding, random helpers, array shuffle, random generators,
and dew-point relative-humidity conversion now preserve their declared
INT/REALtypes instead of failing at runtime when genericLIMITexpressions widened untyped bounds;IS_SORTEDnow reports unsorted nonempty arrays instead of overwriting its detected failure. OSCAT bit-transfer documentation and tests now also honor truST's existing rejection of non-finiteREALvalues. - VS Code: managed runtimes whose status command fails or returns an unrecognized state are now shown as unavailable with lifecycle actions disabled instead of being misreported as stopped and offered a misleading Start action.
- trust-syntax:
ACTION ... END_ACTIONdeclarations nested in aPROGRAMare now parsed as action nodes instead of terminating the program statement list early and cascading top-level parse diagnostics. - PLCopen Motion:
MC_Stopno longer reportsBusyandDonesimultaneously after reaching zero velocity, and public parameter writes to standard read-only Part 1 values are now limited to the documented disabled, idle initialization phase; the vendor BOOL extension is published asPN_TRUST_VendorBool0. Buffered commands flushed byErrorStopnow reportErrorwith the retained axis fault code instead ofCommandAborted, even whenMC_Resetruns before the buffered FB observes the flush. - trust-runtime: removing the final project I/O driver now retains
io.tomlwith an explicit no-driver posture, preserving safe-state policy and project comments outside rewritten driver/safe-state items without implicitly activating machine-wide system I/O fallback. - trust-runtime-core: indexed string replacement now applies the destination
CHAR/WCHARwidth to one-scalarSTRINGandWSTRINGvalues instead of bypassing overflow validation. - trust-hir: unambiguous integer-valued enumerated constants are accepted as array bounds, while nonconstant integer indexes are no longer rejected solely because their declared subrange extends beyond those bounds; their actual computed value remains bounds-checked at runtime.
- VS Code: Devices & Connections now keeps established degraded and failed links solid while using semantic health styling, and renders configured-only links dashed so line style consistently represents topology proof.
- trust-runtime: registry publish help now describes standard bundle detection instead of claiming a current-directory fallback.
- trust-runtime:
hmi init,hmi update, andhmi resetnow preserve standard bundle-detection failures instead of silently treating an undetectable current directory as the selected project. - trust-runtime: versioned deployment now rejects invalid pointer slots before installation, copies enabled ADS and OPC UA client sidecars into the installed bundle, and rejects symbolic links in the copied bundle closure.
- trust-runtime: standalone
ide servenow rejects a non-directory project path before constructing control state or attempting to bind the web server. - trust-runtime: generated runtime templates and shipped examples no longer serialize an empty mesh authentication token that current configuration validation rejects; OSCAT runtime examples also include the required log, retain, watchdog, and fault sections, restoring loadable example projects and offline communication topology.
- trust-runtime: Modbus worker input and output deadline results now preserve
the configured
fault,warn, orignorepolicy; faulted stale input no longer copies a cached snapshot, and timed-out output remains queued. - trust-runtime: direct-address parsing now rejects sized, suffixed, spaced,
and otherwise malformed IEC wildcard forms, plus signed or non-decimal
address components such as
%IW+1, instead of accepting partial or ambiguous locations. - trust-runtime: Modbus
f32point scaling now rejects finite internalf64results that would narrow to infinity in the process image or wire payload. - trust-runtime: MQTT raw reads now select the newest payload drained for a scan, while the default fault policy preserves stale-input, output-deadline, and typed-output preflight failures instead of degrading them to success.
- trust-runtime: OPC UA client configuration now rejects duplicate connection names, empty endpoint authorities, anonymous credential fields, and conflicting access aliases; client PKI operations ignore or reject symbolic links detected at roots, entries, and promotion targets, and one-shot read/write paths now invoke an exact service-result cardinality guard.
- trust-runtime: OPC UA client workers now reject mismatched and stale-session subscription events, preserve newer queued output generations across older completions, reject non-finite scalar outputs before transport, and wake promptly while revoking readable authority during shutdown.
- trust-runtime: ADS server publication now selects bounded symbol sets in canonical-name order, client policy compares parsed exact IP identities and requires explicit opt-in for unpinned entries, and security-first write rejection preserves precise ADS errors without queueing PLC mutations.
- trust-runtime: ADS client workers now reject malformed or miscorrelated handle, poll, subscription, and notification responses before publishing authority; rebuild symbol-derived state only from stable token-bracketed candidates; preserve newer generation-tagged output intent across older completions and error projection; terminally retire only the correlated rejected generation; reject non-finite output values before the shared queue; and revoke cached input authority during prompt shutdown.
- trust-runtime: active ADS device lookup now requires an IP-literal route's host, AMS port, and supplied target AMS identity to identify the same Doctor target instead of accepting a partial endpoint match.
- trust-runtime: the ADS server Doctor now derives health from the live server symbol table, rejects blank external proof and zero handles, requires complete notification samples, and rejects response-direction or endpoint identity mismatches before trusting loopback payloads.
- trust-runtime: ADS client configuration parsing now rejects duplicate connection names, noncanonical target AMS Net IDs, and blank or noncanonical explicit local AMS Net IDs before constructing activation routes.
- trust-runtime: ADS client configuration parsing now rejects zero or non-STRING capacities, overflowing array byte lengths, and index-address sizes that disagree with the declared scalar or array type.
- trust-runtime: ADS client connections now reject noncanonical target or local AMS Net IDs and target AMS port zero before invoking the connector or opening TCP, while preserving cached symbol-handle and subscription state when validation fails.
- trust-runtime: the ADS Doctor now rejects malformed manual endpoints before transport activity, rejects blank or absent explicit symbols at the upload boundary, refuses active-device snapshots for another ADS endpoint, and no longer reports empty, timestamp-less, or degraded live point sets as a passing batch read.
- trust-runtime: ADS diagnostics now fail closed for empty reports and non-blocking failed steps, reject incomplete or role-mismatched TwinCAT production evidence, and require the declared age, clock, deployed-config, and live-status evidence to remain current before reporting readiness.
- trust-runtime: live ADS onboarding discovery now retains distinct runtime endpoints that share one host IP, and guarded write probes attempt and verify restoration after probe-write or read-back failure while preserving any restore failure in the returned diagnostic.
- trust-runtime: Runtime Cloud preflight now rejects empty, blank, or duplicate
target lists instead of allowing an empty dispatch or repeating a target;
cross-site writes in
devandplantprofiles no longer incorrectly require a WAN allowlist, whilewanremains explicit-allow only. - trust-runtime: Runtime Cloud control and I/O proxy planners now reject blank
routing/correlation fields and incompatible I/O proxy API versions before
preflight, returning a
400 contract_violationinstead of manufacturing an invalid action request. - trust-runtime: Runtime Cloud configuration reconciliation now records only the immutable desired snapshot actually applied; a newer desired revision accepted during an in-flight success or failure remains pending instead of being falsely reported in sync or stranded.
- trust-runtime: Runtime Cloud forward-additive schema validation now rejects blank or duplicate field names, zero-sized or overflowing ranges, overlaps, and fields inserted before the prior layout end instead of reporting an ambiguous memory layout as compatible.
- trust-runtime: Runtime Cloud topology no longer reports invented latency or packet-loss values from discovery-only presence, and loopback discovery for one peer no longer classifies unrelated peer runtimes as same-host T0 candidates.
- trust-runtime: Runtime Cloud dual-host HA now detects split-brain candidates across the complete supplied HA group even when an action targets only one candidate, and rejects a duplicate request while its first dispatch remains in flight instead of starting the protected command twice.
- trust-runtime: ADS onboarding now rejects malformed runtime-host and target IP addresses and blank or malformed local AMS Net ID overrides before constructing route authority, and canonicalizes valid six-octet overrides.
- trust-runtime: ADS discovery now preserves distinct PLC runtime endpoints that share one host IP while still deduplicating repeated observations of the same AMS Net ID and AMS port.
- trust-runtime: ADS route planning now encodes generated XML as data before embedding it in PowerShell, preventing route-name here-string breakout, and rejects blank one-shot credentials before calling the route wire.
- trust-runtime: ADS server-control symbol import now confines its three outputs to distinct project-relative non-symlink paths, keeps snapshots in the canonical import cache, and permits follow-up regeneration only when the existing generated ST still matches the current ADS configuration and cached snapshots. Acknowledged Write-only symbols now remain Write-only instead of acquiring a remote Read capability during import.
- trust-runtime: ADS symbol import no longer treats an empty include pattern as
a wildcard, correctly backtracks
*matches to anchored suffixes, and uses the canonical ST lexer to prevent keywords from becoming generated variables. - trust-ads-server: malformed sum-up write and read/write frames are now fully validated before any runtime write is queued, and notification-handle wrap finds a free non-zero handle instead of replacing a live registration.
- benchmarks: mesh query/reply and runtime-cloud dispatch now exercise the
exact
--payload-bytesrequested instead of silently capping at 256 bytes; T0, mesh, and dispatch reports expose the effective payload length. - ADS CLI:
ads server ... --jsonnow rejects control responses withok = falseor a missing Booleanokinstead of printing them and exiting successfully; JSON and human output now share the control-envelope check. - trust-harness: an explicitly empty authoritative
sourceslist now reportsinvalid_argument; failed load/reload attempts preserve the live session, and focused protocol tests lock source precedence, time-alias precedence, zero-cyclerun_until, and stable structural/runtime/boundary error kinds. - docs: the IEC deviations log now contains only concrete normative conflicts
or omissions. IEC-silent runtime/tool/vendor behavior remains in product
specifications, IEC-permitted non-finite
REALchoices are recorded in the decisions log, and the PLCopen LD interchange subset is routed to the PLCopen deviations log. - trust-runtime: rejected online changes and restarts now preserve the live variable/instance image, scan counter, fault state, telemetry scan state, and queued debugger mutations when retained-value migration or restart instance construction fails; warm-restart storage and retained values are staged before the bytecode commit.
- trust-runtime: newly encoded bytecode now preserves the declared IEC resource identity, and named bytecode application rejects an unknown resource instead of silently falling back to a different primary resource while retaining the single-resource legacy-placeholder compatibility path and normalizing it to the requested active resource identity.
- trust-runtime: guided project setup now rebuilds runtime configuration,
generated ST configuration, and bytecode coherently; wizard source discovery
treats paths literally and includes project dependencies; generated resource
names are valid non-reserved ST identifiers; dangling project I/O links are
removed when system I/O is selected; and remote setup fails closed on secure
randomness or token-expiry overflow. Partial browser apply requests now share
the advertised safe defaults and cannot write system-wide I/O unless
write_system_iois explicitly true; interactive browser ports above 65535 are rejected instead of wrapping to another port; and CLI setup rejects browser-only access, bind, port, and token-TTL options instead of silently ignoring them, including explicitly supplied default values. - trust-runtime: runtime and I/O configuration now reject millisecond values that cannot fit the signed-nanosecond duration, typed safe-state integers outside their addressed BYTE/WORD/DWORD width, mixed legacy and multi-driver I/O forms, and blank explicit paths, credentials, scalar selectors, or collection/map entries instead of panicking, wrapping, silently ignoring fields, substituting defaults, or downgrading authentication/source pins.
- trust-runtime:
playnow validates restart and simulation-scale options before auto-creating a selected project; runtime configuration rejects unknown log levels instead of silently treating them asinfo; legacy runtime-root source discovery treats paths literally and recursively; and validation/startup summaries no longer present disabled I/O drivers as active. - trust-runtime: registry publish without
--projectnow preserves standard bundle-detection failure instead of silently substituting a non-project current directory and reporting a later invented-bundle error. - trust-runtime: managed-runtime status no longer reports
stoppedwhen local auth config is invalid or a connected endpoint rejects/mangles the response; lifecycle control now checks response IDs and bounds response lines, read-only status/log calls do not create state, log tailing is memory-bounded by the requested line count, and early child/PID persistence failures are cleaned up. - trust-runtime: HMI scaffolding now discovers compiler project sources and
local dependency sources recursively with literal filesystem paths, so glob
metacharacters in project names no longer produce a false empty project;
wizard Git setup now rejects malformed or dangling
.gitfile markers. - trust-runtime: managed-fleet manifests now reject unsafe, whitespace-padded, or duplicate runtime names, malformed control endpoints, zero ports, and cross-role TCP port collisions before lifecycle mutation; fleet control-token generation now fails closed when operating-system secure randomness is unavailable.
- trust-runtime: deprecated workbench-command aliases no longer let a directory
or non-executable sibling
trust-devshadow a usable PATH command, and Unix signal termination now retains the conventional128 + signalexit status. - trust-runtime: deploy labels can no longer escape the bundle store, automatic labels remain unique within the same second, relative deployment roots and dangling pointers are handled correctly, ordinary files are not overwritten as pointers, external pointers are rejected, and pruning preserves the actual immediate rollback bundle. Deployment summaries now identify the new bundle, detect every runtime and I/O configuration-file change, compare safe states structurally, traverse source paths without glob interpretation, and fail instead of silently discarding source-read errors.
- trust-runtime:
ctlnow keeps the selected project's control token when an explicit endpoint overrides only the endpoint, and exits non-zero for rejected, malformed, oversized, timed-out, or mismatched-ID control responses instead of reporting script success. - trust-runtime: the conformance runner now rejects malformed case IDs,
unknown category directories, case directories without manifests, source
paths that escape their case, and
compile_errorcases that compile successfully instead of silently omitting or blessing invalid corpus entries. - trust-runtime: standalone
ide servenow prioritizes an explicitly selected root runtime, reports malformed primary runtime configuration before binding the web server, and loads the selected workspace runtime's recursive, deterministic build-source set instead of looking only in<workspace>/src. - trust-runtime: benchmark jitter summaries now report zero observations when fewer than two latency samples exist instead of inventing a zero-valued jitter measurement.
- trust-runtime: mistyped top-level commands now receive the correct suggestion
when
--verboseprecedes the command, and the suggestion vocabulary includes the shippedcheckandagentcommand families. - trust-runtime-core: integer operand normalization now has an explicit
fail-closed contract: every integer runtime tag preserves its mathematical
value when representable as
i64, oversizedULINTvalues return overflow, and non-integer tags return a type mismatch. - trust-runtime-core: unsigned integer result materialization now preserves
every representable
USINT,UINT,UDINT, andULINTvalue exactly, rejects destination overflow, and rejects non-unsigned destination tags. - trust-runtime-core: retained snapshot insertion now has an explicit deterministic contract: new names append, reinserting the same resolved name replaces its value in place, and stored runtime values are preserved exactly until the owning persistence or reload boundary validates them.
- trust-runtime-core: task readiness now has an explicit deterministic contract for SINGLE edge sampling, periodic suppression, backward and forward logical-clock jumps, missed intervals, and saturating overrun accounting.
- trust-runtime-core: portable fault-state clearing now has an explicit lifecycle contract: it atomically removes the fault flag and stored error, preserves the configured fault policy, remains idempotent while healthy, and does not itself claim resource recovery or safe-state handling.
- trust-runtime-core: portable fault-policy decisions now have an explicit projection contract for halt, safe-halt, and restart, including the exact safe-state flag and the boundary between returning a decision and executing recovery actions.
- trust-runtime-core: fault recording and fault-policy replacement now have an explicit state-preservation contract, including latest-error replacement and changing policy while faulted without erasing the current fault.
- trust-runtime-core: watchdog policy installation now has an explicit normalization contract: enabled non-positive timeouts floor to one millisecond, valid and disabled timeouts retain their values, and installing policy does not itself execute a watchdog action.
[0.24.54] - 2026-07-20¶
Added¶
- release: added digest-bound artifact provenance and result-derived conformance status assets, plus release checks binding the workspace version, tag, successful workflow, required assets, and GitHub Latest pointer.
- trust-runtime: documented the complete STBC validator-before-apply contract, added product-path malformed-bytecode case execution, and promoted the existing owner, reference, call, jump, stack, schema, checksum, and version rejection checks from quarantine into the regular test suite.
- trust-runtime/vscode: Browser HMI and Devices & Connections now consume the shared connector status and discovery-confidence contract while presenting it with user-facing labels: HMI summarizes connections in one compact header chip, and Network Canvas/Discover use Connection, Health, Verification, and Signals wording instead of backend connector/proof terminology. Devices & Connections now rejects unknown state, health, or confidence values while retaining the affected peer topology and showing the validation error instead of silently dropping all peers.
Changed¶
- branding: replaced font-dependent truST wordmarks and product icons with self-contained path-based assets across the public documentation, runtime Web IDE, and packaged VS Code extension.
Fixed¶
- verification: case-table hashing now canonicalizes Windows CRLF checkout line endings, keeping committed case-file digests stable across runners.
- ci/trust-runtime: the live Modbus+MQTT composition test now retries the transient MQTT snapshot-startup race on non-Linux runners instead of treating the expected freshness delay as a product failure.
- trust-runtime: pending debug writes and active forces are now cleared at runtime fault, stop, and restart boundaries so stale debugger mutations cannot cross a lifecycle transition.
- trust-runtime: pre-commit watchdog and output-handoff faults now restore the last committed output image before applying a partial safe-state map, so unconfigured points cannot receive pending values from the failed scan.
- trust-runtime: explicit conversions to
REALorLREALnow return the stable overflow fault when narrowing, parsing, or IEC bit transfer would synthesize NaN or infinity inside PLC state. - trust-runtime: deterministic simulation-time advancement now saturates at the signed nanosecond bounds instead of panicking or wrapping on overflow.
- trust-runtime: OPC UA server variables backed by periodic PLC snapshots now advertise read-only access and reject client writes instead of acknowledging values that the runtime never applies to PLC storage.
- verification: the runtime communications fuzz gate now selects the live WAN allowlist smoke module instead of exiting successfully after running zero tests; bounded campaigns also reject any zero-test Cargo filter.
- trust-ads-server: restored the command-dispatch cargo-fuzz target after the
symbol snapshot interface moved to shared
Arcownership, so all registered ADS fuzz targets compile and execute under the bounded campaign. - trust-dev: project-scoped commits now abort before mutation when the Git index already contains an in-scope path, while repository-root commits reject any pre-staged path and dry runs leave both the worktree and index unchanged. Project paths must resolve canonically inside the repository, and the index is checked again after interactive prompts so concurrently staged in-scope paths are not absorbed.
- vscode: connector state and health projections now accept only the shared canonical wire vocabulary, so unknown backend values fail visibly instead of flowing into the Devices & Connections UI as an invented status.
- vscode: updated build and test dependencies and pinned patched transitive
versions so the committed extension lockfile passes
npm auditwith zero known advisories; CI and release packaging now enforce the same audit. - trust-lsp: closing an unsaved file-backed document now cancels in-flight
semantic work, restores durable disk contents, and evicts semantic-token and
pull-diagnostic caches; late results are generation-checked under the cache
lock so discarded buffer state cannot survive
didClose. - trust-lsp: invalid incremental edit lines now preserve the last valid buffer and show a full-resynchronization error, while range/on-type formatting uses the shared LF, CRLF, and bare-CR line model instead of an LF-only splitter.
- trust-runtime: MQTT discovery now reports authentication and authorization
CONNACK rejections as
likelyprotocol evidence instead of overclaiming an accepted, confirmed broker session; clean-session DISCONNECT behavior is preserved. - trust-runtime: control requests denied by the reviewed role matrix now return
the stable
insufficient_roleerror code before dispatch; reviewed operation names, fixed minimum roles, and debug-surface classification share one internal registry while unclassified operations remain Admin-only. - trust-runtime: bytecode decoding, validation, VM traps, and runtime failures
now expose stable lower-snake-case machine identifiers without parsing
diagnostic text; HMI type, bounded-string, subrange, and non-finite write
rejections return the matching identifier in
error_codebefore queueing. - trust-runtime: STBC decoding, validation, executable materialization, and VM execution now enforce the documented fixed container, instruction, reference, local, parameter, native-call, operand-stack, call-depth, and per-invocation instruction limits; stack, register, and tier-1 execution charge the same original bytecode instructions across nested calls.
- trust-hir/trust-runtime: implicit numeric assignment now permits only accuracy-preserving widenings, ordinary subrange initializers are checked at both bounds, contextual subrange literals retain their declared runtime tag, binary expressions, function returns, FOR bounds, and call arguments preserve their HIR-resolved or declared numeric type, and crafted primitive-tag mismatches fail before storage.
- trust-runtime: the embedded web server now isolates read-only traffic from a
fixed, bounded body/mutation lane, so incomplete request bodies cannot block
HMI and control reads; saturated lanes fail promptly with HTTP 503 and the
stable
server_busydenial code. - trust-runtime: TON, TOF, and TP elapsed-time accumulation now clamps safely
at
PTinstead of panicking in debug builds or wrapping negative in release builds near the signed TIME/LTIME duration boundary. - trust-runtime: STBC decoding now rejects duplicate standardized section IDs instead of silently selecting the first conflicting section during later validation and execution.
- trust-runtime: bytecode decoding now rejects top-level and nested collection counts that cannot fit within their section payload before reserving the declared collection capacity, preventing tiny malformed containers from requesting disproportionate allocations.
- trust-syntax: malformed
CASE,ELSIF,FOR,WHILE, andREPEATstatements now diagnose missing required delimiters instead of accepting partial syntax as a valid control-flow construct. - trust-lsp: cancelled workspace diagnostic requests now return
ContentModifiedinstead of a successful empty or partial report that could hide diagnostics collected after a newer semantic request superseded them. - trust-lsp: UTF-16 positions now recognize bare-CR lines and keep every CRLF terminator byte at the preceding line end, matching the LSP line-boundary contract for incoming requests and outgoing ranges.
- trust-runtime: debugger statement-boundary pauses no longer count operator dwell time against the active cycle watchdog or output-commit deadline; genuine execution time before and after resume remains bounded.
- trust-runtime: enabling the debug control surface now requires Admin instead of Engineer, and unclassified future control operations fail safe at Admin rather than inheriting Viewer authority.
- trust-runtime: online change now reads and validates the retained snapshot before replacing the live executable or resetting runtime state, preventing a retain-store failure from leaving a partially applied reload.
- trust-runtime: safe-state application now requires confirmed healthy driver
handoff, reports queued/reconnecting worker delivery as unconfirmed, attempts
every configured driver, and prevents a deliberate stop from reporting
Stoppedwhen physical safe-state delivery was not confirmed. - trust-runtime: retained snapshots are now fully validated before any restored global is changed, preventing a later incompatible value from leaving earlier values partially applied after a failed warm load.
- trust-runtime: MQTT typed input-point batches now commit their mapped process-image snapshot only after every payload validates, preventing values from a rejected non-finite batch from leaking into a later successful scan.
- trust-runtime: typed process-image
REALandLREALoutput bindings now reject non-finite values transactionally before changing%Q/%Mbytes or committing output to a physical driver. - trust-runtime: finite
REALEXPandEXPTcalls now fault before assignment when their basic-single result would be NaN or infinity instead of storing a non-finite process value. - trust-runtime: finite
REALbinary arithmetic now faults before assignment when its basic-single result would be NaN or infinity instead of storing a non-finite process value. - trust-runtime:
simulation.tomlnow rejects NaN and infinite coupling thresholds before activating the configuration instead of silently selecting a coupling branch. - trust-debug: managed Live Values writes and forces now parse declared
REAL/LREALvalues semantically and reject NaN, infinities, width overflow, and raw non-finite bit encodings before changing process-image or force state. - trust-hir: cross-file POU imports now preserve
STRING[n]andWSTRING[n]capacities derived from project constants, so equal-capacityVAR_IN_OUTbindings no longer fail as bounded-to-unbounded mismatches. - trust-runtime: file-backed retain snapshots now reject non-finite
REAL/LREALvalues recursively on save and load, preserving the last good durable image and preventing partial application of an invalid snapshot. - trust-runtime: mesh subscriptions now reject integer values outside the subscribed IEC type's range and reject floating-point conversions that would produce a non-finite PLC value instead of silently wrapping or storing infinity.
- trust-runtime: OPC UA client
Float/Doubleinput samples now rejectNaNand positive or negative infinity before cache acceptance, mark the affected point faulted, and leave the PLC target unchanged. - trust-ads-core/trust-runtime: ADS
REAL/LREALclient reads, notifications, arrays, and server writes now rejectNaNand both infinities before cache acceptance, server write queuing, or PLC storage mutation. - trust-hir/trust-runtime: compile-time bounded
STRING[n]andWSTRING[n]literal checks now count Unicode scalar values; call binding rejects width-changingVAR_IN_OUT, and function/function-block output copy-back honors the receiving declaration's capacity. Metadata-free legacy v1.1 bytecode now fails closed when a local string output target's declared type cannot be recovered; compiler and runtime must be upgraded together for this boundary. - trust-runtime: in-process warm and cold restarts now preserve monotonic runtime time while reinitializing timer and task baselines at the preserved value, preventing restart from rewinding simulation time or charging pre-restart elapsed time to recreated timer instances.
- trust-runtime: bounded MQTT and Modbus input workers now preserve the typed
error from a completed protocol read instead of replacing MQTT connection
context and Modbus exception responses with a generic unavailable-snapshot
transport error; MQTT reads without a fresh snapshot report
IoFreshness. - trust-runtime: watchdog faults before physical output commit no longer re-send the pending process-image output when no safe-state outputs are configured; the resource faults visibly without issuing a driver write.
- trust-runtime: TOF and TOF_LTIME now hold
ET = PTafter the off-delay expires whileINremains false, matching IEC 61131-3 Ed.3 Figure 15(c), until a later true-input scan rearms the timer.
[0.24.34] - 2026-07-15¶
Fixed¶
- ci/docs: Browser IDE capture automation now requests the editor session
required to switch the active tutorial project, and code-server captures now
assert the accepted
truST:command category, keeping post-merge docs captures aligned with the shipped authorization and command contracts. - vscode/trust-runtime: Windows ADS discovery now uses the installed TwinCAT router for same-computer port and symbol operations, and Devices & Connections no longer offers an unverified ADS identity as an actionable fallback device.
- vscode: Devices & Connections now renders cached project state before runtime enrichment and schedules polling only after each refresh completes, preventing slow Windows requests from starving the canvas; simulator Start now waits for the real Structured Text debug session and bounds I/O probes so a successful launch is not reported as a timeout.
[0.24.31] - 2026-07-10¶
Fixed¶
- ci: the main-branch version release guard now allows 90 minutes for a matching Release workflow to finish, with a larger job budget and a focused regression test, so healthy cold Windows artifact builds do not fail release evidence at the former 30-minute boundary.
[0.24.30] - 2026-07-10¶
Added¶
- vscode/trust-runtime: Devices & Connections can select any valid ADS server
port before browsing symbols, defaults to PLC port
851, preserves non-851 ports through browse/import/ads.toml, and distinguishes an unavailable ADS port, unsupported Symbol Upload, and an empty or incompatible symbol table.
Fixed¶
- vscode: fixed stale Devices & Connections refresh and discovery sessions so editor-group resize/remounts cannot publish torn capability state or leave old Discover cards actionable; hidden webviews now pause polling and resynchronize when visible.
- vscode: kept Browse-tags symbol selection on accessible native checkbox semantics and added a real X11 quick-click/Space regression journey across multiple Network Canvas refreshes.
- vscode: changed the fail-closed writable ADS import refusal to a complete modal explanation with a Start runtime recovery action.
- docs: Communication overview pages now list Beckhoff ADS alongside Modbus, MQTT, and OPC UA so the public navigation path matches the shipped ADS client/server support.
[0.24.29] - 2026-07-08¶
Added¶
- trust-runtime: added a Viewer-gated
connectors.statuscontrol surface that projects process-image I/O driver health and ADS client/server status into the shared connector status schema without changing the existingstatus.io_drivers,ads.status, orads.server.statusJSON contracts. - vscode: Generated truST projects and bundled examples now hide VS Code's native debug-status selector so the truST sidebar remains the single visible run/debug control surface while keeping the native simulator launch configuration available.
- vscode/trust-debug: Devices & Connections can add browsed ADS tags to a
stopped project through the same deterministic
ads import-symbolspipeline, and simulator/debug launches now load the project's ADS configuration so the generated_qualityglobals prove the live TwinCAT read path in Live Values. - trust-runtime: Modbus TCP I/O can now explicitly select FC01/FC02/FC03 input
reads and FC05/FC06/FC15/FC16 output writes through
input_functionandoutput_function, and can use optional per-point maps with bool/u16/i16/u32/ i32/f32 types, scaling, byte order, and word order while preserving the existing FC04 input-register and FC16 multiple-register default profile. - trust-runtime: MQTT I/O can now use optional typed per-point topic maps with
bool/u16/i16/u32/i32/f32 values, text/JSON/binary payload formats, and linear
scaling while preserving the existing raw
topic_in/topic_outbyte bridge. - trust-runtime: MQTT typed output points can opt into a bounded Sparkplug B
outbound node profile (
spBv1.0, Sparkplug 3.0.0) that publishes NBIRTH, configures NDEATH as the MQTT last will, and emits NDATA metric payloads with Tahu-compatible scalar protobuf encoding. - trust-runtime: added an ignored protocol device-in-the-loop gate for lab EtherCAT, ADS, Modbus, and MQTT targets, including explicit skip artifacts, hardware-required failure mode, nightly/manual workflow wiring, and JSON evidence for topology, discovery, doctor, and broker interop runs.
- trust-runtime: expanded the deterministic conformance suite to v2 with
strings, arrays, structs, enums, nested values, OOP dispatch,
REF_TO, retain/reset matrix, scheduler, and simulated connector-status determinism categories; CI now validates both summary schemas and uploads JSON plus human-readable conformance reports as artifacts. - vscode: added the Libraries surface under Project for reusable ST libraries, including packaged OSCAT and PLCopen Motion curated choices, vendored per-project dependencies, local-folder and Git add flows, expandable function block/function/type browsing, remove/update actions, and shared truST webview theme coverage. The bundled examples now include a portable PLCopen Motion single-axis starter with a vendored motion library and mapped Live Values proof points.
- trust-runtime: added the staged Beckhoff ADS client import workflow with
deterministic
ads.toml+ cached symbol snapshot validation, generated ST value/_qualityglobals, scan-safe background worker/cache handoff,[runtime.ads]bundle loading and runtime scan-cycle wiring, on-change/cyclic ADS notification caching, first-class HMI/debug/historian/OPC UA surface coverage, explicit plain-ADS security warnings and route-write guardrails, CLI failure-mode coverage, the runtime-host/setup/adsonboarding wizard, VS Code ADS commands that reuse the selected Runtime pane context, route artifact generation/removal, remote plain-TCP route-add through the local CLI without forwarding TwinCAT credentials to the runtime, ADS Doctor/status surfaces, multi-target UDP broadcast discovery, liveads browsesymbol listing andads validate --livesymbol validation, guarded Doctor write probes with read-back/restore verification, non-Windows ADS-client StaticRoutes.xml flags, Usermode Runtime route-file discovery, compatible-symbol filtering during live uploads, preview-before-write symbol import, and an offline communication example that compiles without a PLC. - trust-runtime: added Beckhoff ADS server support for exposing selected
runtime globals as ADS symbols through
[runtime.ads_server], including a standalonetrust-ads-serverprotocol crate, fail-closed source-pinned client allowlists, loopback Doctor/status/symbol control surfaces, route artifacts for external clients, audited write-back through a dedicated ADS server write port, online-change symbol-table refresh withSYM_VERSIONbumps, pyads smoke proof tooling, stale snapshot quality reporting, and an ADS server communication example; production readiness still requires a real TwinCAT external-client lab gate. - trust-runtime/vscode: added the native
Structured Text: Communicationsurface with typedcomm.capabilities,comm.schema,comm.apply, andcomm.testruntime control responses, one palette-visible Communication command, hidden-but-compatible ADS command entry points, capability-backed protocol cards, runtime-owned I/O driver setup forms, restart-required apply feedback, explicit multi-instance add/update controls, public-docs actions, rendered card/status/intent UX polish, bounded Modbus/MQTT connection tests, and secret-channel blocking for unsafe remote plain-TCP setup. - vscode: added the
Structured Text: Network Canvasfirst-run topology surface with a zero-config simulator path, intent chooser, guided simulated device setup, strict-CSP rendering, and real runtime-backed first-green states: the canvas now starts the local simulator through the shared runtime lifecycle service, keeps runtime/device nodes pending until status and I/O values are proven, and renders explicit retry/port/log fallbacks for startup failures. The canvas add flow now renders schema-driven setup for the I/O driver family (Modbus TCP, MQTT, EtherCAT, GPIO, simulated, and loopback) through the runtimecomm.schema/comm.apply/comm.testcontract, supports multi-instance add/update/remove controls without overwriting existing drivers, and adds searchable palette/quick-add/template paths, click-to-pin focus, keyboard navigation, fault navigation, terminal overflow handling, and per-edge communication roles. The canvas renderer is split into stage, graph, presentation, and style modules, and the runtime now exposes a Viewer-gatedfleet.topologycontrol surface so VS Code can render Host -> Runtime -> Endpoint fleet topology, degraded endpoint faults, and per-edge mesh/runtime links from runtime evidence instead of fabricated canvas state. The fleet surface now uses a dedicated responsive layout with full-width host/runtime cards, wrapped endpoint labels, a non-overlapping status/footer bar, and search dimming that never changes raw endpoint health or fault rollups; its wide and narrow webview states are now guarded by a headless geometry overlap check wired into the VS Code test script. Thefleet.topologyschema now reports real discovery-backed peer hosts, service-bound liveness evidence, per-endpoint live I/O snapshots, MQTT shared broker nodes, host/container facts, and an additivediscovered[]feed without serializing configured secrets. The fleet contract now emits schema version 3 with explicit link IDs and per-link roles, an ADS client endpoint for[runtime.ads], and configured counterpart nodes/links for ADS targets, Modbus TCP devices, and EtherCAT segments so the canvas no longer needs to infer or fabricate those graph relationships. - trust-runtime: added offline
trust-runtime comm schema,comm topology, andcomm applyCLI commands so VS Code can inspect and edit communication settings from project files without a running runtime; offline topology now includes redacted endpoint params for setup prefill while keeping live status non-green until runtime evidence exists. - trust-runtime: advanced the Communication/Network Canvas setup contract to
comm.schemaversion 4 with device-first protocol categories, config-home metadata, ADS/ADS-server schemas, and universal file-writingcomm.applybehavior forio.toml,runtime.toml, and ADS runtime setup instead of detached paste snippets. - trust-runtime: made OPC UA wire/server support part of the default runtime
build and added
comm.schemaavailability metadata plus a schema-vs-capability regression gate so user-visible default communication protocols cannot be advertised without being compiled into the runtime for the current platform. - trust-runtime: added offline
trust-runtime fleet runtime addandtrust-runtime fleet listcommands for Network Canvas host-level runtime setup; the newfleet.tomlmanifest registers sibling runtime projects, creates loopback-only control/web endpoints with per-project control tokens, and keeps newly-created runtimes offline/configured until they are actually started. - trust-runtime: added
comm.discoverandcomm.browse_symbolscontrol/CLI surfaces for the Network Canvas Browse/Connect flow; the first slice can discover ADS targets, truST runtimes, connect-only Modbus TCP endpoints, and targeted MQTT brokers, returns an honest server-only warning for OPC UA instead of orphaned client candidates, and can browse ADS symbols as a generic tree while reusing the existing ADS import/generate-ST candidate payload. - trust-runtime: completed the Network Canvas Browse/Connect backend by adding
offline local-global symbol browsing for OPC UA server, ADS server, and
OpenOT expose pickers; exact selected-tag
ads.import_symbols.applywritesads.toml, cached symbol snapshots, and generated ST through the existing ADS import pipeline; EtherCAT endpointchildren[]/channel browsing now come from configured modules; and runtime-origin EtherCAT/GPIO discovery now reports real observed bus/modules or GPIO lines without write probes. - trust-runtime: made ADS client wire support and the ADS server part of the default runtime build so normal release binaries include the ADS functionality validated with TwinCAT, without requiring custom feature flags.
- trust-runtime: added
trust-runtime check --json/--cifor VS Code's real Check program backend; it validates project config and compiles sources to bytecode in memory without writingprogram.stbc, returning structured issues for editor rendering. - trust-runtime: added managed local-runtime lifecycle commands under
trust-runtime fleet runtime start|stop|status|logs; they build registered sibling projects before launch, run the local runtime as a background process with captured logs, report reachability through the control endpoint, and stop through the existing runtime shutdown verb. - trust-runtime: added the OPC UA client backend for Network Canvas peer links,
including the
opcua_clientschema,opcua_client.tomlfile writer, targeted discovery/test/browse flows, reversible client certificate trust-store CLI, offline non-green fleet topology, and scan-cycle read/write polling that only reports live status after real OPC UA reads.
Changed¶
- vscode: normalized simulator copy across the sidebar, Devices & Connections,
Live Values, new-project template, and bundled examples so the local runtime
appears as
Simulatorand its local endpoints appear asSimulated I/O/Loopback I/Oinstead of mixingtruST runtime,local simulator, and bare protocol names. - vscode: normalized online-change wording across the sidebar command, command
palette, language-model tool, toasts, and runtime feedback so the user-facing
verb is consistently
Updateinstead of mixing update and hot-reload terms. - vscode: clarified Examples Gallery badges and filters so hardware-required
starters use the shared warning role while category chips display readable
labels such as
ADS,Raspberry Pi, andEtherCAT. - vscode: clarified Libraries row actions and counts by renaming read-only
library access to
View source, showing versioned update buttons such asUpdate to 0.1.0, and using singular/plural symbol counts consistently. - vscode: aligned Devices & Connections runtime setup panes with the shared
inspector breadcrumb so
Set up runtimeandConnect existing runtimeread as part of the same right-pane shell. - vscode: changed Devices & Connections endpoint edit breadcrumbs from role
badge wording such as
CLIENT editto task wording such asEdit Modbus TCP. - vscode: styled the open project name in the truST sidebar as a project identity row with a folder icon so lowercase project names no longer read as placeholder copy.
- vscode/runtime: removed the unfinished 3D twin prototype, typed LM tool, packaged WASM/assets, scene-page HMI contract, and related activation/build and test wiring from the shipped product surface so the PLC IDE focuses on the accepted Run, Devices & Connections, Live Values, HMI, and visual-editor workflows.
trust-runtime test,trust-runtime docs,trust-runtime commit, andtrust-runtime agent servewarnings now include thetrust-devcanonical command and a removal-not-before date of 2026-10-05; actual alias removal requires a separate behavior-change release note.- GPIO I/O now defaults new configurations to the Linux GPIO character-device
backend (
libgpiod/chardev) while keeping legacysysfsselectable, and setup surfaces expose the GPIO chip path alongside the existing line mapping help.
Removed¶
- trust-runtime: removed the abandoned digital-twin/world proof stack, including
the Scena/URDF robot-arm dependencies,
RobotP3MinimalArmbuilt-in function block, world-smoke fixtures, and transform-handoff lint script. The release keeps the normal simulator/runtime surfaces and no longer ships a digital-twin or trust-twin runtime path.
Fixed¶
- ci: release tests now tolerate loaded matrix runners for MQTT/Modbus scan-bound timing, runtime fail-closed state propagation, Windows index-cache mtime fixture updates, and Windows ADS diagnostics fixture line endings.
- ci: Modbus write-failure policy tests now use a deterministic local Modbus exception response instead of assuming a fixed localhost port is closed on every hosted runner.
- trust-runtime: Web IDE workspace path validation now rejects Windows rooted paths with the same absolute-path denial used on Unix, keeping release tests and browser-facing file-scope errors consistent across platforms.
- release: Windows packaging now installs Strawberry Perl before building vendored OpenSSL so release artifacts use the same prerequisites as CI tests.
- ci: Ubuntu release test jobs now serialize all-target Rust linking to avoid
hosted-runner
rust-lldbus errors before the test suite starts. - trust-runtime: Windows
trust-runtime.exenow links with a larger stack so CLI invocations such as ADS route and validation commands do not overflow the default Windows executable stack during release tests. - ci: Windows test jobs now install a full Perl toolchain before building vendored OpenSSL so cross-platform release gates do not fail on missing Perl modules.
- ci: macOS release tests now tolerate
/private/vartemp-path aliases and filesystems that reject invalid non-UTF8 fixture names. - ci: the regression-test-first guard now recognizes Rust module files named
tests.rsas focused regression-test evidence. - docs: public docs release gates now include the checked-in VS Code screenshot inventory entries, current regenerated PlantUML SVGs, and search-regression scoring for the intended VS Code, runtime-panel, and observability pages.
- build/source:
trust-runtimenow uses pinned public Git dependencies for the OpenOT Rust crates, so fresh clones and GitHub source archives can build without a pre-existing sibling../open-ot-refcheckout; the source-build guide now documents that the checkout script is only needed for OpenOT IEC examples and tests that read the ST source package. - vscode: Ladder visual editor right pane now matches the other visual editors' empty-property treatment and no longer repeats passive Rungs/Pan guidance that is already shown in the status bar.
- vscode: Statechart visual editor now keeps added states and transition labels inside the visible canvas at normal sidebar widths and keeps animation CSS out of node text.
- examples: the OpenOT multi-program example now includes a simulated
io.tomlso the normal project Compile/Run workflow can validate and launch it. - vscode: native Settings now contributes product-language
trust.*setting keys with legacytrust-lsp.*fallback, so first-user Settings labels no longer render under the internalTrust-lspheading. - vscode: Compile now distinguishes missing
trust-runtimebinaries from incompatible runtime check-report versions, with short product-language recovery text for Runtime path/update actions. - vscode: the truST sidebar now treats unreadable project manifests as a repair/no-project state instead of showing Target/Compile/Start controls for a project that cannot load.
- trust-plcopen: PLCopen import now accepts Structured Text POUs that include
benign body-level metadata such as
addData, while still rejecting graphical or unknown non-ST bodies with named diagnostics. - trust-syntax: ST exponentiation now follows IEC 61131-3 Ed.3 Table 71
left-to-right equal-precedence evaluation, and unclosed function-call
argument lists now report the missing
)diagnostic. - trust-dev:
trust-dev testnow discovers.stand.poufiles with mixed-case extensions under literal project paths containing glob metacharacters, andtrust-dev commitnow uses OS-safe scoped git pathspecs so unrelated pre-staged files outside the PLC project are not committed. - vscode: the MQTT Add form now exposes the runtime
On errorpolicy so users can choose scan-tolerantwarnhandling for local broker startup instead of editingio.tomlby hand for the successful MQTT use-in-ST path. - trust-ide/trust-lsp: rename now exposes structured refusal reasons and rejects unsafe renames that would capture references through local shadows or create imported-symbol origin conflicts instead of returning behavior-changing edit sets or silent no-op protocol responses.
- trust-lsp: references and workspace-symbol requests that stream partial results now return an empty final response instead of duplicating the streamed locations or symbols in the final payload.
- trust-lsp: cancelled diagnostics requests now fail closed at the protocol
boundary: pull diagnostics return
ContentModified, and push diagnostics skip publication instead of transiently clearing real errors with an empty successful diagnostics set. - trust-lsp: inline runtime values now bound runtime-control connect/read/write waits and return an empty inline-value result on timeout or transport failure instead of parking an LSP worker on a silent endpoint.
- trust-lsp: LSP position handling now advertises UTF-16 and maps hover ranges, incremental edits, diagnostics, refactor edits, and semantic-token start/length fields through UTF-16 code units so supplementary-plane characters no longer desynchronize editor buffers.
- trust-lsp: EOF positions on documents that end with a newline now round-trip through the line index without panicking, fixing inline-value requests whose range ends at the final empty line.
- trust-lsp: HMI TOML pull diagnostics no longer compile a runtime from
disk-only ST sources, so open buffers are not contradicted by stale
unknown binding pathwarnings; the pull path now keeps cheap TOML parse and local widget-property diagnostics only. - trust-ide/trust-lsp: semantic tokens, document highlight, references, struct-field go-to-definition, and call hierarchy now avoid repeated syntax-tree and whole-workspace scans on interactive paths, keeping large-workspace editor requests bounded by the new perf locks.
- trust-runtime: VM process-image stores and function/function-block parameter
copy-in now materialize HIR-legal numeric and bit-string widening into the
declared target tag, so assigning
INTvalues intoREAL/DINTstorage no longer leaves narrow runtime tags that compute integer division or overflow at the wrong width; bytecode VM function-blockSTRING[n]input and in-out parameter copy-in now also honors the declared bound instead of storing over-length strings in the FB instance. - trust-runtime: runtime writes to declared subrange storage now fail visibly
instead of committing out-of-range values across bytecode VM assignment,
function-block/function parameter copy-in, dynamic
REFwrites, HMI/control writes, and retain reload. - trust-runtime: bytecode encoding now rejects manually-mutated
VAR_ACCESSbindings that shadow globals, matching the source-level HIR diagnostics instead of silently rebinding bare global names to access-map targets. - trust-runtime/trust-hir: OpenOT authoring now reports explicit
sourceidownership collisions consistently in HIR and runtime validation, andCompileSessionfails closed on OpenOT instrumentation validation errors instead of building bytecode from uninstrumented source with zero telemetry. - trust-lsp: closing an unsaved file-backed dependency now restores disk truth for cross-file analysis, memory-budget eviction no longer removes closed files from the semantic project, and the persistent index cache verifies disk content before accepting same-size/same-second metadata hits.
- ci/build: Unix runtime signal-shutdown helpers and their unit-test fixtures
are now cfg-gated so Windows warning-deny and pre-push
trust-lsptest compile checks do not fail on unused Unix-only signal code. - trust-hir/runtime:
REF()now rejects function and method return variables before lowering, preventing source programs from persisting frame-local return slot references into longer-lived storage. - trust-runtime: bytecode validation now rejects crafted modules that persist
frame-local reference values into longer-lived storage or mix multiple baked
instance owners in one POU body, and catches operand-stack underflow,
non-empty POU exit stacks, known BOOL operands to arithmetic opcodes, and
incompatible constant stores to typed
STORE_REFtargets, invalid parameter direction metadata, and expression arguments bound to target-onlyOUT/IN_OUTparameters, legacyCALLopcode bodies, and inconsistent operand stack depths at control-flow joins before malformed bytecode can be loaded or executed. - trust-runtime: bytecode lowering now fails closed for unsupported statement
paths instead of silently emitting
NOP; label-only statements remain the explicit no-op path. - vscode: visual ST companions and runtime wrappers now map raw visual-editor
direct I/O operands through generated
ATglobals instead of emitting raw%I/%Q/%Mreferences inside function-block bodies, so the bundled visual examples keep compiling under the fail-closed bytecode lowering contract. - trust-runtime: hardened scheduler and process-image failure handling so resource-cycle panics become visible faults instead of silently killing the scan thread, enabled watchdog policies arm VM execution deadlines and reject zero-timeout scan bricking, Modbus/EtherCAT/MQTT non-fault I/O policies degrade health without faulting the resource, requested stops apply safe outputs and surface retain-save failures, and direct process-image writes are capped at a 16 MiB per-area limit.
- trust-runtime: moved Modbus TCP and MQTT wire/broker waits behind
protocol-owned workers so scan-cycle
read_inputsandwrite_outputsuse bounded snapshot/handoff state instead of blocking on delayed protocol responses, reconnect backoff, or missing fresh broker payloads. - trust-runtime: bounded EtherCAT hardware construction failures after the
ethercrab
PduStoragesplit boundary so a missing/unavailable adapter reports a terminal driver-rebuild-required state instead of repeatedly allocating storage during deferred retry windows. - trust-runtime: tightened runtime correctness boundaries by preserving TP
elapsed time at pulse expiry, enforcing bounded
STRING[n]ingress for assignments, retain reload, HMI writes, and process-image writes, rejecting non-finite REAL/LREAL ingress through HMI, string conversion, raw typed I/O, Modbus maps, and MQTT maps, releasing ADS notifications across symbol-version refresh/drop paths, treating rejected OPC UA point writes as point faults without reconnect churn, and validating online-change task metadata before resizing process images. - ci: restored OpenOT sibling checkout in the Nightly Reliability and HMI Long Soak workflows so path dependencies resolve in long-running reliability gates again.
- ci/release: pinned OpenOT checkout, added release tag/CI preflight, changelog-backed release bodies, SHA256 checksums, cargo-deny/cargo-audit supply-chain gates, and conformance/architecture-safety coverage in the release gate report.
- docs/tooling: made
AGENTS.md,CLAUDE.md, and repo skills the shared versioned agent rule source, added Claude/Codex permission guardrails, and fixed stale HMI and IEC instruction paths. - vscode: the Libraries symbol browser now searches and pages through the full library symbol list, shows declaration detail for the selected symbol, and can insert or copy a declaration snippet instead of rendering a capped list of inert chips.
- trust-runtime/vscode: GPIO setup fields are now conditional on the selected backend, so the default libgpiod form hides legacy Sysfs base settings while sysfs mode hides the libgpiod chip field.
- vscode: Devices & Connections server inspectors now show the server endpoint and exposed globals after OPC UA/ADS expose flows, preserve ADS pinned-client config while reapplying expose changes, and carry ADS live client-count evidence through to the inspector when available.
- vscode: Devices & Connections runtime nodes now project the shared selected
run target back onto the canvas and inspector with a neutral
Run targetbadge, soSet as run targetgives persistent visible feedback. - vscode: visual editors now distinguish
Preview STfromGenerate ST, with tooltips that state whether the action only previews generated code or writes a companion.stfile. - vscode: Devices & Connections discovery now treats reachable
Onlineruntimes as valid EtherCAT/GPIO scan origins instead of leaving hardware scans disabled until a debug-attachedrunningstate appears. - vscode: visual editors now share keyboard/canvas chrome: Blockly toolbox focus uses the truST focus ring, and SFC/Statechart expose the same themed canvas zoom controls as Devices & Connections.
- trust-runtime/vscode: stopped/configured Devices & Connections nodes now use one consistent "runtime is not running" state phrase across runtime and endpoint inspectors instead of mixing project-file and live-health wording.
- vscode: Devices & Connections now preserves the stopped project runtime when a managed local runtime starts, so the topology updates state in place instead of silently removing a runtime node the user just saw.
- vscode: HMI Process SVG previews now inherit the shared truST theme roles, so generated process diagrams no longer render as a white slab inside the dark VS Code theme while remaining readable in Light Modern.
- trust-runtime: Browser HMI cards now suppress standalone unit rows for
widgets whose renderer already shows the unit inline, avoiding duplicate
%/barlines under setpoint and readback values. - trust-runtime: Browser HMI now separates passive header status chips from clickable header actions, including moving the theme toggle into the action group and giving actions visible button affordance.
- trust-runtime: Browser HMI alarm banners and alarm tables now use configured alarm labels while keeping the raw signal path as tooltip detail, so operator alarms do not expose variable-name labels when a human title exists.
- vscode/trust-debug: starter projects and bundled examples now include a
native
truST Simulatorlaunch configuration, and Structured Text debug stack frames display workspace-relative source names while keeping absolute paths for navigation, so the Run and Debug view no longer showsNo Configurationsor long local paths during first-use debugging. - vscode: Devices & Connections now exposes a first-class
+ Addtoolbar action that opens the device/connection picker for the selected/default runtime, instead of teaching first-time users to discover the primary add flow through Edit mode. - vscode: Devices & Connections discovery now disables EtherCAT/GPIO hardware scans with an inline reason until a running runtime is selected, and empty scan results explain concrete recovery checks instead of vague runtime plumbing.
- vscode: Browse tags/nodes now clears stale selections when browse results disappear and keeps Add/write controls neutral-disabled with a visible reason when there is nothing valid to add.
- vscode: OPC UA browse authentication warnings now include an inline
Edit credentialsrecovery action that reopens the connection form with the failed endpoint values prefilled. - vscode: SFC diagrams now use clearer IEC-style initial-step and transition markers so the visual editor reads as SFC instead of a generic flowchart.
- vscode: Ladder diagrams now render symbolic names above mapped
%I/%Q/%Maddresses and use neutral edit-time strokes, reserving status colour for live power-flow. - vscode: visual editor right panes now share the same Tools/Edit/View section order, with Fit View in the pane and Show Code labeled as a generated-ST preview instead of a save action.
- vscode: invalid SFC, Statechart, and Blockly visual-editor files now show an
Open as textrecovery action so users can fix malformed JSON directly in the default VS Code editor. - vscode: Blockly now uses a truST-themed workspace, toolbox, flyout, and muted block palette derived from the shared webview design tokens instead of raw default Blockly hues.
- vscode: Advanced integration cards in the Devices & Connections Add picker
now use intentional badges (
MESH,OT,RT,CLOUD) instead of truncated protocol fragments. - vscode: Simulated and Loopback endpoint node titles now use matching local I/O
naming, leaving the
I/Orole label to the node band. - vscode: Boolean fields in Devices & Connections protocol forms now render as native checkboxes with On/Off labels instead of dropdowns.
- vscode/runtime: generated protocol-form copy now preserves acronym casing in empty states, avoids schema-defaults wording for saved secrets, and the Libraries surface pluralizes symbol counts correctly.
- vscode: Devices & Connections now lands on the saved endpoint after a successful add-device Save, keeps the success result visible, and resolves the selected node from the refreshed topology when the apply response omits an instance id.
- vscode: Devices & Connections draft/advanced mesh fabric nodes now use one
muted draft treatment, with an opaque label knockout and separate
DRAFTchip so dashed wires never read as live state or run through text. - vscode: Compile diagnostics now use the user-facing
truSTProblems source label instead of the staletrust checkname, and Compile resolves the nearesttrust-lsp.tomlfrom the active file before falling back to the workspace root. - vscode: Devices & Connections inspectors now render lifecycle details as a
single Title-Case
Staterow, hiding backendmode/status/detailduplicates unlessModeadds real target context such as Simulator or Managed. - vscode: Blockly editor status now counts all visible Blockly blocks from the live workspace instead of reporting only serialized top-level stacks.
- trust-debug/trust-runtime: debug Variables and Watch/Evaluate output now show
function block/program instance type names instead of leaking raw
Instance(id)runtime handles. - trust-runtime: ST test assertion failures now format expected and actual
values as user-facing ST values instead of leaking Rust enum wrappers such as
Int(1)orReal(1.5). - vscode: refreshing the native Test Explorer now preserves pass/fail state for unchanged Structured Text tests instead of clearing the gutter/status evidence immediately after a run.
- vscode: Live Values now shows a
Forced (N)filter chip when overrides are active, letting operators list only forced points before releasing or inspecting them. - vscode: Live Values now explains the force ceremony in-panel, distinguishing
immediate simulator forcing from the managed/remote
Arm forceflow so the two target types do not look inconsistent. - vscode: Devices & Connections and Live Values now reserve red alarm styling for real failures: stopped local runtimes render as neutral guidance, local socket paths are humanized, and viewer-role permission guidance uses the warning treatment instead of the danger treatment.
- vscode: shared webview theme tokens now handle VS Code High Contrast and High Contrast Light explicitly, so visual editors, Devices & Connections, and other product webviews no longer render as dark/navy or white forced-colors islands under high-contrast captures.
- vscode: Devices & Connections now keeps simulator-mode topologies under one
Simulatorruntime and refits populated remote/auth-failure graphs, so the canvas, summary, and status bar no longer contradict each other during GPIO scans or token-error recovery. - vscode: the extension test runner and development binary resolver now honor
CARGO_TARGET_DIRwhen locating builttrust-lsp,trust-runtime, andtrust-debugbinaries, keeping isolated remote-builder gates aligned with warmed target-cache builds. - vscode: Live Values now renders row Write/Force controls as quiet secondary actions, marks active forces with amber warning treatment instead of success green, and keeps forced rows readable by removing the stale editable value control while showing a disabled Write reason plus Release.
- vscode: Structured Text snippets now prefer identifier-friendly completion aliases while preserving the readable hyphenated aliases, making snippet completions deterministic in the extension test harness.
- trust-runtime/vscode: Live Values snapshots now come from one final post-logic runtime scan with per-snapshot force markers and a visible scan number, preventing stale rows from being decorated with newer force state.
- trust-debug/vscode: running-simulation updates now suppress pre-scan reload I/O events and the conveyor example no longer overlaps bit and word outputs, keeping Live Values update evidence logically consistent.
- vscode: Live Values now auto-expires short success feedback such as released forces while leaving standing-state banners, including active forces and permission-denied guidance, visible until the state changes.
- trust-runtime/vscode: Live Values rows now carry runtime-owned source provenance when available, so mapped Modbus/MQTT rows show the driver endpoint or topic directly in the panel instead of relying on ST comments.
- vscode: Live Values now labels local control-socket targets as
Local runtime (control socket)while keeping the full socket path in the tooltip, so temporary socket filenames no longer leak into the panel header. - vscode: Live Values now includes a compact DEC/HEX/BIN display toggle for
word-like numeric rows, rendering IEC-style
16#and2#literals without changing write/force parsing. - trust-debug/vscode: Live Values now preserves configured I/O value types from
the runtime snapshot, so REAL, TIME, and fixed-length STRING rows render as
typed values instead of raw DWORDs/bytes and local writes accept
1.5,T#250ms, and text values. - testing: VS Code UX evidence PNG hygiene now strips color/text/time chunks and hashes decoded pixels, so duplicate screenshots cannot bypass review by differing only in PNG metadata. The runtime I/O cycle snapshot test now asserts the post-output-commit snapshot contract with a bounded timeout instead of hanging broad gates when a snapshot is missing.
- vscode: Live Values now explains program-driven outputs and memory rows in the panel and on disabled Write controls, pointing users to Force for simulation overrides instead of leaving greyed actions unexplained.
- vscode: Live Values now maps disconnected I/O transport failures to user-facing reconnect guidance instead of surfacing raw socket or cancellation text in the panel.
- vscode: HMI Preview now refreshes through the shared descriptor debounce when HMI descriptor or process SVG files are edited in an open workspace, keeping the panel current without waiting for a separate manual refresh.
- vscode: Live Values now clears stale connected/value rows as soon as a
Structured Text debug session stops or disconnects, keeps remote
disconnected targets labelled
Not connectedinstead of fabricatingRunning, and retries transient runtime-busy write/force/release requests before surfacing a user-visible failure. - vscode: Devices & Connections now only labels a fleet host as
This computerwhen its reported identity also matches a local interface, so remote runtimes on machines with lab-builder hostnames render as genericComputer <address>headlines instead of misleading local-machine labels. - vscode: Add Device forms now clear stale field validation and hide stale apply banners as soon as the user edits the rejected field, so corrected example values no longer remain marked invalid before the next Save.
- vscode: the
Update running simulationcommand now uses the same compile and config gate as the sidebar button, returning the disabled-with-reason message instead of attempting hot reload from an invalid project. - vscode: Live Values now keeps long signal names inside a capped name column, preserves VALUE/TYPE/STATE/ACTIONS alignment at narrow widths, and exposes the full signal/address in the row tooltip instead of letting names overlap values.
- vscode/trust-runtime: Devices & Connections now renders ADS server allowed
clients as readable pins such as
127.0.0.1.1.100 (from 127.0.0.1)and no longer sends the raw ADS client allowlist objects through the topology inspector payload. - vscode/trust-runtime: Live Values now receives runtime role/capability data from the control channel, disables viewer-role Write/Force/Release controls before any unsafe attempt, and shows a visible engineer-token recovery reason; remote auth recovery now distinguishes missing tokens from rejected tokens, managed Stop clears the selected-runtime status consistently, and Update running simulation reports success or compile-failure recovery without leaking raw source paths.
- vscode: the sidebar Start, Debug, and Update-running-simulation controls now
stay visible but disable with a plain recovery reason when compile or
runtime.tomlerrors mean the action cannot succeed. - trust-runtime: advanced communication setup schemas now use one configured-only note for Mesh, Realtime T0, and Runtime cloud so draft links cannot read like live runtime proof.
- examples: OPC UA server and ADS server expose examples now update the exposed globals from ST each scan so protocol proof can show value changes instead of reading only initializer constants.
- vscode: tightened the first-run/sidebar UX by restoring the distinctive
stacked
tru/STactivity icon, replacing the Project bucket with one compact sidebar control surface, making Start from example the no-project primary path, exposing Libraries as a first-class destination, renaming project validation to Compile, and adding the Examples Gallery surface for curated runnable starters. - vscode: Examples Gallery filters now combine hardware requirements and categories and include a clear no-match reset state, so users can find a runnable starter without scrolling a flat list.
- trust-debug/vscode: internal debug-adapter lifecycle trace now uses internal
DAP events instead of the visible Debug Console, so first-time debugging no
longer exposes raw
[trust-debug]backend logs. - vscode: Devices & Connections runtime inspectors now show a direct
Set auth tokenrecovery action for remote runtime authentication failures, reusing the SecretStorage-backed token command instead of leaving users to infer the fix from Settings. - vscode: Run target labels now keep the remote control port, so two runtimes on
the same host no longer appear as identical
127.0.0.1entries. - vscode: shortened bundled starter descriptions in the Start from example picker so hardware notes and first-run purpose text remain readable instead of clipping in the clean-profile onboarding flow.
- trust-runtime: HMI schema generation now marks only explicitly allowlisted
[write]targets as writable/read-write, so browser HMI slider/toggle controls are enabled for safe configured writes without making the whole HMI writable. - trust-runtime/vscode: Devices & Connections endpoint Disable now preserves I/O
drivers as disabled instead of deleting them, skips disabled drivers at
runtime/deploy validation, keeps disabled endpoints visible and non-green in
fleet.topology, and exposes a schema-driven Disable action in the endpoint inspector with explicit re-enable guidance. - trust-runtime/vscode: MQTT setup validation now reports missing TLS CA paths,
mismatched client certificate/key paths, TLS-field misuse, and broker-format
errors against the relevant setup fields, uses an MQTT port example for
broker errors, and treats the schema's empty TLS ALPN default as empty so
Devices & Connections can highlight the exact field instead of showing only a
generic blocked-save message; MQTT broker Test results now use honest
broker-port reachable/not-reachable wording, keep unresolved broker errors
attached to the Broker field, and render saved MQTT broker nodes from fleet
topology instead of leaving publish/subscribe links hidden. The ADS/OPC UA
server expose picker now strips topology-only evidence fields before saving,
so selected globals are persisted instead of being rejected by
comm apply. - vscode/trust-runtime: Devices & Connections endpoint summaries now expose the existing Communication Test action for testable endpoints, translate structured OPC UA client endpoint failures into user-facing recovery text, classify rejected OPC UA anonymous logins as authentication-required recovery, and summarize configured connections instead of rendering raw JSON blobs.
- vscode: the truST sidebar now distinguishes an open non-truST folder from the no-folder first-run state and offers an explicit "Initialize truST here" action instead of showing the generic create/open/example welcome.
- trust-lsp/vscode: Move Namespace now asks the LSP for the computed workspace edit, applies it through VS Code in a text-edits-before-cleanup order, pre-creates missing target files, and removes an empty pre-created target on failure, so VS Code no longer reports success after leaving an empty target file and unchanged references.
- vscode: the namespace refactor command is now labeled "Move Structured Text Namespace" in the Command Palette and editor code action, so the advanced refactor no longer appears as the ambiguous bare "Move Namespace" command.
- vscode: stale package contribution and legacy Live Values titles now use "Devices & Connections" and "Live Values" instead of the retired "Network Canvas", "Runtime Panel", or "Structured Text Runtime" wording.
- vscode: starting the simulator from the truST Run card no longer auto-opens VS Code's Debug Console with raw adapter logs, keeping the first-run running state focused on product surfaces.
- vscode: attach-mode debug sessions no longer auto-open VS Code's Debug Console with raw adapter logs, keeping managed-runtime Start/Connect flows focused on Devices & Connections and Live Values.
- vscode: managed-runtime Start/Stop in Devices & Connections now refits when endpoint nodes appear or disappear, and managed runtime logs are summarized as readable event lines instead of raw JSON records.
- vscode: Devices & Connections now uses the shared truST webview product chrome for its Add pane, so visual-editor right panes and the device setup pane resolve the same title, panel, input, and button theme treatment across Dark+, Light+, and High Contrast themes.
- vscode: Devices & Connections protocol setup forms now use the same shared truST form chrome as the rest of the canvas, and starting a new add/edit workflow clears stale validation banners so one protocol's failed save cannot leak into the next form.
- vscode: Devices & Connections add/edit validation now shows a concise
field-issue banner that fits the toolbar, and saved Modbus endpoints render
as "Modbus TCP" in the graph instead of leaking the raw
modbus_tcpdriver id. - vscode: Devices & Connections endpoint edit forms no longer reset in-progress field changes when background topology refreshes deliver the same endpoint parameters with a new object identity.
- vscode/trust-runtime: Devices & Connections EtherCAT Browse channels now saves selected PDO channel paths back to the EtherCAT driver config instead of incorrectly routing them through the ADS tag-import flow.
- vscode: Devices & Connections endpoint inspectors now render user-facing
status labels such as "Configured" instead of raw backend health IDs such as
configured_policy. - vscode: visual-editor right panes now use surface-specific titles such as "SFC editor" and the shared truST section/button chrome, removing the generic "Editor tools" title and private Ladder/Blockly panel styling that made those editors look like separate products.
- vscode: Blockly toolbox category labels now use the shared normal foreground theme token instead of accent-button text, keeping the toolbox readable in Light+, Dark+, and High Contrast themes.
- vscode: Live Values now uses the same shared truST product theme roles as Devices & Connections and the visual editors instead of a private color-token layer, so cross-surface color checks compare equivalent UI roles.
- vscode: Devices & Connections, SFC, and Statechart canvas grids now use the same shared truST grid-line role instead of separate raw VS Code color variables.
- vscode: Devices & Connections node-summary actions and shared React Flow canvas controls now use the shared truST product chrome, closing the remaining visual-editor parity gaps in Dark+, Light+, and High Contrast themes.
- vscode: the create-project scaffold now writes a
src/config.stCONFIGURATION that instantiatesMain(RESOURCE + TASK +PROGRAM Main WITH MainTask), so a brand-new project opens clean instead of tripping the W009 "unused program" lint on first open (F-02). - trust-debug: the debug/simulator control server now serves HMI schema and
value requests from the launch project and live debug snapshot, so VS Code's
HMI Preview can render against a running simulator instead of timing out on
hmi.schema.get. - trust-debug: simulator launches now load the project
io.tomldriver stack and direct debug-runtime builds size process images from I/O bindings, so Live Values reflects simulated/loopback I/O updates instead of showing forced outputs that never reach mirrored inputs. - vscode: managed local runtimes now import their generated control token into SecretStorage and attach after a successful Start, so Live Values can read, write, force, and release values without manual token setup.
- trust-debug/vscode: Live Values Force and Release now use attach-safe
debug-adapter I/O requests for managed and remote runtimes instead of
setExpression, and managed-runtime token import now accepts both table and dottedruntime.control.auth_tokenTOML forms without rewriting the same SecretStorage entry on every refresh. - trust-runtime: OPC UA server startup no longer aborts when the first runtime
snapshot is not available yet; the server binds immediately and publishes
exposed nodes after real snapshot evidence arrives. Runtime debug breakpoint
control now accepts project-relative source paths such as
src/main.st. - trust-runtime: OPC UA client node browsing now returns each leaf's raw
node_idplus an apply-ready scalardata_type, and OPC UA client browse/test failures now include structured reason codes for certificate, authentication, reachability, browse-denial, and security-profile errors. Explicit OPC UA client certificate trust now promotes a previously rejected server certificate into the trusted store before reconnecting, so the Devices & ConnectionsTrust certificateaction can recover from the first untrusted browse attempt. - vscode: release packaging now bundles
trust-runtimeinside the VSIX next totrust-lspandtrust-debug, so fresh installs can use offline Devices & Connections configuration without requiring a separate runtime binary onPATH. - trust-runtime: Network Canvas communication setup now labels the ADS client
timing knob as an ADS link update interval instead of the internal "worker
tick" term, and offline fleet topology now uses the OS hostname before
falling back to
local-host. - trust-runtime: ADS live symbol browsing and exact-tag import now treat
symbol-upload no-reply/timeouts after a stale route check as structured
route.status = "missing"responses with anads.route_plan, so the Network Canvas can show "Create route" instead of a raw ADS timeout when the TwinCAT return AMS route is missing. - trust-runtime:
comm.schemanow advertises the implemented Discover/Browse actions for EtherCAT, GPIO, OPC UA expose, and ADS-server expose workflows, andcomm.applynow bootstraps Network Canvas I/O edits from single-driver, empty, or comment-onlyio.tomlfiles by writing the canonical multi-driver form instead of blocking on setup placeholder files. - trust-runtime: HMI persistence reload now parses persisted JSONL trend/alarm records through a tolerant record reader, avoiding a deterministic reload failure observed in the full remote runtime gate while preserving the stored wire format.
- trust-debug: attach-mode I/O forcing now forwards VS Code Force/Release
actions to remote runtimes through
io.forceandio.unforce, and runtime controlio.readnow marks forced rows so Live Values can show real remote force state instead of disabling the workflow. - trust-debug: debug-launched local simulator control servers now receive the
launch project root, letting
comm.applycreate or update the project'sio.tomlinstead of failing Network Canvas easy-setup with "No runtime project root is available for io.toml setup." - trust-runtime: ADS server UDP identify now listens on a wildcard UDP socket while advertising the configured concrete runtime identity, so TwinCAT Broadcast Search can reach truST instead of requiring directed identify or static route-file setup.
- trust-runtime: ADS server UDP discovery now acknowledges TwinCAT Broadcast
Search Add Route requests while keeping actual ADS access gated by the
source-pinned
[runtime.ads_server.clients]allowlist. - trust-runtime: ADS server value reads now zero-pad shorter scalar payloads to the requested read length and accept TwinCAT's zero-payload handle release form, matching TwinCAT symbolic FB expectations without changing symbol-upload or datatype-table response lengths.
- trust-runtime: ADS server subscriptions now accept Beckhoff ADS.NET 7 compact
AddDeviceNotificationregistrations, normalize Beckhoff's 100 ns notification cycle/max-delay values to server milliseconds, and retain the registering ADS port per notification so TwinCAT/ADS.NET router-multiplexed clients receive changed-value notifications. - trust-runtime: ADS server
SYM_UPLOADINFO2responses now zero-pad to the requested read length, matching ADS clients such asads-rsthat read the 24-byte upload-info structure through a larger fixed buffer. - trust-runtime: ADS server
GET_SYMINFO_BYNAMEnow returns the compact index-group/index-offset/byte-size tuple expected by ADS clients such asads-rs, whileGET_SYMINFO_BYNAMEEXcontinues to return the full symbol entry used by browsing clients. - trust-runtime: ADS server now handles
SUMUP_READ_EXbatch reads with per-item result-length metadata, matching the batch-read form used by theads-rsclient backend. - trust-runtime: ADS client
read_writebindings now seed their output-change baseline from the first good remote read, preventing local cold-start default values from being written back before the PLC value has been applied. - trust-runtime: ADS guarded write probes now wait for scan-boundary write application before read-back comparison, avoiding false mismatches against scan-applied ADS servers.
[0.24.24] - 2026-06-09¶
Fixed¶
- trust-hir/trust-ide: IEC Table 39 validation functions
IS_VALIDandIS_VALID_BCDnow type-check and surface in editor completion, hover, and signature help;IS_VALID_BCDacceptsBYTE/WORD/DWORD/LWORDand rejectsBOOL. - trust-runtime:
IS_VALID_BCDnow rejectsBOOL, and runtime bytecode now supports Table 17 partial read/write through properly mapped structuredVAR_IN_OUTreferences.
[0.24.23] - 2026-06-09¶
Added¶
- trust-runtime: OpenOT ST-FB telemetry now supports multi-PROGRAM authoring by
draining a configured
producer_instances = [...]list into one shared ring, with deterministic per-program source ids and collision diagnostics. - trust-runtime/trust-lsp: OpenOT authoring now supports the simple
operator/regulated edge events
operator-action,operator-login,operator-logout, andsecurity-failure, including authResult symbol lowering, boundedcontextRefbindings, runtime ST-FB emission, renderer support, completions, validation, and inlay hints. - trust-runtime/trust-lsp: OpenOT audited value declarations now emit
ParameterChangerecords viaaudit := 'true', with compile-time string width and record-size validation, producer baseline rollback on fail-closed drops, resolved audit rendering, completions, diagnostics, and inlay hints. - docs: added a public OpenOT attribute-authoring guide covering the truST
compiler diagnostics, language-server assistance, generated producer path,
definition-file generation, value sampling policies, and
[runtime.openot]source = "st-fb"runtime configuration. - examples/docs: added an OpenOT multi-PROGRAM project showing two
attribute-generated producers drained through
producer_instancesinto one shared ring.
[0.24.22] - 2026-06-07¶
Added¶
- trust-runtime: OpenOT telemetry can now publish real encoded records from a
configured ST OpenOT producer FB via
[runtime.openot] source = "st-fb"andproducer_instance, while keeping the existing heartbeat publisher as the default. - trust-runtime: ST OpenOT producer telemetry now hands off multi-record
transition bursts through the producer FB
ScanRecordsdescriptor outputs, while fail-closing scans whose published-record delta does not match the descriptor count. - trust-runtime: OpenOT declaration attributes (
{attribute 'oot' := ...}) now lower to the hidden ST-FB producer path for value, state, message, and alarm records, including enum-typed state variables whose HIR enum values populate StateTransition payloads and definition-file enum sets, with a reactor example that emits a UTC-rendered, runtime-clocked typed audit log, definition file, and forced-overflow reconciliation artifact.
[0.24.21] - 2026-06-01¶
Fixed¶
- Runtime bytecode execution now supports IEC Table 17 partial
bit/byte/word/dword access on
BYTE,WORD,DWORD, andLWORDfunction-block instance fields, matching the existing program-variable behavior.
[0.24.19] - 2026-05-23¶
Fixed¶
architecture-doctor --changednow follows the currentcrates/trust-runtime/src/host/...runtime host layout for initializer and evaluation checks.
[0.24.15] - 2026-05-05¶
Fixed¶
- Runtime I/O drivers now fail closed for the covered safety paths: MQTT disconnected/stale reads and publish/connect failures return structured runtime errors, EtherCAT discovery and image-size mismatches fault under every policy, Modbus flush/transport failures and Modbus exceptions remain distinguishable, and GPIO read/write failures are reflected in driver health.
- Retain persistence now writes through a temp file, flush/fsync, atomic rename, and parent directory sync, and retain snapshots now use a length-delimited CRC/trailer-protected codec with legacy v1 read support plus explicit retain migration/orphan events.
- Runtime initialization, evaluator assignments, and queued debug writes now
fail closed for the covered safety paths: default materialization failures
return
InitFailedinstead of becomingNULL, undefined evaluator/debug targets are rejected instead of creating globals, and queued debug write failures fault the cycle visibly. - Interface-typed runtime variables now materialize as explicit
NULLreferences during default initialization, and genericANY_INTcounter slots defer to the call argument type instead of failing runtime startup. - Runtime bytecode lowering now materializes HIR-allowed contextual widening at assignment boundaries using the target runtime type, with proof coverage for function inputs/outputs, assignments, initializers, return values, InOut rejection, and narrowing rejection.
- Runtime audit/event, mesh, debug, and runtime-cloud safety paths now fail
closed for the covered cases: closed audit sinks emit
AuditDropped, closed debug event/log streams fall back to in-memory buffers, mesh snapshot timeouts are errors instead of empty maps, corrupt runtime-cloud config state becomes an explicit error state, corrupt link/rollout state prevents web startup, and disabled debug-control requests return structuredfeature_disabled. FULLMAP-RUNTIMESAFEnow runs as a blocking architecture-doctor/CI gate with zero findings and zero allowlist entries.
[0.24.14] - 2026-05-03¶
Added¶
- Added fail-closed runtime boundary watch envelopes for
trust-harnessprotocol v2 andtrust-devagent harness snapshots. Unresolved watch paths now carry structuredstatus: "error"entries instead of silently becomingNULL. - Added a runtime boundary fail-closed architecture gate wired into
architecture-doctor --full-mapand CI.
Changed¶
trust-harnessnow defaults to JSON protocol version 2 and accepts--protocol-version 1orTRUST_HARNESS_PROTOCOL_VERSION=1for the legacy watch-value map shape during migration.
Fixed¶
- Harness boundary inputs now fail closed: typoed
set_inputnames andbind_directtargets return structured boundary errors instead of creating hidden globals or undeclared I/O bindings. CONFIGURATIONbuilds now reject declared top-levelPROGRAMs that are not bound by the configuration, unless a test builder explicitly opts in through extra program instances.
[0.24.13] - 2026-05-03¶
Fixed¶
- External safety follow-up now prunes the Zenoh default-feature dependency
surface to remove the transitive
rsaadvisory path, removes directrustls-pemfileuse from runtime TLS PEM parsing, refreshes ownedcargo denyadvisory metadata for the remaining OPC UA, tiny_http TLS, and Zenohpastepaths, and records the remaining external unsafe-scanner gap in the architecture follow-up checklist.
[0.24.12] - 2026-05-01¶
Added¶
trust-dev docsnow owns ST API documentation generation, andtrust-runtime docsremains as a deprecated forwarding alias during the product/workbench CLI split.trust-dev testnow owns ST test discovery/execution, andtrust-runtime testremains as a deprecated forwarding alias during the product/workbench CLI split.
Changed¶
- Public docs, CI templates, flake probes, and terminal capture scripts now
point ST test and ST documentation workflows at
trust-dev testandtrust-dev docs.
[0.24.11] - 2026-05-01¶
Added¶
trust-dev agent servenow owns the external agent JSON-RPC server. The legacytrust-runtime agent serveentrypoint remains as a deprecated forwarding alias during the product/workbench CLI split.
Changed¶
- Public docs and terminal capture scripts now point agent automation workflows
at
trust-dev agent serve, withtrust-runtime agent servedocumented as a compatibility alias.
[0.24.10] - 2026-05-01¶
Added¶
- Added a
trust-devdeveloper/workbench CLI binary. The first migrated command istrust-dev commit, and release runtime archives now shiptrust-devbesidetrust-runtimeandtrust-bundle-gen.
Changed¶
trust-runtime commitis now a deprecated compatibility alias that forwards totrust-dev commit, preserving the existing command while the product runtime CLI is split from workbench/dev commands.
[0.24.9] - 2026-05-01¶
Changed¶
- The runtime VM benchmark gate now records per-fixture metrics, confidence
spread, optional baseline compare deltas, and
quick-low-noise/full-low-noiserepeated-run profiles in its artifacts. - The runtime VM malformed-bytecode fuzz smoke gate now records progress-visible logs and summary artifacts for deterministic bytecode mutation coverage.
- The runtime VM default-switch readiness ledger now records production-guard, differential, malformed-bytecode fuzz, benchmark, residual-risk, and rollback evidence without pretending blocked performance thresholds are ready.
- VM function and method local initialization now populates VM frame slots directly for initialized locals, static locals, and function-block local member overrides instead of creating a temporary runtime storage frame on the native-call path.
Fixed¶
- Runtime and constant-expression array repetition initializers now shape arrays
from the expanded value count, so declarations such as
[3(1, 2)]materialize as six elements instead of failing type validation as a one-element array. - Bytecode validation now rejects duplicate
POU_INDEXids and unsupported runtime-only opcodes before VM module construction/dispatch instead of silently allowing map overwrites or late runtime traps. - VM instance-owner inference now scans partial-access opcodes with their correct operand width, avoiding a silent owner-context drop in bytecode that mixes instance references with bit/byte/word/dword access.
- Bytecode validation now rejects POU bytecode that uses a local reference outside that POU's declared local-ref range, so malformed local slots fail before VM frame execution, while still accepting owned derived local path references such as local array elements and struct fields.
[0.24.8] - 2026-04-30¶
Changed¶
- Local Rust test recipes now use the
moldlinker on Linux when it is installed,just test-alland CI no longer runcomplete_programtwice, andjust test-hir-fastprovides a focused HIR refactor loop before the final full workspace gate. - VS Code extension tests now build and resolve the required local
trust-debugadapter and reuse the Linux fast-link Rust wrapper for local Rust build steps. - The default OSCAT OOP example catalog test now keeps the all-example folder,
README, source-layout, and pattern checks in the normal Rust suite while
moving the expensive 98-project
trust-runtime test --projectsweep behind an explicit ignored gate with per-project progress, child PID/elapsed reporting, and timeout diagnostics.
Fixed¶
- HIR validation now reports
UndefinedVariablewhen aVAR_ACCESSaccess path points at a missing target instead of silently accepting the declaration. - HIR constant evaluation now reports
CannotResolvefor ambiguous unqualified enum value names instead of picking the first matching enum member from the symbol table. - HIR alias resolution no longer silently stops after sixteen alias hops, so deep but valid alias chains resolve to their base type.
- HIR call checking now reports
UndefinedFunctionwhen a resolved field or non-callable expression is used as a callee instead of silently returning an unknown type. - HIR global symbol lookup now uses the same first-writer collision policy for normal collector inserts and raw/import inserts, avoiding order-dependent duplicate-name lookup drift.
- Project source registration now rejects explicit
FileIdcollisions instead of silently reallocating a different file identity. - HIR type-check context construction now classifies missing POU owners/scopes
and reports
CannotResolveinstead of silently falling back to global scope. - HIR OOP diagnostics now use a shared extends-chain walker so mixed function-block/class inheritance cycles cannot bypass cycle detection.
- HIR diagnostics now distinguish existing symbols used in the wrong semantic role: values used as types, types used as values, and callables used as variables report primary wrong-kind diagnostics instead of silent unknowns or wrong-reason undefined diagnostics.
- HIR expression checking now applies the same wrong-kind diagnostics to namespace-qualified project imports, so imported types used as values no longer type-check silently through field-expression resolution.
- HIR/parser name handling now treats
GETandSETas contextual names in method/member expression positions, so methods such asGet()can be collected, type checked, and called without fallback to global scope. - HIR expression inference now suppresses numeric/bit/unary/index/dereference
and
__DELETEfollow-on type errors when an operand or indexed base already has a primary unresolved-name diagnostic. - HIR validation now reports unresolved or malformed
VAR_CONFIGtargets explicitly instead of letting invalid configuration entries disappear during collection/validation. - Cross-project HIR symbol imports now report duplicate imported global names instead of silently keeping the first imported binding.
- Cross-project HIR type imports now report cyclic aliases with
CyclicDependencyand avoid degrading the cycle into silent unknown-type suppression. - HIR type checking now reports constant-evaluation failures at array bounds,
subrange assignments, CASE label duplicate tracking, and
SIZEOFtype operands instead of collapsingConstEvalErrorintoNone. - HIR assignment compatibility no longer substitutes missing array element or
pointer target
TypeIds withType::Unknown, preventing malformed type identities from being accepted as compatible. - HIR typed literals now report
UndefinedTypewhen their type prefix cannot be resolved instead of silently inferring an unknown expression type. - Project source registration now preserves noncanonical fallback paths when OS canonicalization fails, preventing lexical paths from silently colliding with existing canonical source keys.
- HIR OOP
EXTENDS/IMPLEMENTSresolution no longer treats a missing owner scope as global scope when resolving inherited or implemented types. - HIR interface conformance now resolves inherited interface members through the owner's namespace scope instead of falling back to a global bare name.
- HIR call inference now preserves ambiguous
USINGresolution as the primaryCannotResolvediagnostic instead of adding a wrongUndefinedFunction. - HIR value/type expression inference now preserves ambiguous
USINGresolution as the primaryCannotResolvediagnostic for value reads, assignment targets,SIZEOF, andNEWtype operands instead of degrading into wrong-reason undefined diagnostics. - HIR standard function type checking now suppresses wrong-reason argument diagnostics when an argument already has a primary unresolved-name diagnostic.
- HIR diagnostics now deduplicate exact duplicate diagnostics by full diagnostic identity, preventing repeated semantic probes from reporting the same primary error multiple times while preserving distinct diagnostics.
- HIR
SymbolTablebroad lookup helpers are no longer public API; external callers now use explicit global, qualified-name, or registered-type lookup contracts. - HIR constant precollection now keys constants by full namespace/POU scope identity, so same-named POUs in different namespaces cannot share local constant values for array, subrange, or string bounds.
- HIR
VAR_CONFIGvalidation now treats duplicate bare program instance names across resources as ambiguous and reportsCannotResolveinstead of choosing one instance and emitting follow-on type/address diagnostics. - HIR member and compatibility resolution for raw
EXTENDS/IMPLEMENTSreferences now resolves bare base/interface names from the owning POU scope instead of falling back to a global bare-name lookup. - HIR analysis now exposes a declaration catalog with qualified names, source identity, semantic roles, project-import origin, translated type identities, and classified OOP reference outcomes; runtime POU registration is driven by that catalog and reports catalog/lowering mismatches instead of silently skipping accepted declarations.
- Runtime compilation now registers namespaced
PROGRAMdeclarations and resolves sibling namespace types for local OOPIMPLEMENTSchecks, so namespaced runtime programs and same-namespace interfaces are handled consistently with functions, function blocks, classes, and type declarations. - Runtime bytecode generation now preserves POU namespace context for sibling function calls, keeps method names owner-local inside namespaced class-like POUs, and exposes bare return-variable aliases for namespaced functions.
[0.24.7] - 2026-04-29¶
Changed¶
- MQTT I/O now supports explicit TLS/mTLS configuration instead of carrying
unused default TLS dependencies:
io.params.tls = truerequirestls_ca_path, optionaltls_client_cert_path/tls_client_key_pathenable client authentication,mqtts://andssl://broker schemes imply TLS, Linux release builds use vendored OpenSSL for the selected native TLS backend, and remote plaintext brokers still requireallow_insecure_remote = true.
Fixed¶
- MQTT TLS fixture paths are normalized before being embedded in TOML so the TLS config tests run on Windows paths as well as Unix paths.
[0.24.6] - 2026-04-29¶
Fixed¶
- MQTT
keep_alive_sis now applied to therumqttcsession options instead of being parsed only at validation time. - Dependency hygiene now has explicit
cargo deny,cargo audit, andcargo machetepolicy evidence: unused direct workspace dependencies were removed, the workspace MSRV was raised from Rust1.85to Rust1.95,timewas updated to the patched0.3.47,ratatuiwas updated to0.30,rumqttcno longer enables unused default TLS dependencies,qrcodeno longer pulls its unused image backend, the optionalopcua-wireadvisory paths are documented with owner/rationale/removal metadata,Cargo.lockis tracked for reproducible CI dependency resolution, runtime modulo checks now use the Rust1.95is_multiple_ofAPI so Clippy stays clean on the new MSRV,third_party/tiverse-mmapis an intentional workspace exclude, and the full-map doctor reports the dependency hygiene status with failing policy fixtures.
[0.24.3] - 2026-04-29¶
Fixed¶
- Parser recovery for malformed aggregate/positional initializers is now bounded by shared top-level scan helpers, preserves following declarations at declaration boundaries, and is guarded by full-map doctor and focused mutation evidence.
[0.24.2] - 2026-04-28¶
Fixed¶
- HIR validation now rejects non-repeat call expressions used as array defaults and validates direct array repetition defaults against the repeated element type, closing focused mutation-testing gaps in default initializer analysis.
[0.24.1] - 2026-04-28¶
Added¶
- Structured Text initializers now support named aggregate defaults for
STRUCT/UNIONvalues at VAR, TYPE, and member-default sites, route those defaults through a runtime initializer service, validate unknown/duplicate fields with stable diagnostics, allow legal function-block member overrides, and exposetrust-runtime bench initfor startup/first-cycle initialization benchmark evidence.
Fixed¶
- VS Code activation no longer writes the default runtime control endpoint into workspace folder settings; runtime panels still use the built-in endpoint fallback when the setting is empty.
[0.24.0] - 2026-04-27¶
Added¶
- OSCAT now ships an object-oriented Components companion package at
libraries/oscat/oop, with narrow interfaces for automation context, unit conversion, filtering, PI/PID/PWM control, hysteresis switching, signal generation, FIFO/stack memory objects, latches/toggles/counters, measuring components, calendar/RTC helpers, selected building-control objects, and a single-output device driver. The package is covered by Structured Text parity tests against classic OSCAT. - The OSCAT OOP example suite now includes 49 classic/OOP comparison
pairs under
examples/OSCAT/<example>/{non-oop,oop}: 27 hand-written process-first industrial pattern scenarios, 20 compact component-composition showcases, and 2 compact pattern showcases. Each example folder has one teaching README explaining the process, the OOP pattern, why it helps, how to reuse it, and when classic ST is better. Each project has Structured Text application code and Structured Text unit tests; OOP projects with communication claims include runtime/IO configuration and README integration maps naming%I/%Qbindings and exposed runtime records. A Rust catalog gate now runs every project and checks that the claimed OOP pattern is present insrc/Main.st, covering Factory, Template Method, Strategy, Mediator, Observer, Composite, Iterator-style traversal, Decorator, Facade, Chain of Responsibility, State, Command/Memento, Adapter, Proxy, polymorphism, and composition. Public docs include a library guide plus the new truST Structured Text naming standard.
Changed¶
- OSCAT OOP now uses the settled
OscatOopdependency alias, concrete types in simple examples,PidGainsplusConfigure(...)for PID setup, clearer PID/calendar/astronomy names, and example coverage forSnapshot()and configuration error handling.
Fixed¶
Pt1Filter.Reset()now reinitializes the underlying OSCATFT_PT1state on the next update, andHysteresisSwitch.Reset()now resets the actual hysteresis state instead of only clearing cached wrapper fields.UnitConverternow delegates Celsius/Kelvin and energy/calorie conversions to classic OSCAT functions/function blocks so the component facade cannot silently diverge from the parity oracle.- OSCAT OOP tests now cover reset behavior, multi-scan PT1 parity,
invalid limit rejection, FIFO ordering, and the OSCAT version through
OSCAT_VERSION(IN := FALSE)instead of a hardcoded value. - Public docs links for PLCopen/Oscat example READMEs now resolve under MkDocs strict mode.
[0.23.0] - 2026-04-26¶
Added¶
- PLCopen Motion now ships a second, object-oriented package at
libraries/plcopen_motion/oop, with ST interfaces and concrete objects foritfAxis, command objects, andMC_OopAxis. The package delegates axis behavior to the classic single-axis PLCopen package, includes deterministic unsupported command-object returns for unimplemented OOP methods, and is covered by Structured Text unit tests plus runtime integration drivers. - The public docs now include a PLCopen Motion OOP library guide and five runnable real-world examples: warehouse shuttle, labeling conveyor, pick-and-place lift, indexing table, and feeder axis.
trust-runtimenow lowers STPROPERTYimplementations into runtime getter and setter calls, so OOP interface references can dispatch property reads and assignments across source files and package dependencies.
[0.22.3] - 2026-04-26¶
Fixed¶
- Enum values loaded from
VARinitializers now compare equal to the same enum variant literal in VM execution, covering unqualified, qualified, and literal-on-left equality checks for init-only state-machine selectors. Runtime enum construction now canonicalizes alias-backed enum values and retained enum state before comparison, preventing stale or case-variant type names from silently turning valid enum equality checks intoFALSE. Runtime compound values now also validate struct field identity and array shape at construction and retain-apply boundaries, so corrupted retained structs or arrays fail with diagnostics instead of being silently coerced or defaulted.
[0.22.2] - 2026-04-25¶
Added¶
- The public docs now include a
One Project, Every Surfaceconcept page that positions VS Code, Editor AI tools, Browser IDE, Browser HMI, CLI/CI, Agent API, LSP editors, and truST Mesh as live surfaces over the same project, with an honest surface capability matrix and AI tooling evidence links. The docs homepage, README, workflow chooser, visual-editor docs, and migration path now point into the same narrative, and the README/homepage use a new one-project surface-tour GIF built from real product captures. Migration ecosystem pages now have canonicalmigrate/*URLs, visual-editor pages avoid misleading empty captures by using accurate diagrams, and project/support pages stay outside the lookup-only Reference route. A full public-doc text pass also removed thin wrapper prose, separated HMI authoring from HMI operation, routed Learning Paths into Program, clarified Troubleshooting vs FAQ, expanded First Project into a real walkthrough, added jargon notes to concept pages, tightened the homepage around one route table plus real product proof, added richer example/category routing, kept AI Assistance grounded in diagram/source evidence, added first-figure proof PNGs for the operator HMI overview, daily-check, alarm, and handover pages, and tightened README platform/status wording including Linux PREEMPT_RT support. The public docs nav has since been collapsed to six user-facing doors (What Is truST?,Install,Program,Run,Hardware,Reference), with tutorials/examples, communication, I/O, HMI authoring, migration, automation, and AI work routed throughProgram; OpenPLC is no longer presented as a standalone truST workflow; dense terms blocks and stock "use this guide when" boilerplate were removed; and the one-project GIF was regenerated at higher resolution without the dark banner overlay.
Fixed¶
- Runtime ST test compilation now reuses expression type information from the HIR analysis pass, avoids repeated full-project symbol-table clones during lowering, and skips shared-global task-hazard scans when a project has no configured tasks. This keeps the full OSCAT timeout regression below the test harness 60-second warning while preserving the runtime timeout budget.
- Public docs IA now removes the duplicate Install/Verify Install route, keeps Reference focused on lookup material by moving specifications higher and hiding contributor-only pages, deletes dead interoperability stubs and orphan/broken image assets, embeds the real one-project surface tour on the docs homepage, and expands the IA checker to block generic filler phrases such as "use this page", "after reading", and "you should be able".
- Public docs visual chrome now uses a dark/light palette toggle, top-level navigation tabs for the six main doors, a shorter GitHub repo label, a distinct accent color, Inter/JetBrains Mono typography, and image overflow safeguards so screenshots stay inside the content column. The docs homepage also switches to a dark-mode wordmark variant so the brand stays legible on the slate palette.
- Public docs navigation now exposes the PREEMPT_RT runbook from the Operate sidebar, reuses the interoperability pages under Migrate without moving their canonical URLs, groups Operate by engineering, target-administration, operator/technician, and fleet-delivery personas, surfaces truST Mesh from Connect, links README start paths directly, and avoids confirmed snippet/list-rendering issues in included public documentation. Concept, Reference, Specifications, and Examples indexes now use clearer task and mental-model groupings, the docs maintenance guide codifies nav, snippet, visual, verification, and proof rules, the SFC profile reference now uses a non-numbered slug to avoid a spec-14 collision, visual-editor and operator pages now include public visuals, the Migrate page includes a compatibility matrix, and the example catalog audit understands PyMdown line-range snippet targets used to skip included page titles. A new docs IA lint now enforces section-index/nav coverage, snippet H1 safety, and CommonMark list spacing, and the internal docs checklist records analytics, versioning, success-metric, accessibility, and mobile-review decisions.
- The automated public-doc capture lanes now produce stable dark-mode browser
and code-server screenshots from real app/theme controls, wait for the HMI
schema/export surface before capturing
/hmi, and install/load the truST VS Code extension through one consistent code-server profile so the command-palette capture stops regressing on fresh CI boots.
[0.22.1] - 2026-04-23¶
Fixed¶
- The shipped
examples/oscat_smokeconsumer now matches the current OSCAT named-parameter surface (DEG,DAYS, andDATA), so the example builds cleanly again instead of failing on staleD := ...calls.
[0.22.0] - 2026-04-22¶
Added¶
trust-runtime agent servenow exposeslsp.ast_canonicalizeandlsp.ast_similarityfor the canonical-AST 5-gram normalization path used by benchmark contamination checks, dedup, and self-improvement diversity gates. The existinglsp.diagnosticspayload now also includes end positions and zero-based UTF-8 byte spans, so benchmark/datagen tooling can consume stable machine-readable diagnostics without brittle text scraping.trust-runtime agent servenow also exposes a statelessharness.executefixture wrapper that loads project or inline sources, runs deterministic harness steps, evaluates output/access/direct-I/O assertions, and returns a compact pass/fail report with reduced failure context for benchmark/datagen callers that need one-shot POU or system-fixture execution.
[0.21.0] - 2026-04-22¶
Added¶
trust-runtime plcopennow imports official TC6 XML multi-worksheet ST bodies in execution order, reconstructs all standard POU interface var sections (VAR,VAR_TEMP,VAR_INPUT,VAR_OUTPUT,VAR_IN_OUT,VAR_EXTERNAL,VAR_GLOBAL,VAR_ACCESS), and round-trips CODESYSaddData/methodmetadata forFUNCTION_BLOCKmethods with deterministic method object IDs/project-structure placement.
Fixed¶
trust-lsppush-diagnostics mode now refreshes dependent open documents afterdidOpen,didChange, anddidSaveevents, so cross-file call diagnostics clear and update immediately instead of staying stale until a reopen/save cycle.- Method-call completion and signature help now resolve
FUNCTION_BLOCKmethods withVAR_INPUTparameters correctly, so formal parameter names are offered forfb.Method(...)calls instead of falling back to the owning instance call shape.
[0.20.1] - 2026-04-21¶
Fixed¶
- Linux
PREEMPT_RTsupport no longer breaks non-Linux cargo builds and warning gates: Linux-only helpers/imports intrust-runtimeare now target-scoped so macOS and Windows builds keep compiling cleanly while the RT posture feature remains Linux-only.
[0.20.0] - 2026-04-21¶
Added¶
trust-runtimenow supports a Linuxruntime.realtimestartup profile forPREEMPT_RTposture, including config-schema support, bundle-template defaults, launcher-side kernel/scheduler/affinity/memory-lock verification, richer runtime status/config surfaces, application-level cyclep50/p95/p99metrics, a dedicated operator deployment guide, a referencesystemdunit template, and a shipped validation script for collecting host/kernel/service evidence alongside release-mode benchmark artifacts.
Fixed¶
- The Linux cyclic runtime no longer rebuilds ready-task and background-program
scratch vectors every scan cycle; those scratch buffers are now runtime-owned
and reused so the
PREEMPT_RTpath reduces avoidable hot-path allocation churn while preserving existing Linux behavior. - Linux
PREEMPT_RTposture verification now reads the scheduler thread's user-space realtime priority from/proc/thread-self/statinstead of the kernel-internal/proc/.../schedpriority field, so correctly-configuredSCHED_FIFO/SCHED_RRdeployments no longer report false priority mismatches. The shipped validation script now also exposes whether a target threshold was declared, records the approximate soak window, warns when aPREEMPT_RTrun falls back to the cycle budget, and fails gated RT evidence runs whencyclictestor an explicitTRUST_RT_CYCLE_P95_MAX_USthreshold is missing.
[0.19.1] - 2026-04-20¶
Fixed¶
- The public docs and repo landing surfaces now lead with the strongest truST
story instead of burying it behind browser-first proof shots: the homepage,
Program In VS Code,Debugging And Runtime Panel, andREADME.mdnow show real desktop VS Code screenshots for the runtime panel, IEC-aware diagnostics, paused debugging, and cross-file rename, while browser IDE/HMI surfaces are clearly presented as supporting views of the same project. - The docs site now ships the truST wordmark, a dedicated favicon, and a teal Material theme instead of the default MkDocs branding, and the public pages were rewritten to remove boilerplate "use this page when..." framing and similar filler so the rendered docs read as product documentation instead of internal notes.
- The public docs landing/start/reference/operator pages now describe truST in direct product language, ship real social metadata for sharing, and replace thin operator checklists with concrete tables, screenshots, and escalation placeholders instead of stub prose.
- The checked-in Browser IDE and code-server public-doc captures now prove a
successful truST state more clearly:
/idewaits for a real build to finish before capture, and the VS Code-compatible proof opens the Structured Text Runtime panel instead of showing only a generic workspace shell. - The docs search regression gate now routes
install mac,first project,ladder, andretainqueries back to the intended public docs pages after the docs IA/spec split, including a restoredStart -> First Projectpage and stronger canonical ranking forruntime.tomlretain/watchdog/fault queries. - The docs-capture code-server launcher now keeps mutable user-data and
extension state inside container-local writable directories, avoiding the
GitHub Actions permission failure that previously blocked the code-server
capture lane on repo-mounted
.cachepaths. - Unqualified enum variant names now also resolve in three contexts outside
CASElabels:VARinitializers (state : Phase := IDLE), the right-hand side of assignments (state := RUNNING), and operands of binary comparisons (IF state = RUNNING THEN ...). Before this change the first form failed PROGRAM init withundefined variable 'IDLE', and the other two compiled but silently lowered the bare variant name as an unresolvedExpr::Name, so the target either kept its previous value or the comparison never matched at runtime. The fix introduces a small harness-lowering helper next to the existingenum_literal_valueused byCASElabels and applies it at the VAR-declaration, assignment, and binary-expression call sites, mirroring the pattern introduced by the unqualified-CASE-label fix. HIR type-check was already accepting all three forms, so this is a lowering-layer alignment only; bare names still follow normal symbol resolution, which means local variables/constants continue to shadow same-named enum members, and qualifiedPhase#Xforms plusCASElabels behave as before.
[0.19.0] - 2026-04-20¶
Added¶
- Public-doc screenshots now have a first automated browser-capture lane:
Playwright regenerates checked-in
/ideand/hmiassets against live truST surfaces, code-server provides a truthful VS Code-compatible proof capture for the public docs,docs/public/assets/capture-inventory.jsonrecords the checked-in completeness contract, and a dedicated docs-captures workflow can refresh those assets onmainand nightly without relying onydotoold. - Public-doc terminal captures now have a first automated lane as well: VHS tapes regenerate checked-in CLI verification GIFs for installation and build/validate/test docs, and the same docs-captures workflow refreshes those assets alongside the browser/code-server captures.
Fixed¶
- The code-server screenshot lane now waits for real editor content instead of
only a tab label, dismisses first-run/sign-in chrome, closes the unrelated
chat side panel before capture, and renames the Browser IDE screenshot to the
truthful "tutorial loaded" state instead of implying a welcome screen that
was not what the image actually showed. The installation docs also no longer
tell users to run the nonexistent
cargo build -p trust-harnesspackage command; they now document the real package/build surface.
[0.18.6] - 2026-04-19¶
Added¶
- The public docs now have a workflow-first entry model instead of a mixed
"one start flow for everyone" structure. Home and Start now route five real
user journeys directly: Program in VS Code, Program in Browser IDE, Operate
in Browser HMI, Automate with CLI / CI / agents, and Maintain an Existing
Project. The site now also includes public
About,FAQ,Contribute,Version History,Hardware Compatibility,API Lifecycle And Deprecation, runbook/operator pages, target-host lifecycle pages, and enterprise-integration routing pages so technicians, maintainers, and GitHub visitors can find the right docs path without repo spelunking. - The docs homepage and repo README now surface the published docs site as the primary entry point, use the truST logo directly, and show real browser captures for Browser IDE and Browser HMI instead of forcing readers through repo file paths or VS Code-only framing.
Fixed¶
- The public docs Pages pipeline no longer hard-fails on runners without
ffmpegwhen all committed screenshot assets are already present, and the cross-platformtrust-runtime agent servecontract tests now normalize OS-specific path spellings (/privatealiases,\\?\\prefixes, native separators) instead of failing on macOS/Windows-only path formatting differences.
[0.18.5] - 2026-04-19¶
Added¶
trust-runtime agent servenow provides an initial external agent surface over stdio JSON-RPC withagent.describe,workspace.read,workspace.write,workspace.project_info,lsp.diagnostics,lsp.format,runtime.build,runtime.compile_reload,runtime.validate,runtime.test,runtime.reload,harness.load,harness.reload,harness.cycle,harness.set_input,harness.get_output,harness.advance_time, andharness.run_until, plus stable error codes for unknown methods, workspace-path escapes, harness state, and boundedrun_untiltimeouts. The runtime-facing methods now reuse the existingbuild --ci,validate --ci, JSON test-output path, in-process Web IDE diagnostics/formatting services, and control-serverbytecode.reloadcontract instead of inventing a second reporting surface.trust-runtime agent serve runtime.compile_reloadnow returns one machine-readable payload fordiagnose -> build -> reload, including diagnostics counts/issues plusruntimeStatus/runtimeMessageand the optional nested build/reload results needed for iterative repair loops.trust-runtime agent serve workspace.project_infonow returns a stable orientation payload for agents, including resolved source roots, source files, local dependency roots, runtime/io config presence, parsed runtime control/web/mesh/discovery summary fields, andtrust-lsp.tomlvendor_profilewhen present.trust-harnessis now a real programmable deterministic executor instead of a minimalload/cyclehelper. It now supportsreload,set_input,get_output,set_access,get_access,bind_direct,set_direct_input,get_direct_output,advance_time,run_until,restart, andsnapshot, all over structured JSON-lines with shared typed IEC value encoding and protocol docs indocs/guides/TRUST_HARNESS_PROTOCOL.md.- A canonical public docs site now lives under
docs/public/with question- driven navigation (start,develop,connect,operate,reference,concepts,examples), a MkDocs + Material site config at repo root, a Pages workflow that builds and deploys the docs site, and a public-doc link checker inscripts/check_public_docs_links.py. - The runnable examples catalog is now aligned to the docs IA instead of
acting like one flat dump:
examples/README.mdnow routes by docs category,docs/internal/testing/checklists/example-catalog-audit.mdrecords keep / tweak / merge / archive decisions,examples/sfc/README.mdandexamples/web_ui_complete_project/README.mdwere added, andexamples/simulate_process/README.mdnow marks that family as archive- candidate rather than a first-stop public example. Kept public examples now also link back to their owning docs category, andscripts/check_example_catalog_links.pyenforces that the curated docs catalog points to real runnable example paths. - The public docs are now materially self-contained instead of mostly wrapper pages: editor selection, protocol matrices, runtime-to-runtime transports, visual-editor guidance, runtime control/debug/operator docs, benchmark reference, vendor-profile example routing, and key protocol pages now render substantive in-site content instead of punting readers to raw GitHub pages.
- Public-doc media is now generated through an explicit pipeline:
scripts/capture-public-docs-visual-editors.shcaptures the visual-editor screenshots automatically,scripts/generate_public_docs_media.pysyncs them intodocs/public/assets/images/, and the Pages workflow now rebuilds when those source screenshots or media-generation scripts change. - The docs Pages workflow now has stricter quality gates: local search regression checks, post-deploy verification of the published site, and automated media synchronization all run as part of the docs build path.
- The public docs now cover more real operator/agent search paths directly in
CI, including
scan cycle,project layout,visual editor,vendor profile,watchdog,fault policy,agent serve,hot reload, and test output format queries, so discoverability regressions are caught earlier. - The public docs now include a dedicated diagnostics reference page covering
current
E...,W..., andI...codes, their default severities, meanings, and first-fix guidance, and docs-search regression coverage now includes code queries such asE001,W003,implicit conversion, andmissing else.
Fixed¶
- Warm restart and live
bytecode.reloadnow preserve compiled instance-backed runtime references across the restart boundary, so instance-backed I/O bindings and the newtrust-runtime agent serveruntime.reloadflow no longer tripnull reference dereferencefaults immediately after a reload. - VS Code language-model tool discovery is now consistent again: the extension
registers the same linked-editing, on-type-formatting, call-hierarchy, and
type-hierarchy tool names that
editors/vscode/package.jsondeclares and activates, and the extension test suite now fails if manifest declarations, activation events, andlm.registerTool(...)registrations drift apart. - The public docs workflow now rebuilds on guide/spec/conformance/media-source
edits instead of only direct
docs/public/**changes, closing the stale-site gap for--8<---included content and generated public assets.
[0.18.4] - 2026-04-18¶
Fixed¶
- The ST compiler/runtime build path now closes the remaining open regression
cases around control-flow and declaration lowering: unqualified enum members
now work as
CASElabels in end-to-end runtime/bytecode builds, aggregate array declaration initializers now parse and execute for explicit, partial, and repetition-count forms, struct-field access through indexed arrays stays buildable on the CLI path, and bareRETURN;now works after assigning the implicit function/method result on that path because the HIR checker tracks definite assignment and the bytecode VM lowersRETURNinstead of rejecting it as a legacy C5 edge case.
[0.18.3] - 2026-04-18¶
Added¶
libraries/oscatnow ships the full manual-aligned OSCAT chapter set (03_data_typesthrough26_list_processing) as the primary OSCAT package, including the OSCAT_BUILDING Chapter 23 control/environmental surface, a full ST conformance project underfixtures/oscat/corethat now passes126tests with0failures and0errors, plus a narrowednegative_public_surfacesentinel that exists only to pin the deliberate upstreamOVERRIDE-> truSTOVERRIDE_3name deviation.- The OSCAT user documentation now matches the library’s current shipped
surface:
docs/guides/OSCAT_LIBRARY_GUIDE.md,libraries/oscat/README.md,docs/README.md, andexamples/oscat_smoke/README.mdnow describe the full package layout, validation evidence, public carrier/data-type surface, and the major function-block families instead of the earlier smaller OSCAT BASIC slice. libraries/oscatnow also ships the full Chapter 6Arrayssurface:_ARRAY_ABS,_ARRAY_ADD,_ARRAY_INIT,_ARRAY_MEDIAN,_ARRAY_MUL,_ARRAY_SHUFFLE,_ARRAY_SORT,ARRAY_AVG,ARRAY_GAV,ARRAY_HAV,ARRAY_MAX,ARRAY_MIN,ARRAY_SDV,ARRAY_SPR,ARRAY_SUM,ARRAY_TREND,ARRAY_VAR, andIS_SORTED.libraries/oscatnow also ships the full Chapter 4Other Functionsslice:STATUS_TO_ESR,OSCAT_VERSION,ESR_COLLECT,ESR_MON_B8,ESR_MON_R4, andESR_MON_X8.libraries/oscatnow also ships the full Chapter 5Mathematicssurface, including the remaining error/distribution helpers (ERF,ERFC,GAUSS,GAUSSCD), integer/sequence/fraction helpers (EXPN,FACT,FIB,GCD,REAL_TO_FRAC), transcendental helpers (GDF,GOLD,LAMBERT_W,LANGEVIN,SIGMOID,SINC,SQRTN,TANC), rounding and window helpers (RND,ROUND,WINDOW,WINDOW2), and the OSCAT random helpers (RDM,RDM2,RDMDW).libraries/oscatnow also ships the first Chapter 3 data-type slice:CALENDAR,COMPLEX,CONSTANTS_LOCATION,CONSTANTS_SETUP,ESR_DATA,FRACTION,HOLIDAY_DATA,REAL2,SDT,TIMER_EVENT, andVECTOR_3, plus the preloadedMATH.FACTS[...]factorial table used by later OSCAT helpers.libraries/oscatnow also ships the stateful helper function blocksDELAYandFT_AVG.libraries/oscatnow also ships the first linear/polynomial/ramp math helpers:F_LIN,F_LIN2,F_POLY,F_POWER,F_QUAD, andFRMP_B.- Added the first shipped
libraries/oscatpackage with the initial OSCAT compatibility slice (shared math/physics constants plus core conversion helpers), alongsideexamples/oscat_smokeas the first consumer project wired through[dependencies]. - Added OSCAT compatibility extensions
TIME_TO_DWORD,DWORD_TO_TIME,T_PLC_MS, andT_PLC_US, plus restoredDIR_TO_DEG,F_TO_PT,PT_TO_F, and the liveLANGUAGE.DIRS[...]surface inlibraries/oscat. libraries/oscatnow also ships a larger date/time slice includingDAY_TO_TIME,HOUR_TO_TIME,MINUTE_TO_TIME,SECOND_TO_TIME,DAY_OF_DATE,DAYS_DELTA,DAYS_IN_MONTH,DAYS_IN_YEAR,DATE_ADD,EASTER,WORK_WEEK,HOUR_OF_DT,MINUTE_OF_DT,SECOND_OF_DT,MONTH_BEGIN,MONTH_END,YEAR_BEGIN,YEAR_END, and related leap/year helpers.libraries/oscatnow also ships the first larger string and logic slices, includingMONTH_TO_STRING,WEEKDAY_TO_STRING,DT_TO_STRF,TO_LOWER,TO_UPPER,LOWERCASE,UPPERCASE, theISC_*/IS_*predicate helpers, theFIND_CHAR/FIND_CTRL/FIND_NONUM/FIND_NUM/FINDB*/FINDPsearch helpers, plusCAPITALIZE,CLEAN,COUNT_CHAR,COUNT_SUBSTRING,CODE,DEL_CHARS,TO_UML,DEC_TO_BYTE,DEC_TO_DWORD,DEC_TO_INT,BYTE_TO_STRB,BYTE_TO_STRH,DWORD_TO_STRB,DWORD_TO_STRH,BIN_TO_BYTE,BIN_TO_DWORD,HEX_TO_BYTE,HEX_TO_DWORD,OCT_TO_BYTE,OCT_TO_DWORD,MIRROR,REPLACE_ALL,REPLACE_CHARS,REPLACE_UML,CHARCODE,CHARNAME,TICKER,LTCH,LTCH_4,STORE_8,COUNT_BR,COUNT_DR,TOGGLE,FF_D2E,FF_D4E, andFF_DRE.libraries/oscatnow also ships the rest of the current logic surface:FF_JKE,FF_RSE,SELECT_8,SHR_4E,SHR_4UDE,SHR_8PLE,SHR_8UDE, the full current gate-logic helper slice (DEC_*,MUX_*,BIT_*,BYTE_*,WORD_*,DWORD_*,SHL1,SHR1,SWAP_*,REAL_TO_DW,DW_TO_REAL,CHK_REAL,REFLECT,REVERSE), plus generator trigger FBsA_TRIG,B_TRIG, andD_TRIG.libraries/oscatnow also ships the full current/Logic/generatorsand/Logic/memoryslices:CLICK_CNT,CLICK_DEC,CLK_DIV,CLK_N,CLK_PULSE,CYCLE_4,GEN_BIT,GEN_SQ,SCHEDULER,SCHEDULER_2,SEQUENCE_4,SEQUENCE_8,TONOF,TP_X,FIFO_16,FIFO_32,STACK_16, andSTACK_32.libraries/oscatnow also ships the/Logic/Others,/Buffer Management,/List Processing,/Mathematical/Geometry, and/Mathematical/Double Precisionslices:CRC_GEN,MATRIX,PIN_CODE,_BUFFER_*,BUFFER_*,LIST_*,REAL2,R2_*,CIRCLE_*,CONE_V,ELLIPSE_*,SPHERE_V, andTRIANGLE_A.- Added
docs/guides/OSCAT_LIBRARY_GUIDE.mdplus an expandedexamples/oscat_smokewalkthrough so the shipped OSCAT package now has a user-facing reference and a concrete consumer guide comparable to the motion-library docs. - Fleet topology deterministic ownership slots:
- Fleet topology now renders runtime-owned driver nodes (
mqtt,modbus,ethercat,opcua,gpio) between runtimes and endpoints. - Host assignment is enforced for operational endpoints: remote/discovered endpoints attach to deterministic external host containers instead of free-floating.
- Topology edit flow now requires host placement for manual devices and blocks host removal that would orphan hosted devices.
- Runtime cards now reserve deterministic in-card driver slot zones and driver cards render as compact protocol chips anchored to those fixed slots.
- Empty host containers are hidden in normal view when they have no runtime or endpoint children.
- Runtime/device cards now render a single border layer (removed Cytoscape outer border) to match card design and avoid double-outline artifacts.
- Runtime card subtitle no longer duplicates health state when the status badge already shows it.
- Driver slot placeholders now render without static protocol text; labels appear only on occupied slots.
- Driver nodes are now non-draggable to preserve deterministic runtime slot placement.
- Device edges are normalized to bind via runtime-owned driver nodes (including manual runtime->endpoint edges), and render as direct source-to-target lines from the driver chip.
- Runtime cards now render driver summary text in the body while driver chips are laid out as a single deterministic bottom row (no multi-row chip stacks).
- Driver chips now include compact protocol context subtitles (for example broker/address/adapter/internal) for faster visual inspection.
- Host compound labels are pinned inside host containers for clearer ownership headings.
- Carousel priming now guards missing
runtimeCloudStateto prevent early-page-loadReferenceErrorin browser console. - Fleet graph position caches are now layout-versioned and auto-relayout when stale pinned positions overlap runtime cards.
- External-only host groups now use deterministic anchor lanes so remote endpoint hosts do not collapse onto
(0,0)and overlap each other. - Runtime-internal I/O drivers (
simulated,loopback) now render as deterministic runtime-owned driver cards. - Comms add-driver flow now includes
loopbackalongsidesimulatedfor runtime-internal I/O driver configuration. - Modbus canonical endpoint normalization now handles
addressvalues that already include:port(prevents malformed duplicate-port canonical keys). - Demo topology seed data now keeps manual endpoints hosted (no hostless manual operational devices).
- Added config-mode runtime topology writeback endpoints:
POST /api/config-ui/runtime/createandPOST /api/config-ui/runtime/delete. - Topology edit UI now supports runtime create/remove actions in edit mode and routes adapter actions through TOML-backed config flows.
- Added config-ui live connection manager APIs (
GET/POST /api/config-ui/live/targets,POST /api/config-ui/live/targets/remove,POST /api/config-ui/live/connect,GET /api/config-ui/live/state) with in-memory target profiles and read-only runtime-cloud polling. - Added config-ui runtime lifecycle provider APIs (
GET/POST /api/config-ui/runtime/lifecycle) for runtime status/probe plus managed start/stop/restart actions. - Config-mode
/api/runtime-cloud/statenow supports read-only live overlay of node/edge health from connected runtime-cloud snapshots without changing TOML-derived topology ownership/layout. - Added config-ui integration tests for runtime create/delete and ST/runtime conflict-safe writeback paths.
- Removed active topology-devices overlay route dependency; runtime/config topology remains TOML/API-driven.
Changed¶
trust-runtime testnow prepares one runtime per project and reuses it across discovered cases instead of recompiling the whole ST project for every case, which removes false timeout growth on larger OSCAT fixtures.- truST now accepts direct
CHAR_TO_BYTE/WCHAR_TO_WORDconversion-style helpers, and the shipped OSCATCODE()helper now uses that fast path instead of a brute-force byte scan; the OSCAT core fixture is back to passing at the default--timeout 5. - Structured Text indexing now supports nested
field[index]/index.fieldchains and character indexing onSTRING/WSTRING, so OSCAT-styleLANGUAGE.DIRS[ly, i]access and IEC string element forms compile and execute without fallback workarounds. libraries/oscat/srcand the OSCAT core/negative conformance fixtures now follow the OSCAT manual chapter structure (03_data_typesthrough26_list_processing) instead of a flat source bucket, so continued porting can proceed chapter-by-chapter with tests in the matching chapter first.
Fixed¶
- The OSCAT runtime/compiler path now handles the full shipped OSCAT library
more reliably: bytecode lowering resolves local instance names before global
callable symbols, normalizes OSCAT-style identifier lookup consistently
across locals/reference lowering, and fixes remaining Chapter 19/20/23/24
regressions such as
AOUT/AOUT1scaling,MULTI_INmode0,ACTUATOR_COIL,CTRL_IN,HYST*,PARSET/PARSET2, and first-scanTEMP_EXTsampling. The full OSCAT core fixture now runs green again. - Runtime VM string access, native-call dispatch, and hot-path execution are
now more consistent and predictable: signed unary
NEGon minimum signed integers now reports overflow instead of panicking/wrapping, pure-DINTMOD 0now matches the genericModuloByZerocontract, array offset math and runtime date construction reject invalid edge cases deterministically, runtimeSTRING/WSTRINGindexing and string stdlib helpers now follow a single documented element contract for non-ASCII text,CALL_NATIVEnow caches statically resolvable function targets instead of uppercasing and hashing every call, and the VM hot path now reuses register frame buffers, drops redundant register-file zeroing, batches stack deadline checks, and avoids cloning the debug hook on each statement. The release benchmark path also restores the earlier VM latency envelope by caching per-POU local-init plans and trimming dynamic-ref hot-path overhead instead of rebuilding that work every scan. - The OSCAT BASIC
CALENDARport keeps truST strict about IEC reserved keywords instead of relaxing the parser, so the shipped Chapter 3 data type exposes compliant local-time field namesLOCAL_DT,LOCAL_DATE, andLOCAL_TODin place of upstreamLDT,LDATE, andLTOD. trust-hirnow resolves cross-file user-defined types for rootVAR_GLOBALdeclarations during project-aware symbol collection instead of depending on a late repair pass, so split projects like OSCAT BASIC can keep shared carrier types in one file/chapter and their globals in another while diagnostics, analysis, runtime builds, and VM execution still resolveMATH/PHYS/LANGUAGEcorrectly. The same project-type catalog path now also preserves imported function block/class/interface identity, honorsUSING+ namespaced simple type references, and attaches every declaration inside multi-entryTYPE ... END_TYPEblocks so cross-file POU member access surfaces likeFB_Accumulator,ST_PumpCommand, andST_PumpStatusanalyze correctly.- Typed conversion calls no longer misclassify an outer positional argument as a named/formal argument when the nested inner call uses
IN := ..., so expressions likeUDINT_TO_REAL(DWORD_TO_UDINT(IN := x))type-check correctly again. SIZEOF(...)now resolves the static type of an explicit type name or storage operand (var, field/index access, dereference,THIS.field) during analysis/lowering instead of evaluating a runtime value, soSIZEOF(var)now works in normal expressions and constant contexts likeARRAY[0..SIZEOF(packet)-1], bare-name lookup follows CODESYS-style variable-before-type shadowing even when a top-level type shares the same bare name, pointer/reference operands now report and const-fold to platform pointer word size instead of truST's internal handle layout, and invalid forms such asSIZEOF(call), arithmetic expressions, open arrays, unsized strings, and whole FB/class/interface instances fail deterministically during build instead of lowering to the legacySIZEOF_VALUEpath.- Runtime and VM execution now handle nested field/index lvalue chains, string and WSTRING dynamic indexing, split-local/initializer parity, and debugger force/release on direct instance fields consistently across harness, VM, helper-eval, and debug-adapter paths.
- HIR/syntax now parse and type-check
TIME()as a zero-argument builtin, allowSTRING/WSTRINGindexing in expressions, accept stringCASElabels and fixed-length string comparisons, and document the shipped implementer conversion extensions (TIME_TO_DWORD,DWORD_TO_TIME,CHAR_TO_BYTE,WCHAR_TO_WORD). trust-ide/trust-lspnow surface the shipped pointer/ARRAY[*]/IECCONSTANTsemantics consistently across completion, hover, semantic tokens, signature help, workspace symbols, inline values, refactor const-analysis, and constant-section diagnostics instead of drifting behind the HIR fixes.- Project analysis now honors cross-file
VAR_GLOBAL CONSTANTinteger expressions in type-length contexts such asSTRING[MaxLen], and the OSCAT BASIC CRC/buffer helpers now use the shippedARRAY[*]/POINTER TO ARRAY[*]compatibility forms without giant caller-side ceremony arrays. - IEC
CONSTANTqualifier handling now preserves parameter/VAR_TEMPidentity in HIR, rejects writes through the sharedConstantModificationpath, blocks function block instances inCONSTANTsections, and keeps parameter/VAR_TEMPdeclarations out of compile-time constant-expression evaluation. - Bytecode VM lowering now executes
EXITandCONTINUEcorrectly inFOR,WHILE, andREPEATloops instead of rejecting those statement paths through the generic C5 fallback. - HIR comparison rules now treat same-family
STRINGandWSTRINGvalues as comparable regardless of declared max length, so fixed-length values such asSTRING[1]andWSTRING[1]compare cleanly against compatible literals and variables. - Pointer support docs and regression coverage now explicitly lock the supported string-pointer path (
ADR(str)with typed dereference/indexing such asp^[i]) while keeping raw byte-array reinterpret casts and pointer arithmetic outside the supported runtime model. CASEnow acceptsSTRING/WSTRINGselectors and literal labels in runtime/lowering paths, with an explicitIEC-DEC-024policy that string subranges remain rejected as an implementer decision for the ambiguous IEC area.- FUNCTION_BLOCK omitted
VAR_INPUTcalls now follow the documented runtime semantics: instance creation seeds declared input initializers, first omitted calls observe that seeded value, and later omitted calls reuse the stored instance input instead of reevaluating the declaration on every call. trust-runtime testnow preloads bytecode before per-case timeout accounting, so filtered project runs do not burn their timeout budget on the first lazy VM-module build.- VM execution now initializes declared local storage before user bytecode runs, so local declaration initializers and split-style standard-library writes to function-local outputs behave the same way in runtime and VM paths.
[0.15.0] - 2026-04-14¶
Changed¶
trust-runtimeis now VM-only in production: thelegacy-interpreterfeature, interpreter backend dispatch seam, and interpreter runtime paths are gone; CLI/config startup selection still acceptsvmand now rejectsinterpreterexplicitly.- Removed
trust-runtime bench execution-backendand the interpreter differential gate flow; VM-only syntax-corpus, determinism/reliability, and production-backend guard scripts now carry the runtime evidence path. - Hardened
scripts/runtime_vm_syntax_corpus.shso the default corpus skips stale missing fixture folders instead of aborting halfway through a benchmark run. - Runtime docs/diagrams/specs now describe
program_modelplus boundedhelper_evalhelpers as the surviving support surface around the VM, and executor internals undersrc/eval/**are confined to test-only coverage.
[0.14.0] - 2026-04-13¶
Added¶
- Runtime benchmark scripts now share a documented host-codegen policy via
TRUST_RUNTIME_HOST_CODEGEN=auto|generic|native, defaulting to host-native builds only on Raspberry Pi benchmark hosts while keeping portable/generic builds explicit for shared comparisons and release artifacts. - User-facing build docs now also cover the full host-native and PGO runtime build workflow, including the required
llvm-tools-previewsetup, corpus-training commands, and current Raspberry Pi 5 benchmark evidence (full_demoabout433.501 us -> 404.353 uswithnative, then about292.298 uswithnative+PGO). - Added
docs/diagrams/architecture/runtime-bytecode-vm-execution.pumland corrected the high-level runtime architecture notes so the official diagrams now distinguish production bytecode-VM execution from residualEvalContext/legacy-interpreterhelper paths. scripts/runtime_motion_benchmark_breakdown.shnow forces a freshcargo build --release -p trust-runtime --bin trust-runtimebefore benchmarking so release measurements cannot accidentally reuse a staletarget/release/trust-runtimebinary.trust-runtime bench projectnow emitsvm_profile.ref_opsandvm_profile.call_opsin JSON/table output, andscripts/runtime_motion_benchmark_breakdown.shnow summarizes those counters per workload so VM performance investigation runs show ref-path and FB/frame activity directly instead of hiding it in raw runtime state.trust-runtime bench projectnow also emitsvm_profile.value_opsin JSON/table output, andscripts/runtime_motion_benchmark_breakdown.shnow summarizes value-clone/move attribution (const loads, register reads, read-side value clones, binding expr clones, output copy-back clones) so runtime speed work can distinguish read-path churn from register-file churn.trust-runtime bench projectnow also emitsvm_profile.tier1_specialized_executorin JSON/table output, and the experimental tier-1 compiler now supportsRefField,RefIndex,LoadDynamic, andStoreDynamicso ref-heavy FUNCTION_BLOCK blocks show up in benchmark compile/deopt/execution evidence instead of staying invisible behindnulltier-1 stats.VariableStoragenow caches instance-field resolution per instance, with invalidation on new instance-field insert, so repeated FUNCTION_BLOCK self-field access and parameter binding can skip repeated field-name lookup without assuming that all same-type instances always share the exact same runtime layout.- VM FUNCTION_BLOCK argument binding now uses a direct declared-parameter offset fast path with recursive fallback for inherited or unusual fields, trimming repeated binding overhead on ref-heavy release benchmarks without changing call semantics.
- VM output copy-back and empty-path storage access now use direct-slot
VmWriteTarget/VariableStoragefast paths for locals, globals, and instances, reducing genericValueRefresolution on the hot benchmark path without changing runtime semantics. - VM
INarguments that arrive as empty-path target references now reuse the same direct-slotVmWriteTargetread path for locals, globals, and instances, trimming generic reference reads on ref-heavy hot paths without changing call semantics. - VM dynamic reference shape inspection now borrows storage/local values for
REF_FIELDandREF_INDEXtraversal instead of cloning them first, which materially improves ref-heavy release motion workloads without changing runtime semantics. - VM hot paths now also borrow output target type inspection for
write_output_int()and use borrowed DINT fast paths for fused ref+const/register guards inregister_ir, trimming unnecessary clone/materialization work on ref-heavy micro-benchmarks without changing runtime semantics. - VM FUNCTION_BLOCK binders now skip field resolution when
OUT/IN_OUTparameters are omitted, use an ordered named-argument fast path for the common declaration-order case, and decode native call payloads in pop order instead of reversing/removing a payload vector on every call. - Runtime
Value::Structnow uses shared-on-clone copy-on-write storage, which removes the remaining deep-clone hot path for PLCopen-styleAXIS_REFVAR_IN_OUTtransfers; on the rebuilt motion demo gate this cutsread_value_clonesfrom6467to227, eliminatesoutput_value_clones, and improves release latency to aboutp50 424 us/p95 649 uswith clean semantics. trust-runtime bench projectnow keeps VM profiling enabled by default while leaving the experimental tier-1 specialized executor opt-in via--tier1, so normal speed baselines stay on the primary VM path and diagnostic tier-1 cache/compile/deopt evidence is still available when explicitly requested.trust-runtime bench projectandscripts/runtime_motion_benchmark_breakdown.shnow also surface tier-1 compile-failure reasons alongside deopt reasons, so remaining specialization misses can be attributed to exact unsupported instruction/op buckets instead of only a genericcompile_failurescount.- The experimental tier-1 VM path now also compiles
LoadRefAddrandCallNative, and widens binary specialization to the generic non-DINT fallback path (includingAND/OR), so the motion benchmark matrix now reachescompile_failures = 0/deopts = 0in tier-1 profiling runs while preserving clean motion semantics. - VM ref-binary execution now reuses the first borrowed ref/const read on non-DINT fallback in both the interpreted register executor and the tier-1 executor, removing duplicate storage reads and bogus clone accounting on BOOL/REAL/UINT/WORD-style ref binaries; the rebuilt motion breakdown improves over the tier-1-complete state (
dynamic_refs 34.834 us,full_demo 478.836 us) while preserving clean motion semantics. - The register VM and experimental tier-1 executor now lower and execute
LOAD_SUPER (0x24), so method receiver /SUPERcall paths no longer fall back throughunsupported_opcode_0x24on the current-code benchmark surface.
Changed¶
- Rewrote the user-facing PLCopen motion documentation so the single-axis demo README now explains the example itself and the main library guide now serves as a real reference manual with package wiring, datatype purpose tables, and per-function-block input/output coverage.
[0.13.0] - 2026-04-12¶
Added¶
trust-runtime bench projectnow benchmarks a real project folder with watched-global capture, cycle-budget reporting, per-cycle latency summaries, and budget-overrun counts, so reusable Structured Text libraries can be validated through user-facing example projects instead of only synthetic fixtures.- Added
examples/plcopen_motion_single_axis_demoas the reference consumer forlibraries/plcopen_motion/single_axis_core, plusscripts/runtime_motion_example_bench_gate.shfor focused end-to-end semantic + cycle-budget validation of the shipped PLCopen motion surface. - Added
examples/plcopen_motion_single_axis_benchmarksplusscripts/runtime_motion_benchmark_breakdown.shto isolate runtime floor,MC_Constants(), status/readback, inactive-command, single-command, and constants-once motion costs on the same release benchmark path. trust-runtime bench projectnow emits VM register-profile details for VM-backed workloads, including hot blocks, fallback reasons, and lowering-cache counters, so project benchmarks can explain where scan time is going instead of only reporting latency totals.- Shipped the first PLCopen Motion library profile in Structured Text: Part 1 single-axis classic core, Part 1 synchronization cam/gear subset, Part 4 coordinated-motion core subset, and Part 5 homing core subset, with a compliance matrix, user-facing guide, deferred-surface guards, and deterministic ST conformance fixtures.
- truST now accepts namespaced vendor-style GVL declarations (
NAMESPACE ... VAR_GLOBAL ... END_NAMESPACE) and resolves qualified access such asGVL.sharedin both HIR and runtime execution. - truST now records vendor-parity global access explicitly: top-level GVLs, namespaced GVLs, and direct global access without mandatory
VAR_EXTERNALare documented and covered by regression tests.
Changed¶
- Structured Text
FUNCTION_BLOCKcalls now reuse the previously storedVAR_INPUTvalue when an input argument is omitted, with first-call fallback still using the declared initializer or IEC default; this closes the motion-library command-update semantics gap without inventing separate runtime rules. trust-runtime plcopen importnow defaults to native CODESYS/TwinCAT-style global-list materialization: file-scope GVLs stay asVAR_GLOBALfiles,qualified_onlylists import as namespaced GVLs, and mandatoryVAR_EXTERNALinjection is no longer the default import shape. A strict adapter mode remains available for wrapper + injected-VAR_EXTERNALreshaping when external consumers need it.- The canonical PLCopen motion demo now initializes
MC_Constants()once during startup instead of republishing its outputs every scan, so the reference benchmark reflects the cheaper steady-state usage pattern already documented by the library guide. - Register-IR hotpath coverage now includes
LOAD_SELF (0x23), complex local-ref execution, and CASE block-entry stack modeling, project-bench VM hot-block reports resolve readable POU names, and lowering-cache fallbacks now preserve the failing POU name plus the original lowering error message. On the PLCopen motion demo this removes the oldunsupported_opcode_0x23,complex_local_ref_path, andMainlowering-error buckets and keeps the release gate comfortably under the 10 ms cycle budget with0VM fallbacks.
Fixed¶
trust-runtime bench projectnow applies the project's configured runtime execution backend and the motion example bench gate acceptsTRUST_MOTION_MAX_OVERRUNS/TRUST_MOTION_P95_MAX_USoverrides for hardware-specific performance gating.trust-runtime testnow executes discoveredTEST_PROGRAMs even when a project includes aCONFIGURATION, while normal configured runtime builds continue to register only configured programs.trust-runtime test --projectnow compiles sources from local[dependencies]packages in addition to the project's ownsrc/, so extracted Structured Text libraries can be consumed from real package roots instead of living inside test fixtures.- Multi-file HIR import now preserves configuration/resource global lookup bindings, so vendor-parity bare global access continues to resolve after splitting projects across source files.
VAR_STATnow executes with documented vendor semantics in the runtime: function statics persist across calls, method statics persist per instance/method, and instance-bearing scopes treatVAR_STATas persistent instance storage.
[0.10.0] - 2026-04-11¶
Added¶
trust-harnessnow provides a lightweight JSON-line test driver for compiled ST programs, includingcycle.dt_msvirtual-time advancement and typedTIME/LTIMEwatch output for timer-oriented automation.- LSP/type-checker numeric hazard warnings now flag floating-point equality/inequality comparisons (
W013) andDIV/MODexpressions with literal zero divisors (W014), with a dedicatedwarn_numeric_hazardsdiagnostics toggle for vendor/workspace tuning.
Fixed¶
- Structured Text infix bitwise operators now accept
BOOL/ANY_BIToperands with the same widening behavior as the standardAND/OR/XOR/NOTfunctions, and&now type-checks as theANDsynonym instead of falling through asUNKNOWN.
[0.9.29] - 2026-04-11¶
Fixed¶
- Runtime assignment writes now preserve declared scalar storage types in both interpreter and VM paths, so loop counters keep their declared
INT/UINT/etc. representation and exact-source conversion calls such asINT_TO_DINT(...)no longer fail insideFORandWHILEloops.
[0.9.28] - 2026-03-07¶
Added¶
- VS Code SFC visual editor integration (IEC 61131-3 style step/transition canvas, runtime panel wiring, and bundled EtherCAT Snake SFC examples) is now included in mainline extension workflows.
Changed¶
- Web IDE build/deploy behavior now matches CLI semantics in unified shell flows: build tasks use project-root
--projectresolution, deploy normalizessrc/-prefixed source paths to avoid nestedsrc/srcwrites, and online connection defaults now seed same-origin host/port for faster standalone startup. - Web IDE header controls now use a compact primary toolbar (
Open,Save,Build,Deploy) with overflow menu actions for lower visual clutter while preserving the full command set. - VS Code HMI widget-navigation source scanning fallback was merged on top of current mainline traversal logic so declaration resolution stays deterministic for
.st/.pouprojects after main-branch merge.
Fixed¶
- VS Code SFC webview TypeScript message/edge typing regressions that previously failed extension compile gates in CI.
[0.9.27] - 2026-03-06¶
Added¶
- MP-060 Phase A execution-backend controls for
trust-runtime: newruntime.execution_backendconfig (interpreter|vm), new CLI override--execution-backend=interpreter|vmforrun/play, startup backend-selection log event, backend mode/source fields in controlstatus/config.get, and Prometheus backend info metric (trust_runtime_execution_backend_info).vmselection is intentionally fail-fast until VM execution lands in the next MP-060 phase. - MP-060 Phase B VM core for
trust-runtime:runtime.execution_backend='vm'now runs a real bytecode executor with deterministic program-counter dispatch, operand and call/frame stacks, trap/deadline/budget enforcement, stable trap-to-RuntimeErrormapping, and slot/index-based VM hot-path access with symbol/source mapping tables preserved for external/debug name-based surfaces. Interpreter remains the default backend. - MP-060 Phase C1 call-parity rollout for
trust-runtime: VM codegen/dispatch now emits and executesCALL_NATIVE(kind/symbol/arg_count) for function/FB/method/stdlib paths, routes builtin FB and stdlib behavior through the same native-call convention, preserves named/default/IN_OUT parity, and replaces C1-required silent NOP call fallthrough with deterministic compile/validate/runtime failures. - MP-060 Phase C2 string parity rollout for
trust-runtime: VM bytecode lowering/execution now supportsSTRING/WSTRINGliterals (including stdlib call arguments and comparison expressions), adds deterministic UTF validation for string const payloads, and removes remaining C2 silent-NOP fallback behavior for string-literal control-flow lowering paths. - MP-060 Phase C3 OOP parity rollout for
trust-runtime: VM lowering/runtime now supports explicitTHIS/SUPERreceiver expressions (LOAD_SELF/LOAD_SUPER) for method/native-call paths, adds deterministicSUPERtrap semantics, and extends differential/opcode conformance coverage for method/interface dispatch including malformed method receiver payload rejection. - MP-060 Phase C4 reference/deref parity rollout for
trust-runtime: VM lowering/runtime now supportsREF(...)lowering, dereference execution (LOAD_DYNAMIC/STORE_DYNAMIC), and nested field/index chain composition (REF_FIELD/REF_INDEX) with deterministic validator/runtime failures for invalid field-string indices and malformed dynamic-reference operands. - MP-060 Phase C5 parity rollout for
trust-runtime: VM lowering/runtime now supportsSIZEOF(type)/SIZEOF(expr)(SIZEOF_TYPE/SIZEOF_VALUE), adds deterministic validator/runtime opcode failure coverage for C5 paths, and replaces silent VM-lowering fallthrough for remaining C5 edge statements (?=/JMP/RETURN/EXIT/CONTINUE) with deterministic compile-time rejection. - MP-060 Phase C audit follow-up hardening for
trust-runtime: added per-subphase negative-path interpreter-vs-VM differential coverage (C1..C5), extended C2 string stdlib parity coverage to include bothFINDfound/not-found paths, and added a deterministicSIZEOF_TYPErecursion-depth guard (max depth 128) for deep non-cyclic type-table graphs. - MP-060 Phase D debug/observability parity for
trust-runtime: VM execution now resolves statement stops through bytecode debug-map(pou_id, pc)source mappings, preserves breakpoint/step and debug-write workflow parity with interpreter mode, and keeps backend metrics/runtime-event observability contracts stable across interpreter and VM backends. - MP-060 Phase E online-change groundwork for
trust-runtime: hot-reload semantics are now centralized in a dedicated runtime online-change contract (apply_online_change_bytes) with cycle-boundary swap handling, warm-restart entrypoint invalidation, retain/global/instance migration coverage, deterministic invalid-bytecode diagnostics, and explicit startup-only backend-switch diagnostics for liveconfig.setrequests. - MP-060 Phase E rollout-gate closure: added a dedicated backend migration note (
docs/guides/RUNTIME_EXECUTION_BACKEND_MIGRATION.md) covering CLI/config rollback controls and the two-release interpreter compatibility window policy, plus explicit release-evidence gate documentation forversion-release-guardartifact aggregation. - MP-060 benchmark groundwork: added
trust-runtime bench execution-backend(mp-060-corpus-v1) for interpreter-vs-VM cycle latency/throughput comparison, plus CI evidence capture viascripts/runtime_vm_bench_gate.shand published artifact/docs pointers (gate-artifacts/runtime-vm-bench/**,docs/internal/reports/mp-060-runtime-vm-benchmark-corpus.md). - MP-060 Phase C hotspot profiling: added runtime-scoped register-VM profile controls/snapshot APIs and extended
trust-runtime bench execution-backendfixture reports withvm_profiledetails (register execution/fallback counters, fallback reasons, top hot blocks, and profiling-overhead ratio). - MP-060 Phase D tier-1 specialized register-executor pilot: added an experimental hot-block specialization tier in the register VM (guarded compile subset for arithmetic/compare/branch, deopt-to-register-interpreter fallback, cache capacity/eviction controls, and bytecode-reload invalidation), plus benchmark artifact fields for tier-1 specialized-executor counters/deopt reasons and focused unit coverage for startup/deopt/cache behavior.
- MP-060 register executor now caches lowered register IR per
(module,pou)with runtime-owned invalidation on bytecode reload, eliminating per-cycle relowering and block-map allocation; benchmark reporting now includes lowering-cache hit/miss/eviction stats and uses expandedmp-060-corpus-v2fixtures (call-binding,string-stdlib,refs-sizeof,loop-arith).
Changed¶
- MP-060 production backend policy now enforces VM-only startup for
trust-runtime run/play:--execution-backendacceptsvmonly,runtime.execution_backend='interpreter'is rejected by runtime config validation, omitted backend config defaults tovm, and run/play startup now fails fast when a legacy interpreter backend is present in runtime bundle settings. - MP-060 setup/wizard runtime templates now emit
runtime.execution_backend = "vm"by default so newly generatedruntime.tomlfiles validate and start under VM-only production policy. - MP-060 no-bundle and source-compiled runtime startup now always applies compiled bytecode before execution backend selection so VM-only startup has loaded bytecode metadata in both project and IDE-shell startup flows.
- MP-060 interpreter internals are now explicitly feature-gated behind
legacy-interpreterfor parity-only workflows (differential/benchmark/debug parity tests), while default production builds remain VM-only. - MP-060 runtime/harness defaults now execute through VM paths with automatic bytecode materialization from runtime metadata when VM bytecode has not been preloaded, preventing implicit interpreter execution in default runtime/test flows.
- MP-060 CI now enforces a production backend leak guard (
scripts/runtime_vm_production_backend_guard.sh) to fail if run/config/control/template/runtime-default surfaces regress to interpreter references. - MP-060 migration/docs/checklists updated for restart execution: added detailed recovery board
docs/internal/testing/checklists/mp-060-vm-restart-recovery-checklist.mdand updated backend migration/spec/master-plan text to reflect interpreter test-oracle-only policy. - MP-060 post-C1 recovery pass implemented for register VM hot path (
P0 -> P2 -> P1): boxed large runtime value variants (Array/Struct/Enumwith unboxedReference), added extended register-op fusion (BinaryRefToRef,BinaryRefConstToRef,BinaryConstRefToRef,CmpRefConstJumpIf) with tier-1 specialized-executor support, and added consume-aware per-block register read paths; refreshed lockedmp-060-corpus-v33-run benchmark evidence and comparison artifacts intarget/gate-artifacts/runtime-vm-bench-v3-post-p0-p2-p1-run1/. - MP-060 post-hotpath correction pass:
execution-backendbenchmark corpus upgraded tomp-060-corpus-v4with per-cycle loop-state reset inloop-arith, VM profile guardrails now assert loop-body execution during measured cycles, benchmark comparison now uses 3-run median-of-runs decision metrics (scripts/runtime_vm_bench_compare.sh) with aggregate median derived from per-fixture medians (instead of pooled cross-fixture sample p50), and register-IRCALL_NATIVEnow reuses a program-level pooled operand stack (removing per-call stack allocation churn) alongside cached per-program read metadata and direct block-id indexing in the register executor. - Runtime specs/docs synchronized to VM-default production backend policy:
docs/specs/10-runtime.mdanddocs/specs/README.mdnow describe bytecode VM execution as the primary runtime path, with interpreter execution documented as legacylegacy-interpreterparity/test-oracle flow only.
[0.9.20] - 2026-02-28¶
Added¶
- IDE settings coverage expanded for standalone/offline authoring: new fields for control endpoint/resource name, runtime cloud profile, extended OPC UA options, and observability options.
- IDE settings discoverability improvements:
/ide/settingsnow opens in anAll Settingsview by default and adds filter-by-name/key search so MQTT/PLC/TLS/realtime/debug configuration is immediately accessible. - IDE settings reliability hardening:
/ide/settingsnow includes standalonesimulation.tomlcontrols (simulation.enabled,simulation.seed,simulation.time_scale), shows active filter summary/clear controls to prevent hidden-field confusion, and limits liveconfig.setwrites to backend-supported keys to avoid false save failures. - IDE settings navigation hardening: hardware/quick-action jumps now auto-clear stale settings filters and auto-fallback to the owning category (or
All Settings) so target fields are always visible when jumping to PLC/MQTT/TLS/realtime/debug settings. - IDE I/O settings completeness hardening: both Settings and Hardware editors now preserve
use_system_iofrom loaded I/O configs instead of silently forcing it tofalseon save. - Default standalone Web IDE demo project (
examples/web_ui_complete_project) now includes a multi-driverio.toml(Modbus TCP, MQTT, EtherCAT mock, GPIO, Simulated) so Hardware and Settings communication flows are populated immediately. - IDE communication workflow hardening in the unified shell:
- Settings now shows inline JSON examples for
runtime_cloud.wan.allow_write_jsonandruntime_cloud.links.transports_json. - Realtime link rule parsing now accepts
source/target,from/to, andpatternaliases for faster authoring while still writing validruntime.cloud.links.transportsTOML entries. - Runtime-cloud link transport support now spans
realtime,zenoh,mesh,mqtt,modbus-tcp,opcua,discovery, andwebacross runtime schema validation, config APIs, and the Hardware/Settings editors. - Hardware
Add Linkflow now uses an in-UI transport picker (all 8 transports) plus guided source/target hinting instead of raw text prompt input. - Hardware inspector now deep-links all communication/runtime-cloud modules to their exact Settings fields.
- Added MQTT connectivity probe route
POST /api/io/mqtt-testand Hardware inspectorTest Connectionsupport for MQTT alongside Modbus TCP. - Hardware communication cards now also project
runtime.cloud.wan.allow_writeasCloud WAN Access. - IDE mobile responsiveness hardening for
/ide: stacked layout now keeps the main workspace visible on narrow screens and avoids sidebar-only view lockups. - Fixed settings schema mismatch for Modbus
on_error: UI options now match runtime driver-supported values (fault,warn,ignore). - Unified Web IDE shell with tab-based navigation (Code | Hardware | Settings | Logs):
- Root
/and/setupnow redirect to/ide. - Legacy fleet web routes removed from the runtime web server (
/fleet,/app.js,/styles.css,/runtime-cloud-utils.js, and legacy/modules/*fleet assets). - Added standalone browser IDE startup via
trust-runtime ide serve --project <path> --listen <addr>(with deprecated aliastrust-runtime config-ui serve). - Tab deep links:
/ide/code,/ide/hardware,/ide/settings,/ide/logs. - Keyboard navigation: Ctrl+1..4 for tab switching, URL history with pushState.
- Hardware tab: Cytoscape topology canvas, module palette with drag-and-drop, auto-address allocation, property editor with per-driver forms (Modbus TCP, MQTT, GPIO, EtherCAT), address map table with conflict/unused/used-in-code highlighting, live I/O value polling.
- Hardware tab now auto-hydrates from workspace
io.tomlvia new/api/ide/io/configendpoint so configured hardware/communication drivers appear on the canvas immediately (including deep-link/ide/hardwareloads). - Hardware tab now renders a full workspace layout (summary KPI cards, surface controls, driver/runtime communication cards, persistent inspector), fixes tab-shell row sizing so content fills the page (status bar no longer consumes the main viewport), and hydrates runtime communication sections from
runtime.tomlinto the hardware communication view. - Hardware tab now also hydrates from
runtime.tomlcommunication sections whenio.tomlis missing, so standalone projects still show communication modules on the canvas instead of an empty hardware view. - Settings tab: categorized form (General, Execution, Retention, Communication, Security, Advanced), direct runtime.toml editing, settings export/reset.
- Logs tab: unified log view with severity/source/text filtering, alarm acknowledgment, CSV export.
- Logs CSV export filenames now follow the user-story format exactly:
truST-logs-YYYY-MM-DD-HHMMSS.csv. - Connection dialog: mDNS discovery scan, manual connect, recent connections.
- Deploy flow: error gating, confirmation, sync status indicator (in-sync/modified/not-deployed).
- Debug integration: breakpoints with gutter decorations, debug toolbar (Continue/Step Over/Step Into/Step Out/Stop), variables/call stack/watch panels, live inline value annotations, I/O force/unforce with warning banner.
- New IDE modules:
ide-tabs.js,ide-hardware.js,ide-online.js,ide-debug.js,ide-settings.js,ide-logs.js. - New CSS:
ide-06.css(tabs),ide-07.css(hardware),ide-08.css(online/debug),ide-09.css(settings/logs). - 5 new unified shell contract integration tests in
web_ide_integration_part_09.rs. - IDE session bootstrap now recovers reliably in standalone mode by persisting/reusing session tokens across reloads and evicting the stalest inactive session when the session cap is reached (prevents repeated
/api/ide/session429 lockouts during active web-UI development). - Hardware canvas labels now compact long address ranges and enable wrapped node text to keep TOML-derived module/address data legible on the graph.
- Hardware Cytoscape theming now resolves IDE CSS variables to concrete colors and updates on theme changes, eliminating runtime style warnings while preserving light/dark parity.
- Hardware tab visual redesign now uses a canvas-first layout with a much larger default graph surface, collapsible inspector/driver panels, in-canvas communication legend/filter controls, center/fullscreen actions, and SVG-based component iconography (replacing emoji palette glyphs) for a modernized runtime topology editing workflow.
- Hardware node art polish pass now upgrades node cards with richer per-protocol gradients/glows, glass label treatment, and higher-fidelity SVG chip icons (including distinct analog/communication module icon rendering) without changing interaction workflow.
- Hardware node visuals now render generated card-style SVG backgrounds per node (runtime/endpoint/module variants with accent rails + icon chips) and enforce higher minimum zoom/readability so topology labels/icons stay legible at dense graph sizes.
- Hardware canvas visual hardening now uses Cytoscape-safe SVG icon rendering for node cards (fixes missing icon layers in Chromium canvas rendering) and retunes card dimensions/colors for a cleaner production graph look.
- Hardware tab chrome simplification removes summary KPI cards, runtime/fabric control strip, and active-link/legend toolbar from the canvas stage so palette + topology become the primary workflow surface.
- Hardware node context menu now de-duplicates endpoint settings navigation: endpoint nodes expose only one settings jump action while runtime nodes keep separate runtime and communication settings actions.
- Runtime cloud multi-host topology demo pack:
- Added
examples/runtime_cloud/multi_host_topology_demo/withbootstrap.sh,start-5.sh, andstop-5.sh. - Added per-runtime sample configs for 5 runtimes (
runtime-a..runtime-e) with mixedmqtt,modbus-tcp,ethercat,simulated, andloopbackI/O profiles. - Added preloaded manual topology seed (
topology-devices.runtime-a.json) to exercise host containers, manual devices, and module-slot rendering during UI testing. - Changed bootstrap defaults to TOML/API-first: manual topology JSON and legacy link-transport JSON are now opt-in only (
TRUST_TOPOLOGY_DEMO_SEED_MANUAL=1,TRUST_TOPOLOGY_DEMO_SEED_LINK_JSON=1). - Added TOML runtime-cloud link preference support via
runtime.cloud.links.transportsand seeded demo link lanes fromruntime-a.toml. - Added TOML discovery host grouping via
runtime.discovery.host_groupand host-group-aware runtime-cloud same-host evaluation for localhost multi-host demos. - Runtime cloud topology visual map in Web UI:
- Reworked
Network -> Topologyinto a graphical runtime map with box nodes and connection lines. - Added at-a-glance status-focused KPIs (
online,degraded,offline, link health) for operator use. - Moved diagnostic-heavy controls (filters, tables, timeline, rollout/config control surfaces) into a collapsed advanced section to reduce default clutter.
- Runtime cloud edge transport switching in Fleet view:
- Clicking topology links or edge cards now allows direct
Realtime/Zenohtransport switching. - Added
/api/runtime-cloud/links/transportwith same-host validation forrealtimerouting preferences. - Fleet topology state now reflects selected link transport (
t0_hard_rtoverlay for realtime links). - Promoted topology to primary workflow with in-canvas plane/keyspace/issue filters, contextual node/edge inspector, and batch action bar for selected runtimes.
- Moved compact timeline controls into topology map while keeping deep diagnostics under the advanced drawer to reduce default UI clutter.
- Communication lines now render per-lane overlays (Zenoh + optional Realtime), with left-click showing only critical lane status/settings and right-click lane actions for add/remove realtime communication.
Changed¶
- Local developer test workflow acceleration:
just testnow usescargo-nextestfor the default local loop (trust-runtimelibrary tests), with automatic fallback tocargo testifnextestis unavailable.- Added split test targets in
justfile:test-integration,test-e2e, andtest-all(full previous gate). - Enabled faster local linking via
mold(.cargo/config.toml) and enabledsccacheas Cargorustc-wrapper. - Reduced dev/test debug symbol level to
debug = 1for faster incremental compile/link during local iteration.
Fixed¶
- Standalone Web IDE bootstrap now serves fully composed split module assets (
ide-editor-language,ide-editor-pane,ide-workspace-tree,ide-workspace-files,ide-observability,ide-commands) so session startup/open-project flows no longer fail with missing runtime symbols (for examplerefreshProjectSelection). - Standalone Web IDE bootstrap no longer stalls waiting for WASM analysis readiness when the analysis worker repeatedly restarts; IDE startup now completes and remains usable with analysis disabled.
- Web IDE UI-mode detection now accepts both wrapped (
{ok,result}) and direct ({ok,mode}) API payloads, restoring correct standalone-mode behavior (for example disabling runtime-only controls instandalone-idemode). - Web IDE Hardware tab now rehydrates after IDE session bootstrap events (including deep-link startup on
/ide/hardware) and performs delayed canvas relayout/fit passes after tab activation so hardware + communication modules fromio.toml/runtime.tomlreliably render without clipped/off-screen graphs. - Web IDE workspace tabs now publish explicit ARIA tab semantics (
tablist/tab/tabpanel,aria-selected, and active-tabtabindexmanagement) for keyboard/screen-reader correctness. - Config-UI Fleet topology projection no longer reports planned runtimes/endpoints as online by default;
/api/runtime-cloud/statein config mode now renders offline/degraded runtime state and failed links until live runtime connectivity exists. - Config-UI now serves read-only
/api/runtime-cloud/configand/api/runtime-cloud/rolloutssnapshots in offline mode to prevent Web UI polling 404 noise during TOML/ST-only editing sessions. - Runtime cloud 5-host topology demo is now self-contained and deterministic:
start-5.shauto-bootstraps missing projects, performs clean restarts, waits for runtime-cloud readiness, and ships explicit mesh/link-transport seed data for mixedrealtime/zenohtopology behavior. - Fleet/Comms add-driver deep-link handling now preserves unknown preselected driver types instead of silently falling back to
modbus-tcp. - Fleet/Comms schema-number handling now preserves valid
0values (for example Modbusunit_id = 0) and enforces min/max validation before save. - Fleet topology device context menu now resolves touch coordinates reliably (long-press/taphold) and always unregisters its outside-click dismissal listener on close.
[0.9.15] - 2026-02-18¶
Added¶
- PLCopen CODESYS global/folder parity:
trust-runtime plcopen importnow imports CODESYSaddData/globalVarsinto STVAR_GLOBALsources (plaintext-first with variable-node synthesis fallback).- CODESYS
addData/projectstructureobject trees are now used to place imported POUs/GVLs into mirroredsrc/subfolders (for examplesrc/Application/...). trust-runtime plcopen exportnow emits deterministic CODESYSglobalVarsandprojectstructuremetadata for ST POUs + GVL files.- Import/export JSON reports now include global-list and project-structure counters (
discovered/imported_global_var_lists, folder/object-node counts).
Changed¶
- PLCopen CODESYS import hardening:
trust-runtime plcopen importnow maps CODESYS{attribute 'qualified_only'}global variable lists into a compiler-validTYPE + CONFIGURATION/VAR_GLOBALwrapper model instead of emitting unsupported top-levelVAR_GLOBALfiles.- Imported POUs that reference qualified lists (for example
GVL.start) now receive injectedVAR_EXTERNALdeclarations so cross-file global access resolves in trust-lsp/runtime builds. - Imported
FUNCTIONPOUs without explicit result assignment now get a deterministic fallback self-assignment (for exampleMyFunc := MyFunc;) to preventE206 missing return valueon first import. - Demo Pages WASM startup UX now force-updates the
WASM Readybadge after engine readiness resolves, preventing a staleLoading WASM...state when the workerreadyevent arrives before listener registration. - Demo WASM analysis clients now send an internal bootstrap
statusrequest on worker spawn so startup no longer depends on user interaction beforeWASM Readyappears. - Demo Pages now publishes a root
favicon.icoand links it fromdocs/demo/index.html, avoiding browser default/favicon.ico404s.
[0.9.13] - 2026-02-17¶
Fixed¶
- GitHub Pages demo walkthrough actions are now deterministic and WASM-only for investor flow: steps 1-7 target the correct symbols/files,
Shift+F12andF2are triggered through Monaco actions, cross-file navigation opens in-editor via a registered Monaco editor opener, and bundled Monaco now includes the required references/rename contributions to keep references/rename/go-to-definition working in Pages builds.
[0.9.12] - 2026-02-17¶
Fixed¶
- GitHub Pages demo strict-WASM LSP cursor anchoring is now delimiter-aware (
.,#,:boundaries): completion/definition/references/rename retry nearby identifier edges, context-menu requests set the cursor to the clicked symbol, and rename preflight resolves identifier ranges from the same fallback matrix. - Browser analysis worker-recovery checks now also run demo cursor-position fallback regressions (
docs/demo/lsp-position-resolver.test.mjs) and no longer fail hard when the legacy prototype test fixture path is absent. - Added local GitHub Pages replica serving for the demo (
scripts/serve_demo_local_replica.py,scripts/run_demo_local_replica.sh) with/<repo>/routing and no-cache headers so local behavior matches deployed Pages URLs.
[0.9.11] - 2026-02-17¶
Fixed¶
- Browser IDE/WASM LSP navigation for structured type fields is now project-wide:
Go to Definition,Go to References, and rename now resolveSTRUCT/UNIONmembers across files (fixingNo definition/references foundon demo symbols likeEnable,ActualSpeed, andramp). - GitHub Pages demo rename wiring now consumes WASM rename edits directly, restoring
F2rename in Monaco, and walkthrough instructions now match the exact Monaco UI labels/actions (Ctrl+Left-click,Go to References,Fn+F2note for laptops). - GitHub Pages demo LSP request handling is now resilient on slower browser hosts: WASM timeouts were raised for diagnostics/navigation/rename/completion, Monaco cursor boundary fallback now retries nearby symbol offsets for strict WASM requests, URI mapping accepts normalized location schemes, and diagnostics walkthrough text now reflects a clean-by-default project (use a temporary typo to demonstrate squiggles).
- IDE symbol-target resolution now anchors token/scope lookup to the resolved identifier range (not raw punctuation cursor token), fixing
Go to Definition, references, and rename misses when the cursor lands on adjacent punctuation in demo/editor workflows. - GitHub Pages demo release packaging now rebuilds and ships refreshed
docs/demo/wasm/artifacts from the currenttrust-wasm-analysisengine so deployed Pages behavior matches committed source fixes.
[0.9.9] - 2026-02-16¶
Fixed¶
- Browser WASM completion ranking now prioritizes typed-prefix matches before result limiting, and completion context recovery now keeps in-scope program variables available during parser recovery edits (fixing missing
Status/Pumpsuggestions in the demo).
[0.9.8] - 2026-02-16¶
Fixed¶
- GitHub Pages demo language features are now strict WASM/LSP-only (no local fallback synthesis), completion request flow is hardened via deduplicated document sync and longer WASM request budgets for slower devices, and FB hover signatures now recover declared member type text from source when semantic type IDs are unresolved.
[0.9.6] - 2026-02-16¶
Changed¶
- Web IDE full implementation closure for
/ide: - Added workspace tree + filesystem mutation endpoints and UI flows (
/api/ide/tree,/api/ide/fs/create,/api/ide/fs/rename,/api/ide/fs/move,/api/ide/fs/delete) with conflict-safe errors. - Added IDE navigation/search surface: quick open, workspace text search with include/exclude globs, file/workspace symbol search, and command-palette actions.
- Added project-aware language navigation endpoints and UI wiring for go-to-definition, references, and rename (
/api/ide/definition,/api/ide/references,/api/ide/rename) with cross-file analysis context. - Added build/test/validate task orchestration endpoints (
/api/ide/build,/api/ide/test,/api/ide/validate,/api/ide/task) with streaming output, parsed source-location links, and retry UX in the browser IDE. - Added browser IDE format endpoint and command flow (
/api/ide/format) so formatted content can be applied from the editor command surface. - Added filesystem mutation audit trail endpoint (
/api/ide/fs/audit) and health counters (fs_mutation_events) for IDE security observability. - Added dedicated Web IDE contract/performance/security integration coverage in
crates/trust-runtime/tests/web_ide_integration.rsand parser/unit coverage incrates/trust-runtime/src/web.rs+crates/trust-runtime/src/web/ide.rs. - Added a standalone static browser demo at
docs/demo/with all 7 LSP features running fully client-side through WebAssembly, plus GitHub Pages deployment workflow (.github/workflows/demo-pages.yml) for client sharing. - Added Browser IDE runtime docs for
/ideindocs/guides/WEB_IDE_FULL_BROWSER_GUIDE.mdand linked them fromREADME.mdanddocs/README.md.
Fixed¶
- Browser analysis worker-recovery CI gate now includes the Node test fixture file (
docs/internal/prototypes/browser_analysis_wasm_spike/web/analysis-client.test.mjs) in tracked repository contents, fixing missing-file failures on GitHub runners.
[0.9.5] - 2026-02-16¶
Changed¶
- Web IDE project selection flow:
- Added
/api/ide/projectand/api/ide/project/opento query/switch active project root at runtime. - Added no-bundle startup support for
trust-runtime runso/idecan start first and open a project folder from the browser. - Added integration coverage for no-bundle project-open flow in
crates/trust-runtime/tests/web_ide_integration.rs. - Web IDE workspace scope hardening:
- Changed IDE file/tree scope from fixed
<project>/srcto full active project root with hidden/system path filtering. - Added project-aware command execution guard so build/test/validate require an active project selection.
- Added task status timestamps in
/idetask panel (started/finished) to satisfy workflow traceability requirements. - Web IDE visual styling now aligns with the runtime page sidebar theme treatment (surface, spacing, and footer layout) for a consistent
/and/ideexperience. - Web IDE authoring gating no longer depends on runtime
control_mode=debug; editor capability is now enforced via web auth role and IDE session role (viewer/editor) at API boundaries. - Web IDE performance gates were tightened to product targets (completion/hover p95 <= 150ms, diagnostics p95 <= 300ms, workspace search p95 <= 400ms) in automated integration checks.
Fixed¶
- Web IDE hover behavior now follows VS Code semantics by relying on Monaco hover tooltips; the redundant Output-side Hover panel was removed to prevent duplicate/stale hover text.
- Web IDE hover provider failures are now logged as
[ide] hover failed:in browser console output to improve diagnostics when hover requests fail. - Web IDE analysis cache refresh now skips unrelated unreadable/oversized
.stfiles so hover/completion keep working for the active source, Monaco hover/suggest widgets now use overflow-safe rendering to avoid hidden popups, and/ideassets are served withCache-Control: no-storeso browser refreshes pick up the latest IDE fixes. - Web IDE hover/completion hardening now normalizes Monaco hover content payloads, falls back to local symbol suggestions when completion analysis is unavailable, and debounces hover popup triggering on mouse idle so behavior matches VS Code more closely on Chromium-based runtime kiosks.
[0.9.3] - 2026-02-15¶
Changed¶
- VS Code statechart automated coverage:
- Added editor lifecycle test coverage to verify running statechart sessions are cleaned up when a custom editor panel is disposed.
- Added state machine engine behavior tests for awaited hardware action ordering and fail-closed guard evaluation paths.
- Added runtime client timeout cleanup coverage to ensure request listeners are removed on timeout/error.
- VS Code statechart extension quality gates now lint TSX sources and run explicit webview TypeScript typechecking (
tsconfig.webview.json) during compile. - Statechart import command flow now consistently resolves source/target paths and reuses shared URI/path helpers across statechart commands.
- Statechart hardware helper scripts/docs now use repository-relative paths and group-based socket permissions (
660) instead of world-writable sockets. - Statechart operator/developer guides and helper script output are now standardized to English for consistent repository documentation language.
Fixed¶
- VS Code statechart custom editor packaging now loads the webview template from bundled extension code instead of
src/**runtime paths excluded by.vscodeignore. - VS Code statechart editor lifecycle now stops active execution sessions when the panel closes, ensuring timers/runtime connections are cleaned up.
- State machine engine transition execution now awaits exit/transition/entry hardware actions before completing transitions.
- Hardware-mode guard handling now fails closed on parse/read/runtime errors instead of allowing unsafe transitions.
- Runtime control client request timeout/error handling now removes per-request listeners reliably to prevent listener leaks.
- Statechart traffic-light example now starts from a valid non-dead initial state (
Red).
[0.9.2] - 2026-02-15¶
Fixed¶
- VS Code extension project workflows now follow
src/-based projects consistently: ST test run root detection acceptssrc, PLCopen export integration coverage usessrc, and new-project scaffolding no longer generates legacysources/.
[0.9.1] - 2026-02-15¶
Changed¶
- Standardized example I/O mapping patterns to use
VAR_CONFIGbindings (instead of direct%I/%Qdeclarations inVAR/VAR_GLOBAL) across communication, EtherCAT, Siemens, Mitsubishi, and unit-testing tutorial projects. - Updated example documentation snippets to reflect
PROGRAMvariables plusCONFIGURATION-levelVAR_CONFIGwiring as the recommended deterministic pattern. - Project source layout is now
src/only across runtime/CLI flows: trust-runtime build,test,docs, andrun --projectnow require<project>/srcas the default source root.- Legacy
<project>/sourcesfallback resolution was removed from bundle/project source lookup. - Setup/wizard-generated projects and shipped tutorials/examples now use
src/consistently. - PLCopen import/export project-root resolution now targets
src/only. trust-runtime setupauto-migrates an existing<project>/sourcesfolder to<project>/srcwhen preparing a project.
[0.9.0] - 2026-02-14¶
Changed¶
- HMI Phase 0 scaffold start:
- Added runtime scaffold engine APIs
scaffold_hmi_dirandscaffold_hmi_dir_with_sourcesincrates/trust-runtime/src/hmi.rsto generate deterministichmi/directory artifacts from source metadata. - Added
trust-runtime hmi initCLI workflow (crates/trust-runtime/src/bin/trust-runtime/hmi.rs) with style selection (industrial|classic|mint) and deterministic generation summary output. - Added
trust-lsp.hmiInitworkspace/executeCommandsupport incrates/trust-lsp/src/handlers/commands.rs, reusing runtime scaffold generation from workspace sources and returning structured summary payloads. - Added VS Code LM tool
trust_hmi_initineditors/vscode/src/lm-tools.tsandeditors/vscode/package.json, routing HMI scaffold generation through the new LSP command path. - Added Phase 0 runtime scaffold tests for external symbol filtering, widget mapping by writability/type, deterministic output stability, and repeated-instance section grouping.
- Converted
docs/internal/testing/checklists/hmi-complete-implementation-checklist.mdinto a tracked execution board with active Phase 0 lanes and work item IDs. - HMI Phase 1.6 live descriptor refresh:
- Added runtime descriptor watcher (
notify) with debounce forhmi/*.tomlupdates and safe hot-reload without runtime restart. - Added
schema_revisiontohmi.schema.getpayloads and wired in-memory revision bumps on successful descriptor reload. - Added fail-safe invalid descriptor handling that retains the last known-good schema and prevents runtime crashes on bad TOML edits.
- HMI Phase 2.1 section layout foundation:
- Added schema-driven section rendering in web HMI (
page.sections) with responsive 12-column spans for sections/widgets. - Added explicit fallback to legacy group card rendering when section metadata is absent, preserving existing trend/alarm/dashboard behavior.
- Added widget renderers for
gauge,sparkline,bar,tank,indicator,toggle, andsliderwith value updates and existinghmi.writeintegration for writable controls. - Added transition/micro-animation behavior for gauge/bar/tank/indicator/toggle updates and dark-mode CSS variable overrides via
prefers-color-scheme: dark. - Added integration coverage for section layout assets, renderer assets, and schema span metadata used by the web renderer.
- HMI Phase 2.4 live transport foundation:
- Added
/ws/hmiwebsocket endpoint in the embedded web server for push updates (hmi.values.delta,hmi.schema.revision,hmi.alarms.event). - Added web HMI client websocket transport with reconnect/backoff and automatic fallback to HTTP polling when websocket is unavailable.
- Added schema live-refresh behavior in the web client to re-fetch full
hmi.schema.getpayloads onschema_revisionevents. - Added integration coverage for websocket event flow and export transport metadata (
/ws/hmiroute +config.ws_route). - Added websocket hardening integration gates for local latency SLO budgets (p95/p99), forced socket-failure polling recovery, and reconnect churn stability.
- HMI Phase 2.5 process-page foundation:
- Added process-page schema support (
kind = "process") with page-level SVG asset and bind metadata (svg,bindings) in runtime HMI contracts. - Added secure SVG asset serving route (
/hmi/assets/<svg>) with path containment checks and SVG-only file restrictions. - Added process bind parsing guardrails for safe selector/attribute usage plus optional
format,map, andscaletransform contracts. - Added web client process renderer path to inline SVG assets and apply live bind updates from HMI value streams.
- Added integration coverage for process schema metadata, asset serving, and negative filtering of unsafe/missing bind selectors.
- HMI Phase 2 completion hardening:
- Added renderer-state regression coverage for all new widget kinds (
gauge,sparkline,bar,tank,indicator,toggle,slider) across null/stale/good value paths. - Added responsive breakpoint regression checks for desktop/tablet/mobile class behavior in the web HMI layout engine.
- Added process asset-pack integrity checks for
hmi/pid-symbols/licensing/library presence, stable IDs inhmi/plant.svgandhmi/plant-minimal.svg, and selector alignment inhmi/plant.bindings.example.toml. - HMI Phase 3 core command/tool surface:
- Added LSP command
trust-lsp.hmiBindingswith workspace-root/file scoping and deterministic bindings catalog payloads (programs,globals) including qualifier/writable metadata and available constraints (unit,min,max,enum_values). - Added VS Code LM tools
trust_hmi_get_bindings,trust_hmi_get_layout, andtrust_hmi_apply_patchwith dry-run conflict reporting for descriptor file operations. - Added VS Code command registrations
trust-lsp.hmi.initandtrust-lsp.hmi.refreshFromDescriptorand integrated refresh invocation from HMI patch application flows. - Added VS Code HMI integration coverage for LM tool valid/invalid payload handling and cancellation behavior.
- HMI Phase 3.6/3.7 completion:
- Added LSP diagnostics for
hmi/*.tomlfiles with stable codes for parse failures, unknown bind paths, type/widget mismatches, and invalid widget property combinations. - Added near-match suggestion hints for unknown binding paths in HMI descriptor diagnostics.
- Added multi-root LM-tool integration coverage to verify explicit
rootPathresolution and write routing fortrust_hmi_apply_patch/trust_hmi_get_layout. - HMI Phase 4 panel refresh foundation:
- Expanded VS Code HMI preview refresh relevance to
hmi/*.tomlandhmi/*.svg. - Added debounced filesystem watching of HMI descriptor/assets (
**/hmi/*.{toml,svg}) so open panels refresh on descriptor edits without forced auto-open behavior. - Added section-aware panel rendering (
page.sections+widget_span) for dashboard pages. - Added process-page panel rendering (
kind = "process") with safe binding updates and localhmi/*.svgasset hydration in VS Code preview. - HMI Phase 5 export bundle foundation:
- Updated
/hmi/export.jsonpayload contract toversion = 2. - Added
config.descriptorto export payloads with the resolved livehmi/descriptor content when present (ornullwhen running legacy/no-descriptor projects). - Added export integration coverage for descriptor-backed bundles and renderer-capability assertions in exported
hmi/app.js. - HMI Phase 6 intent-to-evidence foundation:
- Added VS Code LM tool
trust_hmi_plan_intentto generate/update deterministichmi/_intent.tomlartifacts with operator goals, personas, KPI priorities, and constraints. - Added VS Code LM tool
trust_hmi_trace_capturefor deterministic scenario-tagged API-level runtime trace capture (hmi.schema.get/hmi.values.get) intohmi/_evidence/<run>/trace-<scenario>.json. - Added VS Code LM tool
trust_hmi_generate_candidatesfor scaffold-rules-based deterministic candidate generation and ranking by readability/action-latency/alarm-salience metrics withcandidates.jsonevidence output. - Added VS Code LM tool
trust_hmi_validateto run machine-readable HMI validation checks, emit deterministichmi/_lock.json, and write evidence runs underhmi/_evidence/<timestamp>/. - Added VS Code LM tool
trust_hmi_preview_snapshotto emit deterministic desktop/tablet/mobile snapshot artifacts underhmi/_evidence/<run>/screenshots/. - Added VS Code LM tool
trust_hmi_run_journeyfor API/event-level operator journey execution (no headless browser requirement) with pass/fail timing metrics injourneys.json. - Added VS Code LM tool
trust_hmi_explain_widgetfor widget provenance reporting (canonical ID, symbol/type, write-policy allowlist state, and contract endpoint mapping). - Hardened
trust_hmi_run_journeywith tool-side write guardrails (read-only/disabled/allowlist checks) and machine-readable step codes before issuing runtimehmi.writerequests. - Added validation retention support (
prune+retain_runs, default 10) and evidence pruning behavior intrust_hmi_validate. - Added integration tests covering intent generation, candidate-ranking determinism, trace capture, validation artifact emission, snapshot output, API/event journey execution, unauthorized-write safety handling, evidence retention pruning, lock determinism, and cancellation handling for the full Phase 6 toolset.
- Added runtime hardening regressions for write-cycle budget checks, websocket slow-consumer/backpressure stability, malformed process-SVG resilience, and rapid descriptor file-churn no-deadlock behavior.
- Added default gitignore rule for
hmi/_evidence/artifacts. - Guided example/tutorial expansion:
- Added
examples/README.mdas a structured tutorial index with recommended learning order and setup checklist. - Added
examples/tutorials/12_hmi_pid_process_dashboard/as a full process-HMI tutorial with a runnable PLC model, descriptor-drivenhmi/pages, production-stylekind = "process"SVG bindings, setpoint/deviation/alarm coverage, and step-by-step build/run/customization guidance (including screenshot/GIF capture workflow). - Added a full walkthrough for
examples/filling_line/README.md(run, I/O mapping, expected outcomes, and tuning exercise). - Reworked example READMEs (
plant_demo,memory_marker_counter,siemens_scl_v1,mitsubishi_gxworks3_v1,ethercat_ek1100_elx008_v1,plcopen_xml_st_complete,tutorials) into detailed step-by-step VS Code setup guides. - Added
examples/vendor_library_stubs/as a tutorial for user-provided vendor library stub indexing ([[libraries]]) with Siemens-style sample declarations. - Removed
examples/openplc_interop_v1/(content absorbed intoexamples/plcopen_xml_st_complete/). - Removed
examples/browser_analysis_wasm_spike/(prototype assets moved todocs/internal/prototypes/browser_analysis_wasm_spike/). - HMI scaffold update behavior now skips regenerating default
process.tomlwhen curated custom process pages already exist, and skips creating emptycontrol.tomlwhen no writable points are discovered. - HMI auto-schematic Process scaffold now enforces deterministic grid/anchor rules (shared FIT/PT instrument template geometry, value offset one grid row above sensor centerline, stems snapped to process line, connector-anchored routing) and level-fill bindings now update both
yandheightfor percent-consistent tank visuals. - Tutorial
12_hmi_pid_process_dashboardnow uses grid-aligned P&ID SVG layouts (hiddenpid-layout-guideslayer) with compact overview inventory widgets and cleaned page set (no duplicate scaffoldProcesspage / emptyControlpage). - Tutorial
12_hmi_pid_process_dashboardProcess/Bypass P&ID pages now use denser operator layout (integrated status rail, calibrated tank scales, stronger typography hierarchy, and ISA-style symbol/line cleanup) with synchronized level-fill scaling. - Tutorial
12_hmi_pid_process_dashboardProcess/Bypass P&ID pages now remove duplicated PV summary strip and fix tank header text overlap while keeping tank fill scaling aligned to visible geometry. - Tutorial
12_hmi_pid_process_dashboardProcess/Bypass P&ID pages now render pipe runs without the extra lane background panel. - Added grouped communication examples under
examples/communication/for Modbus/TCP, MQTT, OPC UA, EtherCAT, GPIO, and composed multi-driver setups, and documented protocol transport gates (includingethercat-wireunix hardware scope andopcua-wirerequirement) in examples/tutorial indexes and guide docs (PLC_IO_BINDING_GUIDE.md,PLC_NETWORKING.md,PLC_DEVELOPER_GUIDE.md). - Restored the field-tested EtherCAT
EK1100 + EL2008profile asexamples/communication/ethercat_field_validated_es/(Spanish operator-focused commissioning walkthrough) so communication examples now include two EtherCAT tracks. - Added automated runtime CLI regression coverage for grouped communication examples via
crates/trust-runtime/tests/communication_examples_cli.rs, verifyingbuild --sources srcandvalidateformodbus_tcp,mqtt,opcua,ethercat,gpio, andmulti_driverin CI/test runs. - Added advanced operations tutorial
23_observability_historian_prometheusand expanded16_secure_remote_accesswith explicit TLS commissioning/validation steps so observability and remote-hardening flows are covered end-to-end in the examples track.
[0.7.16] - 2026-02-14¶
Changed¶
- EtherCAT bring-up examples:
- Added
examples/ethercat_ek1100_elx008_v2/for EK1100 + EL2008-only hardware chains with a validated 8-output snake pattern. - Added helper run scripts and docs for real-NIC bring-up and deterministic mock-mode fallback.
[0.7.15] - 2026-02-13¶
Changed¶
- Direct Siemens TIA source handoff via export adapters:
trust-runtime plcopen export --target siemensnow emits a Siemens SCL sidecar bundle (<output>.scl/*.scl) alongside PLCopen XML.- Siemens-target export reports now include
siemens_scl_bundle_dirandsiemens_scl_files[]for automation and CI evidence. - Added dedicated Siemens import tutorial with exact TIA path (
External source files->Add new external file->Generate blocks):docs/guides/SIEMENS_TIA_SCL_IMPORT_TUTORIAL.md. - Added runtime + CLI regression coverage for Siemens SCL bundle generation and
PROGRAMtoORGANIZATION_BLOCKconversion.
[0.7.14] - 2026-02-12¶
Changed¶
- VS Code PLCopen import workflow:
- Added command
Structured Text: Import PLCopen XML(trust-lsp.plcopen.import) for CLI-backed PLCopen project import from the editor. - Command flow prompts for input XML + target project folder, validates conflict paths, runs
trust-runtime plcopen import --json, and offers quick access to the generated migration report. - Added VS Code integration coverage for success/cancel/conflict/invalid-input paths in
editors/vscode/src/test/suite/plcopen-import.test.ts. - Added explicit import usage docs in
README.md,docs/README.md,editors/vscode/README.md, and PLCopen example READMEs. - OpenPLC Interop v1 deliverable closure:
- Added dedicated OpenPLC migration guide:
docs/guides/OPENPLC_INTEROP_V1.md. - Added OpenPLC fixture and walkthrough coverage inside the PLCopen ST-complete bundle:
examples/plcopen_xml_st_complete/README.mdexamples/plcopen_xml_st_complete/interop/openplc.xml
- Added regression coverage to keep the OpenPLC sample bundle executable in CI:
crates/trust-runtime/tests/plcopen_command.rscrates/trust-runtime/tests/tutorial_examples.rs
- truST Browser Analysis Spike (Deliverable 10):
- Added new browser/WASM analysis adapter crate
crates/trust-wasm-analysis/exposing deterministic diagnostics, hover, and completion APIs for virtual-document analysis. - Added JSON boundary wrapper
WasmAnalysisEnginefor worker/browser transport integration (applyDocumentsJson,diagnosticsJson,hoverJson,completionJson,statusJson). - Added parity + performance regression suite against native analysis in
crates/trust-wasm-analysis/tests/mp010_parity.rs. - Added browser worker host prototype assets and build pipeline:
docs/internal/prototypes/browser_analysis_wasm_spike/scripts/build_browser_analysis_wasm_spike.shscripts/check_mp010_browser_analysis.sh
- Published scope contract and evidence report:
docs/guides/BROWSER_ANALYSIS_WASM_SPIKE.mddocs/reports/browser-analysis-wasm-spike-20260212.md
- ST unit-testing tutorials:
examples/tutorials/10_unit_testing_101/examples/tutorials/11_unit_testing_102/trust-runtime build --sources <relative-path>now resolves relative source overrides from--project, avoiding path-resolution mismatches from external working directories.
Fixed¶
trust-runtime validateno longer reports falseundefined programerrors for valid bundles; it now validates decoded bytecode/module metadata directly and enforces task-program name integrity at bytecode-validation time.- Bytecode encoding/validation now handles enum-typed constant payloads correctly during module build/validation.
[0.7.10] - 2026-02-12¶
Changed¶
- PLCopen ecosystem detection now recognizes OpenPLC markers (
openplc) for migration reporting/shim selection, with OpenPLC fixture-backed regression coverage. - PLCopen vendor-library shim matching now also normalizes keyword-style vendor aliases (for example
R_EDGE) when used in supported type/call positions.
[0.7.9] - 2026-02-12¶
Fixed¶
- CI Windows build no longer fails on missing
wpcap.libwhenethercat-wireis enabled by default; EtherCAT wire dependency wiring is now unix-target gated while preserving mock-driver support cross-platform. - MP-001 parity baseline updated for newly added Mitsubishi LSP regression tests so discovery parity gate remains deterministic.
[0.7.8] - 2026-02-12¶
Changed¶
- EtherCAT Backend v1 (Deliverable 9):
- Added new runtime I/O driver profile:
io.driver = "ethercat"with EtherCrab-backed hardware transport (adapter = "<nic>") and deterministic mock transport mode (adapter = "mock"). - Added module-chain process-image mapping contract for Beckhoff-style digital I/O profiles (
EK1100,EL1008,EL2008) with size-check diagnostics. - Added startup/discovery diagnostics and cycle-time health telemetry (
ok/degraded/faulted) with driver error policy handling. - Added EtherCAT deterministic integration coverage and runtime example project:
crates/trust-runtime/tests/ethercat_driver.rsexamples/ethercat_ek1100_elx008_v1/
- Published EtherCAT backend guide with scope boundaries and compliance checkpoint:
docs/guides/ETHERCAT_BACKEND_V1.md
- Browser/WASM position mapping now uses UTF-16 column semantics for protocol compatibility in
trust-wasm-analysisrange/position conversions.
[0.7.7] - 2026-02-12¶
Changed¶
- Mitsubishi GX Works3 Compatibility v1 (Deliverable 8):
- Added Mitsubishi vendor profile support in LSP tooling (
vendor_profile = "mitsubishi"and aliasgxworks3) for formatting, stdlib selection defaults, and diagnostics rule-pack aliases. - Added native
DIFU/DIFDedge-alias support in semantic/runtime builtins (mapped to IECR_TRIG/F_TRIGbehavior) for normal ST authoring and execution. - Added Mitsubishi GX Works3 example project:
examples/mitsubishi_gxworks3_v1/. - Added compatibility guide with supported subset, incompatibilities, and migration guidance:
docs/guides/MITSUBISHI_GXWORKS3_COMPATIBILITY.md. - Added dedicated regression coverage across HIR semantics, runtime edge behavior, LSP formatting/diagnostics, and example compile tests.
[0.7.6] - 2026-02-12¶
Changed¶
- Multi-vendor Export Adapters v1 (Deliverable 7):
trust-runtime plcopen exportnow supports--target <generic|ab|siemens|schneider>for vendor-targeted interchange artifacts.- Export JSON contract now includes target adapter evidence fields:
target,adapter_report_path,adapter_diagnostics,adapter_manual_steps, andadapter_limitations. - Vendor-target exports now emit deterministic sidecar adapter reports (
<output>.adapter-report.json) and embeddedtrust.exportAdaptermetadata inaddData. - Published target-specific limitations/manual migration steps guide:
docs/guides/PLCOPEN_EXPORT_ADAPTERS_V1.md.
[0.7.5] - 2026-02-12¶
Changed¶
- Editor Expansion v1 (Deliverable 6):
- Official Neovim setup pack published with reference
nvim-lspconfigprofile and workflow keymaps:editors/neovim/. - Official Zed setup pack published with reference language-server profile:
editors/zed/. - Editor setup/validation guide published:
docs/guides/EDITOR_SETUP_NEOVIM_ZED.md. - New editor smoke gate script
scripts/check_editor_integration_smoke.shvalidates editor config contracts and runs targeted LSP workflow tests for diagnostics/hover/completion/formatting/definition. - CI release-gate aggregation now includes a dedicated
Editor Expansion Smokegate for Neovim/Zed integration coverage.
[0.7.4] - 2026-02-12¶
Changed¶
- PLCopen XML Full ST Project Coverage (Deliverable 5):
- Profile advanced to
trust-st-complete-v1. trust-runtime plcopen importnow supports full ST-project model import for:types/dataTypes(elementary,derived,array,struct,enum,subrange)instances/configurations/resources/tasks/program instances
trust-runtime plcopen exportnow emits supported STTYPEdeclarations and configuration/resource/task/program-instance model back into PLCopen XML.- Import/export JSON contracts now include deterministic ST-project coverage counters:
data_type_count,configuration_count,resource_count,task_count,program_instance_count(export)imported_data_types,discovered_configurations,imported_configurations,imported_resources,imported_tasks,imported_program_instances(import/migration)
- Added CODESYS ST-complete fixture packs (
small/medium/large) with deterministic expected migration artifacts and CI schema-drift parity gate incrates/trust-runtime/tests/plcopen_st_complete_parity.rs. - Updated PLCopen compatibility/spec docs and added end-to-end import/export example project in
examples/plcopen_xml_st_complete/.
[0.7.3] - 2026-02-11¶
Fixed¶
- Release workflow hardening:
- VS Code Marketplace publish now runs per-VSIX with retry/backoff on transient network timeouts and treats already-published artifacts as idempotent success for reruns.
[0.7.2] - 2026-02-11¶
Fixed¶
version-release-guardnow tolerates short ordering races betweenmainand tag pushes by polling for the expected version tag before failing.
[0.7.1] - 2026-02-11¶
Changed¶
- VS Code extension marketplace metadata now declares dual-license SPDX (
MIT OR Apache-2.0) and monorepo repository directory (editors/vscode).
Fixed¶
- GitHub license detection no longer reports an extra
Unknownlicense entry after removing the non-standard rootLICENSEstub (dual-license files remainLICENSE-MITandLICENSE-APACHE). - VS Code Marketplace screenshots now use absolute image URLs from GitHub raw content so images render reliably in extension listing pages.
[0.7.0] - 2026-02-11¶
Changed¶
- Vendor Library Compatibility Baseline (Deliverable 4):
trust-runtime plcopen importnow applies deterministic vendor-library shim mappings for selected Siemens, Rockwell, Schneider/CODESYS, and Mitsubishi aliases.- Import/migration JSON contracts now include
applied_library_shimswith vendor/source/replacement/occurrence metadata. - Vendor-library compatibility matrix and shim catalog published in
docs/guides/VENDOR_LIBRARY_COMPATIBILITY.md. - Siemens SCL Compatibility v1 (Deliverable 3):
- Siemens-style
#-prefixed local references now parse in expression and statement contexts (includingFORloop control variables). - Siemens SCL compatibility guide published:
docs/guides/SIEMENS_SCL_COMPATIBILITY.md. - Siemens SCL example project added:
examples/siemens_scl_v1/. - Regression coverage added across parser, LSP formatting/diagnostics, and runtime example compile tests.
trust-runtime plcopen importcompatibility scoring now accounts for shimmed vendor-library aliases as partial-coverage items.- PLCopen ecosystem detection now recognizes Mitsubishi GX Works markers (
mitsubishi-gxworks3) for migration reporting/shim selection.
Fixed¶
- Parser diagnostics now report a targeted error (
expected identifier after '#') for malformed Siemens SCL#local-reference syntax instead of generic expression errors. - Release packaging metadata:
- VS Code extension package versions are now aligned to the workspace release version to avoid duplicate publish artifacts from prior extension versions.
[0.5.0] - 2026-02-11¶
Changed¶
- PLCopen Interop Hardening (Deliverable 2):
- expanded migration fixture coverage for major ecosystems (
codesys,beckhoff-twincat,siemens-tia,rockwell-studio5000,schneider-ecostruxure) - structured unsupported-node diagnostics in migration reports with code/severity/node/action metadata
- explicit compatibility coverage summary in import/migration reports (
supported_items,partial_items,unsupported_items,support_percent,verdict) - dedicated compatibility/limits guide:
docs/guides/PLCOPEN_INTEROP_COMPATIBILITY.md trust-runtime plcopen exportandtrust-runtime plcopen importnow support--jsonfor machine-readable report output.trust-runtime plcopen profilenow publishes a compatibility matrix plus round-trip limits/known-gaps contract fields.
Fixed¶
- Schneider EcoStruxure vendor detection is now distinct from generic CODESYS-family heuristics in PLCopen migration reports.
[0.4.0] - 2026-02-10¶
Changed¶
- Conformance Suite MVP shipped (Deliverable 1):
- deterministic case pack + versioned expected artifacts in
conformance/cases/andconformance/expected/ - coverage for timers (TON/TOF/TP), edges, scan-cycle ordering, init/reset, arithmetic corner cases, and mapped memory behavior
- negative/error-path coverage for runtime overflow behavior and unresolved wildcard mapping compile errors
- external run guide and submission process (
conformance/external-run-guide.md,conformance/submissions.md) - explicit known-gaps register (
conformance/known-gaps.md) trust-runtime conformanceCLI runner mode:- deterministic
case_idordering - machine-readable JSON summary contract (
trust-conformance-v1) - stable failure reason taxonomy (
conformance/failure-taxonomy.md) --update-expectedmode for deterministic artifact refresh- CI now includes a dedicated conformance gate with repeated-run deterministic comparison.
[0.3.0] - 2026-02-10¶
Changed¶
- New ST assertion functions in runtime/hir:
ASSERT_NOT_EQUALASSERT_GREATERASSERT_LESSASSERT_GREATER_OR_EQUALASSERT_LESS_OR_EQUALtrust-runtime test --listto discover test names without executing.trust-runtime test --timeout <seconds>for per-test execution timeout.- CLI/integration tests for list/filter/timeout behavior and JSON duration fields.
trust-runtime testoutput now reports per-test elapsed time and total elapsed time in human output.trust-runtime test --output jsonnow includesduration_msper test and in summary.- Tutorial 10/11 docs updated for list/timeout usage and expanded assertion coverage.
Fixed¶
Harness::run_untilnow has a default cycle guard and explicitrun_until_maxlimit to prevent hangs.- Filtered test runs now clearly report when zero tests match but tests were discovered.
[0.2.0] - 2026-02-10¶
Changed¶
- Salsa hardening gates and overnight validation scripts/reports:
scripts/salsa_*_gate.shscripts/salsa_overnight_hardening.shdocs/reports/salsa-overnight-hardening-20260209.md- Runtime/UI multi-driver coverage and integration tests for Modbus + MQTT.
- Migrated
trust-hirsemantic path to Salsa-only backend and upgraded Salsa to0.26. - Enabled VS Code extension integration tests in CI under virtual display (
xvfb). - Expanded cancellation checks in workspace-scale LSP operations.
- Documentation organization:
- Public durable reports remain in
docs/reports/. - Working remediation checklists are no longer published in
docs/reports/.
Fixed¶
%MWmemory marker force/write synchronization in runtime I/O panel flow.- Debug adapter force latch behavior and state-lock interaction.
- Debug runner now respects configured task interval pacing.
- Windows CI/test path issues (
PathBufimport and path hygiene guardrails).