sysml2kit 0.3.0__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/CHANGELOG.md +19 -0
  2. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/CLAUDE.md +1 -2
  3. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/PKG-INFO +1 -1
  4. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/rf-library.md +7 -6
  5. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/_version.py +2 -2
  6. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/backends/sysmlpy.py +14 -1
  7. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/cli.py +9 -0
  8. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/text/writer.py +10 -1
  9. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/verify/runner.py +18 -1
  10. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_cli_fmt.py +24 -0
  11. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_verify.py +23 -0
  12. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/.gitignore +0 -0
  13. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/.pre-commit-config.yaml +0 -0
  14. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/.python-version +0 -0
  15. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/CITATION.cff +0 -0
  16. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/CONTRIBUTING.md +0 -0
  17. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/LICENSE +0 -0
  18. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/NOTICE +0 -0
  19. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/README.md +0 -0
  20. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/SECURITY.md +0 -0
  21. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/SPEC.md +0 -0
  22. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docker/compose.yaml +0 -0
  23. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/api-client.md +0 -0
  24. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/backends.md +0 -0
  25. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/cli.md +0 -0
  26. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/concepts.md +0 -0
  27. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/index.md +0 -0
  28. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/interchange.md +0 -0
  29. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/mcp.md +0 -0
  30. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/quickstart.md +0 -0
  31. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/reference.md +0 -0
  32. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/traceability.md +0 -0
  33. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/verification.md +0 -0
  34. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/docs/views.md +0 -0
  35. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/mkdocs.yml +0 -0
  36. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/pyproject.toml +0 -0
  37. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/scripts/slopcheck.sh +0 -0
  38. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/scripts/wait_for_api.sh +0 -0
  39. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/__init__.py +0 -0
  40. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/api/__init__.py +0 -0
  41. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/api/client.py +0 -0
  42. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/api/errors.py +0 -0
  43. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/api/models.py +0 -0
  44. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/backends/__init__.py +0 -0
  45. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/backends/protocol.py +0 -0
  46. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/diff.py +0 -0
  47. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/graph.py +0 -0
  48. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/interchange/__init__.py +0 -0
  49. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/interchange/reader.py +0 -0
  50. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/interchange/typemap.py +0 -0
  51. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/interchange/writer.py +0 -0
  52. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/interop/__init__.py +0 -0
  53. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/interop/requirements.py +0 -0
  54. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/mcp/__init__.py +0 -0
  55. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/mcp/_common.py +0 -0
  56. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/mcp/server.py +0 -0
  57. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/mcp/tools_model.py +0 -0
  58. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/mcp/tools_requirements.py +0 -0
  59. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/__init__.py +0 -0
  60. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/analysis.py +0 -0
  61. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/base.py +0 -0
  62. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/builder.py +0 -0
  63. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/container.py +0 -0
  64. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/metadata.py +0 -0
  65. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/relations.py +0 -0
  66. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/requirements.py +0 -0
  67. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/structure.py +0 -0
  68. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/model/values.py +0 -0
  69. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/py.typed +0 -0
  70. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/query.py +0 -0
  71. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/text/__init__.py +0 -0
  72. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/text/keywords.py +0 -0
  73. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/units.py +0 -0
  74. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/validation.py +0 -0
  75. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/verify/__init__.py +0 -0
  76. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/verify/binding.py +0 -0
  77. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/verify/engines.py +0 -0
  78. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/views.py +0 -0
  79. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/src/sysml2kit/workspace.py +0 -0
  80. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/conftest.py +0 -0
  81. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/fixtures/RFParts.sysml +0 -0
  82. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/fixtures/RFRequirements.sysml +0 -0
  83. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/fixtures/SatcomTerminal28GHz.sysml +0 -0
  84. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/fixtures/vehicle.sysml +0 -0
  85. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_api_client.py +0 -0
  86. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_api_live.py +0 -0
  87. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_backend_fidelity.py +0 -0
  88. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_backend_sysmlpy.py +0 -0
  89. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_cli.py +0 -0
  90. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_cli_api.py +0 -0
  91. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_cli_verify.py +0 -0
  92. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_diff.py +0 -0
  93. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_interchange.py +0 -0
  94. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_interop.py +0 -0
  95. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_mcp.py +0 -0
  96. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_model.py +0 -0
  97. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_package.py +0 -0
  98. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_query.py +0 -0
  99. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_roundtrip_json.py +0 -0
  100. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_roundtrip_text.py +0 -0
  101. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_text_writer/test_vehicle_golden.sysml +0 -0
  102. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_text_writer.py +0 -0
  103. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_validation.py +0 -0
  104. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_values_units.py +0 -0
  105. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_views/test_trace_contains_all_edge_kinds.trace.mmd +0 -0
  106. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_views/test_tree_contains_hierarchy.tree.mmd +0 -0
  107. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_views.py +0 -0
  108. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tests/test_workspace.py +0 -0
  109. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/tools/conformance/run_oracle.py +0 -0
  110. {sysml2kit-0.3.0 → sysml2kit-0.3.1}/uv.lock +0 -0
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1 — 2026-08-21
4
+
5
+ Hotfix release; upgrade recommended.
6
+
7
+ - **`fmt` could silently delete verification bindings and verify links.**
8
+ The loss gate's grammar signature missed MetadataFeature nodes, and
9
+ dependency statements leave no grammar node at all, so a format pass on a
10
+ binding-bearing file removed both without triggering the refusal. The
11
+ signature now covers metadata/annotation/dependency nodes and a textual
12
+ keyword guard catches statements the parser drops entirely; `fmt` on such
13
+ files now refuses unless `--lossy` is passed.
14
+ - Metadata values in textual output were rendered with Python repr
15
+ (single-quoted strings, `True`/`False`); they now render as SysML text
16
+ (double-quoted strings, `true`/`false`).
17
+ - With multiple verificationBinding annotations on one analysis, requirement
18
+ checking used the last binding while write-back provenance named the
19
+ first. Both now use the first binding, extra bindings execute with a
20
+ logged warning, and full multi-binding support is planned.
21
+
3
22
  ## 0.3.0 — 2026-08-21
