TIATool

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.json + fb.scl embedded; overrides from %APPDATA%\TIATool\templates then /templates; SclSelfCheck on every template. MCP: template_list, template_show. — MCP 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/equipment.json (tiatool.equipment/1) → build//: UDTs, FBs, instance DBs, DB_HMI, Equipment/Comms/CyclicControl FCs, tag tables per area, alarms in every culture, SimHarness specs, docs, generation.json. DB_HMI members, status bits and alarm numbers are frozen via model/hmi-interface.lock.json. — MCP 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 compiles just that panel — MCP 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 browse|get --path=; MCP openness_browse, openness_get — MCP 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:/..., hmi:/..., @Attribute last; positions are 0-based. Caveat: Browse reads values of simple attribute types only; engineering objects and collections need openness_get. Caveat: Paths of referenced objects are best effort (Parent walk with a scan budget) and may be omitted. Caveat: Over a one-shot child (no engine) the path travels through the WindowsArgv escaper (CommandLineToArgvW rules, unit-tested); double-quoted names on a real Windows command line are a checklist item. Caveat: Promoted by tiax selftest --live step 20 (docs/verification/windows-checklist-0.8.md).
Generic attribute write and action invoke on any Openness object (two-step confirmation) work.openness_explorer_write tiax openness set|invoke --path= --name=<attribute|action> [--value-json|--args-json]; MCP openness_set, openness_invoke — MCP 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: tiax_backup.zip + .json sidecar (reason, sha256); SafetyRailTests. The old single zip was overwritten by every write. Caveat: MCP write tools cannot disable it. See 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-, zip slip refused, confirm token). Caveat: In place renames folders: TIA Portal must be closed (enforced). See 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=); TargetResolver (also pro actions and unified_build's "device") — MCP 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 \policy.json; TiaTool.Core.Policy (PolicyFile, EffectivePolicy, PolicyEngine); checked in McpVerb.CallToolCore after the argument rails and before the licence gate; denied tools left out of tools/list; tia_status and workspace_status show every effective value and the file that set it; tiax-agent policy show prints the effective policy with the file behind every value, tiax-agent policy init writes a commented example (workspace, or --machine as administrator) — MCP 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 ' (shows the plan, needs a terminal and 'yes'). The two-step confirm_token is kept only under confirm_mode: in_band. The journal records the channel and approver. — MCP 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 <<>> … <<<END … id>>> (id = hash of the content, so the content cannot close the fence); server instructions explain the fence — on get_block, get_screen, who_writes_tag / who_reads_tag, list_blocks, search_project and project_diff 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 : 23 steps on a copy; steps 21-22 run tiax-agent selftest-scripts (C# scripts and the extension pack through the real MCP tools) 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 | --all [--remove]; doctor lists registrations 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 /extensions// (else %APPDATA%\TIATool\extensions) with the script sha256, run id and journal seq; status unverified — MCP 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 -> /extensions; MCP prompt extend_tiatool walks the self-extension loop — MCP 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.json + script.cs, run with extension_run; 7 of them write (tag create/update/delete, tag table create, watch table create, technology object parameter, interface IP) and go through backup, transaction, confirmation and verify — MCP 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/learned/.md (or %APPDATA%\TIATool\knowledge\learned) in the built-in format; KnowledgeIndex loads built-in + learned entries (origin 'learned') — MCP 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_writes site 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 texts and no icon name in the screen record (StyleItem/PictureOff/PictureOn links 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.