@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.
- package/LICENSE +200 -0
- package/NOTICE +8 -0
- package/README.md +152 -0
- package/dist/bundle.js +2887 -0
- package/dist/templates/launchd/ai.semicolony.host-signals-sidecar.plist +40 -0
- package/migrations/001_initial.sql +280 -0
- package/migrations/002_add_indexes.sql +11 -0
- package/migrations/003_add_fk.sql +32 -0
- package/migrations/004_session_count_trigger.sql +35 -0
- package/migrations/005_bot_workspace_audits.sql +16 -0
- package/migrations/006_bot_kb_subscriptions.sql +24 -0
- package/migrations/007_flatten_skill_names.sql +16 -0
- package/migrations/008_bot_workspace_files.sql +14 -0
- package/migrations/009_enforce_flat_skill_names.sql +53 -0
- package/migrations/010_bot_delegation.sql +27 -0
- package/migrations/011_drop_legacy_tables.sql +35 -0
- package/migrations/012_drop_bot_knowledge.sql +16 -0
- package/migrations/013_kb_workspace_domains.sql +88 -0
- package/migrations/014_timestamp_to_timestamptz.sql +33 -0
- package/migrations/015_kb_domain_enforcement.sql +83 -0
- package/migrations/016_ontology_redesign.sql +108 -0
- package/migrations/017_ontology_instance_model.sql +151 -0
- package/migrations/018_type_schema.sql +11 -0
- package/migrations/019_test_management.sql +69 -0
- package/migrations/020_bot_workspace_standard.sql +174 -0
- package/migrations/021_drop_dead_tables.sql +8 -0
- package/migrations/021_test_declarative.sql +19 -0
- package/migrations/022_bot_ontology_type.sql +38 -0
- package/migrations/022_kb_schema_enforcement.sql +77 -0
- package/migrations/023_decision_key_cleanup.ts +154 -0
- package/migrations/024_embedding_backfill.ts +137 -0
- package/migrations/025_kb_flat_keys.sql +33 -0
- package/migrations/026_drop_session_log.sql +19 -0
- package/migrations/027_kb_manager_skill.sql +22 -0
- package/migrations/028_skill_definitions_to_semicolony.sql +19 -0
- package/migrations/029_agents_citation_max_lines.sql +6 -0
- package/migrations/030_team_ontology.sql +54 -0
- package/migrations/031_gfp_tables.sql +101 -0
- package/migrations/031_kb_manager_domain_register.sql +12 -0
- package/migrations/032_gfp_skill_and_callback.sql +40 -0
- package/migrations/032_kebab_case_keys.sql +33 -0
- package/migrations/033_skill_workspace_rules.sql +10 -0
- package/migrations/034_current_to_date_keys.sql +13 -0
- package/migrations/034_current_to_date_keys.ts +104 -0
- package/migrations/035_bot_budgets.sql +51 -0
- package/migrations/036_gfp_stitch_design.sql +35 -0
- package/migrations/037_gfp_qa_sections.sql +13 -0
- package/migrations/038_gfp_phase_split.sql +26 -0
- package/migrations/039_meeting_audio_data.sql +2 -0
- package/migrations/040_bot_commitments.sql +68 -0
- package/migrations/041_palette_previews.sql +13 -0
- package/migrations/042_gfp_parallel_tracks.sql +37 -0
- package/migrations/043_blocker_key.sql +13 -0
- package/migrations/044_gfp_stitch_screenshots.sql +9 -0
- package/migrations/045_service_table_rename_and_projection.sql +150 -0
- package/migrations/046_service_ops_tables.sql +75 -0
- package/migrations/047_update_gfp_manager_skill.sql +16 -0
- package/migrations/048_service_migration_skill.sql +23 -0
- package/migrations/049_service_features.sql +39 -0
- package/migrations/050_service_kpi_action_items.sql +87 -0
- package/migrations/051_update_service_migration_skill_prompt.sql +9 -0
- package/migrations/052_feature_iteration_link.sql +11 -0
- package/migrations/052_services_table_rename.sql +149 -0
- package/migrations/053_feature_discovery.sql +47 -0
- package/migrations/054_deploy_verification.sql +17 -0
- package/migrations/055_feature_lifecycle.sql +37 -0
- package/migrations/056_service_incident_schema.sql +16 -0
- package/migrations/057_decision_date_to_metadata.ts +173 -0
- package/migrations/058_incubator_kb_schema.sql +8 -0
- package/migrations/059_incubator_sessions.sql +29 -0
- package/migrations/060_skill_reference_files.sql +8 -0
- package/migrations/061_cron_trigger_bridge.sql +7 -0
- package/migrations/062_bot_slack_profiles.sql +21 -0
- package/migrations/063_rename_gfp_skills.sql +7 -0
- package/migrations/064_bot_cost_log.sql +11 -0
- package/migrations/065_hub_spoke_orchestration.sql +35 -0
- package/migrations/066_cost_log_commitment.sql +9 -0
- package/migrations/067_module_ontology_type.sql +44 -0
- package/migrations/068_migrate_incubator_data.sql +32 -0
- package/migrations/069_action_items_sot.sql +156 -0
- package/migrations/069_cost_log_tokens.sql +45 -0
- package/migrations/070_services_sot_columns.sql +78 -0
- package/migrations/071_cleanup_deprecated_service_kb.sql +38 -0
- package/migrations/072_incubator_heartbeat.sql +12 -0
- package/migrations/072_service_role_schema.sql +57 -0
- package/migrations/073_service_type_and_platform.sql +8 -0
- package/migrations/074_schema_ref_type.sql +16 -0
- package/migrations/075_routing_delegation.sql +34 -0
- package/migrations/076_meeting_service_link.sql +5 -0
- package/migrations/077_agents_ontology_type.sql +93 -0
- package/migrations/078_incubator_agent.sql +45 -0
- package/migrations/079_drop_audio_blob_url.sql +4 -0
- package/migrations/080_kb_metadata_indexing.sql +9 -0
- package/migrations/081_person_unification.sql +41 -0
- package/migrations/082_discord_channel.sql +3 -0
- package/migrations/084_deprecate_kpi_metrics_table.sql +5 -0
- package/migrations/085_meeting_target_domain.sql +37 -0
- package/migrations/086_skill_routing_new_skills.sql +23 -0
- package/migrations/087_skill_routing_second_opinion.sql +8 -0
- package/migrations/088_meeting_notion_sync.sql +10 -0
- package/migrations/089_bot_commitments_dedup.sql +17 -0
- package/migrations/090_incubator_discord_guild.sql +8 -0
- package/migrations/091_drop_deprecated_gfp_kpi_views.sql +65 -0
- package/migrations/092_ontology_channel_mapping.sql +28 -0
- package/migrations/093_update_bot_workspace_paths.sql +15 -0
- package/migrations/094_cron_run_tracking.sql +28 -0
- package/migrations/095_drop_bot_cron_runs.sql +7 -0
- package/migrations/096_kb_pipeline_keys.sql +66 -0
- package/migrations/097_drop_empty_service_tables.sql +83 -0
- package/migrations/098_migrate_features_materials_drop.sql +119 -0
- package/migrations/099_migrate_sections_drop.sql +52 -0
- package/migrations/099_seed_cron_poller_tick.sql +14 -0
- package/migrations/100_migrate_services_drop.sql +57 -0
- package/migrations/101_drop_services.sql +5 -0
- package/migrations/102_cost_log_session_dedup.sql +13 -0
- package/migrations/103_board.sql +57 -0
- package/migrations/104_agent_factory.sql +284 -0
- package/migrations/105_kb_notify.sql +44 -0
- package/migrations/106_office_id_agents_commands.sql +73 -0
- package/migrations/107_agent_service_credentials.sql +79 -0
- package/migrations/108_drop_l2_ontology_seeds.sql +16 -0
- package/migrations/109_generic_ontology_descriptions.sql +21 -0
- package/migrations/110_kb_manager_skill_l2_cleanup.sql +34 -0
- package/migrations/111_drop_l2_team_ontology_seeds.sql +20 -0
- package/migrations/112_fix_pipeline_schema_and_role_trigger.sql +83 -0
- package/migrations/113_kb_history.sql +53 -0
- package/migrations/114_runtime_target_columns.sql +26 -0
- package/migrations/115_runtime_source_columns.sql +52 -0
- package/migrations/116_commitment_pattern_health.sql +81 -0
- package/migrations/117_commitment_pattern_alert_claims.sql +33 -0
- package/migrations/118_retire_semiclaw_semicolonybot_alias.sql +30 -0
- package/migrations/119_commitment_events.sql +51 -0
- package/migrations/120_host_signals.sql +51 -0
- package/migrations/121_agent_spec_derived_runtime_metadata.sql +68 -0
- package/migrations/122_agent_spec_derived_fk_policy.sql +18 -0
- package/migrations/123_agent_spec_projection_skip_targets.sql +24 -0
- package/migrations/124_bot_commitments_notify.sql +26 -0
- package/migrations/125_agent_personas.sql +36 -0
- package/migrations/128_entity_relations.sql +125 -0
- package/migrations/129_kb_multitenant_gateway.sql +86 -0
- package/migrations/130_jewelry_relation_types.sql +36 -0
- package/migrations/131_concierge_requests.sql +38 -0
- package/migrations/132_kb_supersession.sql +27 -0
- package/migrations/133_kb_claims.sql +52 -0
- package/migrations/134_migration_registry.sql +18 -0
- package/migrations/135_sc_roles.sql +102 -0
- package/migrations/136_runtime_tenant_columns.sql +71 -0
- package/migrations/137_runtime_backfill_team_semicolon.sql +131 -0
- package/migrations/138_runtime_constraints_not_valid.sql +56 -0
- package/migrations/139_tenant_vms.sql +31 -0
- package/migrations/140_fleet_slots.sql +23 -0
- package/migrations/141_provisioning_jobs.sql +29 -0
- package/migrations/142_rls_enable_semicolony.sql +129 -0
- package/migrations/143_rls_enable_public.sql +54 -0
- package/migrations/144_worker_device_registry.sql +211 -0
- package/migrations/145_workspace_profile_workstation_policy.sql +39 -0
- package/migrations/146_worker_profile_repo_scope.sql +24 -0
- package/migrations/147_worker_profile_semo_ops_repo_rename.sql +18 -0
- package/migrations/148_momo_runtime_convergence.sql +92 -0
- package/migrations/README.md +88 -0
- package/migrations-sqlite/kb/001_initial.sql +56 -0
- package/migrations-sqlite/ops/001_initial.sql +53 -0
- package/migrations-sqlite/ops/002_action_items.sql +48 -0
- package/package.json +78 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
-- 100: Phase 4 — services 허브 테이블 (17건) KB 이식 + DROP
|
|
2
|
+
-- 코드는 이미 KB pipeline/config 기반으로 전환됨
|
|
3
|
+
|
|
4
|
+
-- 1. services → KB pipeline/config
|
|
5
|
+
-- drift 복구: 이미 DROP 된 소스 테이블은 to_regclass 가드로 skip (2026-04-23)
|
|
6
|
+
DO $$
|
|
7
|
+
DECLARE
|
|
8
|
+
rec RECORD;
|
|
9
|
+
BEGIN
|
|
10
|
+
IF to_regclass('semicolony.services') IS NULL THEN
|
|
11
|
+
RAISE NOTICE '100-1 skipped: services already dropped';
|
|
12
|
+
RETURN;
|
|
13
|
+
END IF;
|
|
14
|
+
FOR rec IN SELECT * FROM semicolony.services WHERE service_domain IS NOT NULL LOOP
|
|
15
|
+
INSERT INTO semicolony.knowledge_base (domain, key, sub_key, content, created_by, metadata)
|
|
16
|
+
VALUES (
|
|
17
|
+
rec.service_domain, 'pipeline', 'config',
|
|
18
|
+
COALESCE(rec.project_name, ''),
|
|
19
|
+
'migration-100',
|
|
20
|
+
jsonb_build_object(
|
|
21
|
+
'service_id', rec.service_id,
|
|
22
|
+
'project_name', COALESCE(rec.project_name, ''),
|
|
23
|
+
'owner_name', COALESCE(rec.owner_name, ''),
|
|
24
|
+
'owner_contact', rec.owner_contact,
|
|
25
|
+
'current_phase', COALESCE(rec.current_phase, 0),
|
|
26
|
+
'infra_phase', rec.infra_phase,
|
|
27
|
+
'status', COALESCE(rec.status, 'active'),
|
|
28
|
+
'lifecycle', COALESCE(rec.lifecycle, 'build'),
|
|
29
|
+
'service_type', COALESCE(rec.service_type, 'incubator'),
|
|
30
|
+
'parent_service_id', rec.parent_service_id,
|
|
31
|
+
'tech_stack', rec.tech_stack,
|
|
32
|
+
'service_url', rec.service_url,
|
|
33
|
+
'bm', rec.bm,
|
|
34
|
+
'repo', rec.repo,
|
|
35
|
+
'slack_channel', rec.slack_channel,
|
|
36
|
+
'launched_at', rec.launched_at::text,
|
|
37
|
+
'project_metadata', COALESCE(rec.metadata, '{}'::jsonb),
|
|
38
|
+
'created_at', rec.created_at::text
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
ON CONFLICT (domain, key, sub_key) DO UPDATE SET
|
|
42
|
+
content = EXCLUDED.content,
|
|
43
|
+
metadata = EXCLUDED.metadata,
|
|
44
|
+
updated_at = NOW();
|
|
45
|
+
END LOOP;
|
|
46
|
+
END $$;
|
|
47
|
+
|
|
48
|
+
-- 2. 레거시 VIEW DROP
|
|
49
|
+
DROP VIEW IF EXISTS semicolony.service_projects CASCADE;
|
|
50
|
+
DROP VIEW IF EXISTS semicolony.gfp_projects CASCADE;
|
|
51
|
+
DROP VIEW IF EXISTS semicolony.gfp_phase_sections CASCADE;
|
|
52
|
+
DROP VIEW IF EXISTS semicolony.gfp_materials CASCADE;
|
|
53
|
+
DROP VIEW IF EXISTS semicolony.gfp_research_tasks CASCADE;
|
|
54
|
+
DROP VIEW IF EXISTS semicolony.gfp_infra_requests CASCADE;
|
|
55
|
+
|
|
56
|
+
-- 3. services 테이블 DROP → 101_drop_services.sql로 분리
|
|
57
|
+
-- sandbox.ts 에서 semicolony.services 참조(8곳)가 KB 전환 완료된 후 실행
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- 102: bot_cost_log에 세션/메시지 ID 추가 — Stop 훅 기반 턴별 토큰 추적 dedup용
|
|
2
|
+
|
|
3
|
+
BEGIN;
|
|
4
|
+
|
|
5
|
+
ALTER TABLE semicolony.bot_cost_log
|
|
6
|
+
ADD COLUMN IF NOT EXISTS session_id TEXT,
|
|
7
|
+
ADD COLUMN IF NOT EXISTS message_id TEXT;
|
|
8
|
+
|
|
9
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_cost_log_dedup
|
|
10
|
+
ON semicolony.bot_cost_log (session_id, message_id)
|
|
11
|
+
WHERE message_id IS NOT NULL;
|
|
12
|
+
|
|
13
|
+
COMMIT;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
-- 103: 파일 게시판 (자료실) — Dashboard CRUD + Introduction 공개 조회
|
|
2
|
+
-- 권한 enforce는 API 레이어에서 수행 (pg 직접 접근, RLS 미사용)
|
|
3
|
+
-- 파일 바이너리는 bytea로 저장 (meetings.audio_data 패턴 재사용)
|
|
4
|
+
|
|
5
|
+
BEGIN;
|
|
6
|
+
|
|
7
|
+
CREATE TABLE IF NOT EXISTS semicolony.board_categories (
|
|
8
|
+
slug TEXT PRIMARY KEY,
|
|
9
|
+
label TEXT NOT NULL,
|
|
10
|
+
sort_order INT DEFAULT 0,
|
|
11
|
+
is_public_allowed BOOLEAN NOT NULL DEFAULT TRUE,
|
|
12
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE TABLE IF NOT EXISTS semicolony.board_posts (
|
|
16
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
17
|
+
title TEXT NOT NULL,
|
|
18
|
+
description TEXT,
|
|
19
|
+
category_slug TEXT NOT NULL REFERENCES semicolony.board_categories(slug),
|
|
20
|
+
uploader_id UUID,
|
|
21
|
+
uploader_email TEXT,
|
|
22
|
+
uploader_name_snapshot TEXT,
|
|
23
|
+
is_public BOOLEAN NOT NULL DEFAULT FALSE,
|
|
24
|
+
is_hidden BOOLEAN NOT NULL DEFAULT FALSE,
|
|
25
|
+
view_count INT NOT NULL DEFAULT 0,
|
|
26
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
27
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE TABLE IF NOT EXISTS semicolony.board_attachments (
|
|
31
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
32
|
+
post_id UUID NOT NULL REFERENCES semicolony.board_posts(id) ON DELETE CASCADE,
|
|
33
|
+
file_name TEXT NOT NULL,
|
|
34
|
+
mime_type TEXT NOT NULL,
|
|
35
|
+
size_bytes BIGINT NOT NULL,
|
|
36
|
+
file_data BYTEA NOT NULL,
|
|
37
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_board_posts_public
|
|
41
|
+
ON semicolony.board_posts (is_public, created_at DESC)
|
|
42
|
+
WHERE is_hidden = FALSE;
|
|
43
|
+
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_board_posts_category
|
|
45
|
+
ON semicolony.board_posts (category_slug, created_at DESC);
|
|
46
|
+
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_board_attachments_post
|
|
48
|
+
ON semicolony.board_attachments (post_id);
|
|
49
|
+
|
|
50
|
+
-- 시드 카테고리
|
|
51
|
+
INSERT INTO semicolony.board_categories (slug, label, sort_order, is_public_allowed) VALUES
|
|
52
|
+
('materials', '자료/문서', 10, TRUE),
|
|
53
|
+
('press', '보도자료/외부 공개', 20, TRUE),
|
|
54
|
+
('recruiting', '채용/회사 소개', 30, TRUE)
|
|
55
|
+
ON CONFLICT (slug) DO NOTHING;
|
|
56
|
+
|
|
57
|
+
COMMIT;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
-- 104: Agent Factory 기반 마련
|
|
2
|
+
-- 1) bot_status에 Factory 메타 컬럼 추가 (slack/kb/budget/template 하드코딩 제거 준비)
|
|
3
|
+
-- 2) bot_id_aliases: 불변 bot_id + 표시명 변경을 위한 별칭 테이블
|
|
4
|
+
-- 3) bot_seats: Claude Max seat 물리/논리 분리 (런타임 동적 할당)
|
|
5
|
+
-- 4) semiclaw → semicolonybot 리브랜드 1단계 (새 row + alias 활성화)
|
|
6
|
+
-- 5) NOTIFY 트리거: bot_status / bot_delegation / bot_id_aliases 변경 시 핫 리로드 시그널
|
|
7
|
+
|
|
8
|
+
BEGIN;
|
|
9
|
+
|
|
10
|
+
-- ============================================================
|
|
11
|
+
-- 1. bot_status 컬럼 확장
|
|
12
|
+
-- ============================================================
|
|
13
|
+
|
|
14
|
+
ALTER TABLE semicolony.bot_status
|
|
15
|
+
ADD COLUMN IF NOT EXISTS kb_domains TEXT[],
|
|
16
|
+
ADD COLUMN IF NOT EXISTS budget_per_message NUMERIC(6,3),
|
|
17
|
+
ADD COLUMN IF NOT EXISTS slack_username TEXT,
|
|
18
|
+
ADD COLUMN IF NOT EXISTS slack_icon_emoji TEXT,
|
|
19
|
+
ADD COLUMN IF NOT EXISTS created_by_bot_id TEXT,
|
|
20
|
+
ADD COLUMN IF NOT EXISTS template_bot_id TEXT;
|
|
21
|
+
|
|
22
|
+
-- 백필 — 기존 하드코딩 맵(packages/common/src/slack/bot-config.ts)을 DB로 이식
|
|
23
|
+
-- kb_domains: orchestrator는 빈 배열(전체 허용), 나머지는 명시적 리스트
|
|
24
|
+
-- budget_per_message: 봇별 기본 예산
|
|
25
|
+
-- slack_username/icon_emoji: FALLBACK_SLACK_PROFILES
|
|
26
|
+
UPDATE semicolony.bot_status SET
|
|
27
|
+
kb_domains = '{}',
|
|
28
|
+
budget_per_message = 1.5,
|
|
29
|
+
slack_username = 'SemiClaw',
|
|
30
|
+
slack_icon_emoji = ':clipboard:',
|
|
31
|
+
created_by_bot_id = '__genesis__'
|
|
32
|
+
WHERE bot_id = 'semiclaw';
|
|
33
|
+
|
|
34
|
+
UPDATE semicolony.bot_status SET
|
|
35
|
+
kb_domains = ARRAY['semicolon','semicolony'],
|
|
36
|
+
budget_per_message = 1.0,
|
|
37
|
+
slack_username = 'PlanClaw',
|
|
38
|
+
slack_icon_emoji = ':bar_chart:',
|
|
39
|
+
created_by_bot_id = '__genesis__'
|
|
40
|
+
WHERE bot_id = 'planclaw';
|
|
41
|
+
|
|
42
|
+
UPDATE semicolony.bot_status SET
|
|
43
|
+
kb_domains = ARRAY['semicolon'],
|
|
44
|
+
budget_per_message = 1.0,
|
|
45
|
+
slack_username = 'DesignClaw',
|
|
46
|
+
slack_icon_emoji = ':art:',
|
|
47
|
+
created_by_bot_id = '__genesis__'
|
|
48
|
+
WHERE bot_id = 'designclaw';
|
|
49
|
+
|
|
50
|
+
UPDATE semicolony.bot_status SET
|
|
51
|
+
kb_domains = ARRAY['semicolon'],
|
|
52
|
+
budget_per_message = 1.5,
|
|
53
|
+
slack_username = 'WorkClaw',
|
|
54
|
+
slack_icon_emoji = ':hammer_and_wrench:',
|
|
55
|
+
created_by_bot_id = '__genesis__'
|
|
56
|
+
WHERE bot_id = 'workclaw';
|
|
57
|
+
|
|
58
|
+
UPDATE semicolony.bot_status SET
|
|
59
|
+
kb_domains = ARRAY['semicolon'],
|
|
60
|
+
budget_per_message = 0.8,
|
|
61
|
+
slack_username = 'ReviewClaw',
|
|
62
|
+
slack_icon_emoji = ':mag:',
|
|
63
|
+
created_by_bot_id = '__genesis__'
|
|
64
|
+
WHERE bot_id = 'reviewclaw';
|
|
65
|
+
|
|
66
|
+
UPDATE semicolony.bot_status SET
|
|
67
|
+
kb_domains = ARRAY['semicolon','semicolony'],
|
|
68
|
+
budget_per_message = 1.0,
|
|
69
|
+
slack_username = 'InfraClaw',
|
|
70
|
+
slack_icon_emoji = ':gear:',
|
|
71
|
+
created_by_bot_id = '__genesis__'
|
|
72
|
+
WHERE bot_id = 'infraclaw';
|
|
73
|
+
|
|
74
|
+
UPDATE semicolony.bot_status SET
|
|
75
|
+
kb_domains = ARRAY['semicolon'],
|
|
76
|
+
budget_per_message = 0.5,
|
|
77
|
+
slack_username = 'GrowthClaw',
|
|
78
|
+
slack_icon_emoji = ':chart_with_upwards_trend:',
|
|
79
|
+
created_by_bot_id = '__genesis__'
|
|
80
|
+
WHERE bot_id = 'growthclaw';
|
|
81
|
+
|
|
82
|
+
UPDATE semicolony.bot_status SET
|
|
83
|
+
kb_domains = ARRAY['semicolon'],
|
|
84
|
+
budget_per_message = 1.0,
|
|
85
|
+
slack_username = 'Incubator',
|
|
86
|
+
slack_icon_emoji = ':hatching_chick:',
|
|
87
|
+
created_by_bot_id = '__genesis__'
|
|
88
|
+
WHERE bot_id = 'incubator';
|
|
89
|
+
|
|
90
|
+
-- ============================================================
|
|
91
|
+
-- 2. bot_id_aliases — 표시명 변경 시 기존 bot_id 참조 보존
|
|
92
|
+
-- ============================================================
|
|
93
|
+
|
|
94
|
+
CREATE TABLE IF NOT EXISTS semicolony.bot_id_aliases (
|
|
95
|
+
alias TEXT PRIMARY KEY,
|
|
96
|
+
canonical_bot_id TEXT NOT NULL REFERENCES semicolony.bot_status(bot_id),
|
|
97
|
+
retired_at TIMESTAMPTZ,
|
|
98
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
CREATE INDEX IF NOT EXISTS idx_bot_aliases_canonical
|
|
102
|
+
ON semicolony.bot_id_aliases (canonical_bot_id);
|
|
103
|
+
|
|
104
|
+
-- ============================================================
|
|
105
|
+
-- 3. bot_seats — Claude Max seat 할당 (물리 seat ↔ 논리 bot)
|
|
106
|
+
-- ============================================================
|
|
107
|
+
|
|
108
|
+
CREATE TABLE IF NOT EXISTS semicolony.bot_seats (
|
|
109
|
+
seat_id TEXT PRIMARY KEY,
|
|
110
|
+
claude_config_dir TEXT NOT NULL,
|
|
111
|
+
max_concurrent INT NOT NULL DEFAULT 1,
|
|
112
|
+
current_bot_id TEXT REFERENCES semicolony.bot_status(bot_id),
|
|
113
|
+
allocated_at TIMESTAMPTZ,
|
|
114
|
+
status TEXT NOT NULL DEFAULT 'available'
|
|
115
|
+
CHECK (status IN ('available','allocated','quarantined')),
|
|
116
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
117
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
CREATE INDEX IF NOT EXISTS idx_bot_seats_status
|
|
121
|
+
ON semicolony.bot_seats (status);
|
|
122
|
+
|
|
123
|
+
CREATE INDEX IF NOT EXISTS idx_bot_seats_current_bot
|
|
124
|
+
ON semicolony.bot_seats (current_bot_id) WHERE current_bot_id IS NOT NULL;
|
|
125
|
+
|
|
126
|
+
-- ============================================================
|
|
127
|
+
-- 4. semiclaw → semicolonybot 리브랜드 1단계
|
|
128
|
+
-- ============================================================
|
|
129
|
+
|
|
130
|
+
-- 4.1 semicolonybot row 생성 (semiclaw를 템플릿으로 복사)
|
|
131
|
+
INSERT INTO semicolony.bot_status (
|
|
132
|
+
bot_id, name, emoji, role, workspace_path, status,
|
|
133
|
+
kb_domains, budget_per_message, slack_username, slack_icon_emoji,
|
|
134
|
+
created_by_bot_id, template_bot_id
|
|
135
|
+
)
|
|
136
|
+
SELECT
|
|
137
|
+
'semicolonybot', 'SemiColonyBot', ':robot_face:', 'orchestrator',
|
|
138
|
+
REPLACE(workspace_path, 'semiclaw', 'semicolonybot'),
|
|
139
|
+
status,
|
|
140
|
+
kb_domains, budget_per_message, 'SemiColonyBot', ':robot_face:',
|
|
141
|
+
'__genesis__', 'semiclaw'
|
|
142
|
+
FROM semicolony.bot_status WHERE bot_id = 'semiclaw'
|
|
143
|
+
ON CONFLICT (bot_id) DO NOTHING;
|
|
144
|
+
|
|
145
|
+
-- 4.2 alias 활성화 (semiclaw → semicolonybot)
|
|
146
|
+
INSERT INTO semicolony.bot_id_aliases (alias, canonical_bot_id)
|
|
147
|
+
VALUES ('semiclaw', 'semicolonybot')
|
|
148
|
+
ON CONFLICT (alias) DO NOTHING;
|
|
149
|
+
|
|
150
|
+
-- 4.3 semicolonybot agents 온톨로지 row (entity_type='agents')
|
|
151
|
+
INSERT INTO semicolony.ontology (domain, entity_type, schema, description)
|
|
152
|
+
VALUES ('semicolonybot', 'agents', '{}'::jsonb, 'SEMICOLONY 오케스트레이터 (semiclaw 후계)')
|
|
153
|
+
ON CONFLICT (domain) DO UPDATE SET
|
|
154
|
+
entity_type = EXCLUDED.entity_type,
|
|
155
|
+
description = EXCLUDED.description;
|
|
156
|
+
|
|
157
|
+
-- 4.4 semicolonybot KB 키 복사 (identity, delegation, model-config, status, slack-config 등)
|
|
158
|
+
INSERT INTO semicolony.knowledge_base (domain, key, sub_key, content, metadata, created_by)
|
|
159
|
+
SELECT
|
|
160
|
+
'semicolonybot', key, sub_key, content, metadata, 'migration-104'
|
|
161
|
+
FROM semicolony.knowledge_base
|
|
162
|
+
WHERE domain = 'semiclaw'
|
|
163
|
+
AND key IN ('identity','delegation','model-config','status','slack-config','slack-profile','cron-schedule','tools','skills','kb-access')
|
|
164
|
+
ON CONFLICT (domain, key, sub_key) DO NOTHING;
|
|
165
|
+
|
|
166
|
+
-- 4.5 identity 콘텐츠의 이름은 SemiColonyBot으로 덮어쓰기
|
|
167
|
+
UPDATE semicolony.knowledge_base
|
|
168
|
+
SET content = format(
|
|
169
|
+
E'name: %s\nemoji: %s\ntagline: %s\nrole: %s\nagent_type: %s',
|
|
170
|
+
'SemiColonyBot', ':robot_face:', 'SEMICOLONY 오케스트레이터', 'orchestrator', 'orchestrator'
|
|
171
|
+
), updated_at = NOW()
|
|
172
|
+
WHERE domain = 'semicolonybot' AND key = 'identity' AND sub_key = '';
|
|
173
|
+
|
|
174
|
+
-- 4.6 slack-profile 덮어쓰기 (SemiColonyBot 표시명)
|
|
175
|
+
UPDATE semicolony.knowledge_base
|
|
176
|
+
SET content = E'username: SemiColonyBot\nicon_emoji: ":robot_face:"', updated_at = NOW()
|
|
177
|
+
WHERE domain = 'semicolonybot' AND key IN ('slack-profile','slack-config') AND sub_key = '';
|
|
178
|
+
|
|
179
|
+
-- 4.7 bot_delegation 복제 — 기존 semiclaw → X 위임을 semicolonybot → X 로 복제
|
|
180
|
+
-- (semiclaw 행은 alias 호환성 유지 차원에서 삭제하지 않음)
|
|
181
|
+
-- 075에서 unique 제약이 (from,to,type,metadata->>'label')의 composite 으로 바뀌어
|
|
182
|
+
-- ON CONFLICT 타겟을 명시하면 매치 실패. DO NOTHING 타겟 생략으로 변경.
|
|
183
|
+
INSERT INTO semicolony.bot_delegation (
|
|
184
|
+
from_bot_id, to_bot_id, delegation_type, domains, method, channel,
|
|
185
|
+
max_roundtrips, priority, is_active, metadata
|
|
186
|
+
)
|
|
187
|
+
SELECT
|
|
188
|
+
'semicolonybot', to_bot_id, delegation_type, domains, method, channel,
|
|
189
|
+
max_roundtrips, priority, is_active, COALESCE(metadata, '{}'::jsonb)
|
|
190
|
+
FROM semicolony.bot_delegation
|
|
191
|
+
WHERE from_bot_id = 'semiclaw'
|
|
192
|
+
ON CONFLICT DO NOTHING;
|
|
193
|
+
|
|
194
|
+
-- ============================================================
|
|
195
|
+
-- 5. bot_seats 초기 행 — snamanager0 (현재 유일한 Claude Max seat)
|
|
196
|
+
-- ============================================================
|
|
197
|
+
|
|
198
|
+
INSERT INTO semicolony.bot_seats (seat_id, claude_config_dir, current_bot_id, allocated_at, status)
|
|
199
|
+
VALUES ('snamanager0', '$HOME/.claude/snamanager0', 'semicolonybot', NOW(), 'allocated')
|
|
200
|
+
ON CONFLICT (seat_id) DO UPDATE SET
|
|
201
|
+
current_bot_id = EXCLUDED.current_bot_id,
|
|
202
|
+
allocated_at = EXCLUDED.allocated_at,
|
|
203
|
+
status = EXCLUDED.status;
|
|
204
|
+
|
|
205
|
+
-- ============================================================
|
|
206
|
+
-- 6. NOTIFY 트리거 — 라우팅 설정 변경 시 핫 리로드 시그널
|
|
207
|
+
-- ============================================================
|
|
208
|
+
|
|
209
|
+
CREATE OR REPLACE FUNCTION semicolony.notify_routing_reload()
|
|
210
|
+
RETURNS TRIGGER AS $$
|
|
211
|
+
DECLARE
|
|
212
|
+
payload JSONB;
|
|
213
|
+
op TEXT := TG_OP;
|
|
214
|
+
tbl TEXT := TG_TABLE_NAME;
|
|
215
|
+
bot_id TEXT;
|
|
216
|
+
BEGIN
|
|
217
|
+
IF (TG_OP = 'DELETE') THEN
|
|
218
|
+
IF (tbl = 'bot_status') THEN
|
|
219
|
+
bot_id := OLD.bot_id;
|
|
220
|
+
ELSIF (tbl = 'bot_delegation') THEN
|
|
221
|
+
bot_id := OLD.to_bot_id;
|
|
222
|
+
ELSIF (tbl = 'bot_id_aliases') THEN
|
|
223
|
+
bot_id := OLD.canonical_bot_id;
|
|
224
|
+
END IF;
|
|
225
|
+
ELSE
|
|
226
|
+
IF (tbl = 'bot_status') THEN
|
|
227
|
+
bot_id := NEW.bot_id;
|
|
228
|
+
ELSIF (tbl = 'bot_delegation') THEN
|
|
229
|
+
bot_id := NEW.to_bot_id;
|
|
230
|
+
ELSIF (tbl = 'bot_id_aliases') THEN
|
|
231
|
+
bot_id := NEW.canonical_bot_id;
|
|
232
|
+
END IF;
|
|
233
|
+
END IF;
|
|
234
|
+
|
|
235
|
+
payload := jsonb_build_object('op', op, 'table', tbl, 'bot_id', bot_id, 'at', NOW());
|
|
236
|
+
PERFORM pg_notify('semicolony_routing_reload', payload::text);
|
|
237
|
+
RETURN COALESCE(NEW, OLD);
|
|
238
|
+
END;
|
|
239
|
+
$$ LANGUAGE plpgsql;
|
|
240
|
+
|
|
241
|
+
DO $$
|
|
242
|
+
BEGIN
|
|
243
|
+
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_notify_bot_status') THEN
|
|
244
|
+
CREATE TRIGGER trg_notify_bot_status
|
|
245
|
+
AFTER INSERT OR UPDATE OR DELETE ON semicolony.bot_status
|
|
246
|
+
FOR EACH ROW EXECUTE FUNCTION semicolony.notify_routing_reload();
|
|
247
|
+
END IF;
|
|
248
|
+
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_notify_bot_delegation') THEN
|
|
249
|
+
CREATE TRIGGER trg_notify_bot_delegation
|
|
250
|
+
AFTER INSERT OR UPDATE OR DELETE ON semicolony.bot_delegation
|
|
251
|
+
FOR EACH ROW EXECUTE FUNCTION semicolony.notify_routing_reload();
|
|
252
|
+
END IF;
|
|
253
|
+
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_notify_bot_aliases') THEN
|
|
254
|
+
CREATE TRIGGER trg_notify_bot_aliases
|
|
255
|
+
AFTER INSERT OR UPDATE OR DELETE ON semicolony.bot_id_aliases
|
|
256
|
+
FOR EACH ROW EXECUTE FUNCTION semicolony.notify_routing_reload();
|
|
257
|
+
END IF;
|
|
258
|
+
END $$;
|
|
259
|
+
|
|
260
|
+
-- ============================================================
|
|
261
|
+
-- 7. 검증용 헬퍼 뷰
|
|
262
|
+
-- ============================================================
|
|
263
|
+
|
|
264
|
+
CREATE OR REPLACE VIEW semicolony.v_bot_factory_status AS
|
|
265
|
+
SELECT
|
|
266
|
+
bs.bot_id,
|
|
267
|
+
bs.name,
|
|
268
|
+
bs.status,
|
|
269
|
+
bs.role,
|
|
270
|
+
bs.created_by_bot_id,
|
|
271
|
+
bs.template_bot_id,
|
|
272
|
+
bs.slack_username,
|
|
273
|
+
bs.budget_per_message,
|
|
274
|
+
bs.kb_domains,
|
|
275
|
+
COALESCE(
|
|
276
|
+
(SELECT array_agg(alias ORDER BY alias) FROM semicolony.bot_id_aliases WHERE canonical_bot_id = bs.bot_id),
|
|
277
|
+
'{}'
|
|
278
|
+
) AS aliases,
|
|
279
|
+
seat.seat_id AS seat_id,
|
|
280
|
+
seat.claude_config_dir AS claude_config_dir
|
|
281
|
+
FROM semicolony.bot_status bs
|
|
282
|
+
LEFT JOIN semicolony.bot_seats seat ON seat.current_bot_id = bs.bot_id;
|
|
283
|
+
|
|
284
|
+
COMMIT;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- Migration 105: semicolony.knowledge_base 변경 이벤트 NOTIFY 트리거
|
|
2
|
+
--
|
|
3
|
+
-- Phase 1c KbStore.watch() 구현을 지원한다. 클라이언트는
|
|
4
|
+
-- `LISTEN semicolony_kb_change` 로 구독하고 JSON payload 를 파싱한다.
|
|
5
|
+
--
|
|
6
|
+
-- payload 스키마: { "type": "upsert" | "delete", "domain": "...", "key": "...",
|
|
7
|
+
-- "subKey": "...", "kbId": 123 }
|
|
8
|
+
|
|
9
|
+
CREATE OR REPLACE FUNCTION semicolony.notify_kb_change()
|
|
10
|
+
RETURNS TRIGGER AS $$
|
|
11
|
+
DECLARE
|
|
12
|
+
payload JSONB;
|
|
13
|
+
row_data RECORD;
|
|
14
|
+
change_type TEXT;
|
|
15
|
+
BEGIN
|
|
16
|
+
IF TG_OP = 'DELETE' THEN
|
|
17
|
+
row_data := OLD;
|
|
18
|
+
change_type := 'delete';
|
|
19
|
+
ELSE
|
|
20
|
+
row_data := NEW;
|
|
21
|
+
change_type := 'upsert';
|
|
22
|
+
END IF;
|
|
23
|
+
|
|
24
|
+
payload := jsonb_build_object(
|
|
25
|
+
'type', change_type,
|
|
26
|
+
'domain', row_data.domain,
|
|
27
|
+
'key', row_data.key,
|
|
28
|
+
'subKey', COALESCE(row_data.sub_key, ''),
|
|
29
|
+
'kbId', row_data.kb_id
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
PERFORM pg_notify('semicolony_kb_change', payload::text);
|
|
33
|
+
RETURN COALESCE(NEW, OLD);
|
|
34
|
+
END;
|
|
35
|
+
$$ LANGUAGE plpgsql;
|
|
36
|
+
|
|
37
|
+
DO $$
|
|
38
|
+
BEGIN
|
|
39
|
+
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_notify_kb_change') THEN
|
|
40
|
+
CREATE TRIGGER trg_notify_kb_change
|
|
41
|
+
AFTER INSERT OR UPDATE OR DELETE ON semicolony.knowledge_base
|
|
42
|
+
FOR EACH ROW EXECUTE FUNCTION semicolony.notify_kb_change();
|
|
43
|
+
END IF;
|
|
44
|
+
END $$;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
-- 106: office_id 컬럼을 agent_definitions / command_definitions 에도 확장
|
|
2
|
+
-- L2 tenant 격리의 기본 메커니즘 — skill_definitions 에 이미 존재, 동일 패턴으로 확장.
|
|
3
|
+
-- 모든 기존 레코드는 office_id=NULL (공개 L0 카탈로그) 로 유지.
|
|
4
|
+
-- drift 복구: 소스 테이블이 없으면 전체 섹션 skip (2026-04-23)
|
|
5
|
+
BEGIN;
|
|
6
|
+
|
|
7
|
+
-- 1+3. agent_definitions 확장
|
|
8
|
+
DO $$
|
|
9
|
+
BEGIN
|
|
10
|
+
IF to_regclass('semicolony.agent_definitions') IS NULL THEN
|
|
11
|
+
RAISE NOTICE '106 section agent_definitions skipped: table missing';
|
|
12
|
+
RETURN;
|
|
13
|
+
END IF;
|
|
14
|
+
|
|
15
|
+
ALTER TABLE semicolony.agent_definitions
|
|
16
|
+
ADD COLUMN IF NOT EXISTS office_id UUID DEFAULT NULL;
|
|
17
|
+
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_agent_definitions_office
|
|
19
|
+
ON semicolony.agent_definitions(office_id)
|
|
20
|
+
WHERE office_id IS NOT NULL;
|
|
21
|
+
|
|
22
|
+
IF EXISTS (
|
|
23
|
+
SELECT 1 FROM pg_constraint
|
|
24
|
+
WHERE conname = 'agent_definitions_name_key'
|
|
25
|
+
AND conrelid = 'semicolony.agent_definitions'::regclass
|
|
26
|
+
) THEN
|
|
27
|
+
ALTER TABLE semicolony.agent_definitions DROP CONSTRAINT agent_definitions_name_key;
|
|
28
|
+
END IF;
|
|
29
|
+
|
|
30
|
+
IF NOT EXISTS (
|
|
31
|
+
SELECT 1 FROM pg_indexes
|
|
32
|
+
WHERE schemaname = 'semicolony' AND tablename = 'agent_definitions'
|
|
33
|
+
AND indexname = 'agent_definitions_name_office_uniq'
|
|
34
|
+
) THEN
|
|
35
|
+
CREATE UNIQUE INDEX agent_definitions_name_office_uniq
|
|
36
|
+
ON semicolony.agent_definitions(name, COALESCE(office_id, '00000000-0000-0000-0000-000000000000'::uuid));
|
|
37
|
+
END IF;
|
|
38
|
+
END $$;
|
|
39
|
+
|
|
40
|
+
-- 2+4. command_definitions 확장
|
|
41
|
+
DO $$
|
|
42
|
+
BEGIN
|
|
43
|
+
IF to_regclass('semicolony.command_definitions') IS NULL THEN
|
|
44
|
+
RAISE NOTICE '106 section command_definitions skipped: table missing';
|
|
45
|
+
RETURN;
|
|
46
|
+
END IF;
|
|
47
|
+
|
|
48
|
+
ALTER TABLE semicolony.command_definitions
|
|
49
|
+
ADD COLUMN IF NOT EXISTS office_id UUID DEFAULT NULL;
|
|
50
|
+
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_command_definitions_office
|
|
52
|
+
ON semicolony.command_definitions(office_id)
|
|
53
|
+
WHERE office_id IS NOT NULL;
|
|
54
|
+
|
|
55
|
+
IF EXISTS (
|
|
56
|
+
SELECT 1 FROM pg_constraint
|
|
57
|
+
WHERE conname = 'command_definitions_folder_name_key'
|
|
58
|
+
AND conrelid = 'semicolony.command_definitions'::regclass
|
|
59
|
+
) THEN
|
|
60
|
+
ALTER TABLE semicolony.command_definitions DROP CONSTRAINT command_definitions_folder_name_key;
|
|
61
|
+
END IF;
|
|
62
|
+
|
|
63
|
+
IF NOT EXISTS (
|
|
64
|
+
SELECT 1 FROM pg_indexes
|
|
65
|
+
WHERE schemaname = 'semicolony' AND tablename = 'command_definitions'
|
|
66
|
+
AND indexname = 'command_definitions_folder_name_office_uniq'
|
|
67
|
+
) THEN
|
|
68
|
+
CREATE UNIQUE INDEX command_definitions_folder_name_office_uniq
|
|
69
|
+
ON semicolony.command_definitions(folder, name, COALESCE(office_id, '00000000-0000-0000-0000-000000000000'::uuid));
|
|
70
|
+
END IF;
|
|
71
|
+
END $$;
|
|
72
|
+
|
|
73
|
+
COMMIT;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
-- 107: agent service credentials (PAT vault)
|
|
2
|
+
-- SEMICOLONY를 중앙 발급/검증자로 하는 Personal Access Token 저장소.
|
|
3
|
+
-- 관련 결정: `semicolon decision agent-authenticated-action-standard`
|
|
4
|
+
|
|
5
|
+
BEGIN;
|
|
6
|
+
|
|
7
|
+
CREATE TABLE IF NOT EXISTS semicolony.agent_service_credentials (
|
|
8
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
9
|
+
bot_id TEXT NOT NULL,
|
|
10
|
+
service_domain TEXT NOT NULL,
|
|
11
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
12
|
+
token_prefix TEXT NOT NULL,
|
|
13
|
+
scopes TEXT[] NOT NULL DEFAULT '{}',
|
|
14
|
+
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
15
|
+
expires_at TIMESTAMPTZ,
|
|
16
|
+
last_used_at TIMESTAMPTZ,
|
|
17
|
+
revoked_at TIMESTAMPTZ,
|
|
18
|
+
revoked_reason TEXT,
|
|
19
|
+
issued_by TEXT NOT NULL,
|
|
20
|
+
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
21
|
+
|
|
22
|
+
CONSTRAINT chk_token_hash_format
|
|
23
|
+
CHECK (token_hash ~ '^[0-9a-f]{64}$'),
|
|
24
|
+
CONSTRAINT chk_token_prefix_format
|
|
25
|
+
CHECK (token_prefix ~ '^semicolony_pat_(live|test)_[a-z0-9_-]+_$'),
|
|
26
|
+
CONSTRAINT chk_revoke_consistency
|
|
27
|
+
CHECK (revoked_reason IS NULL OR revoked_at IS NOT NULL),
|
|
28
|
+
CONSTRAINT chk_expires_after_issued
|
|
29
|
+
CHECK (expires_at IS NULL OR expires_at > issued_at)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE INDEX IF NOT EXISTS idx_agent_creds_bot_id
|
|
33
|
+
ON semicolony.agent_service_credentials (bot_id);
|
|
34
|
+
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_agent_creds_service
|
|
36
|
+
ON semicolony.agent_service_credentials (service_domain);
|
|
37
|
+
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_agent_creds_active
|
|
39
|
+
ON semicolony.agent_service_credentials (bot_id, service_domain)
|
|
40
|
+
WHERE revoked_at IS NULL;
|
|
41
|
+
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_agent_creds_expires
|
|
43
|
+
ON semicolony.agent_service_credentials (expires_at)
|
|
44
|
+
WHERE revoked_at IS NULL AND expires_at IS NOT NULL;
|
|
45
|
+
|
|
46
|
+
CREATE OR REPLACE VIEW semicolony.v_agent_credentials_audit AS
|
|
47
|
+
SELECT
|
|
48
|
+
id,
|
|
49
|
+
bot_id,
|
|
50
|
+
service_domain,
|
|
51
|
+
token_prefix,
|
|
52
|
+
scopes,
|
|
53
|
+
issued_at,
|
|
54
|
+
expires_at,
|
|
55
|
+
last_used_at,
|
|
56
|
+
revoked_at,
|
|
57
|
+
revoked_reason,
|
|
58
|
+
issued_by,
|
|
59
|
+
CASE
|
|
60
|
+
WHEN revoked_at IS NOT NULL THEN 'revoked'
|
|
61
|
+
WHEN expires_at IS NOT NULL AND expires_at < NOW() THEN 'expired'
|
|
62
|
+
ELSE 'active'
|
|
63
|
+
END AS status,
|
|
64
|
+
metadata
|
|
65
|
+
FROM semicolony.agent_service_credentials;
|
|
66
|
+
|
|
67
|
+
COMMENT ON TABLE semicolony.agent_service_credentials IS
|
|
68
|
+
'PAT vault for agent→service authenticated writes. Plaintext never stored — only HMAC-SHA256(pepper, plaintext) hex. See decision: agent-authenticated-action-standard.';
|
|
69
|
+
|
|
70
|
+
COMMENT ON COLUMN semicolony.agent_service_credentials.token_hash IS
|
|
71
|
+
'64-char lowercase hex of HMAC-SHA256(SEMICOLONY_CREDENTIAL_PEPPER, plaintext). Unique globally.';
|
|
72
|
+
|
|
73
|
+
COMMENT ON COLUMN semicolony.agent_service_credentials.token_prefix IS
|
|
74
|
+
'Deterministic prefix ending in underscore (e.g. "semicolony_pat_live_workclaw_"). Safe to expose to services for audit logging.';
|
|
75
|
+
|
|
76
|
+
COMMENT ON COLUMN semicolony.agent_service_credentials.scopes IS
|
|
77
|
+
'Format "{resource}:{action}" (e.g. ["board:write","kpi:read"]). Wildcard "resource:*" supported in verify.';
|
|
78
|
+
|
|
79
|
+
COMMIT;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- 108: L2 ontology seed cleanup
|
|
2
|
+
-- migration 017 시드된 세미콜론 L2 ontology 행(jungchipan, playland)을 제거.
|
|
3
|
+
-- 외부 OSS 신규 인스턴스에서는 자동 삭제되어 깨끗한 상태로 시작.
|
|
4
|
+
-- 기존 인스턴스에 해당 도메인 KB 데이터가 살아있으면 ontology 행은 보존 (안전).
|
|
5
|
+
--
|
|
6
|
+
-- 관련 인벤토리: docs/L2-INVENTORY.md (HIGH #1)
|
|
7
|
+
|
|
8
|
+
BEGIN;
|
|
9
|
+
|
|
10
|
+
DELETE FROM semicolony.ontology o
|
|
11
|
+
WHERE o.domain IN ('jungchipan', 'playland')
|
|
12
|
+
AND NOT EXISTS (
|
|
13
|
+
SELECT 1 FROM semicolony.knowledge_base kb WHERE kb.domain = o.domain
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
COMMIT;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- 109: ontology JSON Schema description 일반화
|
|
2
|
+
-- migration 015 에서 KPI ontology 의 properties.project.description 에 세미콜론 L2 도메인
|
|
3
|
+
-- 예시("gameland, jungchipan")가 박혀 있었음. OSS 배포 시 외부 사용자에게 노출되므로 일반화.
|
|
4
|
+
--
|
|
5
|
+
-- 관련 인벤토리: docs/L2-INVENTORY.md (MEDIUM #2)
|
|
6
|
+
|
|
7
|
+
BEGIN;
|
|
8
|
+
|
|
9
|
+
UPDATE semicolony.ontology
|
|
10
|
+
SET
|
|
11
|
+
schema = jsonb_set(
|
|
12
|
+
schema,
|
|
13
|
+
'{properties,metadata,properties,project,description}',
|
|
14
|
+
'"프로젝트 ID (예: my-service)"'::jsonb
|
|
15
|
+
),
|
|
16
|
+
version = version + 1
|
|
17
|
+
WHERE domain = 'kpi'
|
|
18
|
+
AND schema #> '{properties,metadata,properties,project,description}'
|
|
19
|
+
= '"프로젝트 ID (예: gameland, jungchipan)"'::jsonb;
|
|
20
|
+
|
|
21
|
+
COMMIT;
|