@team-semicolon/semicolony-cli 4.18.51

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 (164) hide show
  1. package/LICENSE +200 -0
  2. package/NOTICE +8 -0
  3. package/README.md +152 -0
  4. package/dist/bundle.js +2887 -0
  5. package/dist/templates/launchd/ai.semicolony.host-signals-sidecar.plist +40 -0
  6. package/migrations/001_initial.sql +280 -0
  7. package/migrations/002_add_indexes.sql +11 -0
  8. package/migrations/003_add_fk.sql +32 -0
  9. package/migrations/004_session_count_trigger.sql +35 -0
  10. package/migrations/005_bot_workspace_audits.sql +16 -0
  11. package/migrations/006_bot_kb_subscriptions.sql +24 -0
  12. package/migrations/007_flatten_skill_names.sql +16 -0
  13. package/migrations/008_bot_workspace_files.sql +14 -0
  14. package/migrations/009_enforce_flat_skill_names.sql +53 -0
  15. package/migrations/010_bot_delegation.sql +27 -0
  16. package/migrations/011_drop_legacy_tables.sql +35 -0
  17. package/migrations/012_drop_bot_knowledge.sql +16 -0
  18. package/migrations/013_kb_workspace_domains.sql +88 -0
  19. package/migrations/014_timestamp_to_timestamptz.sql +33 -0
  20. package/migrations/015_kb_domain_enforcement.sql +83 -0
  21. package/migrations/016_ontology_redesign.sql +108 -0
  22. package/migrations/017_ontology_instance_model.sql +151 -0
  23. package/migrations/018_type_schema.sql +11 -0
  24. package/migrations/019_test_management.sql +69 -0
  25. package/migrations/020_bot_workspace_standard.sql +174 -0
  26. package/migrations/021_drop_dead_tables.sql +8 -0
  27. package/migrations/021_test_declarative.sql +19 -0
  28. package/migrations/022_bot_ontology_type.sql +38 -0
  29. package/migrations/022_kb_schema_enforcement.sql +77 -0
  30. package/migrations/023_decision_key_cleanup.ts +154 -0
  31. package/migrations/024_embedding_backfill.ts +137 -0
  32. package/migrations/025_kb_flat_keys.sql +33 -0
  33. package/migrations/026_drop_session_log.sql +19 -0
  34. package/migrations/027_kb_manager_skill.sql +22 -0
  35. package/migrations/028_skill_definitions_to_semicolony.sql +19 -0
  36. package/migrations/029_agents_citation_max_lines.sql +6 -0
  37. package/migrations/030_team_ontology.sql +54 -0
  38. package/migrations/031_gfp_tables.sql +101 -0
  39. package/migrations/031_kb_manager_domain_register.sql +12 -0
  40. package/migrations/032_gfp_skill_and_callback.sql +40 -0
  41. package/migrations/032_kebab_case_keys.sql +33 -0
  42. package/migrations/033_skill_workspace_rules.sql +10 -0
  43. package/migrations/034_current_to_date_keys.sql +13 -0
  44. package/migrations/034_current_to_date_keys.ts +104 -0
  45. package/migrations/035_bot_budgets.sql +51 -0
  46. package/migrations/036_gfp_stitch_design.sql +35 -0
  47. package/migrations/037_gfp_qa_sections.sql +13 -0
  48. package/migrations/038_gfp_phase_split.sql +26 -0
  49. package/migrations/039_meeting_audio_data.sql +2 -0
  50. package/migrations/040_bot_commitments.sql +68 -0
  51. package/migrations/041_palette_previews.sql +13 -0
  52. package/migrations/042_gfp_parallel_tracks.sql +37 -0
  53. package/migrations/043_blocker_key.sql +13 -0
  54. package/migrations/044_gfp_stitch_screenshots.sql +9 -0
  55. package/migrations/045_service_table_rename_and_projection.sql +150 -0
  56. package/migrations/046_service_ops_tables.sql +75 -0
  57. package/migrations/047_update_gfp_manager_skill.sql +16 -0
  58. package/migrations/048_service_migration_skill.sql +23 -0
  59. package/migrations/049_service_features.sql +39 -0
  60. package/migrations/050_service_kpi_action_items.sql +87 -0
  61. package/migrations/051_update_service_migration_skill_prompt.sql +9 -0
  62. package/migrations/052_feature_iteration_link.sql +11 -0
  63. package/migrations/052_services_table_rename.sql +149 -0
  64. package/migrations/053_feature_discovery.sql +47 -0
  65. package/migrations/054_deploy_verification.sql +17 -0
  66. package/migrations/055_feature_lifecycle.sql +37 -0
  67. package/migrations/056_service_incident_schema.sql +16 -0
  68. package/migrations/057_decision_date_to_metadata.ts +173 -0
  69. package/migrations/058_incubator_kb_schema.sql +8 -0
  70. package/migrations/059_incubator_sessions.sql +29 -0
  71. package/migrations/060_skill_reference_files.sql +8 -0
  72. package/migrations/061_cron_trigger_bridge.sql +7 -0
  73. package/migrations/062_bot_slack_profiles.sql +21 -0
  74. package/migrations/063_rename_gfp_skills.sql +7 -0
  75. package/migrations/064_bot_cost_log.sql +11 -0
  76. package/migrations/065_hub_spoke_orchestration.sql +35 -0
  77. package/migrations/066_cost_log_commitment.sql +9 -0
  78. package/migrations/067_module_ontology_type.sql +44 -0
  79. package/migrations/068_migrate_incubator_data.sql +32 -0
  80. package/migrations/069_action_items_sot.sql +156 -0
  81. package/migrations/069_cost_log_tokens.sql +45 -0
  82. package/migrations/070_services_sot_columns.sql +78 -0
  83. package/migrations/071_cleanup_deprecated_service_kb.sql +38 -0
  84. package/migrations/072_incubator_heartbeat.sql +12 -0
  85. package/migrations/072_service_role_schema.sql +57 -0
  86. package/migrations/073_service_type_and_platform.sql +8 -0
  87. package/migrations/074_schema_ref_type.sql +16 -0
  88. package/migrations/075_routing_delegation.sql +34 -0
  89. package/migrations/076_meeting_service_link.sql +5 -0
  90. package/migrations/077_agents_ontology_type.sql +93 -0
  91. package/migrations/078_incubator_agent.sql +45 -0
  92. package/migrations/079_drop_audio_blob_url.sql +4 -0
  93. package/migrations/080_kb_metadata_indexing.sql +9 -0
  94. package/migrations/081_person_unification.sql +41 -0
  95. package/migrations/082_discord_channel.sql +3 -0
  96. package/migrations/084_deprecate_kpi_metrics_table.sql +5 -0
  97. package/migrations/085_meeting_target_domain.sql +37 -0
  98. package/migrations/086_skill_routing_new_skills.sql +23 -0
  99. package/migrations/087_skill_routing_second_opinion.sql +8 -0
  100. package/migrations/088_meeting_notion_sync.sql +10 -0
  101. package/migrations/089_bot_commitments_dedup.sql +17 -0
  102. package/migrations/090_incubator_discord_guild.sql +8 -0
  103. package/migrations/091_drop_deprecated_gfp_kpi_views.sql +65 -0
  104. package/migrations/092_ontology_channel_mapping.sql +28 -0
  105. package/migrations/093_update_bot_workspace_paths.sql +15 -0
  106. package/migrations/094_cron_run_tracking.sql +28 -0
  107. package/migrations/095_drop_bot_cron_runs.sql +7 -0
  108. package/migrations/096_kb_pipeline_keys.sql +66 -0
  109. package/migrations/097_drop_empty_service_tables.sql +83 -0
  110. package/migrations/098_migrate_features_materials_drop.sql +119 -0
  111. package/migrations/099_migrate_sections_drop.sql +52 -0
  112. package/migrations/099_seed_cron_poller_tick.sql +14 -0
  113. package/migrations/100_migrate_services_drop.sql +57 -0
  114. package/migrations/101_drop_services.sql +5 -0
  115. package/migrations/102_cost_log_session_dedup.sql +13 -0
  116. package/migrations/103_board.sql +57 -0
  117. package/migrations/104_agent_factory.sql +284 -0
  118. package/migrations/105_kb_notify.sql +44 -0
  119. package/migrations/106_office_id_agents_commands.sql +73 -0
  120. package/migrations/107_agent_service_credentials.sql +79 -0
  121. package/migrations/108_drop_l2_ontology_seeds.sql +16 -0
  122. package/migrations/109_generic_ontology_descriptions.sql +21 -0
  123. package/migrations/110_kb_manager_skill_l2_cleanup.sql +34 -0
  124. package/migrations/111_drop_l2_team_ontology_seeds.sql +20 -0
  125. package/migrations/112_fix_pipeline_schema_and_role_trigger.sql +83 -0
  126. package/migrations/113_kb_history.sql +53 -0
  127. package/migrations/114_runtime_target_columns.sql +26 -0
  128. package/migrations/115_runtime_source_columns.sql +52 -0
  129. package/migrations/116_commitment_pattern_health.sql +81 -0
  130. package/migrations/117_commitment_pattern_alert_claims.sql +33 -0
  131. package/migrations/118_retire_semiclaw_semicolonybot_alias.sql +30 -0
  132. package/migrations/119_commitment_events.sql +51 -0
  133. package/migrations/120_host_signals.sql +51 -0
  134. package/migrations/121_agent_spec_derived_runtime_metadata.sql +68 -0
  135. package/migrations/122_agent_spec_derived_fk_policy.sql +18 -0
  136. package/migrations/123_agent_spec_projection_skip_targets.sql +24 -0
  137. package/migrations/124_bot_commitments_notify.sql +26 -0
  138. package/migrations/125_agent_personas.sql +36 -0
  139. package/migrations/128_entity_relations.sql +125 -0
  140. package/migrations/129_kb_multitenant_gateway.sql +86 -0
  141. package/migrations/130_jewelry_relation_types.sql +36 -0
  142. package/migrations/131_concierge_requests.sql +38 -0
  143. package/migrations/132_kb_supersession.sql +27 -0
  144. package/migrations/133_kb_claims.sql +52 -0
  145. package/migrations/134_migration_registry.sql +18 -0
  146. package/migrations/135_sc_roles.sql +102 -0
  147. package/migrations/136_runtime_tenant_columns.sql +71 -0
  148. package/migrations/137_runtime_backfill_team_semicolon.sql +131 -0
  149. package/migrations/138_runtime_constraints_not_valid.sql +56 -0
  150. package/migrations/139_tenant_vms.sql +31 -0
  151. package/migrations/140_fleet_slots.sql +23 -0
  152. package/migrations/141_provisioning_jobs.sql +29 -0
  153. package/migrations/142_rls_enable_semicolony.sql +129 -0
  154. package/migrations/143_rls_enable_public.sql +54 -0
  155. package/migrations/144_worker_device_registry.sql +211 -0
  156. package/migrations/145_workspace_profile_workstation_policy.sql +39 -0
  157. package/migrations/146_worker_profile_repo_scope.sql +24 -0
  158. package/migrations/147_worker_profile_semo_ops_repo_rename.sql +18 -0
  159. package/migrations/148_momo_runtime_convergence.sql +92 -0
  160. package/migrations/README.md +88 -0
  161. package/migrations-sqlite/kb/001_initial.sql +56 -0
  162. package/migrations-sqlite/ops/001_initial.sql +53 -0
  163. package/migrations-sqlite/ops/002_action_items.sql +48 -0
  164. package/package.json +78 -0
