systemlink-cli 1.21.0__tar.gz → 1.22.0__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 (154) hide show
  1. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/PKG-INFO +1 -1
  2. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/pyproject.toml +1 -1
  3. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/_version.py +1 -1
  4. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/example_click.py +31 -8
  5. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/example_loader.py +31 -2
  6. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/example_provisioner.py +35 -16
  7. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/README.md +42 -4
  8. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/_schema/schema-v1.0.json +37 -3
  9. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/SKILL.md +9 -4
  10. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/commands.md +7 -0
  11. systemlink_cli-1.22.0/slcli/skills/slcli/references/example-authoring.md +193 -0
  12. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/LICENSE +0 -0
  13. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/dff-editor/editor.js +0 -0
  14. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/dff-editor/index.html +0 -0
  15. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/__init__.py +0 -0
  16. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/__main__.py +0 -0
  17. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/alarm_click.py +0 -0
  18. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/asset_click.py +0 -0
  19. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/cli_formatters.py +0 -0
  20. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/cli_utils.py +0 -0
  21. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/comment_click.py +0 -0
  22. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/completion_click.py +0 -0
  23. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/config.py +0 -0
  24. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/config_click.py +0 -0
  25. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/dataframe_click.py +0 -0
  26. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/dff_click.py +0 -0
  27. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/dff_decorators.py +0 -0
  28. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-1/README.md +0 -0
  29. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-1/config.yaml +0 -0
  30. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-2/README.md +0 -0
  31. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-2/config.yaml +0 -0
  32. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-3/config.yaml +0 -0
  33. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-3/leakage-current-results.json +0 -0
  34. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-3/overvoltage-results.json +0 -0
  35. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-3/product-xyz-compliance-report.json +0 -0
  36. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-3/product-xyz-specification.csv +0 -0
  37. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/demo-data-3/thermal-response-results.json +0 -0
  38. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
  39. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
  40. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
  41. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
  42. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
  43. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
  44. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
  45. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
  46. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
  47. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  48. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/feed_click.py +0 -0
  49. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/file_click.py +0 -0
  50. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/function_click.py +0 -0
  51. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/function_templates.py +0 -0
  52. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/main.py +0 -0
  53. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/mcp_click.py +0 -0
  54. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/mcp_reachability.py +0 -0
  55. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/mcp_server.py +0 -0
  56. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/notebook_click.py +0 -0
  57. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/platform.py +0 -0
  58. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/policy_click.py +0 -0
  59. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/policy_utils.py +0 -0
  60. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/profiles.py +0 -0
  61. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/response_handlers.py +0 -0
  62. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/rich_output.py +0 -0
  63. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/routine_click.py +0 -0
  64. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skill_click.py +0 -0
  65. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
  66. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
  67. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/filtering.md +0 -0
  68. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/import-specs.min.json +0 -0
  69. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/job-debugging/overview.md +0 -0
  70. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/nipkg/overview.md +0 -0
  71. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/interfaces.md +0 -0
  72. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/notebook-patterns.md +0 -0
  73. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/overview.md +0 -0
  74. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/python-test/overview.md +0 -0
  75. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/routine-examples.md +0 -0
  76. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/troubleshooting.md +0 -0
  77. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/angular-ui-packages.md +0 -0
  78. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/deployment.md +0 -0
  79. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/layout-patterns.md +0 -0
  80. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/nimble-angular.md +0 -0
  81. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/overview.md +0 -0
  82. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/systemlink-services.md +0 -0
  83. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/troubleshooting.md +0 -0
  84. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/scripts/spec_import_helper.py +0 -0
  85. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/spec_click.py +0 -0
  86. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/ssl_trust.py +0 -0
  87. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/state_click.py +0 -0
  88. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/system_click.py +0 -0
  89. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/system_query_utils.py +0 -0
  90. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/table_utils.py +0 -0
  91. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/tag_click.py +0 -0
  92. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/templates_click.py +0 -0
  93. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/testmonitor_click.py +0 -0
  94. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/universal_handlers.py +0 -0
  95. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/user_click.py +0 -0
  96. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/utils.py +0 -0
  97. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/web_editor.py +0 -0
  98. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_bootstrap.py +0 -0
  99. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_click.py +0 -0
  100. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/.gitignore +0 -0
  101. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/README.md +0 -0
  102. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/angular.json +0 -0
  103. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/package.json +0 -0
  104. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app-routing.module.ts +0 -0
  105. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.html +0 -0
  106. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.scss +0 -0
  107. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.ts +0 -0
  108. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.module.ts +0 -0
  109. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.html +0 -0
  110. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.scss +0 -0
  111. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.ts +0 -0
  112. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/state/app-view-state.service.ts +0 -0
  113. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/systemlink-context.service.ts +0 -0
  114. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/theme-sync.service.ts +0 -0
  115. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/webapp-home-data.service.ts +0 -0
  116. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.html +0 -0
  117. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.scss +0 -0
  118. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.ts +0 -0
  119. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.html +0 -0
  120. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.scss +0 -0
  121. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.ts +0 -0
  122. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.html +0 -0
  123. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.scss +0 -0
  124. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.ts +0 -0
  125. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.html +0 -0
  126. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.scss +0 -0
  127. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.ts +0 -0
  128. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.html +0 -0
  129. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.scss +0 -0
  130. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.ts +0 -0
  131. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.html +0 -0
  132. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.scss +0 -0
  133. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.ts +0 -0
  134. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.html +0 -0
  135. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.scss +0 -0
  136. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.ts +0 -0
  137. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.html +0 -0
  138. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.scss +0 -0
  139. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.ts +0 -0
  140. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.html +0 -0
  141. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.scss +0 -0
  142. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.ts +0 -0
  143. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/states/view-state.model.ts +0 -0
  144. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/assets/README.md +0 -0
  145. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/index.html +0 -0
  146. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/main.ts +0 -0
  147. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/styles.scss +0 -0
  148. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/tsconfig.app.json +0 -0
  149. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/tsconfig.json +0 -0
  150. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/workflow_preview.py +0 -0
  151. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/workflows_click.py +0 -0
  152. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/workitem_click.py +0 -0
  153. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/workspace_click.py +0 -0
  154. {systemlink_cli-1.21.0 → systemlink_cli-1.22.0}/slcli/workspace_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: systemlink-cli
3
- Version: 1.21.0
3
+ Version: 1.22.0
4
4
  Summary: SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates.
5
5
  License-File: LICENSE
6
6
  Author: Fred Visser
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "systemlink-cli"
3
- version = "1.21.0"
3
+ version = "1.22.0"
4
4
  description = "SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates."
5
5
  authors = ["Fred Visser <fred.visser@emerson.com>"]
6
6
  packages = [{ include = "slcli" }]
@@ -1,4 +1,4 @@
1
1
  """Version information for slcli."""
2
2
 
3
3
  # This file is auto-generated. Do not edit manually.
4
- __version__ = "1.21.0"
4
+ __version__ = "1.22.0"
@@ -2,6 +2,7 @@
2
2
 
3
3
  import json
4
4
  import sys
5
+ from pathlib import Path
5
6
  from typing import Any, Dict, List, Optional
6
7
 
7
8
  import click
@@ -281,7 +282,13 @@ def register_example_commands(cli: Any) -> None:
281
282
  handle_api_error(exc)
282
283
 
283
284
  @example.command(name="install")
284
- @click.argument("example_name")
285
+ @click.argument("example_name", required=False)
286
+ @click.option(
287
+ "--file",
288
+ "config_file",
289
+ type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True),
290
+ help="Path to an example config.yaml; referenced files are relative to its directory.",
291
+ )
285
292
  @click.option("--workspace", "-w", required=True, help="Workspace name or ID for resources")
286
293
  @click.option(
287
294
  "--format",
@@ -302,7 +309,8 @@ def register_example_commands(cli: Any) -> None:
302
309
  help="Path to write provisioning results as JSON for auditing.",
303
310
  )
