@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.
- package/README.md +172 -0
- package/dist/commands/agent-session.d.ts +3 -0
- package/dist/commands/agent-session.d.ts.map +1 -0
- package/dist/commands/agent-session.js +67 -0
- package/dist/commands/agent-sessions.d.ts +11 -0
- package/dist/commands/agent-sessions.d.ts.map +1 -0
- package/dist/commands/agent-sessions.js +39 -0
- package/dist/commands/auxiliary/index.d.ts +3 -0
- package/dist/commands/auxiliary/index.d.ts.map +1 -0
- package/dist/commands/auxiliary/index.js +9 -0
- package/dist/commands/auxiliary/validate.d.ts +3 -0
- package/dist/commands/auxiliary/validate.d.ts.map +1 -0
- package/dist/commands/auxiliary/validate.js +123 -0
- package/dist/commands/collector/check.d.ts +6 -0
- package/dist/commands/collector/check.d.ts.map +1 -0
- package/dist/commands/collector/check.js +153 -0
- package/dist/commands/collector/diagnose.d.ts +6 -0
- package/dist/commands/collector/diagnose.d.ts.map +1 -0
- package/dist/commands/collector/diagnose.js +229 -0
- package/dist/commands/collector/index.d.ts +6 -0
- package/dist/commands/collector/index.d.ts.map +1 -0
- package/dist/commands/collector/index.js +16 -0
- package/dist/commands/collector/status.d.ts +6 -0
- package/dist/commands/collector/status.d.ts.map +1 -0
- package/dist/commands/collector/status.js +77 -0
- package/dist/commands/collector/utils.d.ts +92 -0
- package/dist/commands/collector/utils.d.ts.map +1 -0
- package/dist/commands/collector/utils.js +154 -0
- package/dist/commands/coverage.d.ts +9 -0
- package/dist/commands/coverage.d.ts.map +1 -0
- package/dist/commands/coverage.js +252 -0
- package/dist/commands/create.d.ts +6 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +50 -0
- package/dist/commands/events/index.d.ts +3 -0
- package/dist/commands/events/index.d.ts.map +1 -0
- package/dist/commands/events/index.js +9 -0
- package/dist/commands/events/validate.d.ts +3 -0
- package/dist/commands/events/validate.d.ts.map +1 -0
- package/dist/commands/events/validate.js +124 -0
- package/dist/commands/formats.d.ts +6 -0
- package/dist/commands/formats.d.ts.map +1 -0
- package/dist/commands/formats.js +1118 -0
- package/dist/commands/hooks.d.ts +9 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +295 -0
- package/dist/commands/inbox.d.ts +13 -0
- package/dist/commands/inbox.d.ts.map +1 -0
- package/dist/commands/inbox.js +179 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +220 -0
- package/dist/commands/lint.d.ts +11 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +71 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +78 -0
- package/dist/commands/migrate-nodes.d.ts +9 -0
- package/dist/commands/migrate-nodes.d.ts.map +1 -0
- package/dist/commands/migrate-nodes.js +363 -0
- package/dist/commands/migrate-scopes-to-canvas.d.ts +3 -0
- package/dist/commands/migrate-scopes-to-canvas.d.ts.map +1 -0
- package/dist/commands/migrate-scopes-to-canvas.js +143 -0
- package/dist/commands/migration.d.ts +6 -0
- package/dist/commands/migration.d.ts.map +1 -0
- package/dist/commands/migration.js +247 -0
- package/dist/commands/open-studio.d.ts +10 -0
- package/dist/commands/open-studio.d.ts.map +1 -0
- package/dist/commands/open-studio.js +32 -0
- package/dist/commands/opencode/fetch.d.ts +3 -0
- package/dist/commands/opencode/fetch.d.ts.map +1 -0
- package/dist/commands/opencode/fetch.js +25 -0
- package/dist/commands/opencode/index.d.ts +3 -0
- package/dist/commands/opencode/index.d.ts.map +1 -0
- package/dist/commands/opencode/index.js +11 -0
- package/dist/commands/opencode/list-sessions.d.ts +3 -0
- package/dist/commands/opencode/list-sessions.d.ts.map +1 -0
- package/dist/commands/opencode/list-sessions.js +20 -0
- package/dist/commands/repo.d.ts +18 -0
- package/dist/commands/repo.d.ts.map +1 -0
- package/dist/commands/repo.js +233 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +367 -0
- package/dist/commands/scopes/index.d.ts +3 -0
- package/dist/commands/scopes/index.d.ts.map +1 -0
- package/dist/commands/scopes/index.js +9 -0
- package/dist/commands/scopes/validate.d.ts +3 -0
- package/dist/commands/scopes/validate.d.ts.map +1 -0
- package/dist/commands/scopes/validate.js +187 -0
- package/dist/commands/setup.d.ts +6 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +406 -0
- package/dist/commands/starred-collections.d.ts +12 -0
- package/dist/commands/starred-collections.d.ts.map +1 -0
- package/dist/commands/starred-collections.js +310 -0
- package/dist/commands/subsystem-model.d.ts +11 -0
- package/dist/commands/subsystem-model.d.ts.map +1 -0
- package/dist/commands/subsystem-model.js +249 -0
- package/dist/commands/topic.d.ts +15 -0
- package/dist/commands/topic.d.ts.map +1 -0
- package/dist/commands/topic.js +683 -0
- package/dist/commands/tour.d.ts +23 -0
- package/dist/commands/tour.d.ts.map +1 -0
- package/dist/commands/tour.js +783 -0
- package/dist/commands/trace/flow.d.ts +6 -0
- package/dist/commands/trace/flow.d.ts.map +1 -0
- package/dist/commands/trace/flow.js +158 -0
- package/dist/commands/trace/index.d.ts +6 -0
- package/dist/commands/trace/index.d.ts.map +1 -0
- package/dist/commands/trace/index.js +20 -0
- package/dist/commands/trace/inspect.d.ts +6 -0
- package/dist/commands/trace/inspect.d.ts.map +1 -0
- package/dist/commands/trace/inspect.js +187 -0
- package/dist/commands/trace/list.d.ts +6 -0
- package/dist/commands/trace/list.d.ts.map +1 -0
- package/dist/commands/trace/list.js +145 -0
- package/dist/commands/trace/registrations.d.ts +6 -0
- package/dist/commands/trace/registrations.d.ts.map +1 -0
- package/dist/commands/trace/registrations.js +81 -0
- package/dist/commands/trace/validate.d.ts +6 -0
- package/dist/commands/trace/validate.d.ts.map +1 -0
- package/dist/commands/trace/validate.js +276 -0
- package/dist/commands/trail.d.ts +36 -0
- package/dist/commands/trail.d.ts.map +1 -0
- package/dist/commands/trail.js +622 -0
- package/dist/commands/validate.d.ts +12 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +3334 -0
- package/dist/commands/workflow/implementation.d.ts +3 -0
- package/dist/commands/workflow/implementation.d.ts.map +1 -0
- package/dist/commands/workflow/implementation.js +51 -0
- package/dist/commands/workflow/index.d.ts +3 -0
- package/dist/commands/workflow/index.d.ts.map +1 -0
- package/dist/commands/workflow/index.js +19 -0
- package/dist/commands/workflow/inspect.d.ts +3 -0
- package/dist/commands/workflow/inspect.d.ts.map +1 -0
- package/dist/commands/workflow/inspect.js +123 -0
- package/dist/commands/workflow/list.d.ts +3 -0
- package/dist/commands/workflow/list.d.ts.map +1 -0
- package/dist/commands/workflow/list.js +95 -0
- package/dist/commands/workflow/render.d.ts +3 -0
- package/dist/commands/workflow/render.d.ts.map +1 -0
- package/dist/commands/workflow/render.js +88 -0
- package/dist/commands/workflow/test.d.ts +3 -0
- package/dist/commands/workflow/test.d.ts.map +1 -0
- package/dist/commands/workflow/test.js +141 -0
- package/dist/commands/workflow/utils.d.ts +49 -0
- package/dist/commands/workflow/utils.d.ts.map +1 -0
- package/dist/commands/workflow/utils.js +164 -0
- package/dist/commands/workflow/validate.d.ts +3 -0
- package/dist/commands/workflow/validate.d.ts.map +1 -0
- package/dist/commands/workflow/validate.js +344 -0
- package/dist/file-utils.d.ts +15 -0
- package/dist/file-utils.d.ts.map +1 -0
- package/dist/file-utils.js +29 -0
- package/dist/index.cjs +285718 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/lib/bridge-ipc.d.ts +29 -0
- package/dist/lib/bridge-ipc.d.ts.map +1 -0
- package/dist/lib/bridge-ipc.js +128 -0
- package/dist/lib/github-user.d.ts +17 -0
- package/dist/lib/github-user.d.ts.map +1 -0
- package/dist/lib/github-user.js +53 -0
- package/dist/lib/open-url.d.ts +11 -0
- package/dist/lib/open-url.d.ts.map +1 -0
- package/dist/lib/open-url.js +45 -0
- package/dist/lib/subsystem-model-store.d.ts +74 -0
- package/dist/lib/subsystem-model-store.d.ts.map +1 -0
- package/dist/lib/subsystem-model-store.js +292 -0
- package/dist/lib/tour-cache.d.ts +26 -0
- package/dist/lib/tour-cache.d.ts.map +1 -0
- package/dist/lib/tour-cache.js +46 -0
- package/dist/lib/trail-cache.d.ts +40 -0
- package/dist/lib/trail-cache.d.ts.map +1 -0
- package/dist/lib/trail-cache.js +97 -0
- package/dist/lib/viewer-ipc.d.ts +44 -0
- package/dist/lib/viewer-ipc.d.ts.map +1 -0
- package/dist/lib/viewer-ipc.js +68 -0
- 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
|
+
}
|