@@ -0,0 +1,131 @@
1
+ -- 137_runtime_backfill_team_semicolon.sql
2
+ -- 런타임 테이블 tenant_id/scope 2단계 백필 (RLS Phase 2 준비).
3
+ --
4
+ -- 결정: semicolony/decision/rls-trusted-service-guc-boundary-2026-06-19.
5
+ -- 선행: 136_runtime_tenant_columns.sql (컬럼 추가).
6
+ --
7
+ -- 2단계:
8
+ -- (1) 고객 런타임 프로젝션 행(`ag-{slug}-{agent}`): bot_id 에서 파생한 slug 로
9
+ -- public.tenants 를 조인해 tenant_id 를 채우고 scope='tenant-local'.
10
+ -- slug 에 하이픈이 들어갈 수 있으므로(jeongmin-cafe) 첫 하이픈 split 이 아니라
11
+ -- 실제 tenants.slug 에 대해 `bot_id LIKE 'ag-'||slug||'-%'` 매칭한다(가장 긴 slug 우선).
12
+ -- (2) 내부 런타임 행(나머지 — 7봇/시스템): team-semicolon 테넌트에 귀속, scope='tenant-local'.
13
+ --
14
+ -- 가드: team-semicolon 테넌트가 없으면 전체 백필을 skip(부분 적용 방지).
15
+ -- → 러너가 파일을 BEGIN/COMMIT 으로 감싸고 `semicolony.` 를 retarget 한다. public.* 는 그대로.
16
+ --
17
+ -- 검증(적용 후 수동 실행):
18
+ -- SELECT COUNT(*)::int AS missing FROM semicolony.bot_status
19
+ -- WHERE scope = 'tenant-local' AND tenant_id IS NULL; -- 기대: 0
20
+ -- (bot_sessions / bot_commitments / action_items / agent_personas /
21
+ -- agent_persona_revisions 도 동일 쿼리로 missing=0 확인)
22
+
23
+ DO $$
24
+ DECLARE
25
+ team_tid uuid;
26
+ BEGIN
27
+ SELECT id INTO team_tid FROM public.tenants WHERE slug = 'team-semicolon';
28
+ IF team_tid IS NULL THEN
29
+ RAISE NOTICE 'team-semicolon tenant not found — skipping runtime backfill (137)';
30
+ RETURN;
31
+ END IF;
32
+
33
+ -- ── (1) 고객 프로젝션 행: bot_id 의 slug 로 tenant 매칭 ──────────
34
+ -- 가장 긴 매칭 slug 를 택해 접두 충돌(예: 'cafe' vs 'cafe-2')을 방지.
35
+ UPDATE semicolony.bot_status b
36
+ SET tenant_id = m.tenant_id, scope = 'tenant-local'
37
+ FROM (
38
+ SELECT b2.bot_id, t.id AS tenant_id
39
+ FROM semicolony.bot_status b2
40
+ JOIN LATERAL (
41
+ SELECT t.id
42
+ FROM public.tenants t
43
+ WHERE b2.bot_id LIKE 'ag-' || t.slug || '-%'
44
+ ORDER BY length(t.slug) DESC
45
+ LIMIT 1
46
+ ) t ON true
47
+ ) m
48
+ WHERE b.bot_id = m.bot_id
49
+ AND b.tenant_id IS NULL;
50
+
51
+ UPDATE semicolony.agent_personas p
52
+ SET tenant_id = m.tenant_id, scope = 'tenant-local'
53
+ FROM (
54
+ SELECT p2.slug, t.id AS tenant_id
55
+ FROM semicolony.agent_personas p2
56
+ JOIN LATERAL (
57
+ SELECT t.id
58
+ FROM public.tenants t
59
+ WHERE p2.slug LIKE 'ag-' || t.slug || '-%'
60
+ ORDER BY length(t.slug) DESC
61
+ LIMIT 1
62
+ ) t ON true
63
+ ) m
64
+ WHERE p.slug = m.slug
65
+ AND p.tenant_id IS NULL;
66
+
67
+ UPDATE semicolony.bot_commitments c
68
+ SET tenant_id = m.tenant_id, scope = 'tenant-local'
69
+ FROM (
70
+ SELECT c2.id, t.id AS tenant_id
71
+ FROM semicolony.bot_commitments c2
72
+ JOIN LATERAL (
73
+ SELECT t.id
74
+ FROM public.tenants t
75
+ WHERE c2.bot_id LIKE 'ag-' || t.slug || '-%'
76
+ ORDER BY length(t.slug) DESC
77
+ LIMIT 1
78
+ ) t ON true
79
+ ) m
80
+ WHERE c.id = m.id
81
+ AND c.tenant_id IS NULL;
82
+
83
+ UPDATE semicolony.bot_sessions s
84
+ SET tenant_id = m.tenant_id, scope = 'tenant-local'
85
+ FROM (
86
+ SELECT s2.bot_id, s2.session_key, t.id AS tenant_id
87
+ FROM semicolony.bot_sessions s2
88
+ JOIN LATERAL (
89
+ SELECT t.id
90
+ FROM public.tenants t
91
+ WHERE s2.bot_id LIKE 'ag-' || t.slug || '-%'
92
+ ORDER BY length(t.slug) DESC
93
+ LIMIT 1
94
+ ) t ON true
95
+ ) m
96
+ WHERE s.bot_id = m.bot_id AND s.session_key = m.session_key
97
+ AND s.tenant_id IS NULL;
98
+
99
+ -- agent_persona_revisions: 부모 persona 의 tenant 를 상속(파생 안정성).
100
+ UPDATE semicolony.agent_persona_revisions r
101
+ SET tenant_id = p.tenant_id, scope = 'tenant-local'
102
+ FROM semicolony.agent_personas p
103
+ WHERE r.slug = p.slug
104
+ AND p.tenant_id IS NOT NULL
105
+ AND r.tenant_id IS NULL;
106
+
107
+ -- ── (2) 내부 행: 나머지(7봇/시스템) → team-semicolon ───────────
108
+ UPDATE semicolony.bot_status
109
+ SET tenant_id = team_tid, scope = 'tenant-local'
110
+ WHERE tenant_id IS NULL;
111
+
112
+ UPDATE semicolony.bot_sessions
113
+ SET tenant_id = team_tid, scope = 'tenant-local'
114
+ WHERE tenant_id IS NULL;
115
+
116
+ UPDATE semicolony.bot_commitments
117
+ SET tenant_id = team_tid, scope = 'tenant-local'
118
+ WHERE tenant_id IS NULL;
119
+
120
+ UPDATE semicolony.action_items
121
+ SET tenant_id = team_tid, scope = 'tenant-local'
122
+ WHERE tenant_id IS NULL;
123
+
124
+ UPDATE semicolony.agent_personas
125
+ SET tenant_id = team_tid, scope = 'tenant-local'
126
+ WHERE tenant_id IS NULL;
127
+
128
+ UPDATE semicolony.agent_persona_revisions
129
+ SET tenant_id = team_tid, scope = 'tenant-local'
130
+ WHERE tenant_id IS NULL;
131
+ END $$;
@@ -0,0 +1,56 @@
1
+ -- @transaction: off
2
+ -- 138_runtime_constraints_not_valid.sql
3
+ -- 백필(137) 완료 후 scope/tenant_id 무결성 CHECK 를 안전하게 고정.
4
+ --
5
+ -- 결정: semicolony/decision/rls-trusted-service-guc-boundary-2026-06-19.
6
+ -- 선행: 136(컬럼), 137(백필).
7
+ --
8
+ -- 전략(긴 락 회피):
9
+ -- - ADD CONSTRAINT ... NOT VALID → 기존 행 풀스캔 검증 없이 즉시 등록(짧은 락).
10
+ -- - VALIDATE CONSTRAINT → SHARE UPDATE EXCLUSIVE(쓰기 허용)로 기존 행 검증.
11
+ -- 두 단계를 분리하면 라이브 DB 에서 긴 ACCESS EXCLUSIVE 락 없이 제약을 활성화할 수 있다.
12
+ --
13
+ -- @transaction: off 이유:
14
+ -- - VALIDATE CONSTRAINT 은 큰 통합 트랜잭션 밖에서 실행되어야 락 보유 시간을 최소화한다.
15
+ -- - 러너가 이 지시자를 보면 파일을 BEGIN/COMMIT 으로 감싸지 않는다(각 문이 자기 트랜잭션).
16
+ -- - `semicolony.` 는 러너가 활성 스키마로 retarget 한다(`@schema-retarget: off` 아님).
17
+ --
18
+ -- 멱등: 제약 존재 여부를 pg_constraint 로 가드. 재실행해도 중복 추가/검증 안 함.
19
+
20
+ DO $$
21
+ DECLARE
22
+ active_schema text := coalesce(nullif(current_setting('app.runtime_schema', true), ''), 'semicolony');
23
+ tbl text;
24
+ tables text[] := ARRAY[
25
+ 'bot_status', 'bot_sessions', 'bot_commitments', 'action_items',
26
+ 'agent_personas', 'agent_persona_revisions'
27
+ ];
28
+ cname text;
29
+ BEGIN
30
+ FOREACH tbl IN ARRAY tables LOOP
31
+ cname := tbl || '_scope_tenant_chk';
32
+ IF to_regclass(format('%I.%I', active_schema, tbl)) IS NULL THEN
33
+ CONTINUE;
34
+ END IF;
35
+ -- 1) NOT VALID 등록(없을 때만)
36
+ IF NOT EXISTS (
37
+ SELECT 1 FROM pg_constraint
38
+ WHERE conname = cname
39
+ AND conrelid = format('%I.%I', active_schema, tbl)::regclass
40
+ ) THEN
41
+ EXECUTE format(
42
+ 'ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (scope = ''platform-global'' OR tenant_id IS NOT NULL) NOT VALID',
43
+ active_schema, tbl, cname
44
+ );
45
+ END IF;
46
+ -- 2) VALIDATE(아직 미검증일 때만)
47
+ IF EXISTS (
48
+ SELECT 1 FROM pg_constraint
49
+ WHERE conname = cname
50
+ AND conrelid = format('%I.%I', active_schema, tbl)::regclass
51
+ AND convalidated = false
52
+ ) THEN
53
+ EXECUTE format('ALTER TABLE %I.%I VALIDATE CONSTRAINT %I', active_schema, tbl, cname);
54
+ END IF;
55
+ END LOOP;
56
+ END $$;
@@ -0,0 +1,31 @@
1
+ -- 139_tenant_vms.sql
2
+ -- VM-per-tenant 런타임 샌드박스의 SoT. 중앙 sc-control / Proxmox hook worker 만 기록한다.
3
+ -- 결정: semicolony/decision/vm-provisioning-proxmox-api-token-bootstrap-secret-2026-06-19
4
+ -- 선례: 129_kb_multitenant_gateway.sql (public.tenants FK + per-tenant 격리),
5
+ -- gateway_credentials (자격증명 vault).
6
+ --
7
+ -- 러너(db.ts)가 파일 전체를 BEGIN/COMMIT 으로 감싸고 `semicolony.` 한정자만 활성 스키마로 retarget 한다.
8
+ -- → 여기엔 트랜잭션 구문을 넣지 않는다. 이 테이블들은 appdb 공용 자원이므로 `public.` 스키마
9
+ -- (테넌트/슬롯/잡)에 둔다 — retarget 대상이 아니라 flip 후에도 동일 위치를 유지한다.
10
+
11
+ -- ============================================================
12
+ -- public.tenant_vms — 테넌트별 프로비저닝된 VM 레코드 (1 tenant × N vm 가능)
13
+ -- ============================================================
14
+ CREATE TABLE IF NOT EXISTS public.tenant_vms (
15
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
16
+ tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
17
+ vmid INT UNIQUE, -- Proxmox VMID (중앙 할당, 캘러 공급 금지)
18
+ host_node TEXT, -- Proxmox 노드명 (예: hermes)
19
+ ip TEXT, -- VM 내부 IP
20
+ status TEXT
21
+ CHECK (status IN ('provisioning', 'active', 'suspended', 'destroyed')),
22
+ template_version TEXT, -- 클론 소스 템플릿 버전 (예: v1)
23
+ slot_id UUID, -- public.fleet_slots(id) — 140 에서 정의
24
+ gateway_credential_id UUID, -- semicolony.gateway_credentials(id) — KB bearer
25
+ cloudinit_sha256 TEXT, -- 주입된 cloud-init 페이로드 무결성 해시
26
+ last_heartbeat_at TIMESTAMPTZ, -- runtime-control 하트비트 수신 시각
27
+ created_at TIMESTAMPTZ DEFAULT now()
28
+ );
29
+
30
+ CREATE INDEX IF NOT EXISTS idx_tenant_vms_tenant ON public.tenant_vms (tenant_id);
31
+ CREATE INDEX IF NOT EXISTS idx_tenant_vms_status ON public.tenant_vms (status);
@@ -0,0 +1,23 @@
1
+ -- 140_fleet_slots.sql
2
+ -- 워크스테이션(Proxmox) 용량 슬롯. 중앙 sc-control 이 FOR UPDATE SKIP LOCKED 로 원자 할당한다.
3
+ -- 두 워커가 같은 슬롯을 동시에 점유하지 못하게 하는 단일 SoT.
4
+ -- 결정: semicolony/decision/vm-provisioning-proxmox-api-token-bootstrap-secret-2026-06-19
5
+ --
6
+ -- 러너가 파일을 BEGIN/COMMIT 으로 감싼다. appdb 공용 자원이므로 `public.` 스키마 (retarget 비대상).
7
+
8
+ -- ============================================================
9
+ -- public.fleet_slots — host_node × slot_index 단위의 용량 슬롯
10
+ -- ============================================================
11
+ CREATE TABLE IF NOT EXISTS public.fleet_slots (
12
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
13
+ host_node TEXT, -- Proxmox 노드명 (예: hermes)
14
+ slot_index INT, -- 노드 내 슬롯 번호
15
+ status TEXT NOT NULL DEFAULT 'free'
16
+ CHECK (status IN ('free', 'reserved', 'occupied')),
17
+ cpu_limit INT, -- 슬롯 vCPU 상한
18
+ ram_mb INT, -- 슬롯 RAM (MB)
19
+ disk_gb INT, -- 슬롯 디스크 (GB)
20
+ UNIQUE (host_node, slot_index)
21
+ );
22
+
23
+ CREATE INDEX IF NOT EXISTS idx_fleet_slots_status ON public.fleet_slots (status);
@@ -0,0 +1,29 @@
1
+ -- 141_provisioning_jobs.sql
2
+ -- 타입드 프로비저닝 잡 큐 (producer = sc-control, consumer = Proxmox hook worker).
3
+ -- payload 는 검증된 타입드 객체만 (셸 조각·임의 명령·캘러 공급 vmid 금지 — sc-control.ts 에서 가드).
4
+ -- claim 은 FOR UPDATE SKIP LOCKED 로 정확히 한 워커만 점유.
5
+ -- 결정: semicolony/decision/vm-provisioning-proxmox-api-token-bootstrap-secret-2026-06-19
6
+ -- 선례: cron.ts cronTick (FOR UPDATE SKIP LOCKED 원자 claim).
7
+ --
8
+ -- 러너가 파일을 BEGIN/COMMIT 으로 감싼다. appdb 공용 큐이므로 `public.` 스키마 (retarget 비대상).
9
+
10
+ -- ============================================================
11
+ -- public.provisioning_jobs — clone/destroy/suspend/resume/resync 잡 큐
12
+ -- ============================================================
13
+ CREATE TABLE IF NOT EXISTS public.provisioning_jobs (
14
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
15
+ kind TEXT
16
+ CHECK (kind IN ('clone', 'destroy', 'suspend', 'resume', 'resync')),
17
+ tenant_slug TEXT, -- 대상 테넌트 슬러그 (vmid 는 payload 에 없음)
18
+ payload JSONB, -- 타입드 ProvisioningPayload 만
19
+ status TEXT NOT NULL DEFAULT 'queued'
20
+ CHECK (status IN ('queued', 'claimed', 'running', 'done', 'failed')),
21
+ attempts INT NOT NULL DEFAULT 0,
22
+ lease_expires_at TIMESTAMPTZ, -- claim 리스 만료 (stale claim 재할당용)
23
+ claimed_by TEXT, -- worker id
24
+ created_at TIMESTAMPTZ DEFAULT now(),
25
+ updated_at TIMESTAMPTZ DEFAULT now()
26
+ );
27
+
28
+ CREATE INDEX IF NOT EXISTS idx_provisioning_jobs_status_created
29
+ ON public.provisioning_jobs (status, created_at);
@@ -0,0 +1,129 @@
1
+ -- @schema-retarget: off
2
+ -- 142_rls_enable_semicolony.sql
3
+ -- RLS 활성화(마지막 비가역 게이트) — semicolony.* 서비스/멀티테넌트 테이블.
4
+ --
5
+ -- 결정: semicolony/decision/rls-trusted-service-guc-boundary-2026-06-19.
6
+ -- 선행: 135(역할+헬퍼), 136(컬럼), 137(백필), 138(무결성 CHECK).
7
+ -- *** 반드시 서비스 경로가 sc_app 로 통과하는 것을 검증한 뒤에만 적용한다(롤백 드릴 준비). ***
8
+ --
9
+ -- 정책 모델(fail-closed):
10
+ -- USING = public.is_provider() OR scope='platform-global' OR tenant_id = public.current_tenant_id()
11
+ -- WITH CHECK = public.is_provider() OR tenant_id = public.current_tenant_id()
12
+ -- → 읽기: provider / 플랫폼-글로벌 / 자기 테넌트만. GUC 미설정 시 current_tenant_id()=NULL →
13
+ -- tenant_id = NULL 비교는 NULL(=거짓) → 격리(fail-closed).
14
+ -- → 쓰기: provider 또는 자기 테넌트 행만(platform-global 신규 쓰기는 provider 만).
15
+ --
16
+ -- FORCE ROW LEVEL SECURITY: 테이블 소유자에게도 RLS 적용 → 소유자 연결로도 누수 없음.
17
+ --
18
+ -- @schema-retarget: off 이유: public.is_provider()/current_tenant_id() 와 role 참조는
19
+ -- 절대 retarget 되면 안 된다(GUC 계약). 테이블도 semicolony.* 리터럴로 고정한다(이 게이트는
20
+ -- 현행 semicolony 스키마 대상; flip 시 별도 게이트로 재평가).
21
+ --
22
+ -- 멱등: ENABLE/FORCE 는 반복 안전. 정책은 DROP POLICY IF EXISTS → CREATE 로 재적용.
23
+
24
+ -- ============================================================
25
+ -- bot_status
26
+ -- ============================================================
27
+ ALTER TABLE semicolony.bot_status ENABLE ROW LEVEL SECURITY;
28
+ ALTER TABLE semicolony.bot_status FORCE ROW LEVEL SECURITY;
29
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.bot_status;
30
+ CREATE POLICY tenant_isolation ON semicolony.bot_status
31
+ USING (public.is_provider() OR scope = 'platform-global' OR tenant_id = public.current_tenant_id())
32
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
33
+
34
+ -- ============================================================
35
+ -- bot_sessions
36
+ -- ============================================================
37
+ ALTER TABLE semicolony.bot_sessions ENABLE ROW LEVEL SECURITY;
38
+ ALTER TABLE semicolony.bot_sessions FORCE ROW LEVEL SECURITY;
39
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.bot_sessions;
40
+ CREATE POLICY tenant_isolation ON semicolony.bot_sessions
41
+ USING (public.is_provider() OR scope = 'platform-global' OR tenant_id = public.current_tenant_id())
42
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
43
+
44
+ -- ============================================================
45
+ -- bot_commitments
46
+ -- ============================================================
47
+ ALTER TABLE semicolony.bot_commitments ENABLE ROW LEVEL SECURITY;
48
+ ALTER TABLE semicolony.bot_commitments FORCE ROW LEVEL SECURITY;
49
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.bot_commitments;
50
+ CREATE POLICY tenant_isolation ON semicolony.bot_commitments
51
+ USING (public.is_provider() OR scope = 'platform-global' OR tenant_id = public.current_tenant_id())
52
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
53
+
54
+ -- ============================================================
55
+ -- action_items
56
+ -- ============================================================
57
+ ALTER TABLE semicolony.action_items ENABLE ROW LEVEL SECURITY;
58
+ ALTER TABLE semicolony.action_items FORCE ROW LEVEL SECURITY;
59
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.action_items;
60
+ CREATE POLICY tenant_isolation ON semicolony.action_items
61
+ USING (public.is_provider() OR scope = 'platform-global' OR tenant_id = public.current_tenant_id())
62
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
63
+
64
+ -- ============================================================
65
+ -- agent_personas
66
+ -- ============================================================
67
+ ALTER TABLE semicolony.agent_personas ENABLE ROW LEVEL SECURITY;
68
+ ALTER TABLE semicolony.agent_personas FORCE ROW LEVEL SECURITY;
69
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.agent_personas;
70
+ CREATE POLICY tenant_isolation ON semicolony.agent_personas
71
+ USING (public.is_provider() OR scope = 'platform-global' OR tenant_id = public.current_tenant_id())
72
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
73
+
74
+ -- ============================================================
75
+ -- agent_persona_revisions
76
+ -- ============================================================
77
+ ALTER TABLE semicolony.agent_persona_revisions ENABLE ROW LEVEL SECURITY;
78
+ ALTER TABLE semicolony.agent_persona_revisions FORCE ROW LEVEL SECURITY;
79
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.agent_persona_revisions;
80
+ CREATE POLICY tenant_isolation ON semicolony.agent_persona_revisions
81
+ USING (public.is_provider() OR scope = 'platform-global' OR tenant_id = public.current_tenant_id())
82
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
83
+
84
+ -- ============================================================
85
+ -- knowledge_base
86
+ -- READ: provider / 자기 테넌트 / 플랫폼-글로벌이되 게이트웨이 allowlist(metadata->>'tenant_visible'='true')
87
+ -- 에 한해 테넌트에 노출(게이트웨이 노출 계약과 일치).
88
+ -- WRITE: provider 또는 자기 테넌트.
89
+ -- ============================================================
90
+ ALTER TABLE semicolony.knowledge_base ENABLE ROW LEVEL SECURITY;
91
+ ALTER TABLE semicolony.knowledge_base FORCE ROW LEVEL SECURITY;
92
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.knowledge_base;
93
+ CREATE POLICY tenant_isolation ON semicolony.knowledge_base
94
+ USING (
95
+ public.is_provider()
96
+ OR tenant_id = public.current_tenant_id()
97
+ OR (scope = 'platform-global' AND metadata->>'tenant_visible' = 'true')
98
+ )
99
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
100
+
101
+ -- ============================================================
102
+ -- entity_relations
103
+ -- ============================================================
104
+ ALTER TABLE semicolony.entity_relations ENABLE ROW LEVEL SECURITY;
105
+ ALTER TABLE semicolony.entity_relations FORCE ROW LEVEL SECURITY;
106
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.entity_relations;
107
+ CREATE POLICY tenant_isolation ON semicolony.entity_relations
108
+ USING (public.is_provider() OR scope = 'platform-global' OR tenant_id = public.current_tenant_id())
109
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
110
+
111
+ -- ============================================================
112
+ -- kb_claims (scope DEFAULT 'tenant-local')
113
+ -- ============================================================
114
+ ALTER TABLE semicolony.kb_claims ENABLE ROW LEVEL SECURITY;
115
+ ALTER TABLE semicolony.kb_claims FORCE ROW LEVEL SECURITY;
116
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.kb_claims;
117
+ CREATE POLICY tenant_isolation ON semicolony.kb_claims
118
+ USING (public.is_provider() OR scope = 'platform-global' OR tenant_id = public.current_tenant_id())
119
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
120
+
121
+ -- ============================================================
122
+ -- concierge_requests (tenant_id NOT NULL, scope 컬럼 없음 → 순수 tenant 격리)
123
+ -- ============================================================
124
+ ALTER TABLE semicolony.concierge_requests ENABLE ROW LEVEL SECURITY;
125
+ ALTER TABLE semicolony.concierge_requests FORCE ROW LEVEL SECURITY;
126
+ DROP POLICY IF EXISTS tenant_isolation ON semicolony.concierge_requests;
127
+ CREATE POLICY tenant_isolation ON semicolony.concierge_requests
128
+ USING (public.is_provider() OR tenant_id = public.current_tenant_id())
129
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
@@ -0,0 +1,54 @@
1
+ -- @schema-retarget: off
2
+ -- 143_rls_enable_public.sql
3
+ -- RLS 활성화 — public.* 멀티테넌트 facade 테이블.
4
+ --
5
+ -- 결정: semicolony/decision/rls-trusted-service-guc-boundary-2026-06-19.
6
+ -- 선행: 135(역할+헬퍼). 010_customer_tables(tenants/agent_installs/agent_activity).
7
+ --
8
+ -- 정책 모델:
9
+ -- - tenants: 자기 테넌트(id = current_tenant_id()) 또는 provider 만 조회.
10
+ -- - agent_installs: tenant_id = current_tenant_id() 또는 provider.
11
+ -- - agent_activity: tenant_id = current_tenant_id() 또는 provider.
12
+ -- - agent_listings: 플랫폼 카탈로그(공용) → RLS 미적용(UNRESTRICTED). 정책/ENABLE 없음.
13
+ --
14
+ -- GUC 미설정(테넌트 미인증) 시 current_tenant_id()=NULL → 비교가 NULL(=거짓) → fail-closed.
15
+ --
16
+ -- FORCE ROW LEVEL SECURITY: 소유자 연결에도 적용. trusted 서비스는 provider 연결/GUC 로 우회.
17
+ --
18
+ -- @schema-retarget: off: public.* 와 헬퍼 함수는 retarget 대상이 아니다(스키마 flip 무관).
19
+ -- 멱등: ENABLE/FORCE 반복 안전. 정책은 DROP IF EXISTS → CREATE.
20
+
21
+ -- ============================================================
22
+ -- public.tenants — 멤버(자기 테넌트) / provider 가시
23
+ -- ============================================================
24
+ ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY;
25
+ ALTER TABLE public.tenants FORCE ROW LEVEL SECURITY;
26
+ DROP POLICY IF EXISTS tenant_isolation ON public.tenants;
27
+ CREATE POLICY tenant_isolation ON public.tenants
28
+ USING (public.is_provider() OR id = public.current_tenant_id())
29
+ WITH CHECK (public.is_provider());
30
+
31
+ -- ============================================================
32
+ -- public.agent_installs
33
+ -- ============================================================
34
+ ALTER TABLE public.agent_installs ENABLE ROW LEVEL SECURITY;
35
+ ALTER TABLE public.agent_installs FORCE ROW LEVEL SECURITY;
36
+ DROP POLICY IF EXISTS tenant_isolation ON public.agent_installs;
37
+ CREATE POLICY tenant_isolation ON public.agent_installs
38
+ USING (public.is_provider() OR tenant_id = public.current_tenant_id())
39
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
40
+
41
+ -- ============================================================
42
+ -- public.agent_activity
43
+ -- ============================================================
44
+ ALTER TABLE public.agent_activity ENABLE ROW LEVEL SECURITY;
45
+ ALTER TABLE public.agent_activity FORCE ROW LEVEL SECURITY;
46
+ DROP POLICY IF EXISTS tenant_isolation ON public.agent_activity;
47
+ CREATE POLICY tenant_isolation ON public.agent_activity
48
+ USING (public.is_provider() OR tenant_id = public.current_tenant_id())
49
+ WITH CHECK (public.is_provider() OR tenant_id = public.current_tenant_id());
50
+
51
+ -- ============================================================
52
+ -- public.agent_listings — 플랫폼 공용 카탈로그: RLS 미적용(UNRESTRICTED).
53
+ -- 의도적으로 ENABLE ROW LEVEL SECURITY 를 호출하지 않는다(모든 테넌트가 라이브러리 열람).
54
+ -- ============================================================
@@ -0,0 +1,211 @@
1
+ -- 144_worker_device_registry.sql
2
+ -- SEMO Ops worker device registry.
3
+ --
4
+ -- Purpose:
5
+ -- - Store operator-kit/local worker environments in DB so Semi can answer setup status.
6
+ -- - Keep "local hive" as an exception record only for worker-owned always-on workloads.
7
+ --
8
+ -- Safety:
9
+ -- - Additive only: new tables, indexes, triggers, and a read view.
10
+ -- - No token plaintext. gateway_credential_id references the hashed credential vault only.
11
+ -- - Runner wraps this file in BEGIN/COMMIT; do not add transaction statements here.
12
+
13
+ CREATE TABLE IF NOT EXISTS semicolony.workspace_profiles (
14
+ profile_id TEXT PRIMARY KEY,
15
+ version TEXT NOT NULL,
16
+ status TEXT NOT NULL DEFAULT 'active'
17
+ CHECK (status IN ('draft', 'active', 'deprecated', 'retired')),
18
+ kb_gateway_url TEXT,
19
+ repo_provider TEXT,
20
+ repo_remotes JSONB NOT NULL DEFAULT '{}'::jsonb,
21
+ default_workspace_root TEXT,
22
+ tool_adapters TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
23
+ guidance_template_version TEXT,
24
+ required_smokes JSONB NOT NULL DEFAULT '[]'::jsonb,
25
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
26
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
27
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
28
+ );
29
+
30
+ COMMENT ON TABLE semicolony.workspace_profiles IS
31
+ 'SEMO Ops worker setup profile versions. Local worker files are snapshots of this DB/KB source.';
32
+
33
+ CREATE TABLE IF NOT EXISTS semicolony.worker_devices (
34
+ device_id TEXT PRIMARY KEY,
35
+ worker_id TEXT NOT NULL,
36
+ display_name TEXT,
37
+ hostname TEXT,
38
+ os TEXT,
39
+ shell TEXT,
40
+ workspace_root TEXT,
41
+ installed_profile_id TEXT NOT NULL DEFAULT 'semo-ops-worker',
42
+ installed_profile_version TEXT,
43
+ tool_adapters_enabled TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
44
+ kb_access_mode TEXT NOT NULL DEFAULT 'worker-gateway'
45
+ CHECK (kb_access_mode IN ('worker-gateway', 'direct-db-bootstrap', 'kb-portable', 'none')),
46
+ gateway_credential_id UUID REFERENCES semicolony.gateway_credentials(id) ON DELETE SET NULL,
47
+ last_seen_at TIMESTAMPTZ,
48
+ last_doctor_status TEXT NOT NULL DEFAULT 'unknown'
49
+ CHECK (last_doctor_status IN ('unknown', 'pending', 'pass', 'warn', 'fail')),
50
+ last_doctor_summary JSONB NOT NULL DEFAULT '{}'::jsonb,
51
+ setup_progress JSONB NOT NULL DEFAULT '{}'::jsonb,
52
+ status TEXT NOT NULL DEFAULT 'pending-auth'
53
+ CHECK (status IN ('pending-auth', 'pending-setup', 'active', 'blocked', 'stale', 'revoked', 'retired')),
54
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
55
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
56
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
57
+ );
58
+
59
+ COMMENT ON TABLE semicolony.worker_devices IS
60
+ 'Registered SEMO Ops worker devices. One person may own multiple devices.';
61
+ COMMENT ON COLUMN semicolony.worker_devices.setup_progress IS
62
+ 'Per-step setup status map, e.g. {"env_file":"pass","gitlab":"pending","kb_read":"fail"}.';
63
+ COMMENT ON COLUMN semicolony.worker_devices.gateway_credential_id IS
64
+ 'Reference to hashed gateway credential row. Plaintext worker tokens must never be stored here.';
65
+
66
+ CREATE INDEX IF NOT EXISTS idx_worker_devices_worker
67
+ ON semicolony.worker_devices (worker_id, status);
68
+
69
+ CREATE INDEX IF NOT EXISTS idx_worker_devices_status_seen
70
+ ON semicolony.worker_devices (status, last_seen_at DESC NULLS LAST);
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_worker_devices_profile
73
+ ON semicolony.worker_devices (installed_profile_id, installed_profile_version);
74
+
75
+ CREATE TABLE IF NOT EXISTS semicolony.local_hives (
76
+ local_hive_id TEXT PRIMARY KEY,
77
+ device_id TEXT NOT NULL REFERENCES semicolony.worker_devices(device_id) ON DELETE CASCADE,
78
+ worker_id TEXT NOT NULL,
79
+ workload_type TEXT NOT NULL
80
+ CHECK (workload_type IN ('hive-bridge', 'hive-agent', 'hive-cron', 'hive-link', 'hive-watch', 'other')),
81
+ reason TEXT NOT NULL,
82
+ cannot_move_reason TEXT,
83
+ migration_target TEXT NOT NULL,
84
+ expiry_at TIMESTAMPTZ,
85
+ health_check JSONB NOT NULL DEFAULT '{}'::jsonb,
86
+ last_seen_at TIMESTAMPTZ,
87
+ status TEXT NOT NULL DEFAULT 'approved'
88
+ CHECK (status IN ('approved', 'expiring', 'migrated', 'retired')),
89
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
90
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
91
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
92
+ );
93
+
94
+ COMMENT ON TABLE semicolony.local_hives IS
95
+ 'Exception registry for local always-on worker workloads that cannot yet move to workstation/service hive.';
96
+
97
+ CREATE INDEX IF NOT EXISTS idx_local_hives_device
98
+ ON semicolony.local_hives (device_id, status);
99
+
100
+ CREATE INDEX IF NOT EXISTS idx_local_hives_worker
101
+ ON semicolony.local_hives (worker_id, status);
102
+
103
+ CREATE INDEX IF NOT EXISTS idx_local_hives_expiry
104
+ ON semicolony.local_hives (expiry_at)
105
+ WHERE status IN ('approved', 'expiring') AND expiry_at IS NOT NULL;
106
+
107
+ CREATE OR REPLACE FUNCTION semicolony.trg_worker_registry_updated()
108
+ RETURNS TRIGGER AS $$
109
+ BEGIN
110
+ NEW.updated_at = NOW();
111
+ RETURN NEW;
112
+ END;
113
+ $$ LANGUAGE plpgsql;
114
+
115
+ DROP TRIGGER IF EXISTS trg_workspace_profiles_updated ON semicolony.workspace_profiles;
116
+ CREATE TRIGGER trg_workspace_profiles_updated
117
+ BEFORE UPDATE ON semicolony.workspace_profiles
118
+ FOR EACH ROW EXECUTE FUNCTION semicolony.trg_worker_registry_updated();
119
+
120
+ DROP TRIGGER IF EXISTS trg_worker_devices_updated ON semicolony.worker_devices;
121
+ CREATE TRIGGER trg_worker_devices_updated
122
+ BEFORE UPDATE ON semicolony.worker_devices
123
+ FOR EACH ROW EXECUTE FUNCTION semicolony.trg_worker_registry_updated();
124
+
125
+ DROP TRIGGER IF EXISTS trg_local_hives_updated ON semicolony.local_hives;
126
+ CREATE TRIGGER trg_local_hives_updated
127
+ BEFORE UPDATE ON semicolony.local_hives
128
+ FOR EACH ROW EXECUTE FUNCTION semicolony.trg_worker_registry_updated();
129
+
130
+ INSERT INTO semicolony.workspace_profiles (
131
+ profile_id,
132
+ version,
133
+ status,
134
+ kb_gateway_url,
135
+ repo_provider,
136
+ repo_remotes,
137
+ default_workspace_root,
138
+ tool_adapters,
139
+ guidance_template_version,
140
+ required_smokes,
141
+ metadata
142
+ )
143
+ VALUES (
144
+ 'semo-ops-worker',
145
+ '2026-07-09',
146
+ 'active',
147
+ 'https://semicolony.semi-colon.space/kb-gateway',
148
+ 'gitlab',
149
+ '{"semicolony":"https://gitlab.semi-colon.space/semicolon-devteam/semicolony.git"}'::jsonb,
150
+ '~/Desktop/Sources/semicolon',
151
+ ARRAY['codex', 'claude-code'],
152
+ '2026-07-09',
153
+ '[
154
+ {"id":"worker:gateway-env","label":"worker KB gateway env"},
155
+ {"id":"worker:env-file","label":"worker env file"},
156
+ {"id":"worker:mcp-config","label":"KB MCP config"},
157
+ {"id":"worker:guidance:claude","label":"Claude worker guidance"},
158
+ {"id":"worker:guidance:codex","label":"Codex worker guidance"},
159
+ {"id":"worker:kb:hive-service","label":"KB read smoke: hive-service"},
160
+ {"id":"worker:kb:setup-guide","label":"KB read smoke: worker setup guide"},
161
+ {"id":"worker:gitlab","label":"GitLab access"}
162
+ ]'::jsonb,
163
+ '{"scope":"tenant-local","tenant_anchor":"team-semicolon","owner":"SEMO Ops"}'::jsonb
164
+ )
165
+ ON CONFLICT (profile_id) DO UPDATE
166
+ SET version = EXCLUDED.version,
167
+ status = EXCLUDED.status,
168
+ kb_gateway_url = EXCLUDED.kb_gateway_url,
169
+ repo_provider = EXCLUDED.repo_provider,
170
+ repo_remotes = EXCLUDED.repo_remotes,
171
+ default_workspace_root = EXCLUDED.default_workspace_root,
172
+ tool_adapters = EXCLUDED.tool_adapters,
173
+ guidance_template_version = EXCLUDED.guidance_template_version,
174
+ required_smokes = EXCLUDED.required_smokes,
175
+ metadata = semicolony.workspace_profiles.metadata || EXCLUDED.metadata;
176
+
177
+ CREATE OR REPLACE VIEW semicolony.v_worker_environment_status AS
178
+ SELECT
179
+ wd.device_id,
180
+ wd.worker_id,
181
+ wd.display_name,
182
+ wd.hostname,
183
+ wd.os,
184
+ wd.workspace_root,
185
+ wd.installed_profile_id,
186
+ wd.installed_profile_version,
187
+ wd.tool_adapters_enabled,
188
+ wd.kb_access_mode,
189
+ wd.last_seen_at,
190
+ wd.last_doctor_status,
191
+ wd.last_doctor_summary,
192
+ wd.setup_progress,
193
+ wd.status,
194
+ wd.updated_at,
195
+ COALESCE(
196
+ jsonb_agg(
197
+ jsonb_build_object(
198
+ 'local_hive_id', lh.local_hive_id,
199
+ 'workload_type', lh.workload_type,
200
+ 'status', lh.status,
201
+ 'migration_target', lh.migration_target,
202
+ 'expiry_at', lh.expiry_at,
203
+ 'last_seen_at', lh.last_seen_at
204
+ )
205
+ ) FILTER (WHERE lh.local_hive_id IS NOT NULL),
206
+ '[]'::jsonb
207
+ ) AS local_hives
208
+ FROM semicolony.worker_devices wd
209
+ LEFT JOIN semicolony.local_hives lh
210
+ ON lh.device_id = wd.device_id
211
+ GROUP BY wd.device_id;