304
311
  def install_example(
305
- example_name: str,
312
+ example_name: Optional[str],
313
+ config_file: Optional[str],
306
314
  workspace: Optional[str],
307
315
  format: str,
308
316
  dry_run: bool,
@@ -311,14 +319,29 @@ def register_example_commands(cli: Any) -> None:
311
319
  """Provision all resources defined by an example configuration."""
312
320
  try:
313
321
  loader = ExampleLoader()
314
- config = loader.load_config(example_name)
322
+ example_dir: Optional[Path] = None
323
+ if config_file and example_name:
324
+ raise ValueError("Specify either EXAMPLE_NAME or --file, not both.")
325
+ if not config_file and not example_name:
326
+ raise ValueError("Provide an EXAMPLE_NAME or --file path to config.yaml.")
327
+
328
+ if config_file:
329
+ config_path = Path(config_file)
330
+ config = loader.load_config_file(config_path)
331
+ example_name = str(config["name"])
332
+ example_dir = config_path.parent
333
+ else:
334
+ config = loader.load_config(example_name or "")
315
335
 
316
336
  workspace_id = _resolve_workspace_id(get_effective_workspace(workspace))
317
- provisioner = ExampleProvisioner(
318
- workspace_id=workspace_id,
319
- example_name=example_name,
320
- dry_run=dry_run,
321
- )
337
+ provisioner_options: Dict[str, Any] = {
338
+ "workspace_id": workspace_id,
339
+ "example_name": example_name,
340
+ "dry_run": dry_run,
341
+ }
342
+ if example_dir is not None:
343
+ provisioner_options["example_dir"] = example_dir
344
+ provisioner = ExampleProvisioner(**provisioner_options)
322
345
 
323
346
  results, err = provisioner.provision(config)
324
347
  if err:
@@ -105,6 +105,31 @@ class ExampleLoader:
105
105
  f"Example '{example_name}' not found. " f"Config path: {config_path}"
106
106
  )
107
107
 
108
+ return self._load_config(config_path, example_name)
109
+
110
+ def load_config_file(self, config_file: Path) -> Dict[str, Any]:
111
+ """Load and validate an example configuration from an arbitrary file.
112
+
113
+ Args:
114
+ config_file: Path to the example's YAML configuration file.
115
+
116
+ Returns:
117
+ Validated config dictionary.
118
+
119
+ Raises:
120
+ FileNotFoundError: If the config file does not exist.
121
+ ValueError: If config fails validation.
122
+ """
123
+ config_path = config_file.expanduser().resolve()
124
+ if not config_path.exists():
125
+ raise FileNotFoundError(f"Example config not found: {config_path}")
126
+ if not config_path.is_file():
127
+ raise ValueError(f"Example config is not a file: {config_path}")
128
+
129
+ return self._load_config(config_path, str(config_path))
130
+
131
+ def _load_config(self, config_path: Path, source_name: str) -> Dict[str, Any]:
132
+ """Load and validate a config at a known path."""
108
133
  # Load YAML
109
134
  try:
110
135
  with open(config_path, "r") as f:
@@ -118,14 +143,14 @@ class ExampleLoader:
118
143
  # Validate schema
119
144
  errors = self.validate_config(config)
120
145
  if errors:
121
- msg = f"Config validation failed for '{example_name}':\n"
146
+ msg = f"Config validation failed for '{source_name}':\n"
122
147
  msg += "\n".join(f" - {e}" for e in errors)
123
148
  raise ValueError(msg)
124
149
 
125
150
  # Validate references
126
151
  ref_errors = self._validate_references(config)
127
152
  if ref_errors:
128
- msg = f"Reference validation failed for '{example_name}':\n"
153
+ msg = f"Reference validation failed for '{source_name}':\n"
129
154
  msg += "\n".join(f" - {e}" for e in ref_errors)
130
155
  raise ValueError(msg)
131
156
 
@@ -148,6 +173,10 @@ class ExampleLoader:
148
173
  if missing_fields:
149
174
  errors.append(f"Missing required fields: {', '.join(sorted(missing_fields))}")
150
175
 
176
+ name = config.get("name")
177
+ if "name" in config and (not isinstance(name, str) or not name.strip()):
178
+ errors.append("name must be a non-empty string")
179
+
151
180
  # Check format_version is supported
152
181
  version = config.get("format_version")
153
182
  if version and version not in self.SUPPORTED_SCHEMA_VERSIONS:
@@ -62,6 +62,7 @@ class ExampleProvisioner:
62
62
  workspace_id: Optional[str] = None,
63
63
  example_name: Optional[str] = None,
64
64
  dry_run: bool = False,
65
+ example_dir: Optional[Path] = None,
65
66
  ) -> None:
66
67
  """Initialize the provisioner.
67
68
 
@@ -69,10 +70,12 @@ class ExampleProvisioner:
69
70
  workspace_id: Workspace identifier (ID).
70
71
  example_name: Example name for tagging resources.
71
72
  dry_run: When True, does not create any resources (SKIPPED).
73
+ example_dir: Directory containing an externally supplied example config.
72
74
  """
73
75
  self.workspace_id = workspace_id
74
76
  self.example_name = example_name
75
77
  self.dry_run = dry_run
78
+ self.example_dir = example_dir
76
79
  self.id_map: Dict[str, str] = {}
