archsight 0.1.0

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 (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +24 -0
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +186 -0
  5. data/Dockerfile +39 -0
  6. data/LICENSE.txt +201 -0
  7. data/README.md +170 -0
  8. data/SECURITY.md +27 -0
  9. data/exe/archsight +9 -0
  10. data/lib/archsight/annotations/aggregators.rb +109 -0
  11. data/lib/archsight/annotations/annotation.rb +168 -0
  12. data/lib/archsight/annotations/architecture_annotations.rb +59 -0
  13. data/lib/archsight/annotations/backup_annotations.rb +21 -0
  14. data/lib/archsight/annotations/computed.rb +264 -0
  15. data/lib/archsight/annotations/email_recipient.rb +35 -0
  16. data/lib/archsight/annotations/generated_annotations.rb +17 -0
  17. data/lib/archsight/annotations/git_annotations.rb +21 -0
  18. data/lib/archsight/annotations/relation_resolver.rb +160 -0
  19. data/lib/archsight/cli.rb +120 -0
  20. data/lib/archsight/configuration.rb +36 -0
  21. data/lib/archsight/database.rb +183 -0
  22. data/lib/archsight/documentation.rb +171 -0
  23. data/lib/archsight/graph.rb +113 -0
  24. data/lib/archsight/helpers.rb +210 -0
  25. data/lib/archsight/linter.rb +77 -0
  26. data/lib/archsight/mcp/analyze_resource_tool.rb +222 -0
  27. data/lib/archsight/mcp/base.rb +48 -0
  28. data/lib/archsight/mcp/query_tool.rb +113 -0
  29. data/lib/archsight/mcp/resource_doc_tool.rb +87 -0
  30. data/lib/archsight/mcp.rb +6 -0
  31. data/lib/archsight/query/ast.rb +279 -0
  32. data/lib/archsight/query/errors.rb +39 -0
  33. data/lib/archsight/query/evaluator.rb +707 -0
  34. data/lib/archsight/query/lexer.rb +289 -0
  35. data/lib/archsight/query/parser.rb +506 -0
  36. data/lib/archsight/query.rb +68 -0
  37. data/lib/archsight/renderer.rb +134 -0
  38. data/lib/archsight/resources/application_component.rb +346 -0
  39. data/lib/archsight/resources/application_interface.rb +54 -0
  40. data/lib/archsight/resources/application_service.rb +222 -0
  41. data/lib/archsight/resources/base.rb +300 -0
  42. data/lib/archsight/resources/business_actor.rb +195 -0
  43. data/lib/archsight/resources/business_constraint.rb +32 -0
  44. data/lib/archsight/resources/business_process.rb +37 -0
  45. data/lib/archsight/resources/business_product.rb +206 -0
  46. data/lib/archsight/resources/business_requirement.rb +56 -0
  47. data/lib/archsight/resources/compliance_evidence.rb +42 -0
  48. data/lib/archsight/resources/data_object.rb +49 -0
  49. data/lib/archsight/resources/motivation_goal.rb +37 -0
  50. data/lib/archsight/resources/motivation_outcome.rb +33 -0
  51. data/lib/archsight/resources/motivation_stakeholder.rb +38 -0
  52. data/lib/archsight/resources/strategy_capability.rb +38 -0
  53. data/lib/archsight/resources/technology_artifact.rb +154 -0
  54. data/lib/archsight/resources/technology_interface.rb +34 -0
  55. data/lib/archsight/resources/technology_node.rb +42 -0
  56. data/lib/archsight/resources/technology_service.rb +35 -0
  57. data/lib/archsight/resources/technology_system_software.rb +37 -0
  58. data/lib/archsight/resources/view.rb +51 -0
  59. data/lib/archsight/resources.rb +49 -0
  60. data/lib/archsight/template.rb +49 -0
  61. data/lib/archsight/version.rb +5 -0
  62. data/lib/archsight/web/application.rb +290 -0
  63. data/lib/archsight/web/doc/archimate.md +215 -0
  64. data/lib/archsight/web/doc/computed_annotations.md +316 -0
  65. data/lib/archsight/web/doc/icons.md +303 -0
  66. data/lib/archsight/web/doc/index.md.erb +74 -0
  67. data/lib/archsight/web/doc/modeling.md +200 -0
  68. data/lib/archsight/web/doc/search.md +227 -0
  69. data/lib/archsight/web/doc/togaf.md +255 -0
  70. data/lib/archsight/web/doc/tool.md +90 -0
  71. data/lib/archsight/web/public/css/artifact.css +985 -0
  72. data/lib/archsight/web/public/css/base.css +201 -0
  73. data/lib/archsight/web/public/css/graph.css +106 -0
  74. data/lib/archsight/web/public/css/highlight.min.css +10 -0
  75. data/lib/archsight/web/public/css/iconoir.css +22 -0
  76. data/lib/archsight/web/public/css/instance.css +329 -0
  77. data/lib/archsight/web/public/css/layout.css +421 -0
  78. data/lib/archsight/web/public/css/mermaid-layers.css +188 -0
  79. data/lib/archsight/web/public/css/pico.min.css +4 -0
  80. data/lib/archsight/web/public/favicon.ico +0 -0
  81. data/lib/archsight/web/public/img/archimate.png +0 -0
  82. data/lib/archsight/web/public/img/togaf-high-level.png +0 -0
  83. data/lib/archsight/web/public/js/graph-zoom.js +18 -0
  84. data/lib/archsight/web/public/js/highlight.min.js +3899 -0
  85. data/lib/archsight/web/public/js/htmx.min.js +1 -0
  86. data/lib/archsight/web/public/js/mermaid-init.js +88 -0
  87. data/lib/archsight/web/public/js/mermaid.min.js +2811 -0
  88. data/lib/archsight/web/public/js/sparkline.js +42 -0
  89. data/lib/archsight/web/public/js/svg-pan-zoom.min.js +3 -0
  90. data/lib/archsight/web/public/js/svg-zoom-controls.js +93 -0
  91. data/lib/archsight/web/views/index.haml +12 -0
  92. data/lib/archsight/web/views/partials/artifact/_activity.haml +55 -0
  93. data/lib/archsight/web/views/partials/artifact/_agentic.haml +25 -0
  94. data/lib/archsight/web/views/partials/artifact/_deployment.haml +29 -0
  95. data/lib/archsight/web/views/partials/artifact/_git_info.haml +16 -0
  96. data/lib/archsight/web/views/partials/artifact/_language_stats.haml +53 -0
  97. data/lib/archsight/web/views/partials/artifact/_links.haml +24 -0
  98. data/lib/archsight/web/views/partials/artifact/_project_estimate.haml +26 -0
  99. data/lib/archsight/web/views/partials/artifact/_repositories.haml +55 -0
  100. data/lib/archsight/web/views/partials/artifact/_team.haml +83 -0
  101. data/lib/archsight/web/views/partials/artifact/_workflow.haml +69 -0
  102. data/lib/archsight/web/views/partials/components/_activity.haml +37 -0
  103. data/lib/archsight/web/views/partials/components/_git.haml +17 -0
  104. data/lib/archsight/web/views/partials/components/_jira.haml +18 -0
  105. data/lib/archsight/web/views/partials/components/_languages.haml +29 -0
  106. data/lib/archsight/web/views/partials/components/_owner.haml +15 -0
  107. data/lib/archsight/web/views/partials/components/_repositories.haml +37 -0
  108. data/lib/archsight/web/views/partials/components/_status.haml +23 -0
  109. data/lib/archsight/web/views/partials/instance/_detail.haml +99 -0
  110. data/lib/archsight/web/views/partials/instance/_graph.haml +6 -0
  111. data/lib/archsight/web/views/partials/instance/_list.haml +84 -0
  112. data/lib/archsight/web/views/partials/instance/_relations.haml +43 -0
  113. data/lib/archsight/web/views/partials/instance/_requirements.haml +41 -0
  114. data/lib/archsight/web/views/partials/instance/_view_detail.haml +57 -0
  115. data/lib/archsight/web/views/partials/layout/_content.haml +40 -0
  116. data/lib/archsight/web/views/partials/layout/_error.haml +22 -0
  117. data/lib/archsight/web/views/partials/layout/_head.haml +24 -0
  118. data/lib/archsight/web/views/partials/layout/_navigation.haml +20 -0
  119. data/lib/archsight/web/views/partials/layout/_sidebar.haml +27 -0
  120. data/lib/archsight/web/views/search.haml +53 -0
  121. data/lib/archsight.rb +17 -0
  122. metadata +311 -0
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../annotations/annotation"
4
+
5
+ module Archsight
6
+ module Resources
7
+ # Base is the base for all assets, should not be used directly, only by inheritance
8
+ class Base
9
+ attr_accessor :raw, :path_ref, :references
10
+
11
+ def self.inherited(subclass)
12
+ super
13
+ # Auto-register when class is defined
14
+ Archsight::Resources.register(subclass)
15
+ end
16
+
17
+ def self.relation(verb, kind, klass_name)
18
+ @relations ||= []
19
+ @relations << [verb, kind, klass_name]
20
+ end
21
+
22
+ def self.relations
23
+ @relations || []
24
+ end
25
+
26
+ # Define an annotation using the Annotation class
27
+ def self.annotation(key, description: nil, filter: nil, title: nil, format: nil, enum: nil, sidebar: true,
28
+ type: nil, list: false)
29
+ @annotations ||= []
30
+ options = { description: description, filter: filter, title: title, format: format, enum: enum,
31
+ sidebar: sidebar, type: type, list: list }
32
+ @annotations << Archsight::Annotations::Annotation.new(key, options)
33
+ end
34
+
35
+ # Get all annotation definitions
36
+ def self.annotations
37
+ @annotations || []
38
+ end
39
+
40
+ # Define a computed annotation using a block
41
+ # Computed annotations are calculated from related resources after the database is loaded.
42
+ # Supports all the same options as regular annotations.
43
+ # @param key [String] The annotation key (e.g., 'computed/total_cost')
44
+ # @param description [String, nil] Human-readable description
45
+ # @param filter [Symbol, nil] Filter type (:word, :list, or nil)
46
+ # @param title [String, nil] Display title
47
+ # @param format [Symbol, nil] Rendering format (:markdown, :tag_word, :tag_list)
48
+ # @param enum [Array, nil] Allowed values
49
+ # @param sidebar [Boolean] Show in sidebar (default false for computed)
50
+ # @param type [Class, nil] Type for value coercion (Integer, Float, String)
51
+ # @param list [Boolean] Whether values are lists (default false)
52
+ # @yield Block that computes the annotation value, evaluated in Evaluator context
53
+ def self.computed_annotation(key, description: nil, filter: nil, title: nil, format: nil, enum: nil,
54
+ sidebar: false, type: nil, list: false, &)
55
+ require_relative "../annotations/computed"
56
+ @computed_annotations ||= []
57
+ @computed_annotations << Archsight::Annotations::Computed.new(key, description: description, type: type, &)
58
+
59
+ # Also register as a regular annotation so it passes validation and is recognized
60
+ @annotations ||= []
61
+ options = { description: description, filter: filter, title: title, format: format, enum: enum,
62
+ sidebar: sidebar, type: type, list: list }
63
+ @annotations << Archsight::Annotations::Annotation.new(key, options)
64
+ end
65
+
66
+ # Get all computed annotation definitions
67
+ def self.computed_annotations
68
+ @computed_annotations || []
69
+ end
70
+
71
+ # Find annotation definition matching a key (handles patterns)
72
+ def self.annotation_matching(key)
73
+ annotations.find { |a| a.matches?(key) }
74
+ end
75
+
76
+ # Check if key matches any pattern annotation
77
+ def self.matches_annotation_pattern?(key)
78
+ annotations.any? { |a| a.pattern? && a.matches?(key) }
79
+ end
80
+
81
+ # Get filterable annotations as array of Annotation objects
82
+ def self.filterable_annotations
83
+ annotations.select(&:filterable?).reject(&:pattern?)
84
+ end
85
+
86
+ # Get annotations marked for list display
87
+ def self.list_annotations
88
+ annotations.select(&:list_display?).reject(&:pattern?)
89
+ end
90
+
91
+ def self.annotation_title(key)
92
+ annotation_matching(key)&.title || key.split("/").last.capitalize
93
+ end
94
+
95
+ def self.annotation_format(key)
96
+ annotation_matching(key)&.format
97
+ end
98
+
99
+ def self.annotation_enum(key)
100
+ annotation_matching(key)&.enum
101
+ end
102
+
103
+ def self.icon(icon_name = nil)
104
+ if icon_name
105
+ @icon = icon_name
106
+ else
107
+ @icon || "page" # default icon
108
+ end
109
+ end
110
+
111
+ def self.layer(layer_name = nil)
112
+ if layer_name
113
+ @layer = layer_name
114
+ else
115
+ @layer || "other" # default layer
116
+ end
117
+ end
118
+
119
+ def self.description(text = nil)
120
+ if text
121
+ @description = text
122
+ else
123
+ @description
124
+ end
125
+ end
126
+
127
+ # Include annotation modules by symbol name
128
+ # @example include_annotations :git, :architecture, :backup
129
+ # @param names [Array<Symbol>] Symbols representing annotation modules (:git, :architecture, :backup, :generated)
130
+ def self.include_annotations(*names)
131
+ names.each do |name|
132
+ # Convert snake_case to CamelCase (e.g., :git -> 'Git', :my_module -> 'MyModule')
133
+ module_name = name.to_s.split("_").map(&:capitalize).join
134
+ mod = Archsight::Annotations.const_get(module_name)
135
+ include mod
136
+ rescue NameError
137
+ available = Archsight::Annotations.constants
138
+ .select { |c| Archsight::Annotations.const_get(c).is_a?(Module) && Archsight::Annotations.const_get(c).respond_to?(:included) }
139
+ .map { |c| ":#{c.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase}" }
140
+ .sort
141
+ Kernel.raise "Unknown annotation module :#{name}. Available: #{available.join(", ")}"
142
+ end
143
+ end
144
+
145
+ def self.discovered_annotations
146
+ @discovered_annotations ||= Set.new
147
+ end
148
+
149
+ def initialize(raw, path_ref)
150
+ @raw = raw
151
+ @path_ref = path_ref
152
+ @references = []
153
+ # Auto-discover annotation keys
154
+ return unless annotations
155
+
156
+ annotations.each_key do |key|
157
+ self.class.discovered_annotations.add(key)
158
+ end
159
+ end
160
+
161
+ def klass
162
+ self.class.name.split("::").last
163
+ end
164
+
165
+ def kind
166
+ @raw["kind"]
167
+ end
168
+
169
+ def name
170
+ metadata["name"]
171
+ end
172
+
173
+ def annotations
174
+ metadata["annotations"] || {}
175
+ end
176
+
177
+ # Set a computed annotation value
178
+ # This writes to both the computed values cache and the annotations hash
179
+ # so computed values are accessible via the normal annotations interface.
180
+ # @param key [String] The annotation key
181
+ # @param value [Object] The computed value
182
+ def set_computed_annotation(key, value)
183
+ @computed_values ||= {}
184
+ @computed_values[key] = value
185
+ # Write to annotations hash for query compatibility
186
+ @raw["metadata"] ||= {}
187
+ @raw["metadata"]["annotations"] ||= {}
188
+ @raw["metadata"]["annotations"][key] = value
189
+ end
190
+
191
+ # Get a computed annotation value from the cache
192
+ # @param key [String] The annotation key
193
+ # @return [Object, nil] The computed value or nil
194
+ def computed_annotation_value(key)
195
+ @computed_values&.[](key)
196
+ end
197
+
198
+ def metadata
199
+ @raw["metadata"] || {}
200
+ end
201
+
202
+ def spec
203
+ @raw["spec"] || {}
204
+ end
205
+
206
+ def to_s
207
+ "#<#{self.class} name=#{name}>"
208
+ end
209
+
210
+ def abandoned?
211
+ annotations["activity/status"] == "abandoned"
212
+ end
213
+
214
+ def has_relations?
215
+ spec.any? { |_verb, kinds| kinds.is_a?(Hash) && kinds.values.any? { |v| v.is_a?(Array) && v.any? } }
216
+ end
217
+
218
+ def verb_allowed?(verb)
219
+ self.class.relations.any? { |v, _, _| v.to_s == verb.to_s }
220
+ end
221
+
222
+ def verb_kind_allowed?(verb, kind)
223
+ self.class.relations.any? { |v, k, _| v.to_s == verb.to_s && k.to_s == kind.to_s }
224
+ end
225
+
226
+ def relations(verb, kind)
227
+ (spec[verb.to_s] || {})[kind.to_s] || []
228
+ end
229
+
230
+ def set_relations(verb, kind, rels)
231
+ spec[verb.to_s][kind.to_s] = rels
232
+ rels.each { |rel| rel.referenced_by(self, verb) }
233
+ end
234
+
235
+ def referenced_by(inst, verb = nil)
236
+ # Store reference with verb information for grouped display
237
+ existing = @references.find { |r| r[:instance] == inst && r[:verb] == verb }
238
+ @references << { instance: inst, verb: verb } unless existing
239
+ end
240
+
241
+ # Get references grouped by kind and verb for display (incoming)
242
+ # Returns: { "Kind" => { "verb" => [instances...] } }
243
+ def references_grouped
244
+ grouped = {}
245
+ @references.each do |ref|
246
+ inst = ref[:instance]
247
+ verb = ref[:verb]
248
+ kind = inst.klass
249
+ grouped[kind] ||= {}
250
+ grouped[kind][verb] ||= []
251
+ grouped[kind][verb] << inst
252
+ end
253
+ # Sort by kind name, then by verb name
254
+ grouped.sort.to_h.transform_values { |verbs| verbs.sort.to_h }
255
+ end
256
+
257
+ # Get outgoing relations grouped by verb and kind for display
258
+ # Returns: { "verb" => { "Kind" => [instances...] } }
259
+ def relations_grouped
260
+ grouped = {}
261
+ spec.each do |verb, kinds|
262
+ next unless kinds.is_a?(Hash)
263
+
264
+ kinds.each_value do |instances|
265
+ next unless instances.is_a?(Array) && instances.any?
266
+
267
+ instances.each do |inst|
268
+ kind = inst.klass
269
+ grouped[verb] ||= {}
270
+ grouped[verb][kind] ||= []
271
+ grouped[verb][kind] << inst
272
+ end
273
+ end
274
+ end
275
+ # Sort by verb name, then by kind name
276
+ grouped.sort.to_h.transform_values { |kinds| kinds.sort.to_h }
277
+ end
278
+
279
+ def verify!
280
+ spec.each do |verb, kinds|
281
+ raise "unknown verb #{verb}" unless verb_allowed?(verb)
282
+
283
+ kinds.each_key do |kind, _|
284
+ raise "unknown verb #{verb} / kind #{kind} combination" unless verb_kind_allowed?(verb, kind)
285
+ end
286
+ end
287
+ end
288
+
289
+ def merge!(inst)
290
+ # NOTE: path reference is preserved from the original instance
291
+ @raw = Archsight::Helpers.deep_merge(@raw, inst.raw)
292
+ end
293
+
294
+ # raise provides a helper for better error messages including current path and line no
295
+ def raise(msg)
296
+ Kernel.raise(Archsight::ResourceError.new(msg, @path_ref))
297
+ end
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ # BusinessActor represents teams or organizational units
4
+ class Archsight::Resources::BusinessActor < Archsight::Resources::Base
5
+ include_annotations :git, :architecture, :generated
6
+
7
+ description <<~MD
8
+ Represents a team, organizational unit, or external entity that performs business behavior.
9
+
10
+ ## ArchiMate Definition
11
+
12
+ **Layer:** Business
13
+ **Aspect:** Active Structure
14
+
15
+ A business actor represents a business entity that is capable of performing behavior.
16
+ Actors can be individuals, teams, departments, or external organizations that participate
17
+ in business processes or own/maintain system components.
18
+
19
+ ## Usage
20
+
21
+ Use BusinessActor to represent:
22
+
23
+ - Development teams
24
+ - Operations teams
25
+ - External vendors or partners
26
+ - Support organizations
27
+ - Cross-functional groups
28
+ MD
29
+
30
+ icon "community"
31
+ layer "business"
32
+
33
+ annotation "team/lead",
34
+ description: 'Team lead (format: "Name <email>" or "email")',
35
+ title: "Team Lead",
36
+ sidebar: false,
37
+ filter: :word,
38
+ format: :tag_word,
39
+ type: Archsight::Annotations::EmailRecipient
40
+
41
+ annotation "team/members",
42
+ description: 'Team members (format: "Name <email>" or "email")',
43
+ title: "Team Members",
44
+ sidebar: false,
45
+ filter: :list,
46
+ format: :tag_list,
47
+ type: Archsight::Annotations::EmailRecipient
48
+
49
+ annotation "team/jira",
50
+ description: "Jira project keys for issue tracking (primary queue first)",
51
+ title: "Jira Projects",
52
+ sidebar: false,
53
+ filter: :list,
54
+ format: :tag_list
55
+
56
+ annotation "jira/issues/created",
57
+ description: "Issues created per month (comma-separated, last 6 months)",
58
+ title: "Issues Created",
59
+ sidebar: false
60
+
61
+ annotation "jira/issues/resolved",
62
+ description: "Issues resolved per month (comma-separated, last 6 months)",
63
+ title: "Issues Resolved",
64
+ sidebar: false
65
+
66
+ # ITIL 4 General Management Practices
67
+ annotation "itil/general",
68
+ description: "ITIL general management practices",
69
+ title: "ITIL General Practices",
70
+ filter: :list,
71
+ format: :tag_list,
72
+ enum: %w[
73
+ strategy-management portfolio-management architecture-management
74
+ risk-management workforce-management continual-improvement
75
+ knowledge-management measurement-reporting change-management
76
+ project-management relationship-management supplier-management
77
+ financial-management
78
+ ]
79
+
80
+ # ITIL 4 Service Management Practices
81
+ annotation "itil/service",
82
+ description: "ITIL service management practices",
83
+ title: "ITIL Service Practices",
84
+ filter: :list,
85
+ format: :tag_list,
86
+ enum: %w[
87
+ service-design service-catalog service-level-management
88
+ availability-management capacity-management continuity-management
89
+ security-management configuration-management
90
+ ]
91
+
92
+ # ITIL 4 Technical Management Practices
93
+ annotation "itil/technical",
94
+ description: "ITIL technical management practices",
95
+ title: "ITIL Technical Practices",
96
+ filter: :list,
97
+ format: :tag_list,
98
+ enum: %w[
99
+ deployment-management infrastructure-management software-development
100
+ release-management change-enablement service-validation
101
+ incident-management problem-management service-desk
102
+ monitoring-management
103
+ ]
104
+
105
+ relation :compositeOf, :businessActors, :BusinessActor
106
+
107
+ # Computed Annotations
108
+ computed_annotation "team/size",
109
+ title: "Team Size",
110
+ description: "Number of team members (including sub-teams)",
111
+ list: true,
112
+ type: Integer do
113
+ # Count members from this team
114
+ members = @instance.annotations["team/members"]
115
+ count = if members.nil? || members.empty?
116
+ 0
117
+ else
118
+ members.split(/[,\n]/).map(&:strip).reject(&:empty?).size
119
+ end
120
+
121
+ # Add members from sub-teams (teams that are composites of this team)
122
+ incoming_transitive(:BusinessActor).each do |subteam|
123
+ subteam_members = subteam.annotations["team/members"]
124
+ next if subteam_members.nil? || subteam_members.empty?
125
+
126
+ count += subteam_members.split(/[,\n]/).map(&:strip).reject(&:empty?).size
127
+ end
128
+
129
+ count
130
+ end
131
+
132
+ computed_annotation "team/lead/size",
133
+ title: "Team Leads",
134
+ description: "Number of team leads (including sub-teams)",
135
+ type: Integer do
136
+ # Count lead from this team
137
+ count = @instance.annotations["team/lead"] ? 1 : 0
138
+
139
+ # Add leads from sub-teams
140
+ incoming_transitive(:BusinessActor).each do |subteam|
141
+ count += 1 if subteam.annotations["team/lead"]
142
+ end
143
+
144
+ count
145
+ end
146
+
147
+ computed_annotation "repository/artifacts/total",
148
+ title: "Maintained Repositories",
149
+ description: "Number of git repositories maintained by this team",
150
+ type: Integer do
151
+ count(incoming_transitive('TechnologyArtifact: artifact/type == "repo"'))
152
+ end
153
+
154
+ computed_annotation "repository/artifacts/active",
155
+ title: "Active Repositories",
156
+ description: "Number of active git repositories maintained by this team",
157
+ type: Integer do
158
+ count(incoming_transitive('TechnologyArtifact: artifact/type == "repo" & activity/status == "active"'))
159
+ end
160
+
161
+ computed_annotation "repository/artifacts/highBusFactor",
162
+ title: "High Bus Factor Repositories",
163
+ description: "Number of active git repositories with high bus factor maintained by this team",
164
+ type: Integer do
165
+ count(incoming_transitive('TechnologyArtifact: artifact/type == "repo" & activity/status == "active" & activity/busFactor == "high"'))
166
+ end
167
+
168
+ computed_annotation "repository/artifacts/abandoned",
169
+ title: "Abandoned Repositories",
170
+ description: "Number of abandoned git repositories maintained by this team",
171
+ type: Integer do
172
+ count(incoming_transitive('TechnologyArtifact: artifact/type == "repo" & activity/status == "abandoned"'))
173
+ end
174
+
175
+ computed_annotation "repository/artifacts/archived",
176
+ title: "Archived Repositories",
177
+ description: "Number of archived git repositories maintained by this team",
178
+ type: Integer do
179
+ count(incoming_transitive('TechnologyArtifact: artifact/type == "repo" & activity/status == "archived"'))
180
+ end
181
+
182
+ computed_annotation "jira/projectUrl",
183
+ title: "Jira Board",
184
+ description: "Link to Jira board (computed from team/jira)",
185
+ format: :link do
186
+ jira_key = @instance.annotations["team/jira"]
187
+ next nil if jira_key.nil? || jira_key.empty?
188
+
189
+ # Use first key if multiple (comma/newline separated)
190
+ primary_key = jira_key.split(/[,\n]/).first&.strip
191
+ next nil if primary_key.nil? || primary_key.empty?
192
+
193
+ "https://jira.example.com/projects/#{primary_key}/issues"
194
+ end
195
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # BusinessConstraint represents restrictions or limitations on architecture
4
+ class Archsight::Resources::BusinessConstraint < Archsight::Resources::Base
5
+ include_annotations :git, :architecture
6
+
7
+ description <<~MD
8
+ Represents a factor that limits the realization of goals or influences architecture decisions.
9
+
10
+ ## ArchiMate Definition
11
+
12
+ **Layer:** Motivation
13
+ **Aspect:** Passive Structure
14
+
15
+ A constraint represents a factor that prevents or obstructs the realization of goals.
16
+ Constraints are typically imposed by external factors such as regulations, organizational
17
+ policies, or technical limitations.
18
+
19
+ ## Usage
20
+
21
+ Use BusinessConstraint to represent:
22
+
23
+ - Regulatory requirements (GDPR, SOX, PCI-DSS)
24
+ - Security policies
25
+ - Organizational standards
26
+ - Technical limitations
27
+ - Budget or resource constraints
28
+ MD
29
+
30
+ icon "prohibition"
31
+ layer "business"
32
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # BusinessProcess represents a structured business workflow or procedure
4
+ class Archsight::Resources::BusinessProcess < Archsight::Resources::Base
5
+ include_annotations :git, :architecture
6
+
7
+ description <<~MD
8
+ Represents a sequence of business behaviors that achieves a specific outcome.
9
+
10
+ ## ArchiMate Definition
11
+
12
+ **Layer:** Business
13
+ **Aspect:** Behavior
14
+
15
+ A business process represents a sequence of business behaviors that achieves a specific
16
+ outcome such as a defined set of products or business services. It orchestrates the
17
+ activities performed by business actors using application services.
18
+
19
+ ## Usage
20
+
21
+ Use BusinessProcess to represent:
22
+
23
+ - Customer onboarding workflows
24
+ - Incident response procedures
25
+ - Change management processes
26
+ - Release deployment pipelines
27
+ - Support escalation processes
28
+ MD
29
+
30
+ icon "kanban-board"
31
+ layer "business"
32
+
33
+ relation :realizes, :businessConstraints, :BusinessConstraint
34
+ relation :realizes, :businessRequirements, :BusinessRequirement
35
+ relation :servedBy, :applicationServices, :ApplicationService
36
+ relation :performedBy, :businessActors, :BusinessActor
37
+ end