legion-data 1.1.5 → 1.3.7

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +16 -0
  3. data/.gitignore +10 -2
  4. data/.rubocop.yml +41 -18
  5. data/CHANGELOG.md +82 -15
  6. data/CLAUDE.md +199 -0
  7. data/Gemfile +11 -1
  8. data/LICENSE +201 -0
  9. data/README.md +163 -19
  10. data/exe/legionio_migrate +0 -0
  11. data/legion-data.gemspec +22 -35
  12. data/lib/legion/data/connection.rb +39 -25
  13. data/lib/legion/data/encryption/cipher.rb +49 -0
  14. data/lib/legion/data/encryption/key_provider.rb +45 -0
  15. data/lib/legion/data/encryption/sequel_plugin.rb +54 -0
  16. data/lib/legion/data/event_store/projection.rb +56 -0
  17. data/lib/legion/data/event_store.rb +112 -0
  18. data/lib/legion/data/local.rb +77 -0
  19. data/lib/legion/data/migration.rb +5 -3
  20. data/lib/legion/data/migrations/001_add_schema_columns.rb +9 -3
  21. data/lib/legion/data/migrations/002_add_nodes.rb +18 -0
  22. data/lib/legion/data/migrations/003_add_settings.rb +18 -0
  23. data/lib/legion/data/migrations/004_add_extensions.rb +23 -0
  24. data/lib/legion/data/migrations/005_add_runners.rb +21 -0
  25. data/lib/legion/data/migrations/006_add_functions.rb +21 -0
  26. data/lib/legion/data/migrations/{015_add_default_extensions.rb → 007_add_default_extensions.rb} +3 -0
  27. data/lib/legion/data/migrations/008_add_tasks.rb +23 -0
  28. data/lib/legion/data/migrations/009_add_digital_workers.rb +45 -0
  29. data/lib/legion/data/migrations/010_add_value_metrics.rb +19 -0
  30. data/lib/legion/data/migrations/011_add_extensions_registry.rb +30 -0
  31. data/lib/legion/data/migrations/012_add_apollo_tables.rb +66 -0
  32. data/lib/legion/data/migrations/013_add_relationships.rb +21 -0
  33. data/lib/legion/data/migrations/014_add_relationship_columns.rb +27 -0
  34. data/lib/legion/data/migrations/015_add_rbac_tables.rb +49 -0
  35. data/lib/legion/data/migrations/016_add_worker_health.rb +33 -0
  36. data/lib/legion/data/migrations/017_add_audit_log.rb +30 -0
  37. data/lib/legion/data/migrations/018_add_governance_events.rb +21 -0
  38. data/lib/legion/data/migrations/019_add_audit_hash_chain.rb +29 -0
  39. data/lib/legion/data/migrations/020_add_webhooks.rb +37 -0
  40. data/lib/legion/data/model.rb +5 -2
  41. data/lib/legion/data/models/apollo_access_log.rb +13 -0
  42. data/lib/legion/data/models/apollo_entry.rb +18 -0
  43. data/lib/legion/data/models/apollo_expertise.rb +12 -0
  44. data/lib/legion/data/models/apollo_relation.rb +14 -0
  45. data/lib/legion/data/models/audit_log.rb +34 -0
  46. data/lib/legion/data/models/digital_worker.rb +44 -0
  47. data/lib/legion/data/models/extension.rb +0 -0
  48. data/lib/legion/data/models/function.rb +0 -2
  49. data/lib/legion/data/models/node.rb +17 -3
  50. data/lib/legion/data/models/rbac_cross_team_grant.rb +33 -0
  51. data/lib/legion/data/models/rbac_role_assignment.rb +29 -0
  52. data/lib/legion/data/models/rbac_runner_grant.rb +21 -0
  53. data/lib/legion/data/models/relationship.rb +3 -6
  54. data/lib/legion/data/models/runner.rb +0 -0
  55. data/lib/legion/data/models/setting.rb +0 -0
  56. data/lib/legion/data/models/task.rb +0 -0
  57. data/lib/legion/data/models/task_log.rb +0 -0
  58. data/lib/legion/data/settings.rb +37 -8
  59. data/lib/legion/data/version.rb +3 -1
  60. data/lib/legion/data.rb +31 -13
  61. metadata +64 -139
  62. data/.circleci/config.yml +0 -174
  63. data/.rspec +0 -1
  64. data/Gemfile.lock +0 -85
  65. data/Rakefile +0 -55
  66. data/bin/console +0 -14
  67. data/bin/setup +0 -8
  68. data/bitbucket-pipelines.yml +0 -26
  69. data/lib/legion/data/migrations/002_add_users.rb +0 -17
  70. data/lib/legion/data/migrations/003_add_groups.rb +0 -16
  71. data/lib/legion/data/migrations/004_add_chains.rb +0 -25
  72. data/lib/legion/data/migrations/005_add_envs.rb +0 -24
  73. data/lib/legion/data/migrations/006_add_dcs.rb +0 -24
  74. data/lib/legion/data/migrations/007_add_nodes.rb +0 -26
  75. data/lib/legion/data/migrations/008_add_settings.rb +0 -18
  76. data/lib/legion/data/migrations/009_add_extensions.rb +0 -25
  77. data/lib/legion/data/migrations/010_add_runners.rb +0 -21
  78. data/lib/legion/data/migrations/011_add_functions.rb +0 -29
  79. data/lib/legion/data/migrations/012_add_tasks.rb +0 -28
  80. data/lib/legion/data/migrations/013_add_task_logs.rb +0 -23
  81. data/lib/legion/data/migrations/014_add_relationships.rb +0 -27
  82. data/lib/legion/data/migrations/016_change_task_args.rb +0 -7
  83. data/lib/legion/data/migrations/017_add_payload_task.rb +0 -7
  84. data/lib/legion/data/migrations/018_add_migration_column.rb +0 -7
  85. data/lib/legion/data/migrations/019_add_debug_to_relationships.rb +0 -7
  86. data/lib/legion/data/migrations/020_add_delay_debug_to_tasks.rb +0 -8
  87. data/lib/legion/data/models/chain.rb +0 -11
  88. data/lib/legion/data/models/datacenter.rb +0 -11
  89. data/lib/legion/data/models/environment.rb +0 -11
  90. data/lib/legion/data/models/group.rb +0 -10
  91. data/lib/legion/data/models/user.rb +0 -10
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sequel'
4
+ require 'sequel/extensions/migration'
5
+
6
+ module Legion
7
+ module Data
8
+ module Local
9
+ class << self
10
+ attr_reader :connection, :db_path
11
+
12
+ def setup(database: nil, **)
13
+ return if @connected
14
+
15
+ db_file = database || local_settings[:database] || 'legionio_local.db'
16
+ @db_path = db_file
17
+ @connection = ::Sequel.sqlite(db_file)
18
+ @connected = true
19
+ run_migrations
20
+ Legion::Logging.info "Legion::Data::Local connected to #{db_file}" if defined?(Legion::Logging)
21
+ end
22
+
23
+ def shutdown
24
+ @connection&.disconnect
25
+ @connection = nil
26
+ @connected = false
27
+ end
28
+
29
+ def connected?
30
+ @connected == true
31
+ end
32
+
33
+ def register_migrations(name:, path:)
34
+ @registered_migrations ||= {}
35
+ @registered_migrations[name] = path
36
+ end
37
+
38
+ def registered_migrations
39
+ @registered_migrations || {}
40
+ end
41
+
42
+ def model(table_name)
43
+ raise 'Legion::Data::Local not connected' unless connected?
44
+
45
+ ::Sequel::Model(connection[table_name])
46
+ end
47
+
48
+ def reset!
49
+ @connection = nil
50
+ @connected = false
51
+ @db_path = nil
52
+ @registered_migrations = nil
53
+ end
54
+
55
+ private
56
+
57
+ def run_migrations
58
+ return unless local_settings.dig(:migrations, :auto_migrate) != false
59
+
60
+ registered_migrations.each_value do |path|
61
+ next unless File.directory?(path)
62
+
63
+ ::Sequel::TimestampMigrator.new(@connection, path).run
64
+ rescue StandardError => e
65
+ Legion::Logging.warn "Local migration failed for #{path}: #{e.message}" if defined?(Legion::Logging)
66
+ end
67
+ end
68
+
69
+ def local_settings
70
+ return {} unless defined?(Legion::Settings)
71
+
72
+ Legion::Settings[:data]&.dig(:local) || {}
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sequel/extensions/migration'
2
4
 