4
23
 
5
24
  - **Verification execution** (`sysml2kit.verify`): `verificationBinding`
@@ -50,8 +50,7 @@ never edit `_version.py` or add a version literal.
50
50
  - `interop/` — `RequirementSpec` extraction; the `metricKey` attribute
51
51
  convention bridges to phased-array-systems and aedl (adapters live in those
52
52
  repos, not here).
53
- - `cli.py` — typer app. MCP is deferred to v0.2; when added it goes in
54
- `src/sysml2kit/mcp/` following apab's `docs/mcp-conventions.md`.
53
+ - `cli.py` — typer app; `mcp/` follows apab's `docs/mcp-conventions.md`.
55
54
 
56
55
  ## Constraints
57
56
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sysml2kit
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: API-first Python tooling for building, querying, validating, and automating SysML v2 models
5
5
  Project-URL: Homepage, https://github.com/jman4162/sysml2kit
6
6
  Project-URL: Source, https://github.com/jman4162/sysml2kit
@@ -21,12 +21,13 @@ for spec in extract_requirements(model):
21
21
  It ships four library packages (RFVocabulary quantity kinds with units,
22
22
  RFParts part/port definitions, RFRequirements requirement definitions using
23
23
  the metricKey convention, RFAnalyses analysis case definitions) plus
24
- **SatcomTerminal28GHz** a worked example mirroring the aedl `t3-001`
25
- benchmark: a 28 GHz LEO uplink phased-array terminal with eight
26
- machine-checkable requirements (worst-case link margin, sidelobe level,
27
- independent link crosscheck, clear-sky and gain agreement, prime-power and
28
- unit-cost ceilings, grating-lobe margin), each satisfied by a part and
29
- verified by an analysis.
24
+ two worked examples. **SatcomTerminal28GHz** mirrors the aedl `t3-001`
25
+ benchmark: eight machine-checkable requirements, each satisfied by a part
26
+ and verified by an analysis. **SatcomTerminalPAS** is executable: its
27
+ `pasStudy` analysis carries a `verificationBinding` for the
28
+ `phased-array-systems` engine, so `sysml2kit verify` runs a real study and
29
+ all five requirements pass with margin (see
30
+ [verification](verification.md)).
30
31
 
