@principal-ai/principal-studio-cli 0.35.3

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 (184) hide show
  1. package/README.md +172 -0
  2. package/dist/commands/agent-session.d.ts +3 -0
  3. package/dist/commands/agent-session.d.ts.map +1 -0
  4. package/dist/commands/agent-session.js +67 -0
  5. package/dist/commands/agent-sessions.d.ts +11 -0
  6. package/dist/commands/agent-sessions.d.ts.map +1 -0
  7. package/dist/commands/agent-sessions.js +39 -0
  8. package/dist/commands/auxiliary/index.d.ts +3 -0
  9. package/dist/commands/auxiliary/index.d.ts.map +1 -0
  10. package/dist/commands/auxiliary/index.js +9 -0
  11. package/dist/commands/auxiliary/validate.d.ts +3 -0
  12. package/dist/commands/auxiliary/validate.d.ts.map +1 -0
  13. package/dist/commands/auxiliary/validate.js +123 -0
  14. package/dist/commands/collector/check.d.ts +6 -0
  15. package/dist/commands/collector/check.d.ts.map +1 -0
  16. package/dist/commands/collector/check.js +153 -0
  17. package/dist/commands/collector/diagnose.d.ts +6 -0
  18. package/dist/commands/collector/diagnose.d.ts.map +1 -0
  19. package/dist/commands/collector/diagnose.js +229 -0
  20. package/dist/commands/collector/index.d.ts +6 -0
  21. package/dist/commands/collector/index.d.ts.map +1 -0
  22. package/dist/commands/collector/index.js +16 -0
  23. package/dist/commands/collector/status.d.ts +6 -0
  24. package/dist/commands/collector/status.d.ts.map +1 -0
  25. package/dist/commands/collector/status.js +77 -0
  26. package/dist/commands/collector/utils.d.ts +92 -0
  27. package/dist/commands/collector/utils.d.ts.map +1 -0
  28. package/dist/commands/collector/utils.js +154 -0
  29. package/dist/commands/coverage.d.ts +9 -0
  30. package/dist/commands/coverage.d.ts.map +1 -0
  31. package/dist/commands/coverage.js +252 -0
  32. package/dist/commands/create.d.ts +6 -0
  33. package/dist/commands/create.d.ts.map +1 -0
  34. package/dist/commands/create.js +50 -0
  35. package/dist/commands/events/index.d.ts +3 -0
  36. package/dist/commands/events/index.d.ts.map +1 -0
  37. package/dist/commands/events/index.js +9 -0
  38. package/dist/commands/events/validate.d.ts +3 -0
  39. package/dist/commands/events/validate.d.ts.map +1 -0
  40. package/dist/commands/events/validate.js +124 -0
  41. package/dist/commands/formats.d.ts +6 -0
  42. package/dist/commands/formats.d.ts.map +1 -0
  43. package/dist/commands/formats.js +1118 -0
  44. package/dist/commands/hooks.d.ts +9 -0
  45. package/dist/commands/hooks.d.ts.map +1 -0
  46. package/dist/commands/hooks.js +295 -0
  47. package/dist/commands/inbox.d.ts +13 -0
  48. package/dist/commands/inbox.d.ts.map +1 -0
  49. package/dist/commands/inbox.js +179 -0
  50. package/dist/commands/init.d.ts +6 -0
  51. package/dist/commands/init.d.ts.map +1 -0
  52. package/dist/commands/init.js +220 -0
  53. package/dist/commands/lint.d.ts +11 -0
  54. package/dist/commands/lint.d.ts.map +1 -0
  55. package/dist/commands/lint.js +71 -0
  56. package/dist/commands/list.d.ts +6 -0
  57. package/dist/commands/list.d.ts.map +1 -0
  58. package/dist/commands/list.js +78 -0
  59. package/dist/commands/migrate-nodes.d.ts +9 -0
  60. package/dist/commands/migrate-nodes.d.ts.map +1 -0
  61. package/dist/commands/migrate-nodes.js +363 -0
  62. package/dist/commands/migrate-scopes-to-canvas.d.ts +3 -0
  63. package/dist/commands/migrate-scopes-to-canvas.d.ts.map +1 -0
  64. package/dist/commands/migrate-scopes-to-canvas.js +143 -0
  65. package/dist/commands/migration.d.ts +6 -0
  66. package/dist/commands/migration.d.ts.map +1 -0
  67. package/dist/commands/migration.js +247 -0
  68. package/dist/commands/open-studio.d.ts +10 -0
  69. package/dist/commands/open-studio.d.ts.map +1 -0
  70. package/dist/commands/open-studio.js +32 -0
  71. package/dist/commands/opencode/fetch.d.ts +3 -0
  72. package/dist/commands/opencode/fetch.d.ts.map +1 -0
  73. package/dist/commands/opencode/fetch.js +25 -0
  74. package/dist/commands/opencode/index.d.ts +3 -0
  75. package/dist/commands/opencode/index.d.ts.map +1 -0
  76. package/dist/commands/opencode/index.js +11 -0
  77. package/dist/commands/opencode/list-sessions.d.ts +3 -0
  78. package/dist/commands/opencode/list-sessions.d.ts.map +1 -0
  79. package/dist/commands/opencode/list-sessions.js +20 -0
  80. package/dist/commands/repo.d.ts +18 -0
  81. package/dist/commands/repo.d.ts.map +1 -0
  82. package/dist/commands/repo.js +233 -0
  83. package/dist/commands/schema.d.ts +6 -0
  84. package/dist/commands/schema.d.ts.map +1 -0
  85. package/dist/commands/schema.js +367 -0
  86. package/dist/commands/scopes/index.d.ts +3 -0
  87. package/dist/commands/scopes/index.d.ts.map +1 -0
  88. package/dist/commands/scopes/index.js +9 -0
  89. package/dist/commands/scopes/validate.d.ts +3 -0
  90. package/dist/commands/scopes/validate.d.ts.map +1 -0
  91. package/dist/commands/scopes/validate.js +187 -0
  92. package/dist/commands/setup.d.ts +6 -0
  93. package/dist/commands/setup.d.ts.map +1 -0
  94. package/dist/commands/setup.js +406 -0
  95. package/dist/commands/starred-collections.d.ts +12 -0
  96. package/dist/commands/starred-collections.d.ts.map +1 -0
  97. package/dist/commands/starred-collections.js +310 -0
  98. package/dist/commands/subsystem-model.d.ts +11 -0
  99. package/dist/commands/subsystem-model.d.ts.map +1 -0
  100. package/dist/commands/subsystem-model.js +249 -0
  101. package/dist/commands/topic.d.ts +15 -0
  102. package/dist/commands/topic.d.ts.map +1 -0
  103. package/dist/commands/topic.js +683 -0
  104. package/dist/commands/tour.d.ts +23 -0
  105. package/dist/commands/tour.d.ts.map +1 -0
  106. package/dist/commands/tour.js +783 -0
  107. package/dist/commands/trace/flow.d.ts +6 -0
  108. package/dist/commands/trace/flow.d.ts.map +1 -0
  109. package/dist/commands/trace/flow.js +158 -0
  110. package/dist/commands/trace/index.d.ts +6 -0
  111. package/dist/commands/trace/index.d.ts.map +1 -0
  112. package/dist/commands/trace/index.js +20 -0
  113. package/dist/commands/trace/inspect.d.ts +6 -0
  114. package/dist/commands/trace/inspect.d.ts.map +1 -0
  115. package/dist/commands/trace/inspect.js +187 -0
  116. package/dist/commands/trace/list.d.ts +6 -0
  117. package/dist/commands/trace/list.d.ts.map +1 -0
  118. package/dist/commands/trace/list.js +145 -0
  119. package/dist/commands/trace/registrations.d.ts +6 -0
  120. package/dist/commands/trace/registrations.d.ts.map +1 -0
  121. package/dist/commands/trace/registrations.js +81 -0
  122. package/dist/commands/trace/validate.d.ts +6 -0
  123. package/dist/commands/trace/validate.d.ts.map +1 -0
  124. package/dist/commands/trace/validate.js +276 -0
  125. package/dist/commands/trail.d.ts +36 -0
  126. package/dist/commands/trail.d.ts.map +1 -0
  127. package/dist/commands/trail.js +622 -0
  128. package/dist/commands/validate.d.ts +12 -0
  129. package/dist/commands/validate.d.ts.map +1 -0
  130. package/dist/commands/validate.js +3334 -0
  131. package/dist/commands/workflow/implementation.d.ts +3 -0
  132. package/dist/commands/workflow/implementation.d.ts.map +1 -0
  133. package/dist/commands/workflow/implementation.js +51 -0
  134. package/dist/commands/workflow/index.d.ts +3 -0
  135. package/dist/commands/workflow/index.d.ts.map +1 -0
  136. package/dist/commands/workflow/index.js +19 -0
  137. package/dist/commands/workflow/inspect.d.ts +3 -0
  138. package/dist/commands/workflow/inspect.d.ts.map +1 -0
  139. package/dist/commands/workflow/inspect.js +123 -0
  140. package/dist/commands/workflow/list.d.ts +3 -0
  141. package/dist/commands/workflow/list.d.ts.map +1 -0
  142. package/dist/commands/workflow/list.js +95 -0
  143. package/dist/commands/workflow/render.d.ts +3 -0
  144. package/dist/commands/workflow/render.d.ts.map +1 -0
  145. package/dist/commands/workflow/render.js +88 -0
  146. package/dist/commands/workflow/test.d.ts +3 -0
  147. package/dist/commands/workflow/test.d.ts.map +1 -0
  148. package/dist/commands/workflow/test.js +141 -0
  149. package/dist/commands/workflow/utils.d.ts +49 -0
  150. package/dist/commands/workflow/utils.d.ts.map +1 -0
  151. package/dist/commands/workflow/utils.js +164 -0
  152. package/dist/commands/workflow/validate.d.ts +3 -0
  153. package/dist/commands/workflow/validate.d.ts.map +1 -0
  154. package/dist/commands/workflow/validate.js +344 -0
  155. package/dist/file-utils.d.ts +15 -0
  156. package/dist/file-utils.d.ts.map +1 -0
  157. package/dist/file-utils.js +29 -0
  158. package/dist/index.cjs +285718 -0
  159. package/dist/index.cjs.map +7 -0
  160. package/dist/index.d.ts +8 -0
  161. package/dist/index.d.ts.map +1 -0
  162. package/dist/index.js +81 -0
  163. package/dist/lib/bridge-ipc.d.ts +29 -0
  164. package/dist/lib/bridge-ipc.d.ts.map +1 -0
  165. package/dist/lib/bridge-ipc.js +128 -0
  166. package/dist/lib/github-user.d.ts +17 -0
  167. package/dist/lib/github-user.d.ts.map +1 -0
  168. package/dist/lib/github-user.js +53 -0
  169. package/dist/lib/open-url.d.ts +11 -0
  170. package/dist/lib/open-url.d.ts.map +1 -0
  171. package/dist/lib/open-url.js +45 -0
  172. package/dist/lib/subsystem-model-store.d.ts +74 -0
  173. package/dist/lib/subsystem-model-store.d.ts.map +1 -0
  174. package/dist/lib/subsystem-model-store.js +292 -0
  175. package/dist/lib/tour-cache.d.ts +26 -0
  176. package/dist/lib/tour-cache.d.ts.map +1 -0
  177. package/dist/lib/tour-cache.js +46 -0
  178. package/dist/lib/trail-cache.d.ts +40 -0
  179. package/dist/lib/trail-cache.d.ts.map +1 -0
  180. package/dist/lib/trail-cache.js +97 -0
  181. package/dist/lib/viewer-ipc.d.ts +44 -0
  182. package/dist/lib/viewer-ipc.d.ts.map +1 -0
  183. package/dist/lib/viewer-ipc.js +68 -0
  184. package/package.json +68 -0
@@ -0,0 +1,1118 @@
1
+ /**
2
+ * Formats command - Display documentation about file formats
3
+ */
4
+ import { Command } from 'commander';
5
+ import chalk from 'chalk';
6
+ const FORMAT_SECTIONS = {
7
+ overview: `
8
+ ${chalk.bold.cyan('Principal View OTEL File Formats')}
9
+ ${chalk.dim('═'.repeat(70))}
10
+
11
+ The Principal View OTEL workflow uses three main file types:
12
+
13
+ ${chalk.bold('1. Canvas Files')} ${chalk.yellow('.otel.canvas')}
14
+ Define OTEL event schemas and telemetry structure for a feature.
15
+ These are the single source of truth for what events should be emitted.
16
+
17
+ ${chalk.bold('2. Workflow Files')} ${chalk.yellow('.workflow.json')}
18
+ Define scenarios and templates for rendering executions as human-readable
19
+ workflows based on the emitted events.
20
+
21
+ ${chalk.bold('3. Execution Files')} ${chalk.yellow('.otel.json')}
22
+ Captured OTEL spans from test runs or production code, exported for
23
+ visualization and validation against canvas schemas.
24
+
25
+ ${chalk.bold('4. Library Files')} ${chalk.yellow('library.yaml')}
26
+ Define service registry and component libraries for the project.
27
+ Documents OTEL resource attributes and service metadata.
28
+
29
+ ${chalk.bold('5. Scopes Canvas')} ${chalk.yellow('.scopes.canvas')}
30
+ Document instrumentation scope boundaries. Validates that all scopes
31
+ declared in library.yaml owned-scopes are properly documented.
32
+
33
+ ${chalk.bold('6. Spans Canvas')} ${chalk.yellow('.spans.canvas')}
34
+ Define span conventions (operation types) and their colors for visualization.
35
+ Span colors are used as fill colors for events emitted within that span.
36
+
37
+ Run ${chalk.cyan('npx @principal-ai/principal-studio-cli formats <section>')} for details on:
38
+ ${chalk.yellow('canvas')} .otel.canvas format and event schemas
39
+ ${chalk.yellow('workflow')} .workflow.json format and scenario structure
40
+ ${chalk.yellow('execution')} .otel.json format for captured spans
41
+ ${chalk.yellow('library')} library.yaml format and service registry
42
+ ${chalk.yellow('scopes')} .scopes.canvas format and scope boundaries
43
+ ${chalk.yellow('spans')} .spans.canvas format and span conventions
44
+ ${chalk.yellow('colors')} Color contract for renderers (scope→border, span→fill)
45
+ ${chalk.yellow('examples')} Complete example files
46
+ `,
47
+ canvas: `
48
+ ${chalk.bold.cyan('Canvas Format (.otel.canvas)')}
49
+ ${chalk.dim('═'.repeat(70))}
50
+
51
+ Canvas files define the OTEL event schemas for a feature. They document what
52
+ events should be emitted and what attributes each event must/may contain.
53
+
54
+ ${chalk.bold('File Location:')}
55
+ ${chalk.dim('.principal-views/')}${chalk.yellow('<feature-name>.otel.canvas')}
56
+
57
+ ${chalk.bold('Required Structure:')}
58
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
59
+ ${chalk.dim('│')} { ${chalk.dim('│')}
60
+ ${chalk.dim('│')} ${chalk.green('"nodes"')}: [ ${chalk.dim('// Array of event schemas')} ${chalk.dim('│')}
61
+ ${chalk.dim('│')} { ${chalk.dim('│')}
62
+ ${chalk.dim('│')} ${chalk.yellow('"id"')}: "event-id", ${chalk.dim('// Unique identifier')} ${chalk.dim('│')}
63
+ ${chalk.dim('│')} ${chalk.green('"type"')}: "otel-event", ${chalk.dim('// Semantic OTEL node type')} ${chalk.dim('│')}
64
+ ${chalk.dim('│')} ${chalk.green('"label"')}: "Event Name", ${chalk.dim('// Display label')} ${chalk.dim('│')}
65
+ ${chalk.dim('│')} ${chalk.yellow('"x"')}: 0, ${chalk.yellow('"y"')}: 0, ${chalk.yellow('"width"')}: 200, ${chalk.yellow('"height"')}: 100, ${chalk.dim('│')}
66
+ ${chalk.dim('│')} ${chalk.dim('// color: NOT required - derived from scope + span at render time')} ${chalk.dim('│')}
67
+ ${chalk.dim('│')} ${chalk.green('"event"')}: { ${chalk.dim('// Event schema (top-level)')} ${chalk.dim('│')}
68
+ ${chalk.dim('│')} ${chalk.yellow('"name"')}: "feature.event.name", ${chalk.dim('│')}
69
+ ${chalk.dim('│')} ${chalk.yellow('"attributes"')}: { ${chalk.dim('// Attribute definitions')} ${chalk.dim('│')}
70
+ ${chalk.dim('│')} "attr.name": { ${chalk.dim('│')}
71
+ ${chalk.dim('│')} ${chalk.green('"type"')}: "string", ${chalk.dim('│')}
72
+ ${chalk.dim('│')} ${chalk.green('"description"')}: "What this attribute is", ${chalk.dim('│')}
73
+ ${chalk.dim('│')} ${chalk.green('"required"')}: true ${chalk.dim('│')}
74
+ ${chalk.dim('│')} } ${chalk.dim('│')}
75
+ ${chalk.dim('│')} } ${chalk.dim('│')}
76
+ ${chalk.dim('│')} }, ${chalk.dim('│')}
77
+ ${chalk.dim('│')} ${chalk.green('"otel"')}: { ${chalk.dim('// OTEL metadata (top-level)')} ${chalk.dim('│')}
78
+ ${chalk.dim('│')} ${chalk.cyan('"status"')}: "draft", ${chalk.dim('// Required: draft|approved|implemented')} ${chalk.dim('│')}
79
+ ${chalk.dim('│')} ${chalk.cyan('"scope"')}: "my-scope", ${chalk.dim('// Instrumentation scope')} ${chalk.dim('│')}
80
+ ${chalk.dim('│')} ${chalk.cyan('"files"')}: ["src/file.ts"] ${chalk.dim('// Source files')} ${chalk.dim('│')}
81
+ ${chalk.dim('│')} } ${chalk.dim('│')}
82
+ ${chalk.dim('│')} } ${chalk.dim('│')}
83
+ ${chalk.dim('│')} ], ${chalk.dim('│')}
84
+ ${chalk.dim('│')} ${chalk.green('"edges"')}: [], ${chalk.dim('// Optional: event relationships')} ${chalk.dim('│')}
85
+ ${chalk.dim('│')} ${chalk.green('"pv"')}: { ${chalk.dim('│')}
86
+ ${chalk.dim('│')} ${chalk.yellow('"name"')}: "Feature Name", ${chalk.dim('// Feature name (NOT "...Canvas")')} ${chalk.dim('│')}
87
+ ${chalk.dim('│')} ${chalk.yellow('"version"')}: "1.0.0", ${chalk.dim('│')}
88
+ ${chalk.dim('│')} ${chalk.yellow('"markdown"')}: ".principal-views/feature.md" ${chalk.dim('// Required: docs')} ${chalk.dim('│')}
89
+ ${chalk.dim('│')} } ${chalk.dim('│')}
90
+ ${chalk.dim('│')} } ${chalk.dim('│')}
91
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
92
+
93
+ ${chalk.bold('Semantic Node Types for .otel.canvas:')}
94
+
95
+ ${chalk.green('otel-event')} Event schema definition
96
+ ${chalk.green('otel-span-convention')} Span convention (operation type)
97
+ ${chalk.green('otel-scope')} Instrumentation scope
98
+ ${chalk.green('otel-resource')} Resource definition
99
+ ${chalk.green('otel-boundary')} Boundary marker
100
+
101
+ ${chalk.bold('Node Required Fields:')}
102
+
103
+ ${chalk.green('type')} ${chalk.dim('string')} "otel-event" (semantic node type)
104
+ ${chalk.green('label')} ${chalk.dim('string')} Human-readable display name
105
+ ${chalk.green('event')} ${chalk.dim('object')} Event schema with name and attributes (top-level)
106
+ ${chalk.green('otel.status')} ${chalk.dim('string')} "draft" | "approved" | "implemented"
107
+
108
+ ${chalk.bold('Alternative: Event Reference')}
109
+ Instead of inline ${chalk.yellow('event')}, you can use ${chalk.yellow('eventRef')} to reference a library event:
110
+ ${chalk.green('"eventRef"')}: "library.event.name" ${chalk.dim('// Reference event from library')}
111
+
112
+ ${chalk.bold('Note on Colors:')} Event nodes do ${chalk.bold('NOT')} require a color field.
113
+ Colors are derived at render time from:
114
+ - ${chalk.cyan('Border color')}: from scope (defined in library.yaml owned-scopes)
115
+ - ${chalk.cyan('Fill color')}: from span (defined in .spans.canvas)
116
+ See ${chalk.yellow('formats colors')} for the complete color contract.
117
+
118
+ ${chalk.bold('Event Schema Format:')}
119
+ ${chalk.red.bold('IMPORTANT:')} The "event" field must be an ${chalk.bold('object')}, not a string!
120
+
121
+ ${chalk.dim('┌─────────────────────────────────────────────────────────────────┐')}
122
+ ${chalk.dim('│')} ${chalk.green('"event"')}: { ${chalk.dim('// Top-level, NOT inside pv')} ${chalk.dim('│')}
123
+ ${chalk.dim('│')} ${chalk.yellow('"name"')}: "validation.started", ${chalk.dim('// Event name')} ${chalk.dim('│')}
124
+ ${chalk.dim('│')} ${chalk.yellow('"attributes"')}: { ${chalk.dim('// Attribute definitions')} ${chalk.dim('│')}
125
+ ${chalk.dim('│')} "input.recordCount": { ${chalk.dim('│')}
126
+ ${chalk.dim('│')} ${chalk.cyan('"type"')}: "integer", ${chalk.dim('// string|number|integer|boolean|object|array')} ${chalk.dim('│')}
127
+ ${chalk.dim('│')} ${chalk.cyan('"description"')}: "Number of records to validate", ${chalk.dim('│')}
128
+ ${chalk.dim('│')} ${chalk.cyan('"required"')}: true ${chalk.dim('│')}
129
+ ${chalk.dim('│')} } ${chalk.dim('│')}
130
+ ${chalk.dim('│')} } ${chalk.dim('│')}
131
+ ${chalk.dim('│')} } ${chalk.dim('│')}
132
+ ${chalk.dim('└─────────────────────────────────────────────────────────────────┘')}
133
+
134
+ ${chalk.red.bold('DEPRECATED FORMATS:')}
135
+ ${chalk.yellow('type: "text" with pv.event')} - Use ${chalk.green('type: "otel-event" with top-level event')}
136
+ ${chalk.yellow('pv.status')} - Use ${chalk.green('otel.status')} at top level
137
+ ${chalk.yellow('pv.references')} - Use ${chalk.green('otel.files')} at top level
138
+ ${chalk.yellow('"sources"')} - Use ${chalk.green('"otel.files"')} instead
139
+
140
+ ${chalk.bold('Migration:')}
141
+ Run ${chalk.cyan('npx @principal-ai/principal-studio-cli migrate-nodes')} to auto-migrate
142
+ legacy format files to the semantic format.
143
+
144
+ ${chalk.bold('Edge Required Fields:')}
145
+
146
+ ${chalk.green('pv.edgeType')} ${chalk.dim('string')} Edge type (must be defined in pv.edgeTypes)
147
+
148
+ ${chalk.bold('Canvas-Level Required Fields:')}
149
+
150
+ ${chalk.green('pv.name')} ${chalk.dim('string')} Feature name
151
+ ${chalk.green('pv.version')} ${chalk.dim('string')} Schema version
152
+ ${chalk.green('pv.markdown')} ${chalk.dim('string')} Path to documentation file
153
+
154
+ ${chalk.bold('Event Schema Best Practices:')}
155
+
156
+ ${chalk.cyan('1. Canvas represents complete, encompassing functionality:')}
157
+ ✅ API route - Full request/response cycle
158
+ ✅ CLI command - Complete tool execution
159
+ ✅ Business operation - End-to-end workflow
160
+ ❌ Individual helper functions (too granular)
161
+
162
+ ${chalk.cyan('2. Event naming convention:')}
163
+ ${chalk.yellow('<feature>.<operation>.<state>')}
164
+ Examples:
165
+ - validation.started
166
+ - validation.complete
167
+ - validation.error
168
+ - import.parsing.complete
169
+
170
+ ${chalk.cyan('3. Capture the shape of the code:')}
171
+ - Events should reflect actual decision points and state transitions
172
+ - If the code branches, those branches likely need distinct events
173
+ - If the code has phases/stages, those are natural event boundaries
174
+ - Don't limit yourself artificially - match the code structure
175
+
176
+ ${chalk.cyan('4. Attribute naming conventions:')}
177
+ ${chalk.yellow('<category>.<name>')}
178
+ - input.* (input.size, input.recordCount)
179
+ - output.* (output.count, output.success)
180
+ - result.* (result.validCount, result.invalidCount)
181
+ - error.* (error.type, error.message, error.stage)
182
+ - duration.* (duration.ms)
183
+
184
+ ${chalk.cyan('5. Required vs Optional attributes:')}
185
+ - ${chalk.green('required')}: Essential data needed for validation
186
+ - ${chalk.yellow('optional')}: Nice-to-have context, won't fail validation if missing
187
+
188
+ ${chalk.bold('Validation:')}
189
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli validate')}
190
+ `,
191
+ workflow: `
192
+ ${chalk.bold.cyan('Workflow Format (.workflow.json)')}
193
+ ${chalk.dim('═'.repeat(70))}
194
+
195
+ Workflow files define scenarios for rendering execution data as human-readable
196
+ stories. Scenarios are matched based on required events (derived from template.events).
197
+
198
+ ${chalk.bold('File Location:')}
199
+ ${chalk.dim('.principal-views/')}${chalk.yellow('<feature-name>.workflow.json')}
200
+ ${chalk.dim('(co-located with corresponding .otel.canvas file)')}
201
+
202
+ ${chalk.bold('Required Structure:')}
203
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
204
+ ${chalk.dim('│')} { ${chalk.dim('│')}
205
+ ${chalk.dim('│')} ${chalk.green('"version"')}: "1.0.0", ${chalk.dim('// Schema version (required)')} ${chalk.dim('│')}
206
+ ${chalk.dim('│')} ${chalk.green('"canvas"')}: "./feature.otel.canvas", ${chalk.dim('// Canvas reference')} ${chalk.dim('│')}
207
+ ${chalk.dim('│')} ${chalk.green('"name"')}: "Feature Name", ${chalk.dim('// NOT "Feature Name Workflows"')} ${chalk.dim('│')}
208
+ ${chalk.dim('│')} ${chalk.green('"description"')}: "What the feature does", ${chalk.dim('│')}
209
+ ${chalk.dim('│')} ${chalk.green('"spanPattern"')}: "feature.operation", ${chalk.dim('// Exact span name')} ${chalk.dim('│')}
210
+ ${chalk.dim('│')} ${chalk.green('"scenarioSelection"')}: "first-match", ${chalk.dim('// Optional')} ${chalk.dim('│')}
211
+ ${chalk.dim('│')} ${chalk.green('"scenarios"')}: [ ${chalk.dim('│')}
212
+ ${chalk.dim('│')} { ${chalk.dim('│')}
213
+ ${chalk.dim('│')} ${chalk.yellow('"id"')}: "success", ${chalk.dim('// Unique scenario ID')} ${chalk.dim('│')}
214
+ ${chalk.dim('│')} ${chalk.yellow('"priority"')}: 1, ${chalk.dim('// Lower = higher priority')} ${chalk.dim('│')}
215
+ ${chalk.dim('│')} ${chalk.yellow('"description"')}: "Successful execution", ${chalk.dim('│')}
216
+ ${chalk.dim('│')} ${chalk.yellow('"template"')}: { ${chalk.dim('// Workflow template')} ${chalk.dim('│')}
217
+ ${chalk.dim('│')} ${chalk.cyan('"events"')}: { ${chalk.dim('// REQUIRED: per-event templates')} ${chalk.dim('│')}
218
+ ${chalk.dim('│')} "event.started": "Started {{attr}}", ${chalk.dim('│')}
219
+ ${chalk.dim('│')} "event.complete": "Completed successfully" ${chalk.dim('│')}
220
+ ${chalk.dim('│')} }, ${chalk.dim('│')}
221
+ ${chalk.dim('│')} ${chalk.cyan('"summary"')}: "Completed {{count}} items" ${chalk.dim('// Optional')} ${chalk.dim('│')}
222
+ ${chalk.dim('│')} } ${chalk.dim('│')}
223
+ ${chalk.dim('│')} } ${chalk.dim('│')}
224
+ ${chalk.dim('│')} ] ${chalk.dim('│')}
225
+ ${chalk.dim('│')} } ${chalk.dim('│')}
226
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
227
+
228
+ ${chalk.bold('Required Fields:')}
229
+
230
+ ${chalk.cyan('version')} Schema version (e.g., "1.0.0")
231
+ ${chalk.cyan('canvas')} Path to .otel.canvas file
232
+ ${chalk.cyan('name')} Human-readable name (NOT "...Workflows")
233
+ ${chalk.cyan('description')} Feature purpose (NOT "Workflows for...")
234
+ ${chalk.cyan('spanPattern')} Exact span name to match
235
+ ${chalk.cyan('scenarios')} Array of scenario definitions
236
+
237
+ ${chalk.bold('Optional Fields:')}
238
+
239
+ ${chalk.cyan('scope')} Instrumentation scope (must be in library.yaml owned-scopes)
240
+ ${chalk.cyan('status')} "draft", "approved", or "implemented"
241
+ ${chalk.cyan('files')} Source files (required if status is approved/implemented)
242
+ ${chalk.cyan('scenarioSelection')} "first-match" (default) or "manual"
243
+ ${chalk.cyan('formatting')} Display options (showTimestamps, showDuration, etc.)
244
+
245
+ ${chalk.bold('Scenario Best Practices:')}
246
+
247
+ ${chalk.cyan('1. Required scenario fields:')}
248
+ - ${chalk.green('id')} Unique identifier (kebab-case)
249
+ - ${chalk.green('priority')} Selection order (lower = higher priority)
250
+ - ${chalk.green('description')} What this scenario represents
251
+ - ${chalk.green('template')} Template with events mapping
252
+
253
+ ${chalk.cyan('2. Priority ordering (scenarios evaluated in order):')}
254
+ - ${chalk.green('1-10')} Specific scenarios (success/failure cases)
255
+ - ${chalk.yellow('999')} Fallback scenario (catches anything)
256
+
257
+ ${chalk.cyan('3. Standard scenario set:')}
258
+ - ${chalk.green('Success')} (priority 1): Feature completed successfully
259
+ - ${chalk.yellow('Failure')} (priority 2): Feature encountered error
260
+ - ${chalk.dim('Fallback')} (priority 999): Generic execution captured
261
+
262
+ ${chalk.cyan('4. Template syntax (Handlebars):')}
263
+ Templates use Handlebars syntax for dynamic content:
264
+
265
+ ${chalk.bold('Variables:')}
266
+ - {{variable}} Simple variable
267
+ - {{result.count}} Nested property
268
+ - {{error.message}} Deeply nested
269
+
270
+ ${chalk.bold('Conditionals:')}
271
+ - {{#if condition}}...{{/if}} If block
272
+ - {{#if condition}}...{{else}}...{{/if}} If-else
273
+ - {{#if (eq status "ok")}}✅{{else}}❌{{/if}} Comparison
274
+
275
+ ${chalk.bold('Loops:')}
276
+ - {{#each items}}{{this}}{{/each}} Iterate array
277
+ - {{#each items}}{{@index}}: {{this}}{{/each}} With index
278
+
279
+ ${chalk.bold('Comparison helpers:')}
280
+ - eq, ne, lt, gt, lte, gte, and, or, not
281
+ - Example: {{#if (gt count 10)}}Many{{/if}}
282
+
283
+ ${chalk.bold('Span attributes (@span):')}
284
+ Access parent span attributes without duplicating them in events:
285
+
286
+ - {{@span.output.status}} Span attribute "output.status"
287
+ - {{@span.project.name}} Nested span attribute
288
+
289
+ ${chalk.dim('Why use @span?')}
290
+ Span attributes are set once on the span and apply to the entire operation.
291
+ Event attributes are specific to that moment. Using @span avoids duplicating
292
+ span-level data into every event:
293
+
294
+ ${chalk.dim('// In telemetry code - no duplication needed:')}
295
+ ${chalk.dim('span.setAttributes({ "output.status": "success" });')}
296
+ ${chalk.dim('span.addEvent("task.completed", { "task.id": "123" });')}
297
+
298
+ ${chalk.dim('// In template - access both:')}
299
+ ${chalk.dim('"Task {{task.id}} done (status: {{@span.output.status}})"')}
300
+
301
+ ${chalk.cyan('5. Template requirements:')}
302
+ ${chalk.bold('IMPORTANT:')} The ${chalk.yellow('"events"')} field is ${chalk.bold('REQUIRED')} in all templates
303
+ - Must be a non-empty object mapping event names to templates
304
+ - All event names must exist in the canvas
305
+ - Required events are ${chalk.bold('automatically derived')} from template.events keys
306
+ - Example: { "event.name": "Template with {{variables}}" }
307
+
308
+ ${chalk.cyan('6. Template style:')}
309
+ - Clear, concise summary line
310
+ - 3-5 detail steps showing workflow
311
+ - Use emojis for visual scanning (✅ ❌ 📋)
312
+ - Include key metrics and IDs
313
+
314
+ ${chalk.red.bold('DEPRECATED - DO NOT USE:')}
315
+ The ${chalk.yellow('"condition"')} field is no longer supported.
316
+ Required events are now automatically derived from template.events keys.
317
+ Remove any "condition" fields from your workflow files.
318
+
319
+ ${chalk.bold('Validation:')}
320
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli workflow validate')}
321
+ `,
322
+ execution: `
323
+ ${chalk.bold.cyan('Execution Format (.otel.json)')}
324
+ ${chalk.dim('═'.repeat(70))}
325
+
326
+ Execution files use the ${chalk.bold('standard OTLP (OpenTelemetry Protocol) JSON format')}.
327
+ These files are exported by OpenTelemetry SDK and used for visualization
328
+ and validation against canvas schemas.
329
+
330
+ ${chalk.bold('File Location:')}
331
+ ${chalk.yellow('__executions__/')}${chalk.dim('<feature-name>.otel.json')}
332
+ ${chalk.dim('(auto-generated by test infrastructure with OpenTelemetry SDK)')}
333
+
334
+ ${chalk.bold('IMPORTANT:')} __executions__/ directory must be committed to git!
335
+
336
+ ${chalk.bold('File Structure (OTLP JSON Format):')}
337
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
338
+ ${chalk.dim('│')} { ${chalk.dim('│')}
339
+ ${chalk.dim('│')} ${chalk.green('"resourceSpans"')}: [ ${chalk.dim('// OTLP root structure')} ${chalk.dim('│')}
340
+ ${chalk.dim('│')} { ${chalk.dim('│')}
341
+ ${chalk.dim('│')} ${chalk.yellow('"resource"')}: { ${chalk.dim('│')}
342
+ ${chalk.dim('│')} ${chalk.cyan('"attributes"')}: [ ${chalk.dim('│')}
343
+ ${chalk.dim('│')} { "key": "service.name", "value": { "stringValue": "..." }} ${chalk.dim('│')}
344
+ ${chalk.dim('│')} ] ${chalk.dim('│')}
345
+ ${chalk.dim('│')} }, ${chalk.dim('│')}
346
+ ${chalk.dim('│')} ${chalk.yellow('"scopeSpans"')}: [ ${chalk.dim('│')}
347
+ ${chalk.dim('│')} { ${chalk.dim('│')}
348
+ ${chalk.dim('│')} ${chalk.cyan('"scope"')}: { "name": "...", "version": "..." }, ${chalk.dim('│')}
349
+ ${chalk.dim('│')} ${chalk.cyan('"spans"')}: [ ${chalk.dim('│')}
350
+ ${chalk.dim('│')} { ${chalk.dim('│')}
351
+ ${chalk.dim('│')} "traceId": "4bf92f3577b34da6...", ${chalk.dim('// 32 hex chars')} ${chalk.dim('│')}
352
+ ${chalk.dim('│')} "spanId": "00f067aa0ba902b7", ${chalk.dim('// 16 hex chars')} ${chalk.dim('│')}
353
+ ${chalk.dim('│')} "name": "test:feature-name", ${chalk.dim('// Span name')} ${chalk.dim('│')}
354
+ ${chalk.dim('│')} "kind": 0, ${chalk.dim('// 0=INTERNAL')} ${chalk.dim('│')}
355
+ ${chalk.dim('│')} "startTimeUnixNano": "1703...", ${chalk.dim('// Unix nanoseconds')} ${chalk.dim('│')}
356
+ ${chalk.dim('│')} "endTimeUnixNano": "1703...", ${chalk.dim('// Unix nanoseconds')} ${chalk.dim('│')}
357
+ ${chalk.dim('│')} "attributes": [ ${chalk.dim('// Key-value pairs')} ${chalk.dim('│')}
358
+ ${chalk.dim('│')} { "key": "input.size", "value": { "intValue": 42 } } ${chalk.dim('│')}
359
+ ${chalk.dim('│')} ], ${chalk.dim('│')}
360
+ ${chalk.dim('│')} "events": [ ${chalk.dim('// Span events')} ${chalk.dim('│')}
361
+ ${chalk.dim('│')} { ${chalk.dim('│')}
362
+ ${chalk.dim('│')} "timeUnixNano": "1703...", ${chalk.dim('│')}
363
+ ${chalk.dim('│')} "name": "validation.started", ${chalk.dim('│')}
364
+ ${chalk.dim('│')} "attributes": [...] ${chalk.dim('│')}
365
+ ${chalk.dim('│')} } ${chalk.dim('│')}
366
+ ${chalk.dim('│')} ] ${chalk.dim('│')}
367
+ ${chalk.dim('│')} } ${chalk.dim('│')}
368
+ ${chalk.dim('│')} ] ${chalk.dim('│')}
369
+ ${chalk.dim('│')} } ${chalk.dim('│')}
370
+ ${chalk.dim('│')} ] ${chalk.dim('│')}
371
+ ${chalk.dim('│')} } ${chalk.dim('│')}
372
+ ${chalk.dim('│')} ] ${chalk.dim('│')}
373
+ ${chalk.dim('│')} } ${chalk.dim('│')}
374
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
375
+
376
+ ${chalk.bold('OTLP Format Details:')}
377
+
378
+ ${chalk.cyan('resourceSpans')} ${chalk.dim('(array, required)')}
379
+ Root array containing resource-grouped spans
380
+
381
+ ${chalk.cyan('resource.attributes')} ${chalk.dim('(array)')}
382
+ Metadata about the service (e.g., service.name)
383
+
384
+ ${chalk.cyan('scopeSpans')} ${chalk.dim('(array, required)')}
385
+ Instrumentation scope-grouped spans
386
+
387
+ ${chalk.cyan('spans')} ${chalk.dim('(array, required)')}
388
+ Array of span objects with OTLP structure
389
+
390
+ ${chalk.bold('Span Fields (OTLP Standard):')}
391
+
392
+ ${chalk.cyan('traceId')} ${chalk.dim('(string)')} - Unique trace identifier (32 hex chars)
393
+ ${chalk.cyan('spanId')} ${chalk.dim('(string)')} - Unique span identifier (16 hex chars)
394
+ ${chalk.cyan('parentSpanId')} ${chalk.dim('(string, optional)')} - Parent span ID
395
+ ${chalk.cyan('name')} ${chalk.dim('(string)')} - Operation name
396
+ ${chalk.cyan('kind')} ${chalk.dim('(number)')} - 0=UNSPECIFIED, 1=INTERNAL, 2=SERVER, 3=CLIENT, 4=PRODUCER, 5=CONSUMER
397
+ ${chalk.cyan('startTimeUnixNano')} ${chalk.dim('(string)')} - Start time in Unix nanoseconds
398
+ ${chalk.cyan('endTimeUnixNano')} ${chalk.dim('(string)')} - End time in Unix nanoseconds
399
+ ${chalk.cyan('attributes')} ${chalk.dim('(array)')} - Key-value pairs: [{ key, value: { stringValue|intValue|... } }]
400
+ ${chalk.cyan('events')} ${chalk.dim('(array)')} - Span events with timestamps and attributes
401
+ ${chalk.cyan('status')} ${chalk.dim('(object)')} - Status: { code: 1=OK, 2=ERROR }
402
+
403
+ ${chalk.bold('Attribute Value Format:')}
404
+ { "stringValue": "..." } ${chalk.dim('// For strings')}
405
+ { "intValue": 42 } ${chalk.dim('// For integers')}
406
+ { "doubleValue": 3.14 } ${chalk.dim('// For floats')}
407
+ { "boolValue": true } ${chalk.dim('// For booleans')}
408
+
409
+ ${chalk.bold('Generation:')}
410
+ Use OpenTelemetry SDK with OTLP JSON exporter
411
+ Example: @opentelemetry/sdk-trace-node + InMemorySpanExporter
412
+ See: tests/otel-setup.ts in the add-skill repository
413
+
414
+ ${chalk.bold('Validation:')}
415
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli validate-execution <file>')}
416
+
417
+ ${chalk.bold('Resources:')}
418
+ OTLP Spec: https://opentelemetry.io/docs/specs/otlp/
419
+ OpenTelemetry JS: https://github.com/open-telemetry/opentelemetry-js
420
+ `,
421
+ library: `
422
+ ${chalk.bold.cyan('Library Format (library.yaml)')}
423
+ ${chalk.dim('═'.repeat(70))}
424
+
425
+ Library files define the service registry and component libraries for a project.
426
+ They document OTEL resource attributes and provide a central registry of all
427
+ services in the repository.
428
+
429
+ ${chalk.bold('File Location:')}
430
+ ${chalk.dim('.principal-views/')}${chalk.yellow('library.yaml')}
431
+ ${chalk.dim('(created by npx @principal-ai/principal-studio-cli init)')}
432
+
433
+ ${chalk.bold('Required Structure:')}
434
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
435
+ ${chalk.dim('│')} ${chalk.green('version')}: "1.0.0" ${chalk.dim('│')}
436
+ ${chalk.dim('│')} ${chalk.green('name')}: "@org/package-name" ${chalk.dim('// npm package name')} ${chalk.dim('│')}
437
+ ${chalk.dim('│')} ${chalk.green('description')}: "Package description" ${chalk.dim('│')}
438
+ ${chalk.dim('│')} ${chalk.dim('│')}
439
+ ${chalk.dim('│')} ${chalk.yellow('# Service resource registry')} ${chalk.dim('│')}
440
+ ${chalk.dim('│')} ${chalk.yellow('# Define all services in this repository with their OTEL resource')} ${chalk.dim('│')}
441
+ ${chalk.dim('│')} ${chalk.yellow('# attributes. Each service configures its own resources at runtime,')} ${chalk.dim('│')}
442
+ ${chalk.dim('│')} ${chalk.yellow('# but declaring them here provides:')} ${chalk.dim('│')}
443
+ ${chalk.dim('│')} ${chalk.yellow('# - Service documentation/registry')} ${chalk.dim('│')}
444
+ ${chalk.dim('│')} ${chalk.yellow('# - Expected resource schema for validation')} ${chalk.dim('│')}
445
+ ${chalk.dim('│')} ${chalk.yellow('# - Dev workspace trace routing')} ${chalk.dim('│')}
446
+ ${chalk.dim('│')} ${chalk.dim('│')}
447
+ ${chalk.dim('│')} ${chalk.green('resources')}: ${chalk.dim('│')}
448
+ ${chalk.dim('│')} my-service: ${chalk.dim('// Service identifier')} ${chalk.dim('│')}
449
+ ${chalk.dim('│')} ${chalk.cyan('service.name')}: "my-service" ${chalk.dim('// OTEL service.name attribute')} ${chalk.dim('│')}
450
+ ${chalk.dim('│')} ${chalk.cyan('service.version')}: "1.0.0" ${chalk.dim('// OTEL service.version')} ${chalk.dim('│')}
451
+ ${chalk.dim('│')} ${chalk.cyan('deployment.environment')}: "development" ${chalk.dim('// Environment')} ${chalk.dim('│')}
452
+ ${chalk.dim('│')} ${chalk.cyan('project')}: "my-project" ${chalk.dim('// Project/feature name')} ${chalk.dim('│')}
453
+ ${chalk.dim('│')} ${chalk.yellow('# service.repository.url and service.commit.sha are auto-detected')} ${chalk.dim('│')}
454
+ ${chalk.dim('│')} ${chalk.yellow('# from git locally and set via environment variables in production')} ${chalk.dim('│')}
455
+ ${chalk.dim('│')} ${chalk.dim('│')}
456
+ ${chalk.dim('│')} ${chalk.green('nodeComponents')}: {} ${chalk.dim('// Optional component library')} ${chalk.dim('│')}
457
+ ${chalk.dim('│')} ${chalk.green('edgeComponents')}: {} ${chalk.dim('// Optional component library')} ${chalk.dim('│')}
458
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
459
+
460
+ ${chalk.bold('Service Resource Registry:')}
461
+
462
+ The ${chalk.cyan('resources')} section documents all services in the repository that emit
463
+ OpenTelemetry traces. Each service entry should match the resource attributes
464
+ configured in your OTEL SDK initialization.
465
+
466
+ ${chalk.bold('Required Fields:')}
467
+ ${chalk.cyan('version')} Library version (string)
468
+ ${chalk.cyan('name')} Package name, typically npm package name
469
+ ${chalk.cyan('description')} Brief description of the package
470
+ ${chalk.cyan('resources')} Service registry object (can be empty {})
471
+ ${chalk.cyan('nodeComponents')} Component definitions (can be empty {})
472
+ ${chalk.cyan('edgeComponents')} Component definitions (can be empty {})
473
+
474
+ ${chalk.bold('Common OTEL Resource Attributes:')}
475
+
476
+ ${chalk.cyan('service.name')} ${chalk.dim('(required)')}
477
+ The logical name of the service. This is the primary identifier used to
478
+ group traces from the same service.
479
+ Example: "my-api-server", "storybook", "integration-tests"
480
+
481
+ ${chalk.cyan('service.version')} ${chalk.dim('(recommended)')}
482
+ The version of the service. Typically matches the package.json version.
483
+ Example: "1.2.3", "0.14.0"
484
+
485
+ ${chalk.cyan('deployment.environment')} ${chalk.dim('(recommended)')}
486
+ The deployment environment.
487
+ Common values: "development", "staging", "production"
488
+
489
+ ${chalk.bold('Auto-Detected Attributes:')}
490
+
491
+ The following attributes are typically auto-detected from git:
492
+ ${chalk.cyan('service.repository.url')} Git repository URL
493
+ ${chalk.cyan('service.commit.sha')} Current commit hash
494
+
495
+ ${chalk.bold('Use Cases:')}
496
+
497
+ ${chalk.cyan('1. Storybook with OTEL Addon:')}
498
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
499
+ ${chalk.dim('│')} resources: ${chalk.dim('│')}
500
+ ${chalk.dim('│')} my-storybook: ${chalk.dim('│')}
501
+ ${chalk.dim('│')} service.name: "my-storybook" ${chalk.dim('│')}
502
+ ${chalk.dim('│')} service.version: "0.1.0" ${chalk.dim('│')}
503
+ ${chalk.dim('│')} deployment.environment: "development" ${chalk.dim('│')}
504
+ ${chalk.dim('│')} project: "ui-components" ${chalk.dim('│')}
505
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
506
+
507
+ ${chalk.cyan('2. Multiple Services in Monorepo:')}
508
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
509
+ ${chalk.dim('│')} resources: ${chalk.dim('│')}
510
+ ${chalk.dim('│')} api-server: ${chalk.dim('│')}
511
+ ${chalk.dim('│')} service.name: "api-server" ${chalk.dim('│')}
512
+ ${chalk.dim('│')} service.version: "2.0.0" ${chalk.dim('│')}
513
+ ${chalk.dim('│')} deployment.environment: "production" ${chalk.dim('│')}
514
+ ${chalk.dim('│')} worker-service: ${chalk.dim('│')}
515
+ ${chalk.dim('│')} service.name: "worker-service" ${chalk.dim('│')}
516
+ ${chalk.dim('│')} service.version: "1.5.0" ${chalk.dim('│')}
517
+ ${chalk.dim('│')} deployment.environment: "production" ${chalk.dim('│')}
518
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
519
+
520
+ ${chalk.cyan('3. Test Environment:')}
521
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
522
+ ${chalk.dim('│')} resources: ${chalk.dim('│')}
523
+ ${chalk.dim('│')} integration-tests: ${chalk.dim('│')}
524
+ ${chalk.dim('│')} service.name: "integration-tests" ${chalk.dim('│')}
525
+ ${chalk.dim('│')} service.version: "1.0.0" ${chalk.dim('│')}
526
+ ${chalk.dim('│')} deployment.environment: "test" ${chalk.dim('│')}
527
+ ${chalk.dim('│')} test.suite: "integration" ${chalk.dim('│')}
528
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
529
+
530
+ ${chalk.bold('Best Practices:')}
531
+
532
+ ${chalk.cyan('1. Service Name Consistency:')}
533
+ Ensure the ${chalk.yellow('service.name')} in library.yaml matches the resource attributes
534
+ configured in your OTEL SDK initialization code.
535
+
536
+ ${chalk.cyan('2. Version Synchronization:')}
537
+ Keep ${chalk.yellow('service.version')} in sync with your package.json version.
538
+
539
+ ${chalk.cyan('3. Document All Services:')}
540
+ Add an entry for every service in your repository that emits traces.
541
+ This creates a central registry for understanding trace sources.
542
+
543
+ ${chalk.cyan('4. Use Descriptive Service Names:')}
544
+ Service names should clearly identify the component:
545
+ ✅ "payment-processor-api"
546
+ ✅ "user-dashboard-ui"
547
+ ❌ "service1"
548
+ ❌ "app"
549
+
550
+ ${chalk.bold('Integration with OTEL SDK:')}
551
+
552
+ Your OTEL SDK configuration should match library.yaml resources:
553
+
554
+ ${chalk.dim('// Example: @opentelemetry/sdk-trace-web initialization')}
555
+ ${chalk.dim('const provider = new WebTracerProvider({')}
556
+ ${chalk.dim(' resource: new Resource({')}
557
+ ${chalk.dim(' [SEMRESATTRS_SERVICE_NAME]: "my-storybook",')}
558
+ ${chalk.dim(' [SEMRESATTRS_SERVICE_VERSION]: "0.1.0",')}
559
+ ${chalk.dim(' [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: "development",')}
560
+ ${chalk.dim(' }),')}
561
+ ${chalk.dim('});')}
562
+
563
+ ${chalk.bold('Initialization:')}
564
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli init')}
565
+ Creates .principal-views/library.yaml with template and comments
566
+
567
+ ${chalk.bold('More Info:')}
568
+ OpenTelemetry Resource Spec: https://opentelemetry.io/docs/specs/semconv/resource/
569
+ OpenTelemetry JS: https://github.com/open-telemetry/opentelemetry-js
570
+ `,
571
+ scopes: `
572
+ ${chalk.bold.cyan('Scopes Canvas Format (.scopes.canvas)')}
573
+ ${chalk.dim('═'.repeat(70))}
574
+
575
+ Scopes canvas files document instrumentation scope boundaries. They visualize
576
+ which parts of your codebase are instrumented and validate against the
577
+ owned-scopes declared in library.yaml.
578
+
579
+ ${chalk.bold('File Location:')}
580
+ ${chalk.dim('.principal-views/')}${chalk.yellow('architecture.scopes.canvas')}
581
+ ${chalk.dim('(or <name>.scopes.canvas)')}
582
+
583
+ ${chalk.bold('Required Structure:')}
584
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
585
+ ${chalk.dim('│')} { ${chalk.dim('│')}
586
+ ${chalk.dim('│')} ${chalk.green('"nodes"')}: [ ${chalk.dim('│')}
587
+ ${chalk.dim('│')} { ${chalk.dim('│')}
588
+ ${chalk.dim('│')} ${chalk.yellow('"id"')}: "scope-node-id", ${chalk.dim('│')}
589
+ ${chalk.dim('│')} ${chalk.green('"type"')}: "otel-scope", ${chalk.dim('// Required: scope node type')} ${chalk.dim('│')}
590
+ ${chalk.dim('│')} ${chalk.yellow('"x"')}: 0, ${chalk.yellow('"y"')}: 0, ${chalk.yellow('"width"')}: 200, ${chalk.yellow('"height"')}: 100, ${chalk.dim('│')}
591
+ ${chalk.dim('│')} ${chalk.yellow('"color"')}: "#4CAF50", ${chalk.dim('│')}
592
+ ${chalk.dim('│')} ${chalk.green('"otel"')}: { ${chalk.dim('│')}
593
+ ${chalk.dim('│')} ${chalk.cyan('"scope"')}: "my-scope-name", ${chalk.dim('// Must match library.yaml')} ${chalk.dim('│')}
594
+ ${chalk.dim('│')} ${chalk.cyan('"description"')}: "What this scope instruments", ${chalk.dim('│')}
595
+ ${chalk.dim('│')} ${chalk.cyan('"status"')}: "implemented" ${chalk.dim('// draft|approved|implemented')} ${chalk.dim('│')}
596
+ ${chalk.dim('│')} } ${chalk.dim('│')}
597
+ ${chalk.dim('│')} } ${chalk.dim('│')}
598
+ ${chalk.dim('│')} ], ${chalk.dim('│')}
599
+ ${chalk.dim('│')} ${chalk.green('"edges"')}: [] ${chalk.dim('// Optional: scope relationships')} ${chalk.dim('│')}
600
+ ${chalk.dim('│')} } ${chalk.dim('│')}
601
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
602
+
603
+ ${chalk.bold('Node Required Fields:')}
604
+
605
+ ${chalk.green('type')} ${chalk.dim('string')} Must be "otel-scope"
606
+ ${chalk.green('otel.scope')} ${chalk.dim('string')} Scope name (must match library.yaml owned-scopes)
607
+ ${chalk.green('otel.description')} ${chalk.dim('string')} What this scope instruments
608
+
609
+ ${chalk.bold('Relationship to library.yaml:')}
610
+
611
+ The scopes canvas validates against the ${chalk.cyan('owned-scopes')} field in library.yaml:
612
+
613
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
614
+ ${chalk.dim('│')} ${chalk.yellow('# library.yaml')} ${chalk.dim('│')}
615
+ ${chalk.dim('│')} resources: ${chalk.dim('│')}
616
+ ${chalk.dim('│')} my-service: ${chalk.dim('│')}
617
+ ${chalk.dim('│')} service.name: "my-service" ${chalk.dim('│')}
618
+ ${chalk.dim('│')} ${chalk.green('owned-scopes')}: ${chalk.dim('// Scopes this service owns')} ${chalk.dim('│')}
619
+ ${chalk.dim('│')} - validation ${chalk.dim('// Simple scope name')} ${chalk.dim('│')}
620
+ ${chalk.dim('│')} - import-processing ${chalk.dim('// Another scope')} ${chalk.dim('│')}
621
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
622
+
623
+ ${chalk.bold('Validation Rules:')}
624
+
625
+ ${chalk.cyan('1. Scopes canvas required when owned-scopes exist:')}
626
+ If library.yaml defines owned-scopes, a .scopes.canvas must exist.
627
+
628
+ ${chalk.cyan('2. All owned-scopes must be documented:')}
629
+ Each scope in owned-scopes must have a corresponding node with
630
+ pv.otel.scope set to that scope name.
631
+
632
+ ${chalk.cyan('3. Extra scopes trigger warnings:')}
633
+ Scopes in the canvas but not in owned-scopes are flagged as warnings.
634
+ Either add them to library.yaml or remove from canvas.
635
+
636
+ ${chalk.cyan('4. Node structure validation:')}
637
+ - pv.nodeType must be "scope"
638
+ - pv.description should explain what the scope covers
639
+
640
+ ${chalk.bold('Example Scopes Canvas:')}
641
+ ${chalk.dim('─'.repeat(70))}
642
+ ${chalk.yellow('.principal-views/architecture.scopes.canvas')}
643
+
644
+ {
645
+ "nodes": [
646
+ {
647
+ "id": "validation-scope",
648
+ ${chalk.green('"type": "otel-scope"')},
649
+ "x": 0, "y": 0, "width": 200, "height": 120,
650
+ ${chalk.green('"color": "#4CAF50"')},
651
+ ${chalk.green('"otel"')}: {
652
+ ${chalk.green('"scope"')}: "validation",
653
+ ${chalk.green('"description"')}: "Validates incoming data against schemas",
654
+ ${chalk.green('"status"')}: "implemented"
655
+ }
656
+ },
657
+ {
658
+ "id": "import-scope",
659
+ "type": "otel-scope",
660
+ "x": 250, "y": 0, "width": 200, "height": 120,
661
+ "color": "#2196F3",
662
+ "otel": {
663
+ "scope": "import-processing",
664
+ "description": "Handles data import from external sources",
665
+ "status": "implemented"
666
+ }
667
+ }
668
+ ],
669
+ "edges": [
670
+ {
671
+ "id": "import-to-validation",
672
+ "fromNode": "import-scope",
673
+ "toNode": "validation-scope",
674
+ "fromSide": "right",
675
+ "toSide": "left"
676
+ }
677
+ ]
678
+ }
679
+
680
+ ${chalk.bold('Validation:')}
681
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli scopes validate')}
682
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli scopes validate --json')}
683
+ `,
684
+ spans: `
685
+ ${chalk.bold.cyan('Spans Canvas Format (.spans.canvas)')}
686
+ ${chalk.dim('═'.repeat(70))}
687
+
688
+ Spans canvas files define span conventions (operation types) for your project.
689
+ Each span convention defines a pattern for matching spans and a color that is
690
+ used as the fill color for events emitted within that span.
691
+
692
+ ${chalk.bold('File Location:')}
693
+ ${chalk.dim('.principal-views/')}${chalk.yellow('architecture.spans.canvas')}
694
+ ${chalk.dim('(or <name>.spans.canvas)')}
695
+
696
+ ${chalk.bold('Required Structure:')}
697
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
698
+ ${chalk.dim('│')} { ${chalk.dim('│')}
699
+ ${chalk.dim('│')} ${chalk.green('"pv"')}: { ${chalk.dim('│')}
700
+ ${chalk.dim('│')} ${chalk.yellow('"name"')}: "Project Span Conventions", ${chalk.dim('│')}
701
+ ${chalk.dim('│')} ${chalk.yellow('"version"')}: "1.0.0" ${chalk.dim('│')}
702
+ ${chalk.dim('│')} }, ${chalk.dim('│')}
703
+ ${chalk.dim('│')} ${chalk.green('"nodes"')}: [ ${chalk.dim('│')}
704
+ ${chalk.dim('│')} { ${chalk.dim('│')}
705
+ ${chalk.dim('│')} ${chalk.yellow('"id"')}: "validation", ${chalk.dim('│')}
706
+ ${chalk.dim('│')} ${chalk.green('"type"')}: "otel-span-convention", ${chalk.dim('// Required: span node type')} ${chalk.dim('│')}
707
+ ${chalk.dim('│')} ${chalk.yellow('"x"')}: 0, ${chalk.yellow('"y"')}: 0, ${chalk.yellow('"width"')}: 220, ${chalk.yellow('"height"')}: 100, ${chalk.dim('│')}
708
+ ${chalk.dim('│')} ${chalk.green('"color"')}: "#22C55E", ${chalk.dim('// Required: span fill color')} ${chalk.dim('│')}
709
+ ${chalk.dim('│')} ${chalk.green('"otel"')}: { ${chalk.dim('│')}
710
+ ${chalk.dim('│')} ${chalk.cyan('"spanPattern"')}: "validate.*", ${chalk.dim('// Matches span names')} ${chalk.dim('│')}
711
+ ${chalk.dim('│')} ${chalk.cyan('"description"')}: "Validation operations", ${chalk.dim('│')}
712
+ ${chalk.dim('│')} ${chalk.cyan('"status"')}: "implemented" ${chalk.dim('// draft|approved|implemented')} ${chalk.dim('│')}
713
+ ${chalk.dim('│')} } ${chalk.dim('│')}
714
+ ${chalk.dim('│')} } ${chalk.dim('│')}
715
+ ${chalk.dim('│')} ], ${chalk.dim('│')}
716
+ ${chalk.dim('│')} ${chalk.green('"edges"')}: [] ${chalk.dim('// Optional: span relationships')} ${chalk.dim('│')}
717
+ ${chalk.dim('│')} } ${chalk.dim('│')}
718
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
719
+
720
+ ${chalk.bold('Node Required Fields:')}
721
+
722
+ ${chalk.green('type')} ${chalk.dim('string')} Must be "otel-span-convention"
723
+ ${chalk.green('color')} ${chalk.dim('string')} Hex color for this span (used as event fill)
724
+ ${chalk.green('otel.spanPattern')} ${chalk.dim('string')} Pattern to match span names
725
+ ${chalk.green('otel.status')} ${chalk.dim('string')} "draft" | "approved" | "implemented"
726
+
727
+ ${chalk.bold('Relationship to Workflows:')}
728
+
729
+ Span conventions are linked to workflows via the spanPattern field:
730
+
731
+ ${chalk.dim('┌────────────────────────────────────────────────────────────────────┐')}
732
+ ${chalk.dim('│')} ${chalk.yellow('# workflow.json')} ${chalk.dim('│')}
733
+ ${chalk.dim('│')} { ${chalk.dim('│')}
734
+ ${chalk.dim('│')} "id": "data-validation", ${chalk.dim('│')}
735
+ ${chalk.dim('│')} ${chalk.green('"spanPattern"')}: "validate.*", ${chalk.dim('// Matches spans.canvas')} ${chalk.dim('│')}
736
+ ${chalk.dim('│')} "canvas": ".principal-views/validation.otel.canvas", ${chalk.dim('│')}
737
+ ${chalk.dim('│')} "scenarios": [...] ${chalk.dim('│')}
738
+ ${chalk.dim('│')} } ${chalk.dim('│')}
739
+ ${chalk.dim('└────────────────────────────────────────────────────────────────────┘')}
740
+
741
+ ${chalk.bold('Color Usage:')}
742
+
743
+ The span color is used as the ${chalk.cyan('fill color')} for event nodes when
744
+ rendering events that were emitted within that span. This allows visual
745
+ grouping of events by their parent operation.
746
+
747
+ See ${chalk.yellow('formats colors')} for the complete renderer color contract.
748
+
749
+ ${chalk.bold('Validation Rules:')}
750
+
751
+ ${chalk.cyan('1. Color is required:')}
752
+ Every span convention node must have a color field.
753
+
754
+ ${chalk.cyan('2. Implemented spans require workflows:')}
755
+ Spans with status "implemented" must have a matching workflow.json.
756
+
757
+ ${chalk.cyan('3. Draft spans with workflows should be upgraded:')}
758
+ If a workflow exists for a span, its status should be "implemented".
759
+
760
+ ${chalk.bold('Validation:')}
761
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli validate')}
762
+ `,
763
+ colors: `
764
+ ${chalk.bold.cyan('Renderer Color Contract')}
765
+ ${chalk.dim('═'.repeat(70))}
766
+
767
+ This document specifies how renderers (e.g., File City) should apply colors
768
+ to event and span nodes. Colors communicate both scope ownership and
769
+ operational context at a glance.
770
+
771
+ ${chalk.bold('Color Model:')}
772
+
773
+ ${chalk.cyan('┌─────────────────────────────────────────────────────────────────┐')}
774
+ ${chalk.cyan('│')} ${chalk.bold('EVENT NODE')} ${chalk.cyan('│')}
775
+ ${chalk.cyan('│')} ┌───────────────────────────────────────────────────────────┐ ${chalk.cyan('│')}
776
+ ${chalk.cyan('│')} │ │ ${chalk.cyan('│')}
777
+ ${chalk.cyan('│')} │ ${chalk.bgGreen.black(' FILL: from span color ')} │ ${chalk.cyan('│')}
778
+ ${chalk.cyan('│')} │ ${chalk.dim('(defined in .spans.canvas)')} │ ${chalk.cyan('│')}
779
+ ${chalk.cyan('│')} │ │ ${chalk.cyan('│')}
780
+ ${chalk.cyan('│')} └───────────────────────────────────────────────────────────┘ ${chalk.cyan('│')}
781
+ ${chalk.cyan('│')} ${chalk.yellow('↑ BORDER: from scope color')} ${chalk.cyan('│')}
782
+ ${chalk.cyan('│')} ${chalk.dim('(defined in library.yaml owned-scopes)')} ${chalk.cyan('│')}
783
+ ${chalk.cyan('└─────────────────────────────────────────────────────────────────┘')}
784
+
785
+ ${chalk.bold('Color Sources:')}
786
+
787
+ ${chalk.cyan('1. Scope Color → Border')}
788
+ Source: ${chalk.yellow('library.yaml')} resources.[service].owned-scopes.[scope].color
789
+ Purpose: Identifies which instrumentation boundary the event belongs to
790
+
791
+ ${chalk.dim('resources:')}
792
+ ${chalk.dim(' my-service:')}
793
+ ${chalk.dim(' owned-scopes:')}
794
+ ${chalk.dim(' validation:')}
795
+ ${chalk.green(' color: "#22C55E"')} ${chalk.dim('← This becomes border color')}
796
+
797
+ ${chalk.cyan('2. Span Color → Fill')}
798
+ Source: ${chalk.yellow('.spans.canvas')} node color field
799
+ Purpose: Shows which operation type the event was emitted from
800
+
801
+ ${chalk.dim('{')}
802
+ ${chalk.dim(' "id": "validate-operation",')}
803
+ ${chalk.green(' "color": "#3B82F6",')} ${chalk.dim('← This becomes fill color')}
804
+ ${chalk.dim(' "pv": { "otel": { "spanPattern": "validate.*" } }')}
805
+ ${chalk.dim('}')}
806
+
807
+ ${chalk.bold('Rendering Rules:')}
808
+
809
+ ${chalk.cyan('1. Static Context (canvas authoring):')}
810
+ Events in .otel.canvas files do NOT have colors defined.
811
+ Renderers should use a neutral/default color when viewing canvases
812
+ outside of a workflow/span context.
813
+
814
+ ${chalk.cyan('2. Workflow Context:')}
815
+ When viewing events within a specific workflow:
816
+ - Fill = color from the workflow's spanPattern's span convention
817
+ - Border = color from the event's pv.otel.scope in library.yaml
818
+
819
+ ${chalk.cyan('3. Multi-Span Events:')}
820
+ An event may be emitted from multiple span types (e.g., same event
821
+ in different workflows). The renderer should:
822
+ - Use the span color from the current viewing context
823
+ - When viewing "all events", use neutral fill or show indicator
824
+
825
+ ${chalk.cyan('4. Missing Colors:')}
826
+ - Missing scope: Use default gray border (#6B7280)
827
+ - Missing span: Use default gray fill (#9CA3AF)
828
+ - Draft nodes: May use a distinct draft indicator color
829
+
830
+ ${chalk.bold('Shape Conventions:')}
831
+
832
+ ${chalk.green('Event nodes')}: Rectangle (default)
833
+ ${chalk.yellow('Span conventions')}: Hexagon or distinct shape
834
+
835
+ This shape differentiation helps distinguish span definitions from
836
+ event definitions in mixed visualizations.
837
+
838
+ ${chalk.bold('Implementation Notes:')}
839
+
840
+ Renderers should:
841
+ 1. Load scope colors from library.yaml at startup
842
+ 2. Load span colors from .spans.canvas files
843
+ 3. Resolve event → scope via pv.otel.scope field
844
+ 4. Resolve event → span via workflow.json spanPattern matching
845
+ 5. Apply border and fill colors based on current context
846
+ `,
847
+ examples: `
848
+ ${chalk.bold.cyan('Complete File Examples')}
849
+ ${chalk.dim('═'.repeat(70))}
850
+
851
+ ${chalk.bold('Example 1: Data Validator Canvas')}
852
+ ${chalk.dim('─'.repeat(70))}
853
+ ${chalk.yellow('.principal-views/data-validator.otel.canvas')}
854
+
855
+ {
856
+ "nodes": [
857
+ {
858
+ "id": "validation-started",
859
+ ${chalk.green('"type": "otel-event"')}, ${chalk.dim('// Semantic node type')}
860
+ ${chalk.green('"label"')}: "Validation Started", ${chalk.dim('// Display label (not "text")')}
861
+ "x": 0, "y": 0, "width": 200, "height": 100,
862
+ ${chalk.green('"event"')}: { ${chalk.dim('// Top-level, NOT inside pv')}
863
+ ${chalk.green('"name"')}: "validation.started",
864
+ ${chalk.green('"attributes"')}: {
865
+ "input.recordCount": {
866
+ "type": "integer",
867
+ "description": "Number of records to validate",
868
+ "required": true
869
+ },
870
+ "input.source": {
871
+ "type": "string",
872
+ "description": "Source of the data",
873
+ "required": false
874
+ }
875
+ }
876
+ },
877
+ ${chalk.green('"otel"')}: { ${chalk.dim('// Top-level OTEL metadata')}
878
+ ${chalk.green('"status"')}: "draft", ${chalk.dim('// Required status')}
879
+ "scope": "validation",
880
+ "files": ["src/validator.ts"]
881
+ }
882
+ },
883
+ {
884
+ "id": "validation-complete",
885
+ "type": "otel-event",
886
+ "label": "Validation Complete",
887
+ "x": 250, "y": 0, "width": 200, "height": 100,
888
+ "event": {
889
+ "name": "validation.complete",
890
+ "attributes": {
891
+ "result.validCount": {
892
+ "type": "integer",
893
+ "description": "Number of valid records",
894
+ "required": true
895
+ },
896
+ "result.invalidCount": {
897
+ "type": "integer",
898
+ "description": "Number of invalid records",
899
+ "required": true
900
+ },
901
+ "duration.ms": {
902
+ "type": "number",
903
+ "description": "Validation duration in milliseconds",
904
+ "required": false
905
+ }
906
+ }
907
+ },
908
+ "otel": {
909
+ "status": "draft",
910
+ "scope": "validation",
911
+ "files": ["src/validator.ts"]
912
+ }
913
+ },
914
+ {
915
+ "id": "validation-error",
916
+ "type": "otel-event",
917
+ "label": "Validation Error",
918
+ "x": 500, "y": 0, "width": 200, "height": 100,
919
+ "event": {
920
+ "name": "validation.error",
921
+ "attributes": {
922
+ "error.type": {
923
+ "type": "string",
924
+ "description": "Type of error that occurred",
925
+ "required": true
926
+ },
927
+ "error.message": {
928
+ "type": "string",
929
+ "description": "Error message",
930
+ "required": true
931
+ },
932
+ "error.stage": {
933
+ "type": "string",
934
+ "description": "Stage where error occurred",
935
+ "required": false
936
+ }
937
+ }
938
+ },
939
+ "otel": {
940
+ "status": "draft",
941
+ "scope": "validation",
942
+ "files": ["src/validator.ts"]
943
+ }
944
+ }
945
+ ],
946
+ "edges": [
947
+ {
948
+ "id": "started-to-complete",
949
+ "fromNode": "validation-started",
950
+ "toNode": "validation-complete",
951
+ "fromSide": "right",
952
+ "toSide": "left",
953
+ ${chalk.green('"pv"')}: { ${chalk.green('"edgeType"')}: "sequence" }
954
+ },
955
+ {
956
+ "id": "started-to-error",
957
+ "fromNode": "validation-started",
958
+ "toNode": "validation-error",
959
+ "fromSide": "bottom",
960
+ "toSide": "top",
961
+ "pv": { "edgeType": "error-path" }
962
+ }
963
+ ],
964
+ "pv": {
965
+ "name": "Data Validator",
966
+ "version": "1.0.0",
967
+ ${chalk.green('"markdown"')}: ".principal-views/data-validator.md",
968
+ ${chalk.green('"edgeTypes"')}: {
969
+ "sequence": {
970
+ "style": "solid",
971
+ "color": "#666",
972
+ "width": 2,
973
+ "directed": true
974
+ },
975
+ "error-path": {
976
+ "style": "dashed",
977
+ "color": "#F44336",
978
+ "width": 2,
979
+ "directed": true
980
+ }
981
+ }
982
+ }
983
+ }
984
+
985
+ ${chalk.bold('Example 2: Workflow Scenarios')}
986
+ ${chalk.dim('─'.repeat(70))}
987
+ ${chalk.yellow('.principal-views/data-validator.workflow.json')}
988
+
989
+ ${chalk.dim('Note: Workflow files reference event names defined in the canvas.')}
990
+ ${chalk.dim('The canvas uses pv.event.name (e.g., "validation.started"), and')}
991
+ ${chalk.dim('the workflow templates use those same event names.')}
992
+
993
+ {
994
+ "version": "1.0.0",
995
+ "canvas": ".principal-views/data-validator.otel.canvas",
996
+ "name": "Data Validator",
997
+ "description": "Validates data records against defined schemas",
998
+ "spanPattern": "validation.process",
999
+ "scenarioSelection": "first-match",
1000
+ "scenarios": [
1001
+ {
1002
+ "id": "success",
1003
+ "priority": 1,
1004
+ "description": "Validation completed successfully",
1005
+ "template": {
1006
+ "events": {
1007
+ "validation.started": "Started validation of {{input.recordCount}} records",
1008
+ "validation.complete": "Processed {{result.validCount}} valid, {{result.invalidCount}} invalid"
1009
+ },
1010
+ "summary": "Validated {{result.validCount}} records successfully"
1011
+ }
1012
+ },
1013
+ {
1014
+ "id": "error",
1015
+ "priority": 2,
1016
+ "description": "Validation encountered an error",
1017
+ "template": {
1018
+ "events": {
1019
+ "validation.started": "Started validation",
1020
+ "validation.error": "Error: {{error.message}} (type: {{error.type}})"
1021
+ },
1022
+ "summary": "Validation failed: {{error.message}}"
1023
+ }
1024
+ }
1025
+ ]
1026
+ }
1027
+
1028
+ ${chalk.bold.yellow('Note on Fallback Scenarios:')}
1029
+ Avoid creating "fallback" scenarios that are strict subsets of other scenarios.
1030
+ Instead, ensure each scenario has a distinct set of events that differentiates it.
1031
+
1032
+ ${chalk.bold('Example 3: Execution File')}
1033
+ ${chalk.dim('─'.repeat(70))}
1034
+ ${chalk.yellow('__executions__/data-validator.otel.json')}
1035
+
1036
+ {
1037
+ "exportedAt": "2025-01-21T10:30:45.123Z",
1038
+ "serviceName": "my-app-tests",
1039
+ "spanCount": 3,
1040
+ "spans": [
1041
+ {
1042
+ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
1043
+ "spanId": "00f067aa0ba902b7",
1044
+ "parentSpanId": null,
1045
+ "name": "test:data-validator",
1046
+ "kind": "INTERNAL",
1047
+ "startTime": 1703548800000,
1048
+ "endTime": 1703548800150,
1049
+ "duration": 150,
1050
+ "attributes": {
1051
+ "test.name": "data validator success case"
1052
+ },
1053
+ "status": { "code": "OK" },
1054
+ "events": []
1055
+ },
1056
+ {
1057
+ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
1058
+ "spanId": "abc123def4567890",
1059
+ "parentSpanId": "00f067aa0ba902b7",
1060
+ "name": "validation.started",
1061
+ "kind": "INTERNAL",
1062
+ "startTime": 1703548800010,
1063
+ "endTime": 1703548800015,
1064
+ "duration": 5,
1065
+ "attributes": {
1066
+ "input.recordCount": 100,
1067
+ "input.source": "test-data.csv"
1068
+ },
1069
+ "status": { "code": "OK" },
1070
+ "events": []
1071
+ },
1072
+ {
1073
+ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
1074
+ "spanId": "def789abc1234567",
1075
+ "parentSpanId": "00f067aa0ba902b7",
1076
+ "name": "validation.complete",
1077
+ "kind": "INTERNAL",
1078
+ "startTime": 1703548800140,
1079
+ "endTime": 1703548800145,
1080
+ "duration": 5,
1081
+ "attributes": {
1082
+ "result.validCount": 95,
1083
+ "result.invalidCount": 5,
1084
+ "duration.ms": 130
1085
+ },
1086
+ "status": { "code": "OK" },
1087
+ "events": []
1088
+ }
1089
+ ]
1090
+ }
1091
+
1092
+ ${chalk.bold('Next Steps:')}
1093
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli validate')} Validate canvas
1094
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli workflow validate')} Validate workflows
1095
+ ${chalk.cyan('npx @principal-ai/principal-studio-cli validate-execution')} Validate execution
1096
+ `,
1097
+ };
1098
+ export function createFormatsCommand() {
1099
+ const command = new Command('formats');
1100
+ command
1101
+ .description('Display documentation about file formats')
1102
+ .argument('[section]', 'Section to display: overview, canvas, workflow, execution, library, scopes, examples')
1103
+ .action((section) => {
1104
+ const validSections = Object.keys(FORMAT_SECTIONS);
1105
+ if (!section) {
1106
+ console.log(FORMAT_SECTIONS.overview);
1107
+ return;
1108
+ }
1109
+ const normalizedSection = section.toLowerCase();
1110
+ if (!validSections.includes(normalizedSection)) {
1111
+ console.log(chalk.red(`Unknown section: ${section}`));
1112
+ console.log(`Valid sections: ${validSections.join(', ')}`);
1113
+ process.exit(1);
1114
+ }
1115
+ console.log(FORMAT_SECTIONS[normalizedSection]);
1116
+ });
1117
+ return command;
1118
+ }