3
5
  module Legion
4
6
  module Data
5
7
  module Migration
6
8
  class << self
7
- def migrate(connection = Legion::Data.connection, path = "#{__dir__}/migrations", **opts)
8
- Legion::Settings[:data][:migrations][:version] = Sequel::Migrator.run(connection, path, **opts)
9
- Legion::Logging.info("Legion::Data::Migration ran successfully to version #{Legion::Settings[:data][:migrations][:version]}") # rubocop:disable Layout/LineLength
9
+ def migrate(connection = Legion::Data.connection, path = "#{__dir__}/migrations", **)
10
+ Legion::Settings[:data][:migrations][:version] = Sequel::Migrator.run(connection, path, **)
11
+ Legion::Logging.info("Legion::Data::Migration ran successfully to version #{Legion::Settings[:data][:migrations][:version]}")
10
12
  Legion::Settings[:data][:migrations][:ran] = true
11
13
  end
12
14
  end
@@ -1,10 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sequel/extensions/migration'
2
4
 
3
5
  Sequel.migration do
4
6
  up do
5
- run 'ALTER TABLE `schema_info` ADD `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `version`;'
6
- run 'ALTER TABLE `schema_info` ADD `updated_at` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP AFTER `created_at`;'
7
- run 'ALTER TABLE `schema_info` ADD `catalog` VARCHAR(255) NULL DEFAULT NULL AFTER `version`;'
7
+ alter_table(:schema_info) do
8
+ # SQLite does not support non-constant defaults in ALTER TABLE ADD COLUMN,
9
+ # so we omit the default here and let the application set timestamps.
10
+ add_column :created_at, DateTime, null: true
11
+ add_column :updated_at, DateTime, null: true
12
+ add_column :catalog, String, size: 255, null: true
13
+ end
8
14
  end