31
32
  The library demonstrates the intended division of labor: domain vocabulary
32
33
  as SysML v2 model content, generic mechanics in the kit, physics engines
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.3.0'
22
- __version_tuple__ = version_tuple = (0, 3, 0)
21
+ __version__ = version = '0.3.1'
22
+ __version_tuple__ = version_tuple = (0, 3, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -257,7 +257,20 @@ def _import_texts(node: dict[str, Any]) -> list[str]:
257
257
 
258
258
  #: Grammar node names counted by :func:`grammar_signature`; infrastructure
259
259
  #: wrappers whose names merely end in Usage are excluded.
260
- _SIGNATURE_EXTRA = frozenset({"Package", "Documentation", "NamespaceImport", "MembershipImport"})
260
+ _SIGNATURE_EXTRA = frozenset(
261
+ {
262
+ "Package",
263
+ "Documentation",
264
+ "NamespaceImport",
265
+ "MembershipImport",
266
+ # Metadata and annotation nodes carry verification bindings; missing
267
+ # them let fmt silently delete bindings before 0.3.1.
268
+ "MetadataFeature",
269
+ "MetadataDefinition",
270
+ "AnnotatingElement",
271
+ "Dependency",
272
+ }
273
+ )
261
274
  _SIGNATURE_EXCLUDE = frozenset({"Usage", "SubjectUsage"})
262
275
 
263
276
 
@@ -326,6 +326,15 @@ def fmt(
326
326
  for kind in sorted(set(before) | set(after)):
327
327
  if before.get(kind, 0) != after.get(kind, 0):
328
328
  losses.append(f"{kind}: {before.get(kind, 0)} -> {after.get(kind, 0)}")
329
+ # Dependency statements leave no node in the grammar signature at all, so
330
+ # a textual keyword count is the only guard that can see them.
331
+ import re as _re
332
+
333
+ for keyword in ("dependency", "metadata", "satisfy", "allocate"):
334
+ n_before = len(_re.findall(rf"\b{keyword}\b", source))
335
+ n_after = len(_re.findall(rf"\b{keyword}\b", formatted))
336
+ if n_before != n_after:
337
+ losses.append(f"'{keyword}' statements: {n_before} -> {n_after}")
329
338
  entries = diff_models(model, backend.parse(formatted), by_name=True)
330
339
  if entries:
331
340
  losses.append(render_diff(entries))
@@ -175,5 +175,14 @@ def _render_metadata(model: Model, element: MetadataUsage, indent: str) -> str:
175
175
  if not element.values:
176
176
  return f"{indent}metadata{name}{about};"
177
177
  inner = indent + _INDENT
178
- lines = [f"{inner}{escape_name(k)} = {v!r};" for k, v in element.values.items()]
178
+ lines = [f"{inner}{escape_name(k)} = {_metadata_value(v)};" for k, v in element.values.items()]
179
179
  return f"{indent}metadata{name}{about} {{\n" + "\n".join(lines) + f"\n{indent}}}"
180
+
181
+
182
+ def _metadata_value(value: str | float | bool) -> str:
183
+ """Render a metadata value in SysML syntax, not Python repr."""
184
+ if isinstance(value, bool):
185
+ return "true" if value else "false"
186
+ if isinstance(value, str):
187
+ return '"' + value.replace('"', '\\"') + '"'
188
+ return str(value)
@@ -8,6 +8,7 @@ into the model with provenance, opt-in.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import logging
11
12
  from collections.abc import Sequence
12
13
  from pathlib import Path
13
14
  from typing import Literal
@@ -22,6 +23,8 @@ from sysml2kit.model.metadata import MetadataUsage
22
23
  from sysml2kit.verify.binding import VerificationBinding, build_payload, extract_bindings
23
24
  from sysml2kit.verify.engines import EngineRegistry
24
25
 
26
+ logger = logging.getLogger(__name__)
27
+
25
28
  VERDICT_NAME = "verificationVerdict"
26
29
  _RESULT_SOURCE_PREFIX = "sysml2kit.verify"
27
30
 
@@ -141,7 +144,21 @@ def run_verification(
141
144
  for binding in bindings:
142
145
  results.append(_run_binding(binding, registry, base_dir, engine_versions))
143
146
 
144
- by_analysis = {r.analysis: r for r in results}
147
+ # First binding per analysis wins for checking, matching apply_results'
148
+ # selection, so provenance always names the engine that produced the
149
+ # verdict. Full multi-binding (fidelity ladder) support is planned; until
150
+ # then extra bindings execute but are not checked, and we say so.
151
+ by_analysis: dict[str, AnalysisResult] = {}
152
+ for result in results:
153
+ if result.analysis in by_analysis:
154
+ logger.warning(
155
+ "analysis %s has multiple bindings; only the first (%s) is "
156
+ "checked against requirements",
157
+ result.analysis,
158
+ by_analysis[result.analysis].engine,
159
+ )
160
+ continue
161
+ by_analysis[result.analysis] = result
145
162
  verdicts = [
146
163
  _verdict(spec, by_analysis[qualified])
147
164
  for spec in extract_requirements(model)
@@ -97,3 +97,27 @@ def test_fmt_rejects_json(tmp_path):
97
97
  f.write_text("[]")
98
98
  result = runner.invoke(app, ["fmt", str(f)])
99
99
  assert result.exit_code != 0
100
+
101
+
102
+ BINDING_BEARING = """package P {
103
+ part b;
104
+ requirement <'R1'> A;
105
+ analysis ana;
106
+ dependency from ana to A; // verify
107
+ metadata verificationBinding about ana {
108
+ engine = "fake";
109
+ }
110
+ }
111
+ """
112
+
113
+
114
+ def test_fmt_refuses_binding_bearing_file(tmp_path):
115
+ """Regression: before 0.3.1 fmt silently deleted bindings and verify links."""
116
+ f = tmp_path / "m.sysml"
117
+ f.write_text(BINDING_BEARING)
118
+ result = runner.invoke(app, ["fmt", str(f)])
119
+ assert result.exit_code == 1
120
+ assert "refusing" in result.output
121
+ assert "dependency" in result.output
122
+ assert "metadata" in result.output
123
+ assert f.read_text() == BINDING_BEARING # untouched
@@ -214,3 +214,26 @@ def test_write_back_provenance_and_idempotency():
214
214
  )
215
215
  == 1
216
216
  )
217
+
218
+
219
+ def test_multiple_bindings_first_wins_consistently(caplog):
220
+ """Hotfix regression: checking and write-back provenance must agree."""
221
+ model = bound_model()
222
+ analysis = model.find(name="RangeAnalysis")[0]
223
+ builder.metadata(model, analysis, {"engine": "second"}, name="verificationBinding")
224
+ registry = EngineRegistry()
225
+ registry.register("fake", lambda p: {"range_km": 420.0})
226
+ registry.register("second", lambda p: {"range_km": 1.0}) # would fail the check
227
+ import logging
228
+
229
+ with caplog.at_level(logging.WARNING):
230
+ run = run_verification(model, registry=registry)
231
+ assert len(run.analyses) == 2 # both executed
232
+ (verdict,) = run.requirements
233
+ assert verdict.status == "pass" # checked against the FIRST binding (fake)
234
+ assert "multiple bindings" in caplog.text
235
+ apply_results(model, run)
236
+ written = next(
237
+ el for el in model.owned_by(analysis) if getattr(el, "declared_name", "") == "range_km"
238
+ )
239
+ assert "fake==" in written.value.source # provenance names the checked engine
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes