@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,34 @@
1
+ -- 110: kb-manager 스킬 프롬프트 L2 leakage 정리
2
+ -- migrations 027/031 가 skill_definitions(name='kb-manager') 의 prompt 에
3
+ -- 'reus' (팀원 닉네임), 'by-buyer' (세미콜론 L2 서비스), 그리고 027 의 경우
4
+ -- '/Users/reus/Desktop/Sources/...' 하드코딩 경로까지 박아 두었다.
5
+ -- 027/031 인라인 텍스트는 일반화 (이 migration 시리즈와 함께 적용),
6
+ -- 기존 인스턴스의 row 는 이 migration 이 정확 매칭일 때만 갱신.
7
+ --
8
+ -- 관련 인벤토리: docs/L2-INVENTORY.md (HIGH — kb-manager skill_definitions row)
9
+
10
+ BEGIN;
11
+
12
+ UPDATE semicolony.skill_definitions
13
+ SET
14
+ prompt = REPLACE(
15
+ REPLACE(
16
+ REPLACE(
17
+ REPLACE(
18
+ REPLACE(prompt,
19
+ 'semicolony kb get semicolon team reus', 'semicolony kb get <org> team alice'),
20
+ 'semicolony kb get by-buyer po', 'semicolony kb get my-service po'),
21
+ 'semicolony kb search "PO" --service by-buyer', 'semicolony kb search "PO" --service my-service'),
22
+ 'semicolony kb upsert semicolon decision 2026-03-24/example', 'semicolony kb upsert <org> decision 2026-03-24/example'),
23
+ 'export SEMICOLONY_ROOT="/Users/reus/Desktop/Sources/semicolon/projects/semicolony"\ncd $SEMICOLONY_ROOT/packages/cli\n', ''
24
+ ),
25
+ updated_at = NOW()
26
+ WHERE name = 'kb-manager'
27
+ AND office_id IS NULL
28
+ AND (
29
+ prompt LIKE '%semicolony kb get semicolon team reus%'
30
+ OR prompt LIKE '%--service by-buyer%'
31
+ OR prompt LIKE '%/Users/reus/Desktop/Sources/semicolon/projects/semicolony%'
32
+ );
33
+
34
+ COMMIT;
@@ -0,0 +1,20 @@
1
+ -- 111: 030 이 시드한 세미콜론 팀원 ontology rows 정리
2
+ -- migration 030 가 OSS fresh install 시 세미콜론 팀원 10명 (reus, garden, roki, yeomso,
3
+ -- bon, kyago, bae, harry, goni, kai) 의 ontology row 를 자동 시드했음.
4
+ -- 030 인라인은 이 migration 시리즈와 함께 INSERT 블록 제거.
5
+ -- 기존 인스턴스에서 이미 시드된 row 는 KB 데이터가 0건일 때만 삭제 (108 과 동일 패턴).
6
+ --
7
+ -- 관련 인벤토리: docs/L2-INVENTORY.md (HIGH — migration 030 team seeds)
8
+
9
+ BEGIN;
10
+
11
+ DELETE FROM semicolony.ontology o
12
+ WHERE o.domain IN (
13
+ 'reus','garden','roki','yeomso','bon','kyago','bae','harry','goni','kai'
14
+ )
15
+ AND o.entity_type IN ('team', 'person')
16
+ AND NOT EXISTS (
17
+ SELECT 1 FROM semicolony.knowledge_base kb WHERE kb.domain = o.domain
18
+ );
19
+
20
+ COMMIT;
@@ -0,0 +1,83 @@
1
+ -- 112: pipeline 키 스키마 정합화 + role/po 트리거 services 드리프트 복구
2
+ --
3
+ -- 두 이슈 동시 수정:
4
+ -- 1. `semicolony kb upsert <domain> role po` 가 `relation "semicolony.services" does not exist` 로 실패
5
+ -- (migration 072 가 만든 trg_sync_role_po 트리거가 migration 101 에서 DROP 된 services 테이블을 참조)
6
+ -- 2. service 타입 `pipeline` 키가 schema=singleton 이지만 실제 데이터는 sub_key='config' (migration-100 이식 + SoT 문서)
7
+ -- → 신규 도메인 등록 시 sub_key='' 로 들어가 도메인 간 형태 갈라짐
8
+ --
9
+ -- KB 트래킹:
10
+ -- - semicolony process kb-role-upsert-services-drift (2026-04-27)
11
+ -- - semicolony process pipeline-key-schema-sot-mismatch (2026-04-27)
12
+ --
13
+ -- 선행 조건: 111
14
+
15
+ BEGIN;
16
+
17
+ -- ============================================================
18
+ -- 1. role/po 동기화 트리거 재구성: services 테이블 → KB pipeline/config metadata
19
+ -- ============================================================
20
+ -- 기존 트리거는 DROP 된 semicolony.services.owner_name 을 UPDATE 하려 해서 항상 실패.
21
+ -- pipeline/config metadata.owner_name 이 이제 SoT 이므로 그곳을 업데이트한다.
22
+ -- 무한 재귀 방지: pipeline/config 업데이트는 key='role' 조건과 매칭되지 않으므로 트리거 재진입 없음.
23
+
24
+ DROP TRIGGER IF EXISTS trg_sync_role_po ON semicolony.knowledge_base;
25
+
26
+ CREATE OR REPLACE FUNCTION semicolony.sync_role_po_to_owner()
27
+ RETURNS TRIGGER AS $$
28
+ DECLARE
29
+ _owner text;
30
+ BEGIN
31
+ IF NEW.key = 'role' AND NEW.sub_key = 'po' THEN
32
+ _owner := TRIM(SPLIT_PART(NEW.content, ',', 1));
33
+ UPDATE semicolony.knowledge_base
34
+ SET metadata = jsonb_set(COALESCE(metadata, '{}'::jsonb), '{owner_name}', to_jsonb(_owner), true),
35
+ updated_at = NOW()
36
+ WHERE domain = NEW.domain
37
+ AND key = 'pipeline'
38
+ AND sub_key = 'config';
39
+ END IF;
40
+ RETURN NEW;
41
+ END;
42
+ $$ LANGUAGE plpgsql;
43
+
44
+ CREATE TRIGGER trg_sync_role_po
45
+ AFTER INSERT OR UPDATE ON semicolony.knowledge_base
46
+ FOR EACH ROW
47
+ WHEN (NEW.key = 'role' AND NEW.sub_key = 'po')
48
+ EXECUTE FUNCTION semicolony.sync_role_po_to_owner();
49
+
50
+ -- ============================================================
51
+ -- 2. pipeline 키 schema: singleton → collection (sub_key='config' SoT)
52
+ -- ============================================================
53
+ -- 기존 17개 도메인은 sub_key='config' 로 존재 (migration-100).
54
+ -- service-metadata-sot 문서, 글로벌 CLAUDE.md, 코드(channel-router, dashboard 등)
55
+ -- 모두 'pipeline/config' 를 가정. schema 만 어긋나 있어 schema 를 데이터에 맞춘다.
56
+
57
+ UPDATE semicolony.kb_type_schema
58
+ SET key_type = 'collection',
59
+ value_hint = 'sub_key=''config'' 고정. metadata: {service_id, project_name, owner_name, current_phase, infra_phase, status, lifecycle, service_type, parent_service_id, tech_stack, service_url, repo, slack_channel, launched_at, ...}',
60
+ scheme_description = '프로젝트 파이프라인 워크플로우 상태 (sub_key=''config'' 단일 사용)'
61
+ WHERE type_key = 'service'
62
+ AND scheme_key = 'pipeline';
63
+
64
+ -- ============================================================
65
+ -- 3. 신규 등록 도메인 백필: pipeline (sub_key='') → pipeline/config
66
+ -- ============================================================
67
+ -- migration-100 이후 등록되었지만 schema=singleton 강제로 sub_key='' 로 들어간 행을
68
+ -- sub_key='config' 로 이동. 충돌 시(이미 config 가 있으면) sub_key='' 행은 그대로 두고
69
+ -- 사람이 확인 후 정리하도록 남긴다.
70
+
71
+ UPDATE semicolony.knowledge_base AS kb
72
+ SET sub_key = 'config',
73
+ updated_at = NOW()
74
+ WHERE key = 'pipeline'
75
+ AND sub_key = ''
76
+ AND NOT EXISTS (
77
+ SELECT 1 FROM semicolony.knowledge_base kb2
78
+ WHERE kb2.domain = kb.domain
79
+ AND kb2.key = 'pipeline'
80
+ AND kb2.sub_key = 'config'
81
+ );
82
+
83
+ COMMIT;
@@ -0,0 +1,53 @@
1
+ -- 113_kb_history.sql
2
+ -- P1-5: KB 변경 이력 — knowledge_base 의 INSERT/UPDATE/DELETE 자동 기록
3
+ --
4
+ -- 목적: context push / kb upsert 가 잘못된 값으로 덮어썼을 때 롤백·감사·diff 분석 가능.
5
+ -- snapshot 형식: kb_snapshot JSONB 에 OLD/NEW 행 통째 직렬화 (스키마 확장에 robust).
6
+
7
+ CREATE TABLE IF NOT EXISTS semicolony.knowledge_base_history (
8
+ history_id BIGSERIAL PRIMARY KEY,
9
+ kb_id BIGINT,
10
+ operation TEXT NOT NULL CHECK (operation IN ('insert', 'update', 'delete')),
11
+ changed_by VARCHAR(100),
12
+ changed_at TIMESTAMPTZ DEFAULT NOW(),
13
+ -- INSERT/DELETE: 해당 행 통째. UPDATE: 변경 전(OLD) 행 통째.
14
+ kb_snapshot JSONB NOT NULL
15
+ );
16
+
17
+ CREATE INDEX IF NOT EXISTS idx_kb_history_kb_id
18
+ ON semicolony.knowledge_base_history(kb_id);
19
+ CREATE INDEX IF NOT EXISTS idx_kb_history_changed_at
20
+ ON semicolony.knowledge_base_history(changed_at DESC);
21
+ -- 도메인/키 검색 위해 JSONB 부분 인덱스 (kb_snapshot.domain / .key)
22
+ CREATE INDEX IF NOT EXISTS idx_kb_history_domain_key
23
+ ON semicolony.knowledge_base_history(
24
+ (kb_snapshot->>'domain'),
25
+ (kb_snapshot->>'key')
26
+ );
27
+
28
+ CREATE OR REPLACE FUNCTION semicolony.kb_history_trigger_fn() RETURNS trigger AS $$
29
+ BEGIN
30
+ IF TG_OP = 'INSERT' THEN
31
+ INSERT INTO semicolony.knowledge_base_history(kb_id, operation, changed_by, kb_snapshot)
32
+ VALUES (NEW.kb_id, 'insert', NEW.created_by, to_jsonb(NEW));
33
+ RETURN NEW;
34
+ ELSIF TG_OP = 'UPDATE' THEN
35
+ INSERT INTO semicolony.knowledge_base_history(kb_id, operation, changed_by, kb_snapshot)
36
+ VALUES (OLD.kb_id, 'update', COALESCE(NEW.created_by, OLD.created_by), to_jsonb(OLD));
37
+ RETURN NEW;
38
+ ELSIF TG_OP = 'DELETE' THEN
39
+ INSERT INTO semicolony.knowledge_base_history(kb_id, operation, changed_by, kb_snapshot)
40
+ VALUES (OLD.kb_id, 'delete', OLD.created_by, to_jsonb(OLD));
41
+ RETURN OLD;
42
+ END IF;
43
+ RETURN NULL;
44
+ END;
45
+ $$ LANGUAGE plpgsql;
46
+
47
+ DROP TRIGGER IF EXISTS kb_history_trigger ON semicolony.knowledge_base;
48
+ CREATE TRIGGER kb_history_trigger
49
+ AFTER INSERT OR UPDATE OR DELETE ON semicolony.knowledge_base
50
+ FOR EACH ROW EXECUTE FUNCTION semicolony.kb_history_trigger_fn();
51
+
52
+ COMMENT ON TABLE semicolony.knowledge_base_history IS
53
+ 'P1-5: KB 변경 이력. AFTER INSERT/UPDATE/DELETE 트리거로 자동 기록.';
@@ -0,0 +1,26 @@
1
+ -- 114_runtime_target_columns.sql
2
+ -- P5-6: ExecutionTarget / Runtime Portable DB 스키마 확장
3
+ --
4
+ -- 봇이 어떤 호스트에서 어떤 모델로 어떤 도구·투사 채널과 결합해 동작할지를
5
+ -- DB SoT 로 표현. 기존 capability 는 packages/common/src/runtime 인터페이스에
6
+ -- 코드로만 있었지만, 봇 인스턴스별 선호/제약은 DB 가 SoT.
7
+ --
8
+ -- 모든 컬럼 nullable — 기존 봇은 변경 없이 동작 (회귀 0).
9
+
10
+ ALTER TABLE semicolony.bot_status
11
+ ADD COLUMN IF NOT EXISTS runtime_hint TEXT[],
12
+ ADD COLUMN IF NOT EXISTS tool_capabilities TEXT[],
13
+ ADD COLUMN IF NOT EXISTS projection_targets JSONB;
14
+
15
+ COMMENT ON COLUMN semicolony.bot_status.runtime_hint IS
16
+ 'P5-6: 선호 호스트 우선순위 배열. 예: [''claude-code'', ''codex-cli'', ''ollama-cli'']. RuntimeHarness 가 첫 번째 사용 가능 host 선택.';
17
+
18
+ COMMENT ON COLUMN semicolony.bot_status.tool_capabilities IS
19
+ 'P5-6: 봇이 호출 가능한 도구 이름 배열. ToolGateway register 결과와 매칭. 예: [''kb_get'', ''kb_upsert'', ''commitments_create''].';
20
+
21
+ COMMENT ON COLUMN semicolony.bot_status.projection_targets IS
22
+ 'P5-6: 기본 투사 타깃 JSONB 배열. 예: [{"channel":"slack-block","destination":"C123","options":{"botId":"semiclaw"}}]. ProjectionEmitter.emitAll 에 전달.';
23
+
24
+ -- 인덱스 — runtime_hint 로 호스트별 봇 조회 자주 발생 시
25
+ CREATE INDEX IF NOT EXISTS idx_bot_status_runtime_hint
26
+ ON semicolony.bot_status USING GIN (runtime_hint);
@@ -0,0 +1,52 @@
1
+ -- 115_runtime_source_columns.sql
2
+ -- 2026-05-06: OpenClaw + slack-router 공존 운영 — runtime_source 메타 추가.
3
+ -- Codex review (semicolon-plan-review): "두 운영 루프가 commitments/cron 공유 시
4
+ -- source 구분 필수. 최소값: slack-router | openclaw | manual | cron"
5
+ --
6
+ -- 모든 컬럼 nullable — 회귀 0. 기존 row 는 source_type 기반 추론 backfill.
7
+
8
+ -- ── bot_commitments ──
9
+ ALTER TABLE semicolony.bot_commitments
10
+ ADD COLUMN IF NOT EXISTS runtime_source TEXT;
11
+
12
+ COMMENT ON COLUMN semicolony.bot_commitments.runtime_source IS
13
+ '운영 런타임 출처. slack-router | openclaw | manual | cron. 2026-05-06 추가 (KB semicolony decision/openclaw-architecture-b-coexist).';
14
+
15
+ -- backfill: source_type → runtime_source 추론
16
+ UPDATE semicolony.bot_commitments
17
+ SET runtime_source = CASE
18
+ WHEN source_type = 'slack-inbox' THEN 'slack-router'
19
+ WHEN source_type = 'cron' THEN 'cron'
20
+ WHEN source_type = 'claude-code-local' THEN 'manual'
21
+ WHEN source_type LIKE 'openclaw%' THEN 'openclaw'
22
+ ELSE NULL
23
+ END
24
+ WHERE runtime_source IS NULL;
25
+
26
+ CREATE INDEX IF NOT EXISTS idx_bot_commitments_runtime_source
27
+ ON semicolony.bot_commitments (runtime_source)
28
+ WHERE runtime_source IS NOT NULL;
29
+
30
+ -- ── bot_cron_jobs ──
31
+ ALTER TABLE semicolony.bot_cron_jobs
32
+ ADD COLUMN IF NOT EXISTS runtime_source TEXT;
33
+
34
+ COMMENT ON COLUMN semicolony.bot_cron_jobs.runtime_source IS
35
+ '잡을 fire 하는 런타임. cron(=cron-poller, Architecture B) | openclaw(=OpenClaw 자체 cron) | manual.';
36
+
37
+ -- 기본 backfill: 기존 cron 잡은 cron-poller 가 fan-out 하던 것이므로 'cron'
38
+ UPDATE semicolony.bot_cron_jobs
39
+ SET runtime_source = 'cron'
40
+ WHERE runtime_source IS NULL;
41
+
42
+ -- ── action_items ──
43
+ ALTER TABLE semicolony.action_items
44
+ ADD COLUMN IF NOT EXISTS runtime_source TEXT;
45
+
46
+ COMMENT ON COLUMN semicolony.action_items.runtime_source IS
47
+ '액션 아이템 생성 런타임. slack-router | openclaw | manual | cron.';
48
+
49
+ -- 기본 backfill: 기존 action_item 은 사용자 직접 / 봇이 만든 것이라 'manual'
50
+ UPDATE semicolony.action_items
51
+ SET runtime_source = 'manual'
52
+ WHERE runtime_source IS NULL;
@@ -0,0 +1,81 @@
1
+ -- 116_commitment_pattern_health.sql
2
+ -- 2026-05-06: PAL 스타일 패턴 단위 실패 누적 추적 (C3 of Ouroboros adoption).
3
+ -- Ouroboros routing/escalation.py 의 EscalationManager 패턴을 SEMICOLONY 에 변형 차용.
4
+ -- 검증 근거: /tmp/ouroboros-sandbox/evidence/c3_results.json — 실 SEMICOLONY failed
5
+ -- commitment 500개 replay 시 16 escalation + 432 stagnation, 7 cron 패턴이 Frontier
6
+ -- 까지 도달 (cron poller 250x, commitment-watchdog 71x 등 침묵 실패 누적).
7
+ --
8
+ -- 이번 마이그레이션은 추적 스키마만 도입한다. 알림 부수효과(Slack DM, #bot-ops 포스팅)
9
+ -- 는 PR2 에서 슬랙 라우터에 부착. 이 PR 은 신호를 DB 에 적재하고, fail/done 전이에서
10
+ -- 카운터를 갱신하는 데까지만 한다 — 회귀 0.
11
+
12
+ -- ── pattern health 추적 ─────────────────────────────────────────────────────
13
+ CREATE TABLE IF NOT EXISTS semicolony.commitment_pattern_health (
14
+ -- 패턴 키: "{bot_id}::{canonical_prefix}#{hash}".
15
+ -- - canonical_prefix: title 을 NFKC + invisibles strip + whitespace collapse +
16
+ -- lowercase 후 첫 60 code point.
17
+ -- - hash: sha256(canonical title) 의 첫 10 hex — prefix 가 같지만 본문이 다른
18
+ -- title 들을 분리.
19
+ -- 키 생성/정규화 SoT 는 packages/cli/src/commitment-escalation.ts.
20
+ pattern_id TEXT PRIMARY KEY,
21
+
22
+ bot_id TEXT NOT NULL,
23
+ title_prefix TEXT NOT NULL,
24
+
25
+ consecutive_failures INTEGER NOT NULL DEFAULT 0
26
+ CHECK (consecutive_failures >= 0),
27
+
28
+ last_failure_at TIMESTAMPTZ,
29
+ last_success_at TIMESTAMPTZ,
30
+
31
+ -- 에스컬레이션 상태머신: none → notified (2 fails) → paged (5 fails)
32
+ -- 성공 시 즉시 'none' 으로 리셋. 알림 부수효과는 PR2.
33
+ state TEXT NOT NULL DEFAULT 'none'
34
+ CHECK (state IN ('none', 'notified', 'paged')),
35
+
36
+ -- 상태 전환을 마지막으로 관측한 시각. 진단/대시보드용.
37
+ -- ⚠ 알림 dedup 용도로는 신뢰하지 말 것 — 동시 실패 호출 시 두 트랜잭션 모두
38
+ -- "전이 발생" 으로 관측될 수 있다. PR2 에서 별도 claim 컬럼 또는
39
+ -- notification dedup 테이블로 idempotency 를 잡는다.
40
+ state_changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
41
+
42
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
43
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
44
+ );
45
+
46
+ COMMENT ON TABLE semicolony.commitment_pattern_health IS
47
+ '봇 약속의 (bot_id, title_prefix) 패턴별 연속 실패 누적과 에스컬레이션 상태. '
48
+ 'Ouroboros C3 (PAL escalation 변형). 2026-05-06 추가.';
49
+
50
+ COMMENT ON COLUMN semicolony.commitment_pattern_health.pattern_id IS
51
+ '"{bot_id}::{canonical_prefix}#{sha256_10}". canonical = NFKC + invisibles strip + '
52
+ 'whitespace collapse + lowercase. SoT: packages/cli/src/commitment-escalation.ts.';
53
+
54
+ COMMENT ON COLUMN semicolony.commitment_pattern_health.state IS
55
+ 'none | notified (2회 연속 실패) | paged (5회 연속 실패). '
56
+ '성공 1회로 즉시 none 으로 reset.';
57
+
58
+ -- 활성 escalation 모니터링 인덱스 — paged 상태 패턴이 가장 시급.
59
+ CREATE INDEX IF NOT EXISTS idx_commitment_pattern_health_active_state
60
+ ON semicolony.commitment_pattern_health (state, last_failure_at DESC)
61
+ WHERE state <> 'none';
62
+
63
+ -- bot 별 health 조회 (대시보드용).
64
+ CREATE INDEX IF NOT EXISTS idx_commitment_pattern_health_bot_id
65
+ ON semicolony.commitment_pattern_health (bot_id);
66
+
67
+ -- updated_at 자동 갱신 트리거 — bot_commitments 와 동일 컨벤션.
68
+ CREATE OR REPLACE FUNCTION semicolony.fn_commitment_pattern_health_updated_at()
69
+ RETURNS TRIGGER AS $$
70
+ BEGIN
71
+ NEW.updated_at := NOW();
72
+ RETURN NEW;
73
+ END;
74
+ $$ LANGUAGE plpgsql;
75
+
76
+ DROP TRIGGER IF EXISTS trg_commitment_pattern_health_updated
77
+ ON semicolony.commitment_pattern_health;
78
+
79
+ CREATE TRIGGER trg_commitment_pattern_health_updated
80
+ BEFORE UPDATE ON semicolony.commitment_pattern_health
81
+ FOR EACH ROW EXECUTE FUNCTION semicolony.fn_commitment_pattern_health_updated_at();
@@ -0,0 +1,33 @@
1
+ -- 117_commitment_pattern_alert_claims.sql
2
+ -- 2026-05-06: PR2 of Ouroboros C3 — alert claim 컬럼 추가.
3
+ --
4
+ -- PR1 은 state 전환을 추적했지만 (notified/paged), 동시 실패 호출 시 두 caller
5
+ -- 모두 state_changed=true 를 관측할 수 있다 (CTE old 가 pre-lock 값을 읽음).
6
+ -- 알림 부수효과는 race-safe 가 아니라는 뜻이다.
7
+ --
8
+ -- PR2 는 DB-backed claim 으로 idempotency 를 잡는다:
9
+ -- - notified_at: 'notified' 상태 진입 후 첫 성공한 claim 시각.
10
+ -- - paged_at: 'paged' 상태 진입 후 첫 성공한 claim 시각.
11
+ -- claim 패턴: UPDATE … SET notified_at = NOW() WHERE pattern_id = $1
12
+ -- AND state = 'notified' AND notified_at IS NULL RETURNING …;
13
+ -- 첫 caller 만 row 를 받고 (rowCount=1), 나머지는 0 — 이게 알림 발송 권한.
14
+ --
15
+ -- success 시 (state → 'none') 두 컬럼 모두 NULL 로 reset 해서, 다음 회복-실패
16
+ -- 사이클에서 새 claim 가능.
17
+
18
+ ALTER TABLE semicolony.commitment_pattern_health
19
+ ADD COLUMN IF NOT EXISTS notified_at TIMESTAMPTZ,
20
+ ADD COLUMN IF NOT EXISTS paged_at TIMESTAMPTZ;
21
+
22
+ COMMENT ON COLUMN semicolony.commitment_pattern_health.notified_at IS
23
+ '"notified" 상태 진입 후 첫 알림 claim 시각. 알림 dedup 용 — '
24
+ 'UPDATE … WHERE notified_at IS NULL RETURNING 으로 atomic 발송 권한 결정.';
25
+
26
+ COMMENT ON COLUMN semicolony.commitment_pattern_health.paged_at IS
27
+ '"paged" 상태 진입 후 첫 알림 claim 시각. notified_at 와 동일한 dedup 패턴.';
28
+
29
+ -- 미발송 alert 빠르게 찾기.
30
+ CREATE INDEX IF NOT EXISTS idx_commitment_pattern_health_pending_alert
31
+ ON semicolony.commitment_pattern_health (state, last_failure_at DESC)
32
+ WHERE (state = 'notified' AND notified_at IS NULL)
33
+ OR (state = 'paged' AND paged_at IS NULL);
@@ -0,0 +1,30 @@
1
+ -- 118_retire_semiclaw_semicolonybot_alias
2
+ --
3
+ -- 2026-04-23 의 migration 104 가 'semiclaw → semicolonybot' 리브랜드 1단계로 alias 를 깔았다.
4
+ -- 그러나 2026-05-06 의사결정(KB: semicolony decision/semicolonybot-independent-agent-2026-05-06)으로
5
+ -- SemiColonyBot 은 SemiClaw 의 후계가 아니라 별 system_guide 에이전트로 분리되었다.
6
+ --
7
+ -- 현재 alias 가 살아 있는 동안에는:
8
+ -- * channel-router 의 모든 'semiclaw' 입력(직접 [Route: semiclaw] / 하드코딩된
9
+ -- orchestrator 폴백 / phase 폴백 등)이 'semicolonybot' 으로 재라우팅된다.
10
+ -- * SemiColonyBot 의 runtime_source 는 'slack-router-system' (persona only, inbox 없음).
11
+ -- * 결과적으로 orchestrator 트래픽이 처리 불능 상태로 빠지는 잠재적 버그.
12
+ --
13
+ -- 본 마이그레이션은 alias 를 retire(soft delete) 한다. 향후 다시 켜고 싶다면
14
+ -- retired_at = NULL 로 되돌리면 된다. 'orphan' 로 보존되는 row 자체는 삭제하지
15
+ -- 않는다 (감사용).
16
+ --
17
+ -- 별 후속 정리 항목 (이 PR 범위 밖):
18
+ -- * SemiColonyBot bot_status row 의 role 을 'orchestrator' → 'system_management' 로 정리
19
+ -- * SemiColonyBot 의 bot_delegation 행들(SemiClaw 복제) 정리 — persona only 이므로 위임 불필요
20
+ -- * SemiColonyBot 의 seat_id (snamanager0) 가 다른 봇에 할당되지 않은 sanity check
21
+
22
+ BEGIN;
23
+
24
+ UPDATE semicolony.bot_id_aliases
25
+ SET retired_at = NOW()
26
+ WHERE alias = 'semiclaw'
27
+ AND canonical_bot_id = 'semicolonybot'
28
+ AND retired_at IS NULL;
29
+
30
+ COMMIT;
@@ -0,0 +1,51 @@
1
+ -- 119_commitment_events
2
+ --
3
+ -- Ouroboros C5 — Event Replay (dual-write phase).
4
+ -- 자세한 설계: /tmp/ouroboros-sandbox/evidence/c5_schema_draft.md (action item 4b406bbd)
5
+ -- · taxonomy: 8 종 — commitment_created/status_changed/heartbeat/step_done/
6
+ -- claimed/released/stale_reaped/cron_run_recorded
7
+ -- · idempotency_key UNIQUE — 같은 이벤트 재기록 방지 (재시도 흡수)
8
+ -- · transaction boundary: EscalationQueryable 패턴 (caller 의 client 받아 같이 commit/rollback)
9
+ -- · projector acceptance: 7일 dual-write 동안 divergence=0 이면 read source flip 검토
10
+ -- · write failure policy: best-effort, 원본 mutation 차단하지 않음
11
+ --
12
+ -- 이 마이그레이션은 *append-only* 이벤트 로그를 만들 뿐이며, 기존 bot_commitments
13
+ -- 의 read 경로는 건드리지 않는다. dual-write phase 동안에는 bot_commitments 가
14
+ -- SoT, commitment_events 는 audit/replay 용 redundant write.
15
+
16
+ BEGIN;
17
+
18
+ CREATE TABLE IF NOT EXISTS semicolony.commitment_events (
19
+ event_id BIGSERIAL PRIMARY KEY,
20
+ commitment_id TEXT NOT NULL,
21
+ event_type TEXT NOT NULL CHECK (event_type IN (
22
+ 'commitment_created', 'status_changed', 'heartbeat', 'step_done',
23
+ 'claimed', 'released', 'stale_reaped', 'cron_run_recorded'
24
+ )),
25
+ payload JSONB NOT NULL DEFAULT '{}'::jsonb,
26
+ occurred_at TIMESTAMPTZ NOT NULL,
27
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
28
+ bot_id TEXT,
29
+ source_type TEXT,
30
+ runtime_source TEXT,
31
+ idempotency_key TEXT NOT NULL,
32
+ UNIQUE (idempotency_key)
33
+ );
34
+
35
+ CREATE INDEX IF NOT EXISTS idx_commitment_events_by_commitment_time
36
+ ON semicolony.commitment_events (commitment_id, occurred_at);
37
+
38
+ CREATE INDEX IF NOT EXISTS idx_commitment_events_recent
39
+ ON semicolony.commitment_events (recorded_at DESC);
40
+
41
+ CREATE INDEX IF NOT EXISTS idx_commitment_events_by_bot_recent
42
+ ON semicolony.commitment_events (bot_id, recorded_at DESC)
43
+ WHERE bot_id IS NOT NULL;
44
+
45
+ COMMENT ON TABLE semicolony.commitment_events IS
46
+ 'Append-only event log for bot_commitments lifecycle (Ouroboros C5 dual-write phase, migration 119, 2026-05-06). '
47
+ 'During dual-write phase, bot_commitments remains read source. After 7+ days '
48
+ 'with zero replay divergences, read source flips to events table (separate PR). '
49
+ 'Write failures are logged but do not block the original mutation.';
50
+
51
+ COMMIT;
@@ -0,0 +1,51 @@
1
+ -- 120_host_signals
2
+ --
3
+ -- BE-1 v2 — host filesystem signal snapshots for /api/system/health.
4
+ -- 액션아이템 3ac42503 의 후속 인프라.
5
+ --
6
+ -- 배경:
7
+ -- semicolony-dashboard (Next.js) 는 OKE Docker pod 안에서 동작 → host 의 pgrep / PID 파일 /
8
+ -- ~/.openclaw-{bot}/auth-profiles.json / log 파일에 직접 접근 불가.
9
+ --
10
+ -- 따라서 v2 는 push 모델로 동작:
11
+ -- 1. host(=Reus Mac mini) 에서 동작하는 sidecar daemon (별 트랙) 이 pgrep/PID/auth
12
+ -- 파일을 주기적으로 스캔
13
+ -- 2. 결과 스냅샷을 이 테이블에 UPSERT
14
+ -- 3. dashboard /api/system/health 는 이 테이블을 읽어 신선한 (recorded_at >
15
+ -- NOW() - 5min) 행만 client 응답에 포함
16
+ --
17
+ -- v1 (DB-only) 와 호환: 이 테이블이 비어있어도 API 는 정상 동작 (host_signals=null/empty).
18
+
19
+ BEGIN;
20
+
21
+ CREATE TABLE IF NOT EXISTS semicolony.host_signals (
22
+ id BIGSERIAL PRIMARY KEY,
23
+ source_host TEXT NOT NULL, -- 예: 'reus-mac-mini' — sidecar self-id
24
+ signal_type TEXT NOT NULL CHECK (signal_type IN (
25
+ 'process', -- pgrep 결과 (slack-router/discord-router/openclaw-gateway)
26
+ 'pid_file', -- ~/.semicolony/run/*.pid 살아있는 PID
27
+ 'auth_profile', -- auth-profiles.json expires + token 형식
28
+ 'log_grep', -- 로그 패턴 매칭 (Slack connected/socket failed 등)
29
+ 'ancestry' -- ps PPID/TTY 검증 (cmux ancestry 가드와 동일)
30
+ )),
31
+ target_id TEXT NOT NULL, -- bot_id 또는 process 이름 (예: 'slack-router', 'planclaw')
32
+ status TEXT NOT NULL CHECK (status IN ('ok', 'degraded', 'fail', 'expired', 'unknown')),
33
+ payload JSONB NOT NULL DEFAULT '{}'::jsonb, -- pid, ppid, tty, expires_at, log_tail 등 구체적인 값
34
+ observed_at TIMESTAMPTZ NOT NULL, -- sidecar 가 관측한 실제 시점
35
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
36
+ expires_at TIMESTAMPTZ, -- 명시적 TTL — 없으면 freshness 만 사용
37
+ UNIQUE (source_host, signal_type, target_id)
38
+ );
39
+
40
+ CREATE INDEX IF NOT EXISTS idx_host_signals_recent
41
+ ON semicolony.host_signals (recorded_at DESC);
42
+
43
+ CREATE INDEX IF NOT EXISTS idx_host_signals_target
44
+ ON semicolony.host_signals (target_id, signal_type, recorded_at DESC);
45
+
46
+ COMMENT ON TABLE semicolony.host_signals IS
47
+ 'Host-level filesystem/process signals pushed by sidecar daemons. '
48
+ 'semicolony-dashboard reads only — write path is sidecar (host-resident) '
49
+ 'because dashboard pod has no host fs/process access. Migration 120, 2026-05-07.';
50
+
51
+ COMMIT;
@@ -0,0 +1,68 @@
1
+ -- 121_agent_spec_derived_runtime_metadata
2
+ --
3
+ -- AgentSpec projection metadata for derived/helper bots.
4
+ --
5
+ -- derived_from:
6
+ -- Canonical source bot for derived runtime sessions such as semiclaw-overflow.
7
+ --
8
+ -- reply_as:
9
+ -- Persona/bot id used when a derived runtime should reply as another bot.
10
+ -- Example: semiclaw-overflow has bot_id=semiclaw-overflow but reply_as=semiclaw.
11
+
12
+ BEGIN;
13
+
14
+ ALTER TABLE semicolony.bot_status
15
+ ADD COLUMN IF NOT EXISTS derived_from TEXT REFERENCES semicolony.bot_status(bot_id),
16
+ ADD COLUMN IF NOT EXISTS reply_as TEXT REFERENCES semicolony.bot_status(bot_id);
17
+
18
+ COMMENT ON COLUMN semicolony.bot_status.derived_from IS
19
+ 'Source bot id for derived/helper runtime sessions rendered through AgentSpec.';
20
+
21
+ COMMENT ON COLUMN semicolony.bot_status.reply_as IS
22
+ 'Bot/persona id to use when a derived runtime replies externally.';
23
+
24
+ INSERT INTO semicolony.bot_status (
25
+ bot_id,
26
+ name,
27
+ emoji,
28
+ role,
29
+ workspace_path,
30
+ status,
31
+ kb_domains,
32
+ budget_per_message,
33
+ slack_username,
34
+ slack_icon_emoji,
35
+ created_by_bot_id,
36
+ template_bot_id,
37
+ runtime_hint,
38
+ tool_capabilities,
39
+ projection_targets,
40
+ derived_from,
41
+ reply_as
42
+ )
43
+ SELECT
44
+ 'semiclaw-overflow',
45
+ 'SemiClaw Overflow',
46
+ emoji,
47
+ 'Derived overflow worker for SemiClaw',
48
+ REPLACE(workspace_path, 'semiclaw', 'semiclaw-overflow'),
49
+ status,
50
+ kb_domains,
51
+ budget_per_message,
52
+ slack_username,
53
+ slack_icon_emoji,
54
+ '__genesis__',
55
+ 'semiclaw',
56
+ runtime_hint,
57
+ tool_capabilities,
58
+ projection_targets,
59
+ 'semiclaw',
60
+ 'semiclaw'
61
+ FROM semicolony.bot_status
62
+ WHERE bot_id = 'semiclaw'
63
+ ON CONFLICT (bot_id) DO UPDATE SET
64
+ derived_from = EXCLUDED.derived_from,
65
+ reply_as = EXCLUDED.reply_as,
66
+ template_bot_id = COALESCE(semicolony.bot_status.template_bot_id, EXCLUDED.template_bot_id);
67
+
68
+ COMMIT;
@@ -0,0 +1,18 @@
1
+ -- 122_agent_spec_derived_fk_policy
2
+ --
3
+ -- Derived/helper bot references must not block retirement/deletion of a source bot.
4
+ -- If the source disappears, the derived runtime falls back to its own identity.
5
+
6
+ BEGIN;
7
+
8
+ ALTER TABLE semicolony.bot_status
9
+ DROP CONSTRAINT IF EXISTS bot_status_derived_from_fkey,
10
+ DROP CONSTRAINT IF EXISTS bot_status_reply_as_fkey;
11
+
12
+ ALTER TABLE semicolony.bot_status
13
+ ADD CONSTRAINT bot_status_derived_from_fkey
14
+ FOREIGN KEY (derived_from) REFERENCES semicolony.bot_status(bot_id) ON DELETE SET NULL,
15
+ ADD CONSTRAINT bot_status_reply_as_fkey
16
+ FOREIGN KEY (reply_as) REFERENCES semicolony.bot_status(bot_id) ON DELETE SET NULL;
17
+
18
+ COMMIT;
@@ -0,0 +1,24 @@
1
+ -- 123_agent_spec_projection_skip_targets
2
+ --
3
+ -- Some bot_status rows are helper/runtime support identities, not full projected
4
+ -- OpenClaw/ClaudeCode personas. Keep the policy in bot_status instead of
5
+ -- hardcoding helper names in renderers.
6
+
7
+ BEGIN;
8
+
9
+ ALTER TABLE semicolony.bot_status
10
+ ADD COLUMN IF NOT EXISTS is_helper BOOLEAN NOT NULL DEFAULT FALSE,
11
+ ADD COLUMN IF NOT EXISTS skip_projection_targets TEXT[] NOT NULL DEFAULT '{}';
12
+
13
+ COMMENT ON COLUMN semicolony.bot_status.is_helper IS
14
+ 'True for helper/support bot identities that should not be treated as full projected personas.';
15
+
16
+ COMMENT ON COLUMN semicolony.bot_status.skip_projection_targets IS
17
+ 'AgentSpec runtime targets to skip for this bot_id. Values: claude-code, codex-skill, openclaw.';
18
+
19
+ UPDATE semicolony.bot_status
20
+ SET is_helper = TRUE,
21
+ skip_projection_targets = ARRAY['openclaw']
22
+ WHERE bot_id = 'kb-sidekick';
23
+
24
+ COMMIT;
@@ -0,0 +1,26 @@
1
+ -- P2-C (2026-05-28): bot_commitments 변경 NOTIFY trigger
2
+ -- Dashboard 의 /api/bots/stream SSE handler 가 LISTEN 'semicolony_commitment_change' 로 받음.
3
+ -- Payload: { id, bot_id, status, runtime_source, source_type }
4
+
5
+ CREATE OR REPLACE FUNCTION semicolony.notify_bot_commitment_change()
6
+ RETURNS TRIGGER AS $$
7
+ DECLARE
8
+ payload JSON;
9
+ BEGIN
10
+ payload := json_build_object(
11
+ 'op', TG_OP,
12
+ 'id', COALESCE(NEW.id, OLD.id),
13
+ 'bot_id', COALESCE(NEW.bot_id, OLD.bot_id),
14
+ 'status', COALESCE(NEW.status, OLD.status),
15
+ 'runtime_source', COALESCE(NEW.runtime_source, OLD.runtime_source),
16
+ 'source_type', COALESCE(NEW.source_type, OLD.source_type)
17
+ );
18
+ PERFORM pg_notify('semicolony_commitment_change', payload::text);
19
+ RETURN COALESCE(NEW, OLD);
20
+ END;
21
+ $$ LANGUAGE plpgsql;
22
+
23
+ DROP TRIGGER IF EXISTS trig_bot_commitments_notify ON semicolony.bot_commitments;
24
+ CREATE TRIGGER trig_bot_commitments_notify
25
+ AFTER INSERT OR UPDATE OR DELETE ON semicolony.bot_commitments
26
+ FOR EACH ROW EXECUTE FUNCTION semicolony.notify_bot_commitment_change();