9
15
 
10
16
  down do
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:nodes) do
6
+ primary_key :id
7
+ String :name, size: 128, null: false, default: '', unique: true
8
+ String :status, size: 255, null: false, default: 'unknown', index: true
9
+ TrueClass :active, null: false, default: true, index: true
10
+ DateTime :created, null: false, default: Sequel::CURRENT_TIMESTAMP
11
+ DateTime :updated, null: true
12
+ end
13
+ end
14
+
15
+ down do
16
+ drop_table :nodes
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:settings) do
6
+ primary_key :id
7
+ String :key, size: 128, null: false, unique: true
8
+ String :value, size: 256, null: false
9
+ TrueClass :encrypted, null: false, default: false
10
+ DateTime :created, null: false, default: Sequel::CURRENT_TIMESTAMP
11
+ DateTime :updated, null: true
12
+ end
13
+ end
14
+
15
+ down do
16
+ drop_table :settings
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:extensions) do
6
+ primary_key :id
7
+ TrueClass :active, null: false, default: true, index: true
8
+ String :name, size: 128, null: false, index: true
9
+ String :namespace, size: 128, null: false, default: '', index: true
10
+ String :exchange, size: 255, null: true
11
+ String :uri, size: 256, null: true
12
+ Integer :schema_version, null: false, default: 0, index: true
13
+ DateTime :updated, null: true
14
+ DateTime :created, null: false, default: Sequel::CURRENT_TIMESTAMP
15
+
16
+ unique %i[name namespace]
17
+ end
18
+ end
19
+
20
+ down do
21
+ drop_table :extensions
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:runners) do
6
+ primary_key :id
7
+ foreign_key :extension_id, :extensions, null: false, on_delete: :cascade, on_update: :cascade
8
+ String :name, size: 256, null: false, default: ''
9
+ String :namespace, size: 256, null: false, default: ''
10
+ TrueClass :active, null: false, default: true
11
+ String :queue, size: 256, null: true
12
+ String :uri, size: 256, null: true
13
+ DateTime :created, null: false, default: Sequel::CURRENT_TIMESTAMP
14
+ DateTime :updated, null: true
15
+ end
16
+ end
17
+
18
+ down do
19
+ drop_table :runners
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:functions) do
6
+ primary_key :id
7
+ String :name, size: 128, null: false, index: true
8
+ TrueClass :active, null: false, default: true, index: true
9
+ foreign_key :runner_id, :runners, null: false, on_delete: :cascade, on_update: :cascade, index: true
10
+ String :args, text: true, null: true
11
+ DateTime :created, null: false, default: Sequel::CURRENT_TIMESTAMP
12
+ DateTime :updated, null: true
13
+
14
+ unique %i[runner_id name]
15
+ end
16
+ end
17
+
18
+ down do
19
+ drop_table :functions
20
+ end
21
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Sequel.migration do
2
4
  up do
3
5
  lex = from(:extensions).insert(namespace: 'Legion::Extensions::Lex', name: 'lex', exchange: 'lex', uri: 'lex')
@@ -17,6 +19,7 @@ Sequel.migration do
17
19
  from(:runners).insert row
