@team-semicolon/semicolony-cli 4.18.57 → 4.18.59
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.
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- 165_skill_release_retirements.sql
|
|
2
|
+
-- governed skill 은퇴 프리미티브: append-only retirement 기록.
|
|
3
|
+
-- 판정: (channel, skill_name)의 최신 retirement.retired_at > 최신 activation.activated_at ⇒ 은퇴.
|
|
4
|
+
-- 재활성화(새 activation)가 은퇴를 해제한다. 어떤 기존 행도 수정/삭제하지 않는다.
|
|
5
|
+
|
|
6
|
+
CREATE TABLE IF NOT EXISTS semicolony.skill_release_retirements (
|
|
7
|
+
retirement_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
8
|
+
channel text NOT NULL CHECK (NULLIF(btrim(channel), '') IS NOT NULL),
|
|
9
|
+
skill_name text NOT NULL CHECK (skill_name ~ '^[a-z0-9]+(?:-[a-z0-9]+)*$'),
|
|
10
|
+
reason text NOT NULL CHECK (NULLIF(btrim(reason), '') IS NOT NULL),
|
|
11
|
+
lease_id uuid NOT NULL REFERENCES semicolony.skill_capability_leases(lease_id),
|
|
12
|
+
approval_reference text NOT NULL CHECK (NULLIF(btrim(approval_reference), '') IS NOT NULL),
|
|
13
|
+
idempotency_key text NOT NULL UNIQUE CHECK (NULLIF(btrim(idempotency_key), '') IS NOT NULL),
|
|
14
|
+
retired_by_principal text NOT NULL CHECK (NULLIF(btrim(retired_by_principal), '') IS NOT NULL),
|
|
15
|
+
retired_at timestamptz NOT NULL DEFAULT clock_timestamp()
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE INDEX IF NOT EXISTS skill_release_retirements_lookup
|
|
19
|
+
ON semicolony.skill_release_retirements (channel, skill_name, retired_at DESC);
|
|
20
|
+
|
|
21
|
+
DROP TRIGGER IF EXISTS protect_skill_release_retirements ON semicolony.skill_release_retirements;
|
|
22
|
+
CREATE TRIGGER protect_skill_release_retirements
|
|
23
|
+
BEFORE UPDATE OR DELETE ON semicolony.skill_release_retirements
|
|
24
|
+
FOR EACH ROW EXECUTE FUNCTION semicolony.reject_skill_governance_mutation();
|
|
25
|
+
|
|
26
|
+
CREATE OR REPLACE VIEW semicolony.v_current_skill_release_retirements AS
|
|
27
|
+
SELECT DISTINCT ON (channel, skill_name)
|
|
28
|
+
retirement_id, channel, skill_name, reason, lease_id,
|
|
29
|
+
approval_reference, idempotency_key, retired_by_principal, retired_at
|
|
30
|
+
FROM semicolony.skill_release_retirements
|
|
31
|
+
ORDER BY channel, skill_name, retired_at DESC, retirement_id DESC;
|
|
32
|
+
|
|
33
|
+
REVOKE ALL ON TABLE
|
|
34
|
+
semicolony.skill_release_retirements,
|
|
35
|
+
semicolony.v_current_skill_release_retirements
|
|
36
|
+
FROM PUBLIC, sc_app;
|
|
37
|
+
|
|
38
|
+
GRANT SELECT, INSERT ON TABLE
|
|
39
|
+
semicolony.skill_release_retirements
|
|
40
|
+
TO sc_app;
|
|
41
|
+
|
|
42
|
+
GRANT SELECT ON TABLE
|
|
43
|
+
semicolony.v_current_skill_release_retirements
|
|
44
|
+
TO sc_app;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
-- 166_lease_operations_retire.sql
|
|
2
|
+
-- 161_skill_governance.sql이 정의한 profile→operations CHECK 제약은 'operate'
|
|
3
|
+
-- 프로필에 허용된 연산을 리터럴 배열로 고정한다. skill-governance.ts의 governed
|
|
4
|
+
-- 은퇴 경로(`skills.release.retire`, operate-only)가 이미 이 연산을 요구하지만
|
|
5
|
+
-- 161의 배열에는 없어 모든 은퇴 리스 발급이 이 CHECK로 거부된다.
|
|
6
|
+
--
|
|
7
|
+
-- 이 제약은 161에서 이름 없이(anonymous) 생성되어 Postgres가 자동 명명한
|
|
8
|
+
-- 이름을 신뢰할 수 없다. pg_constraint에서 정의 텍스트로 대상을 찾아
|
|
9
|
+
-- 안전하게 DROP한 뒤, 동일한 read/make 분기와 skills.release.retire를
|
|
10
|
+
-- 추가한 operate 분기로 NAMED 제약을 재생성한다. 마이그레이션 러너가
|
|
11
|
+
-- 트랜잭션 경계를 제공하므로 BEGIN/COMMIT은 넣지 않는다.
|
|
12
|
+
|
|
13
|
+
ALTER TABLE semicolony.skill_capability_leases
|
|
14
|
+
DROP CONSTRAINT IF EXISTS skill_capability_leases_profile_operations_check;
|
|
15
|
+
|
|
16
|
+
DO $$
|
|
17
|
+
DECLARE
|
|
18
|
+
legacy_conname TEXT;
|
|
19
|
+
BEGIN
|
|
20
|
+
SELECT pg_constraint.conname
|
|
21
|
+
INTO legacy_conname
|
|
22
|
+
FROM pg_constraint
|
|
23
|
+
WHERE pg_constraint.conrelid = 'semicolony.skill_capability_leases'::regclass
|
|
24
|
+
AND pg_constraint.contype = 'c'
|
|
25
|
+
AND pg_get_constraintdef(pg_constraint.oid) LIKE '%skills.release.stage%'
|
|
26
|
+
AND pg_get_constraintdef(pg_constraint.oid) LIKE '%profile%'
|
|
27
|
+
LIMIT 1;
|
|
28
|
+
|
|
29
|
+
IF legacy_conname IS NOT NULL THEN
|
|
30
|
+
EXECUTE format(
|
|
31
|
+
'ALTER TABLE semicolony.skill_capability_leases DROP CONSTRAINT %I',
|
|
32
|
+
legacy_conname
|
|
33
|
+
);
|
|
34
|
+
END IF;
|
|
35
|
+
END
|
|
36
|
+
$$;
|
|
37
|
+
|
|
38
|
+
ALTER TABLE semicolony.skill_capability_leases
|
|
39
|
+
ADD CONSTRAINT skill_capability_leases_profile_operations_check
|
|
40
|
+
CHECK (
|
|
41
|
+
(
|
|
42
|
+
profile = 'read'
|
|
43
|
+
AND operations <@ ARRAY[
|
|
44
|
+
'skills.health.read',
|
|
45
|
+
'skills.release.read'
|
|
46
|
+
]::TEXT[]
|
|
47
|
+
)
|
|
48
|
+
OR
|
|
49
|
+
(
|
|
50
|
+
profile = 'make'
|
|
51
|
+
AND operations <@ ARRAY[
|
|
52
|
+
'skills.health.read',
|
|
53
|
+
'skills.release.read',
|
|
54
|
+
'skills.proposal.write',
|
|
55
|
+
'skills.projection.reconcile',
|
|
56
|
+
'skills.project.write',
|
|
57
|
+
'skills.release.stage'
|
|
58
|
+
]::TEXT[]
|
|
59
|
+
)
|
|
60
|
+
OR
|
|
61
|
+
(
|
|
62
|
+
profile = 'operate'
|
|
63
|
+
AND operations <@ ARRAY[
|
|
64
|
+
'skills.health.read',
|
|
65
|
+
'skills.release.read',
|
|
66
|
+
'skills.proposal.write',
|
|
67
|
+
'skills.projection.reconcile',
|
|
68
|
+
'skills.project.write',
|
|
69
|
+
'skills.release.stage',
|
|
70
|
+
'skills.project.publish',
|
|
71
|
+
'skills.release.activate',
|
|
72
|
+
'skills.audience.manage',
|
|
73
|
+
'skills.release.retire'
|
|
74
|
+
]::TEXT[]
|
|
75
|
+
)
|
|
76
|
+
);
|
package/migrations/README.md
CHANGED
|
@@ -14,6 +14,8 @@ SEMICOLONY CLI의 PostgreSQL 마이그레이션 파일들.
|
|
|
14
14
|
| `005_bot_workspace_audits.sql` | Bot workspace 감사 테이블 |
|
|
15
15
|
| `006_bot_kb_subscriptions.sql` | 봇별 KB 구독 테이블 |
|
|
16
16
|
| `007_flatten_skill_names.sql` | Flat skill naming 마이그레이션 |
|
|
17
|
+
| `165_skill_release_retirements.sql` | 거버넌스 스킬 은퇴 프리미티브 (append-only 기록 + 뷰) |
|
|
18
|
+
| `166_lease_operations_retire.sql` | `skill_capability_leases` profile→operations CHECK 재생성: operate 프로필에 `skills.release.retire` 추가 |
|
|
17
19
|
|
|
18
20
|
## 007: Flat Skill Naming
|
|
19
21
|
|