77
80
  self._test_results_deleted: bool = False
78
81
  self._files_deleted: bool = False
@@ -3480,15 +3483,10 @@ class ExampleProvisioner:
3480
3483
  Returns:
3481
3484
  File contents as bytes, or None if not found.
3482
3485
  """
3483
- from pathlib import Path
3484
-
3485
3486
  try:
3486
- if self.example_name:
3487
- # Path relative to slcli/examples/{example_name}/
3488
- example_dir = Path(__file__).parent / "examples" / self.example_name
3489
- full_path = example_dir / file_path
3490
- else:
3491
- full_path = Path(file_path)
3487
+ full_path = self._resolve_example_file(file_path)
3488
+ if full_path is None:
3489
+ return None
3492
3490
 
3493
3491
  if not full_path.exists():
3494
3492
  click.echo(
@@ -3518,6 +3516,31 @@ class ExampleProvisioner:
3518
3516
  )
3519
3517
  return None
3520
3518
 
3519
+ def _resolve_example_file(self, file_path: str) -> Optional[Path]:
3520
+ """Resolve a fixture file and contain it when a fixture directory is configured.
3521
+
3522
+ Without a fixture directory, return the path as provided for backwards
3523
+ compatibility with callers that supply their own working-directory context.
3524
+ """
3525
+ if self.example_dir:
3526
+ example_dir = self.example_dir
3527
+ elif self.example_name:
3528
+ example_dir = Path(__file__).parent / "examples" / self.example_name
3529
+ else:
3530
+ return Path(file_path)
3531
+
3532
+ try:
3533
+ resolved_dir = example_dir.resolve()
3534
+ resolved_path = (resolved_dir / file_path).resolve()
3535
+ resolved_path.relative_to(resolved_dir)
3536
+ return resolved_path
3537
+ except (OSError, ValueError):
3538
+ click.echo(
3539
+ f"Warning: File path must remain inside the example directory: {file_path}",
3540
+ err=True,
3541
+ )
3542
+ return None
3543
+
3521
3544
  def _get_notebook_by_name(self, name: str) -> Optional[str]:
3522
3545
  """Look up an example-owned notebook by name and workspace."""
3523
3546
  if not name:
@@ -3566,16 +3589,12 @@ class ExampleProvisioner:
3566
3589
 
3567
3590
  if not name or not file_path:
3568
3591
  return None
3569
- from pathlib import Path
3570
3592
 
3571
3593
  try:
3572
- # Resolve file path relative to example directory
3573
- if self.example_name:
3574
- # Path relative to slcli/examples/{example_name}/
3575
- example_dir = Path(__file__).parent / "examples" / self.example_name
3576
- notebook_file = example_dir / file_path
3577
- else:
3578
- notebook_file = Path(file_path)
3594
+ # Resolve file path relative to the example directory
3595
+ notebook_file = self._resolve_example_file(file_path)
3596
+ if notebook_file is None:
3597
+ return None
3579
3598
 
3580
3599
  if not notebook_file.exists():
3581
3600
  return None
@@ -76,6 +76,12 @@ slcli example install demo-data-2 -w <workspace> --audit-log install-log.json --
76
76
  slcli example install demo-data-3 \
77
77
  -w <workspace> --format json --audit-log nigel-fixture-manifest.json
78
78
 
79
+ # Install an example from any local directory. Paths in config.yaml are
80
+ # resolved relative to the directory containing config.yaml.
81
+ slcli example install \
82
+ --file ./fixtures/example-resources/config.yaml \
83
+ --workspace <workspace>
84
+
79
85
  # Delete example resources from a workspace
80
86
  slcli example delete demo-data-2 -w <workspace-id> --dry-run
81
87
  slcli example delete demo-data-2 -w <workspace>
@@ -85,6 +91,10 @@ slcli example delete demo-data-2 -w <workspace> --audit-log delete-log.json --fo
85
91
 
86
92
  ## Creating New Examples
87
93
 
94
+ For the complete authoring contract, including external fixture directories,
95
+ resource references, file-backed resources, and validation caveats, see the
96
+ [Example Fixture Authoring Guide](../skills/slcli/references/example-authoring.md).
97
+
88
98
  1. Create a new directory in `examples/`:
89
99
 
90
100
  ```
@@ -93,13 +103,37 @@ slcli example delete demo-data-2 -w <workspace> --audit-log delete-log.json --fo
93
103
  └── README.md # Optional: Setup guide
94
104
  ```
95
105
 
96
- 2. Create `config.yaml` following the schema in `_schema/schema-v1.0.json`
106
+ 2. Create `config.yaml` following the schema in
107
+ [`_schema/schema-v1.0.json`](_schema/schema-v1.0.json). Use the closest
108
+ bundled example as the reference for resource-specific `properties`.
97
109
 
98
110
  3. Test locally:
99
- ```bash
100
- slcli example info my-example
111
+ ```bash
112
+ slcli example install my-example --workspace <workspace> --dry-run
101
113
  ```
102
114
 
115
+ External fixtures use the same format and do not need to be copied into the
116
+ package:
117
+
118
+ ```text
119
+ fixtures/example-resources/
120
+ ├── config.yaml
121
+ ├── README.md
122
+ └── product-xyz-specification.csv
123
+ ```
124
+
125
+ ```bash
126
+ slcli example install \
127
+ --file fixtures/example-resources/config.yaml \
128
+ --workspace <workspace> \
129
+ --dry-run
130
+ ```
131
+
132
+ Paths in `file_path`, `rows_file`, and `data_file` properties are resolved
133
+ relative to the directory containing `config.yaml`. Only files named by a
134
+ resource are uploaded; a sibling README or data file is not uploaded merely
135
+ because it is present.
136
+
103
137
  ## Configuration Format
104
138
 
105
139
  All examples use YAML format with the following structure:
@@ -158,9 +192,13 @@ cleanup:
158
192
  ## Notes
159
193
 
160
194
  - Examples are versioned with the `format_version` field (currently 1.0)
161
- - Resource cleanup is tag-based and order-aware
195
+ - Resource cleanup uses example ownership tags and reverse resource order. The
196
+ `cleanup` block is descriptive metadata; `example delete` does not currently
197
+ consume its custom order or confirmation settings.
162
198
  - References use `${id_reference}` syntax for interpolation
199
+ - Resources are provisioned in list order, so referenced resources must appear first
163
200
  - All resources created by an example are tagged with the example name for safe deletion
201
+ - The JSON Schema is the authoring reference; runtime validation currently checks only a subset of its types and patterns
164
202
  - Examples with `install_manifest: true` emit grouped resource actions and a
165
203
  `validation.complete` flag when JSON output is requested. Unsupported
166
204
  capabilities or failed relationships make the command return a nonzero exit
@@ -25,6 +25,10 @@
25
25
  "type": "string",
26
26
  "description": "Human-readable example title"
27
27
  },
28
+ "example_version": {
29
+ "type": "string",
30
+ "description": "Fixture data version used in the optional install manifest"
31
+ },
28
32
  "description": {
29
33
  "type": "string",
30
34
  "description": "Detailed description of what example includes"
@@ -64,6 +68,15 @@
64
68
  ],
65
69
  "description": "Target workspace name or null for default"
66
70
  },
71
+ "workspace_name": {
72
+ "type": "string",
73
+ "description": "Workspace name recorded in the optional install manifest"
74
+ },
75
+ "install_manifest": {
76
+ "type": "boolean",
77
+ "default": false,
78
+ "description": "Emit the grouped install manifest when JSON output is requested"
79
+ },
67
80
  "resources": {
68
81
  "type": "array",
69
82
  "items": {
@@ -79,19 +92,19 @@
79
92
  "items": {
80
93
  "type": "string"
81
94
  },
82
- "description": "Resource types to delete in this order"
95
+ "description": "Descriptive intended deletion order; generic example delete currently reverses the resources list"
83
96
  },
84
97
  "filter_tags": {
85
98
  "type": "array",
86
99
  "items": {
87
100
  "type": "string"
88
101
  },
89
- "description": "Only delete resources with these tags"
102
+ "description": "Tags intended for cleanup filtering"
90
103
  },
91
104
  "require_confirmation": {
92
105
  "type": "boolean",
93
106
  "default": true,
94
- "description": "Require confirmation before cleanup"
107
+ "description": "Descriptive confirmation preference; generic example delete does not currently prompt from this field"
95
108
  }
96
109
  }
97
110
  },
@@ -102,6 +115,27 @@
102
115
  "type": "boolean",
103
116
  "default": true,
104
117
  "description": "Workspace must exist before install"
118
+ },
119
+ "required_relationships": {
120
+ "type": "array",
121
+ "items": {
122
+ "type": "string"
123
+ },
124
+ "description": "Relationships the fixture expects to be queryable after install"
125
+ },
126
+ "failed_relationships": {
127
+ "type": "array",
128
+ "items": {
129
+ "type": "string"
130
+ },
131
+ "description": "Relationships that failed validation"
132
+ },
133
+ "unsupported": {
134
+ "type": "array",
135
+ "items": {
136
+ "type": "string"
137
+ },
138
+ "description": "Capabilities intentionally not represented by the fixture"
105
139
  }
106
140
  }
107
141
  },
@@ -4,7 +4,9 @@ description: >-
4
4
  Query and manage NI SystemLink resources using the slcli command-line interface.
5
5
  Use when the user asks about test results, assets, systems, work items,
6
6
  specifications, webapps, notebooks, dataframes, files, feeds, tags,
7
- authorization, users, or other SystemLink resource workflows.
7
+ authorization, users, example fixture authoring, example config.yaml files,
8
+ or other SystemLink resource workflows. Use it when the user asks to create,
9
+ validate, review, install, or troubleshoot a SystemLink example fixture.
8
10
  argument-hint: >-
9
11
  Describe the SystemLink workflow you want to inspect, automate, scaffold, or
10
12
  troubleshoot.
@@ -20,6 +22,7 @@ SystemLink resource workflow that should be driven from the CLI.
20
22
  - Querying or managing SystemLink resources through `slcli`
21
23
  - Looking up exact command syntax, flags, or JSON output patterns
22
24
  - Building command sequences for analysis, provisioning, packaging, or cleanup
25
+ - Creating, validating, or reviewing `slcli example` fixture YAML
23
26
  - Troubleshooting CLI behavior, platform gating, or command selection
24
27
 
25
28
  ## Reference docs
@@ -29,6 +32,7 @@ Load only what the current task needs.
29
32
  | Topic | File | When to load |
30
33
  | --- | --- | --- |
31
34
  | CLI command reference | [commands.md](./references/commands.md) | Looking up command syntax, options, or examples |
35
+ | Example fixture authoring | [example-authoring.md](./references/example-authoring.md) | Creating or reviewing `config.yaml`, resource references, file-backed fixtures, or validation behavior |
32
36
  | Datasheet-to-spec workflow | [datasheet-workflow.md](./references/datasheet-workflow.md) | Importing specifications from PDF, CSV, or structured text |
33
37
  | Minimal spec import payload | [import-specs.min.json](./references/import-specs.min.json) | Need a bundled create-compatible starter payload or conditions |
34
38
  | Spec import helper | [spec_import_helper.py](./scripts/spec_import_helper.py) | Scaffold or validate a datasheet spec import payload |
@@ -49,9 +53,10 @@ Load only what the current task needs.
49
53
  4. Use convenience filters first, then fall back to `--filter` with `--substitution` for complex queries.
50
54
  5. Stay scoped to the user’s requested resource or workflow.
51
55
  6. Load deeper references only when the command surface alone is not enough.
52
- 7. Prefer workspace IDs over names in scripted workflows when an endpoint is strict about identity.
53
- 8. Use `make_api_request` from `slcli.utils` for helper scripts so auth, SSL, and error handling stay consistent.
54
- 9. For datasheet imports, default to autonomy when the product or workspace can be resolved unambiguously.
56
+ 7. For example authoring or review, load `example-authoring.md` before proposing YAML.
57
+ 8. Prefer workspace IDs over names in scripted workflows when an endpoint is strict about identity.
58
+ 9. Use `make_api_request` from `slcli.utils` for helper scripts so auth, SSL, and error handling stay consistent.
59
+ 10. For datasheet imports, default to autonomy when the product or workspace can be resolved unambiguously.
55
60
 
56
61
  ## Common command groups
57
62
 
@@ -932,5 +932,12 @@ for training, testing, or evaluation.
932
932
  slcli example list [-f json] # List available examples
933
933
  slcli example info <EXAMPLE_ID> # Show example details
934
934
  slcli example install <EXAMPLE_ID> [--workspace NAME] # Provision example resources
935
+ slcli example install --file PATH [--workspace NAME] # Install a local fixture
935
936
  slcli example delete <EXAMPLE_ID> [--workspace NAME] # Remove provisioned resources
936
937
  ```
938
+
939
+ For a local fixture, `PATH` points to its `config.yaml`; referenced files are
940
+ resolved relative to the directory containing that file.
941
+
942
+ For the YAML contract and authoring workflow, load
943
+ [example-authoring.md](./example-authoring.md).
@@ -0,0 +1,193 @@
1
+ # Example Fixture Authoring
2
+
3
+ Use this guide when creating a SystemLink example for `slcli example install`.
4
+ The machine-readable contract is [schema-v1.0.json](../../../examples/_schema/schema-v1.0.json).
5
+ The schema defines the common YAML envelope; the bundled examples show the
6
+ SystemLink API properties for each resource type.
7
+
8
+ ## Quick Start
9
+
10
+ An example is a directory containing `config.yaml` and any files referenced by
11
+ that configuration:
12
+
13
+ ```text
14
+ fixtures/example-resources/
15
+ ├── config.yaml
16
+ ├── README.md
17
+ ├── product-xyz-specification.csv
18
+ └── notebooks/
19
+ └── analysis.ipynb
20
+ ```
21
+
22
+ Install a bundled example by name:
23
+
24
+ ```bash
25
+ slcli example install demo-data-1 \
26
+ --workspace <workspace> --dry-run
27
+ ```
28
+
29
+ Install an example from any local directory with `--file`:
30
+
31
+ ```bash
32
+ slcli example install \
33
+ --file fixtures/example-resources/config.yaml \
34
+ --workspace <workspace> \
35
+ --dry-run
36
+ ```
37
+
38
+ For `--file` installs, all relative file references are resolved from the
39
+ directory containing `config.yaml`. A README or an unreferenced file is not
40
+ uploaded automatically; add a `file` or `notebook` resource when it should be
41
+ published to SystemLink.
42
+
43
+ ## Configuration Contract
44
+
45
+ The required top-level fields are:
46
+
47
+ | Field | Meaning |
48
+ | --- | --- |
49
+ | `format_version` | Schema version. Use the string `"1.0"`. |
50
+ | `name` | Lowercase example slug using letters, numbers, and hyphens. It is also used for ownership tagging. |
51
+ | `title` | Human-readable title. |
52
+ | `resources` | Ordered list of resources to create. |
53
+
54
+ Common optional fields include `description`, `author`, `created_date`,
55
+ `updated_date`, `example_version`, `tags`, `estimated_setup_time_minutes`,
56
+ `required_systemlink_version`, `target_workspace`, `workspace_name`,
57
+ `install_manifest`, `cleanup`, `validation`, and `post_install`.
58
+
59
+ Each resource has this envelope:
60
+
61
+ ```yaml
62
+ - type: "location"
63
+ name: "Production Test Lab"
64
+ properties:
65
+ city: "Austin"
66
+ id_reference: "loc_lab"
67
+ tags: ["example-resources"]
68
+ ```
69
+
70
+ - `type` selects one of the supported resource handlers.
71
+ - `name` is the resource name sent to SystemLink.
72
+ - `properties` contains fields specific to that resource type.
73
+ - `id_reference` is a local identifier using lowercase letters, numbers, and
74
+ underscores. Use it in later properties as `${id_reference}`.
75
+ - `tags` is used for organization and cleanup filtering.
76
+
77
+ Resources are processed in list order. Put a resource before any resource that
78
+ references its ID. References are resolved recursively inside `properties`, but
79
+ only a complete string such as `${loc_lab}` is substituted; embedded text such
80
+ as `location-${loc_lab}` is not substituted.
81
+
82
+ ## Resource Types
83
+
84
+ The supported `type` values are:
85
+
86
+ | Type | Typical use | Example |
87
+ | --- | --- | --- |
88
+ | `location` | Physical location hierarchy | [demo-data-1](../../../examples/demo-data-1/config.yaml) |
89
+ | `product` | Product definition and part number | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
90
+ | `system` | Test system or virtual system | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
91
+ | `asset` | Instrument or equipment asset | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
92
+ | `dut` | Device under test | [demo-data-1](../../../examples/demo-data-1/config.yaml) |
93
+ | `testtemplate` | Test plan template | [demo-data-2](../../../examples/demo-data-2/config.yaml) |
94
+ | `workflow` | Work item state machine | [demo-data-1](../../../examples/demo-data-1/config.yaml) |
95
+ | `work_item` | Work item instance | [demo-data-1](../../../examples/demo-data-1/config.yaml) |
96
+ | `work_order` | Work order | [demo-data-1](../../../examples/demo-data-1/config.yaml) |
97
+ | `test_result` | Test Monitor result and optional steps | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
98
+ | `data_table` | DataFrame table and optional rows | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
99
+ | `file` | Supporting file upload | [spec-compliance-notebooks](../../../examples/spec-compliance-notebooks/config.yaml) |
100
+ | `notebook` | Jupyter notebook upload | [spec-compliance-notebooks](../../../examples/spec-compliance-notebooks/config.yaml) |
101
+ | `feed` | Package feed metadata | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
102
+ | `state` | Deployment state metadata | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
103
+ | `tag` | Workspace tag and optional history | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
104
+ | `specification` | Product specification and conditions | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
105
+ | `alarm` | Alarm instance or transition | [demo-data-3](../../../examples/demo-data-3/config.yaml) |
106
+
107
+ The `properties` object is intentionally resource-specific and open-ended in
108
+ the common schema. Start with the closest bundled example and verify the
109
+ property names against the corresponding `slcli` command or API model.
110
+
111
+ ## File-Backed Resources
112
+
113
+ These are the file-reference keys consumed by the generic provisioner:
114
+
115
+ | Resource | Property | Value |
116
+ | --- | --- | --- |
117
+ | `file` | `file_path` | Relative path to bytes to upload. `description`, `content_type`, `keywords`, and `tags` are also supported metadata. |
118
+ | `notebook` | `file_path` | Relative path to an `.ipynb` file. `notebook_interface` selects the notebook interface, for example `File Analysis`. |
119
+ | `data_table` | `rows_file` or `data_file` | Relative path to JSON rows. The JSON may be a list of rows or an object containing `frame.columns` and `frame.data`. |
120
+
121
+ A data table may use inline `rows` instead of a rows file, but it must not
122
+ define both. Row values are normalized to strings when appended. The configured
123
+ `columns` must match the columns in the rows payload when both are provided.
124
+
125
+ Example:
126
+
127
+ ```yaml
128
+ - type: "file"
129
+ name: "product-xyz-specification.csv"
130
+ properties:
131
+ file_path: "product-xyz-specification.csv"
132
+ content_type: "text/csv"
133
+ id_reference: "file_specification"
134
+
135
+ - type: "data_table"
136
+ name: "Overvoltage Results"
137
+ properties:
138
+ columns:
139
+ - name: "timestamp"
140
+ - name: "value"
141
+ rows_file: "data/overvoltage-results.json"
142
+ id_reference: "table_overvoltage"
143
+ ```
144
+
145
+ ## Validation And Dry Runs
146
+
147
+ The JSON Schema is the authoring reference, but the current loader performs a
148
+ smaller runtime validation set. It checks required fields, the supported format
149
+ version, supported resource types, basic resource reference identifiers, and
150
+ undefined `${reference}` values. It does not enforce every JSON Schema type or
151
+ pattern, and `properties` remains API-specific.
152
+
153
+ Use a dry run before making API calls:
154
+
155
+ ```bash
156
+ slcli example install \
157
+ --file fixtures/example-resources/config.yaml \
158
+ --workspace <workspace> \
159
+ --dry-run \
160
+ --format json
161
+ ```
162
+
163
+ For a real install, inspect the JSON result when checking automation:
164
+
165
+ ```bash
166
+ slcli example install \
167
+ --file fixtures/example-resources/config.yaml \
168
+ --workspace <workspace> \
169
+ --format json \
170
+ --audit-log install.json
171
+ ```
172
+
173
+ A successful resource list does not prove that every intended relationship or
174
+ capability was created. If the configuration uses `install_manifest: true`,
175
+ check `validation.complete` and the `unsupported` and `failed_relationships`
176
+ entries in the JSON manifest.
177
+
178
+ The `cleanup` block is currently descriptive metadata. `slcli example delete`
179
+ uses the example ownership marker and reverses the resource list; it does not
180
+ currently apply a custom cleanup order or confirmation setting from the YAML.
181
+
182
+ ## Authoring Checklist
183
+
184
+ Before sharing an example:
185
+
186
+ 1. Put `config.yaml` and every referenced file in the fixture directory.
187
+ 2. Set a unique lowercase `name` and `format_version: "1.0"`.
188
+ 3. Give every resource a unique `id_reference` and place resources in dependency order.
189
+ 4. Use the closest bundled fixture as the reference for resource-specific properties.
190
+ 5. Add explicit `file` or `notebook` resources for files that should be uploaded.
191
+ 6. Run the external-file dry run and inspect JSON output for failed resources.
192
+ 7. Add a README with the workspace prerequisites, expected resources, and post-install steps.
193
+ 8. Add an `install_manifest` and `validation` section when the fixture has known limitations.
File without changes