18
20
  end
19
21
  end
22
+
20
23
  down do
21
24
  from(:extensions).where(namespace: 'Legion::Extensions::Lex').delete
22
25
  from(:extensions).where(namespace: 'Legion::Extensions::Node').delete
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:tasks) do
6
+ primary_key :id
7
+ Integer :relationship_id, null: true
8
+ foreign_key :function_id, :functions, null: true
9
+ String :status, size: 255, null: false, index: true
10
+ foreign_key :parent_id, :tasks, null: true, on_delete: :set_null, on_update: :cascade, index: true
11
+ foreign_key :master_id, :tasks, null: true, on_delete: :set_null, on_update: :cascade, index: true
12
+ String :function_args, text: true, null: true
13
+ String :results, text: true, null: true
14
+ String :payload, text: true, null: true
15
+ DateTime :created, null: false, default: Sequel::CURRENT_TIMESTAMP
16
+ DateTime :updated, null: true
17
+ end
18
+ end
19
+
20
+ down do
21
+ drop_table :tasks
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:digital_workers) do
6
+ primary_key :id
7
+ String :worker_id, null: false, unique: true, size: 36
8
+ String :name, null: false, size: 255
9
+ String :entra_app_id, null: false, unique: true, size: 255
10
+ String :entra_object_id, null: true, size: 255
11
+ String :owner_msid, null: false, size: 255
12
+ String :owner_name, null: true, size: 255
13
+ String :extension_name, null: false, size: 255
14
+ String :business_role, null: true, size: 255
15
+ String :risk_tier, null: true, size: 50
16
+ String :lifecycle_state, null: false, default: 'bootstrap', size: 50
17
+ String :consent_tier, null: false, default: 'supervised', size: 50
18
+ Float :trust_score, null: false, default: 0.0
19
+ String :team, null: true, size: 255
20
+ String :manager_msid, null: true, size: 255
21
+ DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
22
+ DateTime :updated_at, null: true
23
+ DateTime :retired_at, null: true
24
+ String :retired_by, null: true, size: 255
25
+ String :retired_reason, null: true, text: true
26
+ index :owner_msid
27
+ index :lifecycle_state
28
+ index :team
29
+ end
30
+
31
+ alter_table(:tasks) do
32
+ add_column :worker_id, String, null: true, size: 36
33
+ add_index :worker_id
34
+ end
35
+ end
36
+
37
+ down do
38
+ alter_table(:tasks) do
39
+ drop_index :worker_id
40
+ drop_column :worker_id
41
+ end
42
+
43
+ drop_table :digital_workers
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:value_metrics) do
6
+ primary_key :id
7
+ String :worker_id, null: false, size: 36, index: true
8
+ String :metric_name, null: false, size: 255, index: true
9
+ String :metric_type, null: false, size: 50
10
+ Float :value, null: false, default: 0.0
11
+ String :metadata, text: true, null: true
12
+ DateTime :recorded_at, null: false, default: Sequel::CURRENT_TIMESTAMP, index: true
13
+ end
14
+ end
15
+
16
+ down do
17
+ drop_table :value_metrics
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:extensions_registry) do
6
+ primary_key :id
7
+ String :name, null: false, unique: true, size: 100
8
+ String :module_name, null: false, size: 100
9
+ String :category, null: false, size: 50, default: 'cognition'
10
+ String :description, text: true
11
+ String :cognitive_concept, text: true
12
+ String :metaphor_description, text: true
13
+ Integer :build_batch
14
+ DateTime :build_date
15
+ String :status, null: false, size: 20, default: 'active'
16
+ Integer :spec_count, default: 0
17
+ Integer :spec_pass_count, default: 0
18
+ String :wired_phase, size: 100
19
+ Float :health_score, default: 1.0
20
+ Integer :invocation_count, default: 0
21
+ DateTime :last_invoked_at
22
+ DateTime :created_at
23
+ DateTime :updated_at
24
+
25
+ index :category
26
+ index :status
27
+ index :health_score
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ next unless adapter_scheme == :postgres
6
+
7
+ run 'CREATE EXTENSION IF NOT EXISTS vector'
8
+ run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
9
+
10
+ create_table(:apollo_entries) do
11
+ column :id, :uuid, default: Sequel.lit('uuid_generate_v4()'), primary_key: true
12
+ String :content, text: true, null: false
13
+ String :content_type, null: false, size: 50
14
+ Float :confidence, default: 0.5
15
+ String :source_agent, null: false, size: 100
16
+ column :source_context, :jsonb, default: Sequel.lit("'{}'::jsonb")
17
+ column :tags, :'text[]', default: Sequel.lit("'{}'::text[]")
18
+ String :status, null: false, size: 20, default: 'candidate'
19
+ Integer :access_count, default: 0
20
+ DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
21
+ DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
22
+ DateTime :confirmed_at
23
+
24
+ index :status
25
+ end
26
+ run 'ALTER TABLE apollo_entries ADD COLUMN embedding vector(1536)'
27
+ run 'CREATE INDEX idx_apollo_entries_embedding ON apollo_entries USING hnsw (embedding vector_cosine_ops)'
28
+ run 'CREATE INDEX idx_apollo_entries_tags ON apollo_entries USING gin (tags)'
29
+
30
+ create_table(:apollo_relations) do
31
+ column :id, :uuid, default: Sequel.lit('uuid_generate_v4()'), primary_key: true
32
+ foreign_key :from_entry_id, :apollo_entries, type: :uuid, null: false, index: true
33
+ foreign_key :to_entry_id, :apollo_entries, type: :uuid, null: false, index: true
34
+ String :relation_type, null: false, size: 50
35
+ Float :weight, default: 1.0
36
+ String :source_agent, size: 100
37
+ DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
38
+ end
39
+
40
+ create_table(:apollo_expertise) do
41
+ column :id, :uuid, default: Sequel.lit('uuid_generate_v4()'), primary_key: true
42
+ String :agent_id, null: false, size: 100, index: true
43
+ String :domain, null: false, size: 100, index: true
44
+ Float :proficiency, default: 0.0
45
+ Integer :entry_count, default: 0
46
+ DateTime :last_active_at, default: Sequel::CURRENT_TIMESTAMP
47
+ end
48
+
49
+ create_table(:apollo_access_log) do
50
+ column :id, :uuid, default: Sequel.lit('uuid_generate_v4()'), primary_key: true
51
+ foreign_key :entry_id, :apollo_entries, type: :uuid, index: true
52
+ String :agent_id, null: false, size: 100
53
+ String :action, null: false, size: 20
54
+ DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
55
+ end
56
+ end
57
+
58
+ down do
59
+ next unless adapter_scheme == :postgres
60
+
61
+ drop_table(:apollo_access_log) if table_exists?(:apollo_access_log)
62
+ drop_table(:apollo_expertise) if table_exists?(:apollo_expertise)
63
+ drop_table(:apollo_relations) if table_exists?(:apollo_relations)
64
+ drop_table(:apollo_entries) if table_exists?(:apollo_entries)
65
+ end
66
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:relationships) do
6
+ primary_key :id
7
+ foreign_key :trigger_id, :functions, null: true, on_delete: :set_null, index: true
8
+ foreign_key :action_id, :functions, null: true, on_delete: :set_null, index: true
9
+ String :name, size: 255, null: true
10
+ String :status, size: 50, null: false, default: 'active', index: true
11
+ String :relationship_type, size: 50, null: false, default: 'chain'
12
+ String :options, text: true, null: true
13
+ DateTime :created, null: false, default: Sequel::CURRENT_TIMESTAMP
14
+ DateTime :updated, null: true
15
+ end
16
+ end
17
+
18
+ down do
19
+ drop_table :relationships
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ alter_table(:relationships) do
6
+ add_column :delay, Integer, null: false, default: 0
7
+ add_column :chain_id, Integer, null: true, index: true
8
+ add_column :debug, TrueClass, null: false, default: false
9
+ add_column :allow_new_chains, TrueClass, null: false, default: false
10
+ add_column :conditions, String, text: true, null: true
11
+ add_column :transformation, String, text: true, null: true
12
+ add_column :active, TrueClass, null: false, default: true, index: true
13
+ end
14
+ end
15
+
16
+ down do
17
+ alter_table(:relationships) do
18
+ drop_column :delay
19
+ drop_column :chain_id
20
+ drop_column :debug
21
+ drop_column :allow_new_chains
22
+ drop_column :conditions
23
+ drop_column :transformation
24
+ drop_column :active
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:rbac_role_assignments) do
6
+ primary_key :id
7
+ String :principal_type, null: false, size: 10
8
+ String :principal_id, null: false, size: 255
9
+ String :role, null: false, size: 100
10
+ String :team, null: true, size: 255
11
+ String :granted_by, null: false, size: 255
12
+ DateTime :granted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
13
+ DateTime :expires_at, null: true
14
+ unique %i[principal_type principal_id role team]
15
+ index :principal_id
16
+ index :team
17
+ end
18
+
19
+ create_table(:rbac_runner_grants) do
20
+ primary_key :id
21
+ String :team, null: false, size: 255
22
+ String :runner_pattern, null: false, size: 500
23
+ String :actions, null: false, size: 255
24
+ String :granted_by, null: false, size: 255
25
+ DateTime :granted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
26
+ unique %i[team runner_pattern]
27
+ index :team
28
+ end
29
+
30
+ create_table(:rbac_cross_team_grants) do
31
+ primary_key :id
32
+ String :source_team, null: false, size: 255
33
+ String :target_team, null: false, size: 255
34
+ String :runner_pattern, null: false, size: 500
35
+ String :actions, null: false, size: 255
36
+ String :granted_by, null: false, size: 255
37
+ DateTime :granted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
38
+ DateTime :expires_at, null: true
39
+ unique %i[source_team target_team runner_pattern]
40
+ index :source_team
41
+ end
42
+ end
43
+
44
+ down do
45
+ drop_table :rbac_cross_team_grants
46
+ drop_table :rbac_runner_grants
47
+ drop_table :rbac_role_assignments
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ alter_table(:digital_workers) do
6
+ add_column :health_status, String, size: 20, default: 'unknown', null: false
7
+ add_column :last_heartbeat_at, DateTime, null: true
8
+ add_column :health_node, String, size: 255, null: true
9
+ add_index :health_status
10
+ end
11
+
12
+ alter_table(:nodes) do
13
+ add_column :metrics, :text, null: true
14
+ add_column :hosted_worker_ids, :text, null: true
15
+ add_column :version, String, size: 50, null: true
16
+ end
17
+ end
18
+
19
+ down do
20
+ alter_table(:digital_workers) do
21
+ drop_index :health_status
22
+ drop_column :health_node
23
+ drop_column :last_heartbeat_at
24
+ drop_column :health_status
25
+ end
26
+
27
+ alter_table(:nodes) do
28
+ drop_column :version
29
+ drop_column :hosted_worker_ids
30
+ drop_column :metrics
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:audit_log) do
6
+ primary_key :id
7
+ String :event_type, null: false, size: 50
8
+ String :principal_id, null: false, size: 255
9
+ String :principal_type, null: false, size: 20
10
+ String :action, null: false, size: 100
11
+ String :resource, null: false, size: 500
12
+ String :source, null: false, size: 20
13
+ String :node, null: false, size: 255
14
+ String :status, null: false, size: 20
15
+ Integer :duration_ms, null: true
16
+ column :detail, :text, null: true
17
+ String :record_hash, null: false, size: 64
18
+ String :prev_hash, null: false, size: 64
19
+ DateTime :created_at, null: false
20
+
21
+ index :event_type
22
+ index :principal_id
23
+ index :created_at
24
+ end
25
+ end
26
+
27
+ down do
28
+ drop_table :audit_log
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:governance_events) do
6
+ primary_key :id
7
+ String :stream_id, null: false
8
+ String :event_type, null: false
9
+ Integer :sequence_number, null: false
10
+ column :data_json, :text
11
+ column :metadata_json, :text
12
+ String :event_hash, size: 64
13
+ String :previous_hash, size: 64
14
+ DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
15
+
16
+ index %i[stream_id sequence_number], unique: true
17
+ index :event_type
18
+ index :created_at
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ return unless table_exists?(:audit_log)
6
+
7
+ cols = schema(:audit_log).map(&:first)
8
+
9
+ alter_table(:audit_log) do
10
+ add_column :record_hash, String, size: 64 unless cols.include?(:record_hash)
11
+ add_column :previous_hash, String, size: 64 unless cols.include?(:previous_hash)
12
+ add_column :retention_tier, String, size: 10, default: 'hot' unless cols.include?(:retention_tier)
13
+ add_index :record_hash, unique: true, if_not_exists: true
14
+ add_index :retention_tier, if_not_exists: true
15
+ end
16
+ end
17
+
18
+ down do
19
+ return unless table_exists?(:audit_log)
20
+
21
+ cols = schema(:audit_log).map(&:first)
22
+
23
+ alter_table(:audit_log) do
24
+ drop_column :record_hash if cols.include?(:record_hash)
25
+ drop_column :previous_hash if cols.include?(:previous_hash)
26
+ drop_column :retention_tier if cols.include?(:retention_tier)
27
+ end
28
+ end
29
+ end