TIATool capabilities — and what is missing, broken, impossible, or unproven
Generated from docs/capabilities.json by python3 tools/capabilities.py render — edit the
JSON, not this file. The same registry is served to AI clients as tiatool://capabilities,
and non-proven MCP tools carry a status line in their description. A live selftest
(tiax selftest --live) overlays selftest_verified / regressions on the machine it ran on.
Legend:
- proven — proven against a real project
- selftest_verified — passed the live selftest on a machine (overlay; never committed)
- unit_tested — logic covered by unit tests; not yet confirmed on a live TIA project
- unverified — code exists, never run against TIA Portal
- not_implemented — Openness allows it, TIATool does not do it yet
- impossible — platform limit, tested and documented
Totals: proven 17 · unit_tested 56 · unverified 20 · not_implemented 6 · impossible 9
1. Understanding a project (no TIA Portal needed)
| Capability | How | Status | Evidence / caveats |
|---|---|---|---|
Full PLC export → AI-readable JSON cache cache.scan_plc |
export → scan (.tiax: index.jsonl + per-artifact records, fidelity taxonomy) — MCP project_stats, list_blocks, get_block, refresh_scan |
proven | Reference machines; V19 Project_C 170 artifacts 100% readable (2026-09-01). Caveat: Format 3 (0.5) adds remanence/External* and pin-correct LAD/FBD writes: re-scan older caches. See docs/tiax/format-3.md |
Full HMI export → cache (screens, tag tables, text lists, cycles) cache.scan_hmi |
same pipeline; format ≥ 2 — MCP list_screens, get_screen |
proven | 34/34 reference artifacts native. Caveat: Classic WinCC only; Unified export/scan is M5. |
Render any block as SCL cache.scl_render |
scl (SCL native; LAD/FBD from the wire graph; STL lifted) — MCP get_block |
proven | Every FlgNet rendering is audited against a census of the raw XML. Caveat: LAD/FBD gaps (OR rails into coils, EN-rail Inc/Add, RET, negated coils/pins) are stamped // TIAX-LOSSY and the network fidelity becomes lossy. Never judge an interlock from a lossy rendering. See knowledge/openness/scl-render-drops-parallel-rails-and-coils.md |
Who writes / who reads a tag, with the logic cache.who_writes |
query writes/reads, MCP who_writes_tag / who_reads_tag — MCP who_writes_tag, who_reads_tag |
unit_tested | 0.5: one pin table (FlgNetPins) — coil-family operands are writes, edge bits read+write, call InOut read+write; SCL calls walked; DirectionTests. Caveat: Windows confirmation pending: re-scan Project_C and check pcValveControl network 2 lists its writes. Caveat: STL and GRAPH produce no edges by design; SCL direction is medium confidence. See knowledge/simatic-ml/flgnet-sink-parts.md |
Call graph, callers, dead-code candidates, call sites with bound actuals cache.call_graph |
query callers/calls/unreachable; call_sites[] per network (0.5) — MCP who_calls, what_block_calls, unreachable_blocks |
proven | CallInfo-based, not name matching. Caveat: call_sites[] is new in format 3 (unit_tested). |
Free-text search incl. HMI texts (multilingual) cache.search |
query search, MCP search_project (SQLite FTS5 trigram under tiax-agent) — MCP search_project |
proven | |
HMI tag ↔ screens map; where a PLC name appears on the panel cache.hmi_map |
query hmi-map/hmi-uses — MCP hmi_tag_map, where_is_tag_used_on_hmi |
proven | Caveat: Matched BY NAME — the SIMATIC ML export does not carry the HMI→PLC ControllerTag link; every answer says so. See knowledge/openness/controllertag-basic-panels.md |
HTML explorer + Markdown/Mermaid docs cache.report |
report, MCP export_docs — MCP export_docs |
proven | Caveat: PLC only; HMI records not yet in the report. |
Staleness and cache-truth warnings on every cache answer cache.staleness |
built-in — MCP tia_status |
unit_tested | 0.5: culture-proof timestamps (CacheJson), 10 s verdict cache, format-3 truth warning; StalenessTests under en-GB/et-EE. Caveat: Bug fixed in 0.5: day/month were swapped under some regional settings. |
Project audit with persistent findings and verdicts (base + style-scl-v1 profiles) verify.audit |
audit_project, finding_verdict; findings.json — MCP audit_project, finding_verdict |
unit_tested | 0.5: AuditTests — one positive and one negative case per rule; Modbus map self-check per knowledge/patterns/db-is-the-modbus-register-map.md. Caveat: HMI matches are by name (heuristic). Caveat: Declared-never-written is low confidence when STL/GRAPH/lossy blocks exist (their writes are not recorded). See knowledge/patterns/db-is-the-modbus-register-map.md knowledge/standards/hardcoded-db-offsets.md knowledge/simatic-ml/omitted-fb-input-is-silent.md |
Project diff between two caches (incl. remanence/External* per member) cache.diff |
MCP project_diff — MCP project_diff |
unit_tested | ProjectDiff.MemberMap (format 3). |
who_writes / who_reads answers state what they cannot see (STL, GRAPH, lossy, know-how protected) cache.blind_spots |
TiaxIndex.BlindSpots, appended to every who_writes_tag / who_reads_tag answer, with text mentions of the symbol inside blind-spot networks — MCP who_writes_tag, who_reads_tag |
unit_tested | 0.8: FormatThreeTests (STL fixture: counted, mentioned, absent from who_writes) and McpVerbTests (hit and miss answers both carry the statement). Caveat: Text mentions are substring matches on the SCL lift / reconstruction, direction unknown — a pointer to read the raw XML, not an answer. Caveat: The CLI 'tiax query writes/reads' does not print the paragraph yet (MCP only). See knowledge/openness/scl-render-drops-parallel-rails-and-coils.md |
Parallel scan with a byte-identical cache cache.scan_parallel |
ProjectScanner: constants pre-pass, PLC and HMI parsing and record writes in parallel (ScanOptions.MaxParallelism; 1 = single-threaded); ids, collisions, index and rollups in order; a timing line per scan | unit_tested | 0.8: ScannerParallelTests (8 threads vs 1 on a 3-PLC / 3-HMI tree from the fixtures: every cache file byte-identical; delta scan identical to a full one). |
2. Working on a project (TIA Portal required, headless)
| Capability | How | Status | Evidence / caveats |
|---|---|---|---|
Export (read-only by default; compile opt-in) live.export |
export / export_project — MCP export_project |
proven | Default no longer compiles (0.5 also defaults ExportAll compileFirst=false). Caveat: Blocks TIA considers inconsistent are listed in the failure ledger. Caveat: Stations in device groups / distributed IO: fixed in 0.5 (DeviceEnumerator), Windows confirmation pending. |
Device tree, modules, I/O addresses live.devices |
devices / list_devices — MCP list_devices |
unit_tested | 0.5: DeviceEnumerator walks root devices, nested DeviceGroups and UngroupedDevicesGroup (DeviceEnumeratorTests on fakes). Caveat: Windows confirmation pending: OtherProject must show its 16 PROFINET drives. |
Generate blocks from SCL sources, plug/verify hardware, tag tables, delete named blocks/types, dry run live.build |
build / build_from_source — MCP build_from_source |
proven | 24 blocks on the reference machine; 60 orphan types removed in one pass. Caveat: Hardware is plug/verify only; creating stations is M5. See knowledge/openness/external-sources-break-project-search.md |
Compile everything + save live.compile |
compile / compile_project — MCP compile_project |
proven | |
Import one edited block XML surgically (or create as sibling) live.import_block |
import-block / import_block — MCP import_block |
proven | See knowledge/simatic-ml/retain-attributes-are-not-settable-from-scl.md |
Delete leftover external sources + stale XRef live.clean_sources |
clean-sources / clean_sources — MCP clean_sources |
proven | |
Classic WinCC: project languages, screens, per-tag HMI tags live.hmi_classic |
hmi --languages/--screens/--tag-files — MCP hmi_import_screens, hmi_import_tags, add_project_languages |
proven | Caveat: Tags before screens; screens imported twice; one bad construct kills TIA — import one experimental screen at a time. Caveat: Per-tag import cannot set the PLC link on Basic panels. See knowledge/simatic-ml/import-order-and-two-pass.md knowledge/simatic-ml/screen-name-and-import-crash.md |
V19 ↔ V21 transparent dispatch by project extension live.flavours |
tiax.exe ↔ tiax-v21.exe — MCP tia_status |
proven | V19 validated 2026-09-01 against Project_C.ap19. Caveat: V17/V18/V20 flavours build but are unverified. |
Equipment template library (template.json + fb.scl, overridable) gen.templates |
templates/template_list, template_show |
unit_tested | TemplateLibraryTests (load, override chain, SclSelfCheck), TemplateFacadeGoldenTests (generate_equipment byte-identical after migration). Caveat: Template SCL is not compiled on Linux: selftest step 17 builds every built-in on a live TIA project. See knowledge/simatic-ml/version-must-be-two-part.md |
Equipment model → build folder (model_validate / model_generate) gen.model |
model_validate, model_generate |
unit_tested | ModelGenerationTests (golden sample model, validation errors, additive regeneration and refusals, MCP in a workspace); Linux e2e stdio session. Caveat: Generated SCL is not compiled on Linux: build_from_source on a scratch project is on the Windows checklist. Caveat: SimHarness specs that set model-wired inputs only run against bare instances (selftest step 17). See knowledge/patterns/hmi-interface-db-frozen-names.md |
Model helpers: I/O list → draft model, FB → template (model_from_io_list / template_extract) gen.model_helpers |
model_from_io_list groups I/O tags by equipment tag (I_BP1_Running…) and picks the best-fitting template; every guess goes to the assumptions register as to confirm. template_extract turns a scanned FB into a template skeleton (interface → parameters/statuses; body only for native SCL). The build_from_model prompt runs validate → generate → review → build → verify_change → plcsim_test as plan tasks. — MCP model_from_io_list, template_extract |
unit_tested | ModelHelperTests (grouping and template choice, draft validates, MCP in a workspace, extraction from the fixture cache); build_from_model prompt test. Caveat: Naming-pattern matching is heuristic: every instance is a guess until confirmed. Caveat: Extracted templates have no signals, alarms or tests until completed by hand. See knowledge/patterns/hmi-interface-db-frozen-names.md |
HMI from the equipment model (Unified screens JSON, Classic screen/tag XML) gen.hmi |
model_generate also writes HMI/hmi-tags.json, Unified per-area and Overview screen specs (faceplate items, set_bit, visibility, navigation, multilingual alarms) for unified_build, and Classic screen and DB_HMI tag XML for import (C# port of the Python writer, parity-tested). | unit_tested | HmiGenerationTests (Python writer parity, unified specs, classic XML, every built-in template); golden sample build; selftest step 17 (Windows). Caveat: Classic XML import and unified_build of the generated screens are on the Windows checklist. Caveat: Faceplate layouts are templates defaults: review on the panel resolution. See knowledge/patterns/hmi-interface-db-frozen-names.md knowledge/simatic-ml/screenlayer-nesting.md |
Export --mirror (stale files of deleted/renamed blocks removed and reported) + .tiax-export.json manifest live.export_mirror |
export --mirror / --no-mirror (default on in a git work tree or over a previous manifest); manifest: path → type, device, name, Openness ModifiedDate, sha256 — MCP export_project |
unit_tested | 0.8: ExportMirrorTests on staged folders (stale removal, ownership limits, empty run, previous-manifest devices, round trip, byte-stable manifest). Never run against a TIA export. Caveat: Windows confirmation pending: delete a block in TIA, re-export into a git-tracked folder, check the XML is removed and listed. Caveat: ModifiedDate is read with GetAttribute("ModifiedDate") on blocks, tag tables and PLC data types — unverified on TIA; artifacts without it carry no date. Caveat: Only files tiax owns are removed: those in the previous manifest, and *.xml / ExternalSources files inside the device folders the run wrote. Nothing is removed when the run exported nothing. |
Compile WinCC Unified panels through the device-level ICompilable live.compile_unified |
compile / compile_project (also the compile step of build and hmi); compile --hmi compile_project |
unverified | The device-level ICompilable is proven by the PowerShell recipe (§8); the tiax code path compiles against the Openness stub and has never run on TIA. Caveat: Windows confirmation pending: a project with a Unified panel compiles it and reports its errors (previously: 'no compiler service available - NOT compiled'). Caveat: A PC station hosting both a software PLC and Unified RT is compiled as a whole device, so its PLC may be compiled twice. See knowledge/openness/unified-ui-generation-recipe.md knowledge/openness/unified-panels-fail-silently.md |
Live Openness object-model explorer: browse and read anything Openness exposes, by path work.openness_explorer |
tiax openness openness_browse, openness_get |
unverified | Path grammar, explorer, value conversion and the MCP tools are unit-tested on a fake object graph (tests/TiaTool.Agent.Tests/OpennessExplorerTests.cs). The Openness adapter (EngineeringObjectNode: GetAttributeInfos / GetCompositionInfos / GetInvocationInfos, typed-property fallback) compiles against the reference stub but has never run on TIA Portal. Caveat: Paths: Devices["PLC_1"]/DeviceItems[0]/..., plc: |
Generic attribute write and action invoke on any Openness object (two-step confirmation) work.openness_explorer_write |
tiax openness openness_set, openness_invoke |
unverified | Conversion (enums by name, numbers with range checks, booleans, strings, object references by path), overload choice, the confirm-token flow, licence classification and journal behaviour are unit-tested on fakes. Never run on TIA Portal. Caveat: Writes: licence, plan + confirm_token, backup zip, save only when Openness accepts the change (an Openness exception discards the session unsaved), journal, post-write pipeline. Caveat: Only attributes listed by GetAttributeInfos with Write access can be set; values convert by the attribute's SupportedTypes. Caveat: Every invoke is a write except GetAttributes, which the explorer serves itself without Openness Invoke. Caveat: No default verify targets: pass verify_targets when the write is meant to change specific blocks. |
Run C# scripts in the engine: read in a never-committed transaction, write licensed + confirmed + backed up scripts.run |
script_run → 'tiax script' on the live path (engine or one-shot child). Read: ExclusiveAccess + Transaction disposed uncommitted, never saved, session reset on failure. Write: licence, confirm token bound to the code sha256 + args, backup, CommitOnDispose only when Run returned, save, journal (code sha256), post-write pipeline — MCP script_run |
unverified | The engine host compiles in FullCompileCheck; the MCP side (argv, result file, two-step confirmation, journal, post-write pipeline) and the Siemens-free runner (load, invoke, serialize) are unit-tested with a fake engine. Never run against TIA Portal. Caveat: Calls Openness forbids inside a transaction need no_transaction: read mode then closes the project unsaved; write mode still backs up and never saves a failure. Caveat: Openness objects in a return value come back as {$openness, name}; values are capped at 1 MiB of JSON and 32 levels. Caveat: Promoted by tiax selftest --live step 21 (docs/verification/windows-checklist-0.8.md). |
Run saved extensions (built-in, user, workspace) with argument validation extensions.run |
extension_run: ExtensionLibrary override chain → parameters schema check (required, types, enum, unknown names, defaults) → the script_run path with the manifest's mode — MCP extension_run |
unverified | Argument validation and the read/write/cache routing are unit-tested with a fake engine. Never run against TIA Portal. Caveat: Cache (JavaScript) extensions run with cache_query, not here. Caveat: Promoted by tiax selftest --live step 22 (docs/verification/windows-checklist-0.8.md). |
Incremental export: only blocks, tag tables and types changed since the last export; deleted ones removed live.export_incremental |
export --incremental: ModifiedDate (+ InterfaceModifiedDate / CodeModifiedDate) and the file SHA-256 against .tiax-export.json; full export with the reason when the manifest is missing, corrupt, of another project file / TIA version or another TIATool version; the post-write pipeline seeds each snapshot with the previous export and exports incrementally (workspace pipeline.incremental_export, default on) — MCP export_project |
unverified | 0.8: IncrementalExportTests (keep / new / modified / interface or code date newer / no date / file missing or edited / stale removal / seed) and IncrementalPipelineTests on fakes. The Openness half (reading the date attributes during the walk) compiles against the stub (FullCompileCheck) and has never run on TIA. Caveat: Windows confirmation pending: which date attributes each object kind really exposes (blocks, tag tables, PLC data types) and that they move on every edit — an attribute that does not exist only costs a re-export. Caveat: Watch tables, external sources and HMI artifacts carry no dates and are exported every run. Caveat: Implies mirror semantics: the files of objects the project no longer has (listed in the previous manifest) are removed. Caveat: Promoted by tiax selftest --live step 18 (docs/verification/windows-checklist-0.8.md). |
Read-only attach: export and device tree through the TIA Portal UI that has the project open live.attach_read |
--attach on export and devices (MCP attach=true on export_project / list_devices): TiaPortal.GetProcesses() → the one UI process whose ProjectPath is the project → Attach(); disposing only disconnects, the project is never closed; every write refuses with UI_SESSION_OPEN — MCP export_project, list_devices |
unverified | 0.8: the decision (TiaGuard.Decide) is unit-tested in AttachAndLifecycleTests (write + attach refused, no Portal → headless, UI with another project → ATTACH_NOT_OPEN listing what is open, headless Openness Portals never chosen). AttachSession and the attached TiaPortalService compile against the stub and have never run on TIA. Caveat: Windows confirmation pending: that Dispose() of an attached TiaPortal leaves the UI and its project open, and that the engineer's Openness access prompt appears once. Caveat: export --attach --compile is refused (UI_SESSION_OPEN): a compile changes the project. See knowledge/openness/orphaned-portal-processes.md |
Project create, archive (.zapNN) and retrieve into a new folder live.project_lifecycle |
tiax project create|archive|retrieve — Projects.Create(DirectoryInfo, name), Project.Archive(FileInfo, ProjectArchivationMode.Compressed), Projects.Retrieve(FileInfo, DirectoryInfo); nothing that exists is overwritten, the TIA version comes from the extension and is never upgraded — MCP project_create, project_archive, project_retrieve |
unverified | 0.8: path rules unit-tested (ProjectLifecycle in AttachAndLifecycleTests) and the MCP argv in OpennessToolTests; the Openness calls compile against the stub (src/Siemens.Engineering.Stubs/Depth.cs) and have never run on TIA. Caveat: Windows confirmation pending: the folder layout Projects.Create and Projects.Retrieve really produce (the verbs report the path TIA returns), and that archiving leaves the project unchanged. Caveat: create and retrieve start their own headless Portal (inside the engine too). Caveat: Promoted by tiax selftest --live step 19 (docs/verification/windows-checklist-0.8.md). See knowledge/openness/zap-archives-are-plain-zips.md knowledge/standards/backups-must-not-overwrite.md |
Apply one SCL source to one PLC: lint, backup, generate via a temporary source, compile that PLC, structured messages live.scl_apply |
tiax scl-apply / scl_apply: lint (errors refuse, warnings need confirm) → backup → temporary external source with a unique name (never replaces one; deleted afterwards) → GenerateBlocksFromSource(KeepOnError) → compile of the target PLC → save; new blocks optionally moved into a block group (export + delete + import in one transaction); compiler messages as _meta.structured {block, line, network, severity, text, kb_hits[]} — MCP scl_apply |
unverified | 0.8: lint gate, argv, temp-source handling and the structured result unit-tested (OpennessToolTests, SclLintTests); the Openness side compiles against the stub and has never run on TIA. Caveat: Windows confirmation pending: the group move (Openness has no move: export of a compiled block, delete and re-import inside an ExclusiveAccess transaction) and the compiler message locations (a bare number is read as the source line, 'Network n' as the network). Caveat: --group needs the compile: only a consistent block can be exported for the move. Caveat: Promoted by tiax selftest --live step 23 (docs/verification/windows-checklist-0.8.md). See knowledge/openness/compiler-messages-are-nested.md knowledge/openness/external-sources-break-project-search.md |
Compile scoped to blocks of one PLC, with structured compiler messages and knowledge-base hits live.compile_scoped |
compile --block=A,B (block-level ICompilable) and --result-json; compile_project block + _meta.structured.messages[] — MCP compile_project |
unverified | 0.8: the message tree → lines flattening is identical to before and the structured view is unit-tested on fakes (SclLintTests: CompileReport); block-level compile compiles against the stub and has never run on TIA. Caveat: Windows confirmation pending: block-level ICompilable on V19 and V21. Caveat: Promoted by tiax selftest --live step 23 (docs/verification/windows-checklist-0.8.md). See knowledge/openness/compiler-messages-are-nested.md |
3. Pro surface (0.4) and generators
| Capability | How | Status | Evidence / caveats |
|---|---|---|---|
CAx (AML) hardware import pro.aml |
pro cax-import / hardware_import — MCP hardware_import |
unverified | Reflection-only, never run on TIA. Caveat: Selftest step 14 (--include-risky) proves it. |
Library types: list, instantiate, update pro.libraries |
pro library-list / library-use — MCP library_list, library_use |
unverified | Reflection-only, never run on TIA. Caveat: 0.5: library-list never upgrades a library (only library_use may). |
Alarm texts export/import (xlsx) pro.alarm_texts |
pro alarm-texts-* — MCP alarm_texts |
unverified | Reflection-only, never run on TIA. |
Online state and download (two-step confirmation) pro.online |
pro online-state / download — MCP plc_online, plc_download |
unverified | Reflection-only, never run on TIA. Caveat: Download requires a confirm token over MCP and --confirmed on the CLI. |
WinCC Unified build (screens, items, tags, alarms) from a spec pro.unified_build |
pro unified-build — MCP unified_build |
unverified | Reflection-only, never run on TIA. Caveat: Multilingual alarm texts fix pending on Windows (OpennessPro UnifiedBuild). |
PLCSIM Advanced control and scripted tests pro.plcsim |
plcsim / plcsim_test — MCP plcsim, plcsim_test |
unverified | Reflection-only, never run against PLCSIM. Caveat: Selftest step 15 runs when PLCSIM Advanced is installed. |
Equipment / IO list / alarm generators (0.4) gen.equipment_legacy |
generate_equipment, io_list_import, generate_alarms — MCP generate_equipment, io_list_import, generate_alarms |
unit_tested | Golden tests; 3 Bool-only templates (replaced by the M4 template library). Caveat: Generated SCL is not compile-proven until the selftest runs. |
SCL lint for any source: units, line numbers, the source-file limits TIA does not report gen.scl_lint |
SclSelfCheck.Lint — FUNCTION_BLOCK / FUNCTION / DATA_BLOCK / TYPE / ORGANIZATION_BLOCK; errors: three-part VERSION, global constant as CASE label, unquoted name with a space, empty statement after THEN/ELSE/DO/OF, unbalanced blocks, unclosed/duplicate units, empty TYPE; warnings: undeclared #symbols, CASE labels not in the block's own VAR CONSTANT, FC without return type; optional naming style — MCP scl_lint |
unit_tested | 0.8: SclLintTests (each rule, line numbers, a clean five-unit source, typed literals not mistaken for symbols); the template gate (SclSelfCheck.Check) is unchanged. Caveat: Not a compiler: types, calls and addresses are not checked; the compile in scl_apply is the authority. Caveat: Promoted by tiax selftest --live step 23 (docs/verification/windows-checklist-0.8.md). See knowledge/simatic-ml/version-must-be-two-part.md knowledge/standards/scl-language-limits.md knowledge/simatic-ml/editor-accepts-what-source-rejects.md knowledge/simatic-ml/hasquotes-and-quoted-identifiers.md |
Labs: offline vs online comparison of a PLC (never downloads) pro.compare_online |
tiax pro online-compare / plc_compare_online: OnlineProvider (configured connection, or ApplyConfiguration with the given PC interface) → GoOnline → PlcSoftware.CompareToOnline() → the CompareResult tree (elements that are not identical) → GoOffline; nothing is saved — MCP plc_compare_online |
unverified | 0.8: reflection-bound like the rest of OpennessPro (no stub members needed); the MCP argv is unit-tested; never run against a PLC or PLCSIM. Caveat: Windows confirmation pending on PLCSIM Advanced: element names and ComparisonResult values; whether CompareToOnline needs the explicit GoOnline. |
4. Safety rails
| Capability | How | Status | Evidence / caveats |
|---|---|---|---|
Timestamped restore point before every write (+ sidecar, verify, prune 20) safety.backup |
all writing verbs; backup_project — MCP backup_project |
unit_tested | 0.5: knowledge/standards/backups-must-not-overwrite.md |
Verify gate: five playbook checks between baseline and candidate cache verify.gate |
verify_change (targets, interface_delta, allow_attribute_changes); verify.json + journal + task state — MCP verify_change |
unit_tested | 0.5: VerifyGateTests mutate cache copies, one per check; inconclusive is never a pass. Caveat: Compares caches: re-export + refresh_scan after the write so the candidate is current (the post-write pipeline does this automatically). See knowledge/openness/fb-regeneration-widens-external-access.md knowledge/simatic-ml/retain-attributes-are-not-settable-from-scl.md |
Restore a project from a restore point (new folder, or in place with confirmation and roll-back) safety.restore |
restore_backup (two-step confirm for in_place) / tiax restore — MCP restore_backup |
unit_tested | 0.5: RestoreTests (new folder, in place keeps .replaced-knowledge/standards/backups-must-not-overwrite.md |
Post-write pipeline: export → scan → verify gate → journal, served cache switches verify.pipeline |
automatic after every project write when pipeline.auto_verify (default) or verify=true | unit_tested | 0.5: RestoreTests pipeline cases with a fake live runner. Caveat: Adds one export (40–90 s) to every write; verify=false skips it for one call. |
Refuse to run while TIA Portal is open safety.portal_guard |
TiaGuard (CLI override only) | proven | See knowledge/openness/orphaned-portal-processes.md |
Failed pro action never saves the project safety.no_save_on_failure |
ProResult + ProActionFailedException before SaveProject | unit_tested | 0.5; unit-tested decision logic. Caveat: Windows confirmation pending (B7 checklist). |
No MCP argument can switch a rail off (schema check, --key=value argv, Windows-correct command line) safety.mcp_argument_rails |
McpVerb.Rails (inputSchema check before the licence gate, argv rail before every child), VerbArgs, WindowsArgv | unit_tested | 0.8: RailInjectionTests feeds 29 hostile values into every string argument of every catalog tool (6090 probes): each is refused or reaches the child as an argv that round-trips the command line and parses to the same options as a benign call. LicensingPreviewTests: dry_run / what_if waive licence, journal and verify only on the tools that honour them. Caveat: The command-line escaper is proven against its own CommandLineToArgvW-rule parser on Linux; the real Windows child is exercised by the live selftest only. Caveat: A string argument starting with '-' is refused except the documented free-text arguments (McpVerb.FreeTextArguments); a relative path that starts with '-' needs a .\ prefix. See knowledge/standards/backups-must-not-overwrite.md |
Explicit PLC/HMI targets: an ambiguous target is refused (AMBIGUOUS_TARGET), never guessed safety.explicit_targets |
--plc / --hmi on build, import-block, hmi, compile and scl-apply (MCP: plc / hmi arguments, forwarded as --plc=build_from_source, import_block, hmi_import_screens, hmi_import_tags, compile_project, scl_apply |
unit_tested | 0.8: TargetResolverTests on fakes (one, several, unknown, alias, shadowing). The TiaPortalService wiring compiles against the Openness stub (FullCompileCheck) and has never run on TIA. Replaces first-PLC/targets[0] picks, the 'first station holding a PLC' hardware fallback, and HMI screen/tag-table imports that went into every panel. Caveat: Windows confirmation pending: two-PLC/two-HMI scratch project — unnamed target refused before any change, named target written, import-block of a 'Main' that exists in both PLCs refused without --plc. |
Every write refuses with UI_SESSION_OPEN while a TIA Portal runs — attach or not safety.ui_session_open |
TiaGuard.Decide (pure) behind TiaGuard.EnsurePortalClosed; TiaPortalService refuses every mutating call (import, delete, generate, compile, save, ...) when bound to an attached Portal | unit_tested | 0.8: AttachAndLifecycleTests. The service-level refusals compile against the stub; not run on TIA. |
plc_download never re-initialises data blocks unless allow_reinit was approved safety.download_reinit |
OpennessPro.PreDownloadAnswer: DataBlockReinitialization → StopPlcAndReinitialize only with allow_reinit AND allow_stop; AllBlocksDownload (every DB reloaded with its start values) only with allow_reinit; anything else unknown cancels; allow_reinit is bound to the confirm token and passes the argv rail only on the redeemed download — MCP plc_download |
unit_tested | 0.8: OpennessToolTests (answers, token binding, argv rail). Caveat: Behaviour change: a download that TIA can only do as 'download all blocks' (e.g. the first one to a fresh PLCSIM) now needs allow_reinit=true. Caveat: Windows confirmation pending: the configuration and selection names (a wrong name cancels the download — the safe failure). |
Policy file: machine policy over workspace policy (allow/deny tools, read-only, online, scripts, path roots, confirmations, write rate), enforced before the licence safety.policy |
%ProgramData%\TIATool\policy.json (Linux /etc/tiatool/policy.json; TIATOOL_POLICY names one only where none is installed, so a client configuration cannot replace an administrator's file) over tia_status, workspace_status |
unit_tested | 0.8: PolicyTests (strict parsing with the file named in every error; each field enforced through the MCP server; merge rules; reload after an edit; invalid file fails closed for writes while reads keep working; protected paths), TrustHostTests (tools/list filtering through the SDK host; a real tiax-agent process reading the machine policy from TIATOOL_POLICY). PolicyCommandTests (policy show sources, init template parses with every example switched on, overwrite refusal + timestamped copy, machine policy needs elevation). Caveat: allow_online defaults to false: plc_download, plc_online and plc_compare_online are refused until a policy sets "allow_online": true (tiax-agent policy init --allow-online). Caveat: The machine policy is only as protected as its folder: by default ProgramData lets users create files. 'tiax-agent policy init --machine' restricts %ProgramData%\TIATool to Administrators/SYSTEM (Users read) before it writes the file — unverified on Windows; otherwise an administrator must set those ACLs. Caveat: A workspace policy can tighten anything and loosen only what the machine policy leaves unset (scalars: machine wins; deny_tools / require_confirmation: union; allow_tools and roots: every list must match). Caveat: Path roots are checked on the normalised argument (.. resolved, relative paths refused when roots are set, device paths refused); symbolic links and junctions are not resolved. Caveat: The policy governs what reaches TIATool through MCP. The tiax / tiax-agent command lines a person (or an AI client with its own shell tool) runs are not policed by it: restrict shell access in the AI client as well. |
Human approval the model cannot give itself: MCP elicitation, else out-of-band 'tiax-agent approve safety.human_approval |
plc_download, restore_backup mode=in_place, write scripts / generic Openness writes, and every tool a policy lists in require_confirmation. tiax-agent: elicitation form (server-to-client request for 2025-xx clients, input-required round trip for 2026-07-28 clients, state HMAC-bound to tool + arguments, single use); otherwise an approval request file answered with 'tiax-agent approve plc_download, restore_backup, script_run, extension_run, openness_set, openness_invoke |
unit_tested | 0.8: TrustToolTests (fake elicitation responder: approve / decline / unavailable → out of band; out-of-band request bound to exact arguments, single use, denial; restore in place), TrustHostTests (official SDK client over HTTP answering the form; a real tiax-agent process over stdio with a 2025-06-18 client; round-trip state forgery/replay/expiry; approve command needs a terminal). Caveat: Never run with a real MCP client UI (Claude Desktop, Claude Code, Cursor …): how each client shows the form is unverified. Caveat: Out-of-band request files live in the user's profile: a model that can run arbitrary commands as the same user could forge an answer; 'approve' refuses redirected input so it cannot simply be piped a 'yes'. Elicitation is the stronger channel. Caveat: Over HTTP, clients that use the older initialize handshake get the out-of-band fallback (the transport runs stateless and cannot send them a request). |
Tamper-evident journal (SHA-256 hash chain, OS user / host / MCP client per entry), on by default through implicit workspaces safety.journal_chain |
Journal.Append (prev_hash, os_user, host, mcp_client); without a configured workspace the first write creates one per project under %LOCALAPPDATA%\TIATool\workspaces<sha1(project path)>, so the journal and the post-write verify run by default; tiax journal verify|export [--csv] (also tiax-agent journal) — MCP workspace_status |
unit_tested | 0.8: JournalChainTests (edit / delete / insert / reorder / first-line removal detected, legacy journals verify from the first hashed line, CRLF checkouts, crash-truncated lines, 200 KB lines, concurrent appends, CSV quoting and formula defusing, verb exit codes), TrustToolTests (implicit workspace on the first write, journal records the approval channel). Caveat: A hash chain cannot show that the end of the journal was cut off: record the head hash 'tiax journal verify' prints somewhere else when that matters. Caveat: Lines written before 0.8 carry no hash and are not covered (only the last one is, through the first hashed line). Caveat: With implicit workspaces every write is followed by the post-write export → scan → verify (40–90 s); verify=false skips it for one call. |
Project text returned verbatim is fenced as untrusted data (prompt-injection mitigation) safety.untrusted_project_text |
get_block, get_screen, who_writes_tag / who_reads_tag, list_blocks, search_project, project_diff wrap project text in << |
unit_tested | 0.8: TrustToolTests (fence present, a forged closing marker inside the content is neutralised). Caveat: A mitigation, not a guarantee: a model can still be misled by project text. structuredContent carries block names (identifiers) unfenced. |
5. AI integration
| Capability | How | Status | Evidence / caveats |
|---|---|---|---|
MCP server (official SDK, stdio + local HTTP), two lanes, continue_id paging ai.mcp |
tiax-agent mcp — MCP tia_status |
unit_tested | 0.5: cache calls never queue behind live calls; page 2 is served from memory; first run without a cache; missing argument = tool error with KB hits. |
Map an error message to the knowledge base ai.explain_error |
explain_error — MCP explain_error |
unit_tested | KnowledgeIndex over Symptom: blocks; KnowledgeTests on the real KB. |
Field-proven knowledge base with verbatim error texts ai.knowledge |
knowledge/ (MCP resources tiatool://knowledge/…) | proven | See knowledge/INDEX.md |
HMI screen/tag/alarm/translation generation (Python) ai.generators_py |
generators/simaticml | proven | Byte-identical golden proof vs the delivered reference machine. |
Project brain: workspace, journal, plan life cycle, assumptions register, decisions, baselines ai.workspace |
workspace_init / workspace_status / task / assumption / decision_add / baseline_set; tiax-agent mcp --workspace; resources tiatool://workspace/* — MCP workspace_init, workspace_status, task, assumption, decision_add, baseline_set |
unit_tested | 0.5: WorkspaceTests + WorkspaceToolTests (journal concurrency, illegal transitions, CRLF byte-identical register edits, 1b numbering, every write journaled with its backup). Caveat: Writes are journaled only when the server has a workspace. See knowledge/standards/assumptions-register.md |
Capability registry visible to the AI ai.capabilities |
docs/capabilities.json, tiatool://capabilities, status line in tool descriptions | unit_tested | 0.5. |
Live selftest on a scratch project promotes capabilities ai.selftest |
tiax selftest --live --template |
unit_tested | Runner unit-tested with a fake verb runner (SelftestTests); the agent half with a fake engine that keeps a saved project (AgentSelftestTests: rollback, commit, extension compile/run, licence and policy skips). Caveat: Never run on TIA yet: Windows checklist step 2. |
Toolsets (explore / generate / commission / admin) and find_tool over the full catalog ai.toolsets |
tiax-agent mcp --toolset explore|generate|commission|admin|all — MCP find_tool |
unit_tested | 0.8: ToolsetTests — every catalog tool mapped; tools/list filtered over HTTP with the SDK client while unlisted tools stay callable; explore lists no paid tool; tools/list size per toolset measured (0.8.0: explore 28 tools ≈ 9.1k tokens, budget 10k; all 75 tools ≈ 31k); find_tool ranks the full catalog with one-line usage. Caveat: Toolsets shape the listing only; they are not a permission boundary (policy does that). |
MCP client registration: Claude Desktop, Cursor, VS Code, Claude Code, Windsurf, Cline, Codex CLI, Gemini CLI ai.client_setup |
tiax-agent setup --client
|
unit_tested | 0.8: SetupClientTests on a temporary home — per-client path and entry shape, merge keeps other servers and user keys, ~/.claude.json round-trips verbatim, Codex config.toml edited as text (sub-tables and comments kept), idempotent re-runs, a new timestamped backup per change, legacy env MCP_TIMEOUT dropped, --all writes installed clients only. Caveat: Config locations and formats follow each client's documentation as of 2026; not yet exercised against every client's current release on Windows. Caveat: Claude Code's tool timeout (MCP_TOOL_TIMEOUT) is the client's own environment; setup cannot set it. |
Demo workspace without TIA Portal (init --demo) ai.demo |
tiax-agent init --demo | unit_tested | 0.8: DemoTests build the workspace from the shipped sample on Linux temp folders; the registered entry serves it and answers project_stats, get_screen, audit_project and model_validate. Caveat: The installer's samples\ folder and the Windows end-to-end run are not yet verified on a release build. |
CI front-end: analyze audit / diff / verify as SARIF 2.1.0, JUnit, Markdown, JSON + GitHub Action ai.ci_analyze |
tiax-agent analyze audit|diff|verify; .github/actions/tiatool-analyze | unit_tested | 0.8: AnalyzeTests — golden SARIF/JUnit on the fixture export (audit, verify, diff), SARIF required fields and physical locations, exit codes 0/1/2, baseline suppression, export folders scanned on the fly. Caveat: The composite action has not run on GitHub yet; it needs a Linux tiax-agent build (agent-path or agent-url). Caveat: Project-level results (verify scan shape) point at the export folder, not a file. |
C# Openness scripts: compile + guard rail, nothing runs scripts.check |
tiax-agent compiles with Roslyn into a net48 DLL against the project's Openness assemblies (registry, default install folder or TIATOOL_OPENNESS_REFS) plus a Tia helper prelude; a syntax + semantic guard-rail walk refuses process, registry, network, code loading/emitting, file delete/move, native interop and session-lifecycle APIs — MCP script_check |
unit_tested | Agent tests compile against the .NET Framework 4.8 reference assemblies and the Openness stub: the DLL references mscorlib (no .NET Core), diagnostics carry the caller's own line:column, every denylisted API is refused, the prelude compiles. Caveat: CI compiles against the Openness stub; the real Siemens.Engineering assemblies are only used on a TIA machine. Caveat: A guard rail, not a sandbox: reflection or dynamic can reach anything the engine process can. |
Promote a script that ran to a reusable extension, with provenance extensions.save |
extension_save: only code that compiled and ran successfully in this server session (or force_unverified plus a passing compile) is written to extension_save |
unit_tested | Refusal, provenance, overwrite and from_run code matching are unit-tested. Caveat: Successful runs are remembered in memory until the server restarts. |
Extension library: search and inspect built-in, user and workspace extensions ai.extension_library |
MCP extension_list {query, category, kind} and extension_show {id}; override chain built-in -> %APPDATA%\TIATool\extensions -> extension_list, extension_show |
unit_tested | 0.8: ExtensionPackTests (every built-in manifest validates, ids unique, embedded set = extensions/ folders) and SelfExtensionTests (listing, filters, override display, show, prompt order). Caveat: Running live script extensions (extension_run) and promoting scripts (extension_save) are separate capabilities. See docs/self-extension.md |
Built-in extension pack: 43 live Openness scripts (watch/force tables, text lists, technology objects, software units, libraries, network, HMI, UMAC, PLC tags, UDTs, blocks, cross-reference, catalog) ai.extension_pack |
extensions/extension_run |
unverified | 0.8: every script compiles on Linux CI against .NET Framework 4.8 + the Openness reference stub (ExtensionPackTests); read/write mode checked against the code. Never run against TIA Portal. Caveat: Compiling against the stub proves type-checking against OUR stub, not the real API: selftest step 22 compiles every one against the installed Siemens.Engineering assemblies, runs the read-only ones that need no argument and one write round trip (tag_table_create). Caveat: software_units_list, umac_users_roles, cross_reference, hmi_alarm_classes and the Unified parts of hmi_connections / hmi_screens_list look their Openness API up by name at run time and answer available=false where it is missing. Caveat: Classic HMI text-list entries come from the scanned cache (hmi_text_list_entries), not from Openness. Caveat: Promoted by tiax selftest --live step 22 (docs/verification/windows-checklist-0.8.md). See docs/self-extension.md |
Offline JavaScript over the scanned cache (cache_query) and 10 cache extensions ai.cache_query |
MCP cache_query {code | extension, args} in tiax-agent (Jint, strict mode, no CLR access, no eval): read-only tia API over TiaxIndex; cache extensions unused_tags, blocks_by_language, largest_blocks, write_conflicts, alarm_text_coverage, hmi_tags_without_plc, naming_violations, call_depth, udt_usages, hmi_text_list_entries — MCP cache_query |
unit_tested | 0.8: SelfExtensionTests run every API call and every cache extension against a synthetic cache with known answers, and prove the time, memory, recursion, statement and result limits; ExtensionPackTests run every cache extension on the fixture cache. Caveat: Limits are a guard rail, not a sandbox: 5 s script time and 64 MB script allocations (host-side cache loading excluded), 30 s wall clock, 10 M statements, recursion 256, 1 M-character result. Caveat: Not available in the legacy net48 'tiax mcp' server (answers 'requires tiax-agent'). Caveat: Answers are as good as the cache: STL/GRAPH bodies record no edges, HMI-to-PLC matches are by name. See docs/self-extension.md |
Learning knowledge base: knowledge_add entries found by explain_error, tool errors and the knowledge resources ai.learned_knowledge |
MCP knowledge_add {title, symptom, cause, fix, evidence, tags} writes knowledge_add, explain_error |
unit_tested | 0.8: SelfExtensionTests — write, journal, explain_error round trip, resource listing and read, duplicate refusal, user-folder fallback, Markdown round trip through the symptom parser. Caveat: Learned entries are written by an AI session and marked 'not reviewed'. Caveat: The legacy net48 'tiax mcp' server loads %APPDATA% learned entries but not the workspace's in explain_error (tiax-agent loads both). See docs/self-extension.md |
MCP tool annotations (title, read-only / destructive / idempotent hints) and structuredContent ai.tool_annotations |
McpVerb.Annotations (licence class + per-tool overrides), mapped in McpHost.ToSdkTool and the catalog; a result's "structured" object → structuredContent (who_writes_tag, who_reads_tag, compile_project, verify_change, audit_project, project_diff, tia_status) — MCP who_writes_tag, verify_change, audit_project, project_diff, compile_project |
unit_tested | 0.8: TrustToolTests (every tool titled, read-only never destructive, paid never read-only), TrustHostTests (annotations and structuredContent through the official SDK client). Caveat: Hints help a client decide what to ask; the server never relies on them (policy and approvals enforce). |
6. Product and licensing
| Capability | How | Status | Evidence / caveats |
|---|---|---|---|
Licence: Ed25519 tokens, activation, no free mode, read/write allow-list commercial.licence |
TiaTool.Licensing | unit_tested | 0.5: allow-list (unknown tools gated); env overrides only in dev builds. 0.7: no free mode — NoFreeTierTests. |
One product version stamped from the release tag commercial.version |
Directory.Build.props TiaToolVersion + Directory.Build.targets | unit_tested | VersionTests. Caveat: Windows confirmation of old-style MSBuild stamping pending; fallback: committed ProductVersion.cs. |
Self-contained .NET 10 agent (no runtime prerequisite) commercial.runtime |
build.cmd publishes win-x64 self-contained | unverified | Caveat: .NET 8 support ends 2026-11-10; the agent now targets net10.0. |
Self-serve 14-day trial (server): one per email domain (per address on public webmail) and per machine commercial.trial |
License server POST /api/license/trial (web/src/lib/licensing.ts startTrial; trials table, atomic claim) | unit_tested | 0.8: web vitest (billing.test.ts trial suite, routes.test.ts) + Postgres suite run against PGlite (concurrent claims → exactly one trial). Caveat: Server half; the client half is commercial.licence_trial (tiax-agent license trial). Caveat: Trials are not email-verified (0.8 contract returns the key at once): anyone can use up a company domain's trial; support resets it by deleting the trials row. See web/AUDIT.md |
Offline licence file (server): token valid to the period end, for yearly, enterprise, offline and edu plans commercial.offline_licence |
License server POST /api/license/offline (tiatool.offline-request/1 → tiatool.offline-license/1); counts as a machine activation | unit_tested | 0.8: web vitest (offline suite: plan gate, seat limit, 404/403/409/400) + Postgres suite on PGlite. Caveat: Server half; the client half (request file, import on the PC) is commercial.licence_offline. Caveat: An offline token cannot be revoked before its period end (regenerating the key or a refund stops online machines only). See web/AUDIT.md |
Customer account area: sign-up and sign-in by emailed link, licences, billing, settings commercial.accounts |
Web app /signup, /login, /auth/confirm, /account (overview, licenses, billing, settings); POST /api/auth/{signup,magic-link,confirm}, /api/account/*; accounts table (migration 003), session versioning, CSRF token + same-origin check on every account POST | unit_tested | 0.8: web vitest (accounts.test.ts: sign-up/sign-in/confirm, no enumeration, rate limits, single-use and expiring links, CSRF refusals, sign out everywhere, checkout-session scope, machines, regenerate, offline upload, trial on first sign-in, profile + VAT sync, email change, export, delete; routes.test.ts: checkout attaches to the account, success creates it) and the Postgres suite on PGlite (accounts, email change, website trial, delete/export); next start preview with Playwright screenshots at 1440 and 390 px. Caveat: Emails go out (through Unosend) only when UNOSEND_API_KEY and EMAIL_FROM are set; otherwise they are logged and dropped. Caveat: A checkout signs the buyer in for that purchase only (the Stripe email is not proven); billing and settings need an emailed link. Caveat: Website trials are bound to the account and to the first PC that activates them; they are not email-domain verified beyond the confirmed address. See web/AUDIT.md |
Stripe billing: product filter, refunds/disputes suspend, seat downgrades enforced, single-use checkout success, key email commercial.billing |
web/src/lib/billing.ts handleEvent + lib/licensing.ts enforceMachineLimit; migration 002_plans.sql | unit_tested | 0.8: web vitest with signed test events (webhook.test.ts: foreign product, refund, dispute won/lost, stale update after delete, trial → paid, seat downgrade 50 → 1). Caveat: Not yet exercised against Stripe live mode; the charge → subscription lookup (InvoicePayments) is mocked in tests. Caveat: Prices unchanged: EUR 99.99 / month, EUR 999.99 / year. See web/AUDIT.md |
Update channel (server): stable/beta release metadata for the client update check commercial.update_channel |
GET /api/releases/latest?channel= from web/src/data/releases.json, written by web/scripts/set-release.mjs | unit_tested | 0.8: web vitest (releases.test.ts, routes.test.ts); beta = 0.7.0-preview.6 with the published SHA-256. Caveat: Client half: commercial.update_check. Caveat: The stable channel answers 404 no_release until a stable build is published with set-release.mjs. See web/AUDIT.md |
Licence features[] and multi-key (kid) verification; trial/offline/edu/nfr/enterprise plans commercial.licence_features |
TiaTool.Licensing LicenseToken + LicenseKeys (kid → key, missing = k1) + LicensePolicy.RequiredFeatures/Denial; LicenseStatus.CanWrite requires 'write' when features[] is present | unit_tested | LicensingTests: Node-issued license-v1.json and license-v2.json vectors verify in C#; kid k1/k2/unknown; features write/scripts/none; trial until exp. Caveat: Only 'write' is enforced (via CanWrite). 'scripts' is defined (LicensePolicy.RequiredFeatures) but script_run / extension_save do not check it yet. Caveat: The website issues features[] and kid (k1) in new tokens (commercial.trial, commercial.offline_licence). |
Self-serve 14-day trial (client): tiax-agent license trial commercial.licence_trial |
POST /api/license/trial {email, mid, name?} → key + token stored like activate | unit_tested | LicenseCommandTests against a fake server: request body, stored key/token, 409 trial_used, refusal to replace an active paid licence. Caveat: Server half: commercial.trial. Not yet run end to end against the deployed website. |
Offline licence file (client): air-gapped request file → website → licence file import commercial.licence_offline |
tiax-agent license request-offline (tiatool.offline-request/1) / license import (tiatool.offline-license/1): verified offline, machine-bound, no online refresh; website upload at /account/licenses (POST /api/account/offline returns tiatool-offline-license.json) | unit_tested | LicenseCommandTests: request → signed answer → import round trip; other PC, expired, forged, wrong file refused; no grace countdown; never refreshed. 0.8 web: accounts.test.ts offline upload (download, plan gate, foreign key, licence file instead of request, junk). Caveat: Server half: commercial.offline_licence. Windows checklist: an air-gapped VM activates from files end to end (the request file is uploaded and the licence file downloaded on /account/licenses). |
Background licence refresh with back-off and server failover commercial.licence_refresh |
LicenseRefresher (15 min timer, 24 h due window, 5 min → 6 h back-off) in tiax-agent mcp; ProductEndpoints.LicenseServers ordered list; failover on network/timeout/5xx/non-JSON only | unit_tested | LicenseRefresherTests with a fake clock (due, back-off, revoked, offline file, no overlap, evaluation never blocked); LicensingTests failover order, no failover on 4xx, fallback 'unknown key' never revokes. |
Licence and update calls through authenticating (NTLM/Kerberos) corporate proxies commercial.licence_proxy |
LicenseHttp: system proxy + DefaultProxyCredentials = CredentialCache.DefaultCredentials | unverified | Handler configuration unit-tested only. Caveat: Never run through a real authenticating proxy: Windows checklist item. |
Update check (client): tiax-agent update --check and the doctor Updates line commercial.update_check |
GET /api/releases/latest?channel=stable|beta; semver precedence; prints link + SHA-256; never downloads or installs | unit_tested | LicenseCommandTests (newer, up to date, below min_supported, 404, malformed fields) and LicenseDoctorTests (never FAIL, 4 s budget). Caveat: Server half: commercial.update_channel. The stable channel answers "no release" until a stable build is published with web/scripts/set-release.mjs. Caveat: No self-update: installing stays manual in 0.8. |
Rolling logs, redacted support bundle, privacy statement (no telemetry) commercial.support_privacy |
tiax-agent support-bundle --out x.zip · tiax-agent privacy · %LOCALAPPDATA%\TIATool\logs | unit_tested | 0.8: SupportTests — 5 size-capped log files; redaction of licence keys, TT1/bearer/tt_ tokens, secret JSON keys, e-mails, user and machine names (also inside paths); the bundle carries only TIATool's own client entry; every ProductEndpoints URL is classified in the privacy output. |
Account billing: invoices, payment method, Stripe portal (payment method, cancel), monthly to yearly, more seats commercial.account_billing |
web/src/lib/billing-account.ts (subscriptions/invoices/payment method read live from Stripe; subscriptions.update with always_invoice + pending_if_incomplete; billing portal flow_data), /account/billing, POST /api/portal and /api/account/subscription; licenses.cancel_at kept by the webhook | unit_tested | 0.8: web vitest with a recording Stripe fake (accounts.test.ts billing suite: other products and drafts filtered, Stripe-only invoice links, outage and unconfigured states, portal flows scoped to the viewer, yearly switch keeps the key, failed payment changes nothing, seats only upwards). Caveat: Not yet exercised against Stripe test or live mode. Caveat: The site creates its own Customer Portal configuration (metadata tiatool=1) on first use. Cancel is at period end. The account default is never used. Caveat: Fewer seats are not self-serve on the account page (portal or support). See web/AUDIT.md |
7. Not yet implemented
| Capability | How | Status | Evidence / caveats |
|---|---|---|---|
Create a new project / add stations from scratch gap.create_project |
Openness Projects.Create + device creation | not_implemented | Planned M5. |
WinCC Unified export/scan gap.unified_scan |
HmiSoftware export | not_implemented | Planned M5. Today export records an HmiDevice omission (exit 2). |
Version Control Interface workspace sync gap.vci |
not_implemented | TIATool workspaces are git-friendly instead. | |
Text list / graphic list import gap.text_graphic_lists |
not_implemented | Exports parse; import never attempted (unproven constructs can kill TIA). | |
HMI records in tiax report gap.report_hmi |
not_implemented | Report is block-centric. | |
Multiuser, F-safety validation, Teamcenter, OPC, Startdrive gap.out_of_scope |
not_implemented | No tool support planned. |
8. Impossible or platform-limited (tested — do not retry without new information)
| Capability | How | Status | Evidence / caveats |
|---|---|---|---|
Set the HMI tag → PLC tag link on a Basic panel imp.controllertag_basic |
impossible | set_ControllerTag is not supported by Siemens.Engineering.Hmi.Tag.Tag (tested twice). See knowledge/openness/controllertag-basic-panels.md |
|
Tag-table exports carrying the PLC link imp.table_reimport |
impossible | The export omits ControllerTag — re-importing a table destroys every link; hence per-tag import. See knowledge/simatic-ml/per-tag-import-vs-table.md |
|
Press-and-hold buttons in generated screen XML imp.press_hold |
impossible | SetBitWhileKeyPressed undiscoverable; a two-event button crashed TIA. Latching buttons + PLC watchdog instead. | |
More than one visibility animation per object (Basic panels) imp.multi_visibility |
impossible | Rejected on import; PLC-side selected-unit mirror instead. See knowledge/hmi/one-animation-per-object.md |
|
Scripting / indirect tag addressing on Basic panels imp.basic_scripting |
impossible | Platform limit. | |
Declaring an OB event class in an SCL source imp.ob_event_class |
impossible | Startup/DiagnosticError OBs must be hand-added. | |
Setting Retain from an SCL source imp.retain_from_scl |
impossible | Manual TIA action; regeneration resets it. See knowledge/simatic-ml/retain-attributes-are-not-settable-from-scl.md |
|
Symbolic constants as CASE labels imp.const_case_labels |
impossible | TIA SCL compiler rejects them; use IF/ELSIF. See knowledge/standards/scl-language-limits.md |
|
Exporting know-how-protected / ProDiag / some system blocks imp.protected_export |
impossible | Reported in the export failure ledger. |
Known cosmetic/inherited quirks
who_writessite snippets can show a network's first line rather than the exact assignment in single-network SCL blocks (display only; the symbol/edge is correct).- Fixed in 0.5 (were open fix candidates 2026-09-01): staleness banner day/month swap; FBD/LAD assignment targets recorded as reads; no Remanence/External* in the cache. Re-scan caches built before 0.5.
- 41 mirrored doc pages are tiny hub/landing pages — the live pages genuinely hold only navigation.
- Graphic-mode buttons whose only label is an icon have no
textsand no icon name in the screen record (StyleItem/PictureOff/PictureOnlinks are dropped).
Built from docs/tiax/capabilities.md in the product repository. Links to files that are not published here are shown as plain text.