판정 먼저
- 방향은 타당하다 — 그리고 이미 우리 것이다. 사건 하나를 단일 코어로 두고 여러 렌즈로 색인하는 구조는 우리가 저널리즘 6하에서 독립적으로 도달한 자리이며, 백지에서 다시 그려도 같은 자리에 온다. SEM·CIDOC·schema.org 는 그 자리에 먼저 와 있던 표준들이다.
- 그러나 'SEM을 문자 그대로 코어로 삼는' 재설계는 아니다. SEM은 우리에게 손실 라벨이고(
sem:Time노드에 대응할 시간 개체가 없고Subject는 깨끗한 대응처가 없다), SEM 고유의 기제(View·Temporary·punning·RDF reification)는 우리가 안 쓰거나 정반대로 한다. 이것은 'SEM 채택'이 아니라 '이미 한 것을 표준 계보로 검증·정렬'하는 일이다 — 저장 표현은 우리 것이고, 런타임 변화는 0이다. - 백지 자유를 실제로 쓰면, 진짜 재설계는 따로 있다. proto가 우연히 어긋나 있는 것들 — 쪼개진 '단일' 소스, 없는 역인덱스, 엔진 밖에 사는 배열, 누락된 서사 층 — 을 이상형으로 바로잡는 재구현. 그건 SEM으로의 전환이 아니라 '사건 코어 + 다섯 층'의 깨끗한 재구현이며, 마침 네이티브 이식(SwiftData)이 그 재구현의 자연스러운 계기다.
sem:View/accordingTo는 '먼 친척'이 아니라 바로 그 저장 기제가 된다. 지금 우리는 그 제품이 아니다 → §10에 분기점으로 남긴다.아래 §1–§3은 판정 ①②의 근거(수렴과 차용, 정렬과 어긋남), §4–§7은 판정 ③의 이상형 설계와 그 확장성, §8은 실제 상황 10벌, §9–§10은 정직한 한계와 갈림길.
1 · 우리는 바퀴를 재발명하지 않았다 — 그러나 SEM에서 빌린 것도 거의 없다
개념 모델 제2장은 사건을 '인생의 한 문장'으로, 제4장은 관계 메커니즘을 성분(Involvement)과 참조(Link) 둘로 못박는다. 참고 페이지는 이 구조가 CIDOC·schema.org·SEM 같은 사건 중심 온톨로지와 겹친다고 이미 적었다. 문제는 그 겹침이 '차용'인가 '수렴'인가다 — 둘을 뭉뚱그리면 SEM의 기여가 부풀려진다.
role·relationType을 비워 두고 출발. 이 목록은 SEM에서 빌린 게 아니라 6하와 프로토타이핑에서 스스로 도달한 것이다. SEM이 같은 자리에 있다는 사실은 검증이지 출처가 아니다.origin·destination·capital). 이것이 우리 Involvement의 장소 역할(at/from/to)과 다중 경유의 선례가 된다 — 다만 경유 '순서'(order) 인덱스 자체는 SEM이 주지 않는 우리 확장이다.
② SEM의 설계 격언 "specify less rather than more"(적게 명세하는 편이 낫다)는 우리 확장 가드레일 일곱 질문의 철학적 동반자다.그 밖의 SEM 고유 장치는 전부 우리가 안 쓰거나·미루거나·정반대로 한다(§2–§3에서 하나씩). 그러므로 정직한 문장은 이렇다 — "우리 모델은 스스로가 코어다. SEM·CIDOC·schema.org는 그 코어를 세 용도로만 빌리는 대상이다: (1) 검증 체크리스트, (2) 공용 어휘 사전, (3) export 매핑."
View·Temporary를 두는데 우리도 둬야 하지 않나?" 식 기능 유입). 그래서 이 한 줄을 박는다 — 표준은 검증 체크리스트로만 쓰고, 새 개념의 추가 정당화 근거로는 쓰지 않는다.2 · 사건 중심 표준 계보와의 정렬표
SEM은 3층 구조다 — Core(Event·Actor·Place·Time) / Type(외부 어휘 차용) / Constraint(같은 지층의 세 형제 Role·Temporary·View). 우리와 정렬하는 부분과 어긋나는 부분을 나눠 본다.
| 표준 어휘 | 우리 개념 | 관계 |
|---|---|---|
| E5 Event · sem:Event · schema:Event | 사건 Event | 정렬 — 1급 객체·서술의 중심 |
| P11 had participant · sem:hasActor | 성분 — 누가 Person | 정렬 — Involvement가 참여를 잇는다 |
| P7 took place at · sem:hasPlace | 성분 — 어디서 Place | 정렬 |
| P14.1 in the role of · sem:roleType | Involvement.role | 정렬(+SEM은 장소 role 허용 = 차용 ①) |
| schema:startDate/endDate · sem:hasTimeStamp | 일정 Event.start/end | 부분 — 우리 시간은 리터럴 필드 |
| sem:Time (상징 시간 개체) | — | 비대응 — 우리에겐 시간 '노드'가 없다 |
| sem:Object / schema:about · E55 Type | 대상 Subject | 비대응 — Object(수동 Actor)로 강제 불가 |
| sem:Role / sem:View / sem:Temporary | Involvement / 시나리오 겹 / 파생 | 어긋남 — §3·§5에서 상술 |
sem:hasTime으로 상징 Time 개체를 가리키지만(타임스탬프 없이 순서만도 표현), 우리는 start/end를 Event의 리터럴로 들고 있다 — schema.org의 startDate/endDate·sem:hasTimeStamp에 가깝지 sem:hasTime이 아니다. 대상: Subject는 자동차 같은 구체물만이 아니라 '비상금'·'전세 보증금'·'책 원고' 같은 추상·범주를 담는다 — SEM의 Object(=수동적 Actor)로 밀면 범주 오류다. 그래서 L0을 "SEM 코어(Event/Actor/Place/Time)"로 부르지 않는다. SEM은 위에 얹는 코어가 아니라 옆에 두는 대조 표준이다.startDate·endDate·location·attendee가 우리 리터럴 필드와 직결)와 CIDOC(아카이브 정합)이다. 훗날 사건 유형을 도입할 때 어휘를 주는 쪽도 CIDOC(E67 Birth·E9 Move)·schema.org 하위타입 트리다 — SEM은 구체 유형을 스스로 두지 않는 placeholder라, 유형에 관한 한 "남의 어휘를 빌려라"라는 유보일 뿐 도움이 아니다.3 · 세 어긋남은 버그가 아니라 정체성이다
표준과 어긋나는 세 지점은, 셋 다 이 제품이 무엇인지를 드러낸다. 초안은 이 셋을 살짝 미화했다 — 여기서 정밀하게 고쳐 적는다.
① 주어 — '암묵 주어 없음' vs '암묵 나'
표준은 기관 간 상호운용을 위한 subject-explicit·관점중립 기술이다: 모든 참여자가 명시적 Actor이고 특권적 주어가 없다(SEM이 1인칭 '나'를 Actor로 두는 걸 금하는 건 아니다 — 그러니 '3인칭 vs 1인칭'은 부정확하다). 진짜 어긋남은 '암묵 주어 없음' vs '모든 기록이 한 사람 나에게 귀속'이다. 우리는 표준이 아니라 자서전을 만든다. (곁가지: sem:Actor ⊑ sem:Authority — 어떤 Actor도 Authority가 될 수 있으므로, 우리 암묵 '나' 자신이 Authority가 될 수 있다. §5의 유비가 여기서 날카로워진다.)
② 시제 — 거울상이 아니라 직교
초안은 "SEM은 sem:Temporary로 유효구간을 저장, 우리는 파생 — 같은 문제의 정반대 답"이라 적었다. 이 대칭은 과장이다. sem:Temporary는 시변(時變) 속성/진술의 유효구간을 reify한 것이지(예: 인도네시아의 placeType=republic이 1945년부터 유효) 사건의 '시제'가 아니다. 게다가 SEM에는 움직이는 '지금(now)'이 아예 없어 시제 파생이라는 문제를 마주치지도 않는다. 우리는 앵커 시각은 저장하되 now-상대 상태(시제·롤업·곡선)만 파생한다. 즉 거울상 대칭이 아니라 서로 직교한 선택이다.
sem:hasTime은 리터럴 타임스탬프 없이 상징 Time만으로 사건의 순서·관계를 표현할 수 있고, begin/end의 earliest/latest 7개 타임스탬프 속성은 fuzzy가 아니라 구간 경계(bounded uncertainty)를 준다. 우리의 now-상대 파생은 오히려 리터럴 타임스탬프를 반드시 요구한다. 파생의 우월을 주장하기 전에, SEM의 시간 장치가 우리보다 풍부한 지점을 인정한다.③ 유형 — 표면은 같고 정신은 반대
둘 다 내부에 풍부한 유형 분류를 하드코딩하지 않는다(같은 표면). 그러나 동기가 정반대다: SEM은 최대 개방(OWL2 punning으로 어떤 외부 어휘든 꽂는다)이고, 우리는 의도적 최소 폐쇄(모양 셋 milestone·span·index, 외부 플러그인 의도 없음)다. 그래서 이건 순수 동일성도 순수 차이도 아니다 — 표면 같고 정신 반대. 유형을 우리가 언젠가 도입한다면, 저장 없이 파생(제목·성분에서 추정)으로 충분한지 가드레일에 먼저 걸고, 어휘가 필요하면 CIDOC·schema.org에서 빌린다.
4 · 이상형: 사건 코어 + 다섯 층
백지에서 다시 그린다면 도달하는 구조는 이것이다. 층화(層化) 자체는 SEM의 산물이 아니라 우리 것이다 — 봉합선(파생·돈·시나리오·소유·서사)을 명명하고, 일곱 질문 가드레일이 새 개념을 '어느 층에 드는가'로 라우팅한다. SEM이 실제로 '소유'하는 건 부분적 L0뿐이다.
Event(6하의 한 문장) · Involvement(→ Person·Place·Subject) · 마스터. 시간은 리터럴(start/end). ≈ sem:Event/hasActor/hasPlace, 단 Time 노드·Subject는 비대응NetWorthSnapshot) + 거처(Pocket) · 이동(Flow — 일회성은 사건에 앵커) · 배정(Allocation) · 목표(Goal). 사건(L0)에 앵커되는 별도 층User+RLS · Publication(기본 비공개) · 렌즈 = 필터+투영(데이터 안 소유). CQRS 읽기 투영 — §6Interview·Reflection·Question(엔진) · 책자·백업(산출). 정본 7층에 있으나 초안이 누락했던 층 — 복원정본 가드레일 7층 대응: 문장·성분·마스터 → L0 · 돈 4역 → L2 · 겹 → L3 · 계정 → L4 · 서사 → L5. (L1 파생은 '층'이 아니라 원칙 7의 관철이라 7층 목록엔 없다.)
sem:Temporary의 정반대(파생)다. 뒤의 §6·§7이 보이는 CQRS·@Relationship 역인덱스·MCP 1:1 이득도 전부 '사건 중심성'의 이득이지 'SEM'의 이득이 아니다 — 이미 모두가 동의하는 사건 코어를 지지할 뿐, SEM 특유의 근거를 더하지 않는다.백지 자유가 실제로 바로잡는 것 — proto의 우연한 어긋남
이상형이 기존 구조와 갈라지는 지점은 개념이 아니라 구현의 우연이다. 재정초의 실익은 여기 있다(각 항목은 proto 코드에서 확인됨):
| proto의 현 상태 | 이상형(재구현) |
|---|---|
'단일 소스'가 실은 둘 — 엔진 AppStore(사건·레인) + 대형 캐시 JD(성분·돈·겹·서사) | 하나의 사건 코어(SwiftData @Model)가 유일한 진실. 엔진은 데이터 소유자에서 표시 컴포넌트로 강등('store-master 역전' 완성) |
성분 역인덱스가 아예 없다 — 모든 2차 렌즈가 전방 byEvent 맵의 선형 스캔 | Involvement를 1급 @Model로 승격 → person.involvements 역참조가 2차 렌즈 인덱스를 native하게 제공 |
날짜 미정(dateless)·가려진 done(doneHidden)이 엔진 밖 배열로 살고, 소비 렌즈마다 병합 규칙이 다르다 | start == nil + 게이트 = 쿼리 술어. 물리적 곁배열이 사라지고 병합 불일치가 원천 소거 |
죽은 budget 슬롯(엔진 역전 대비 자리표시), 서사 층이 도식에 없음 | 깨끗한 층 경계(L0–L5). export는 phasing이 아니라 토대 요구사항 |
5 · 시나리오 겹은 어떤 표준 지층에 앉는가
초안의 가장 매력적인 주장은 "시나리오 겹 = accordingTo를 미래로 돌리고 결정 연산자를 더한 것"이었다. 적대 검증 결과, 친족성은 진짜이되 초안이 그것을 준(準)-동일성으로 과대포장했다. 우리 자신의 ontology.html(§우리 모델과의 접점)이 이미 이 관계를 더 신중하게 "먼 친척"이라 적었다 — 초안이 기존 정본보다 뜨거웠다. 정밀하게 다시 적는다.
Constraint 층은 같은 건축학적 지층에 속한다 — 둘 다 평평한 '참' 공간에서 명제/레코드를 끄집어내 어떤 틀(Scenario / Authority)에 상대화하는 reification(문맥화) 층이고, 그 결과 대안/상충이 모순 없이 공존한다. 카디널리티도 일치한다: 한 Authority가 무수한 View를 뒷받침하듯, 한 Scenario가 무수한 소속 레코드를 스코프한다("하나의 틀, 여러 스코프된 명제"). 이건 우연한 유비가 아니라 L3가 애드혹 볼트온이 아님을 검증해 준다.그러나 초안이 지목한 '같은 장치(accordingTo)'는 정밀 층위에서 틀렸고, '차이는 둘뿐'이라는 열거는 절반만 맞다. 네 가지가 어긋난다:
| 축 | SEM (View/accordingTo) | 우리 시나리오 겹 |
|---|---|---|
| 입도(granularity) | 명제(단일 속성) — 같은 hasActor 노드를 공유한 채 roleType 하나만 갈라 붙인다(노드 공유) | 문장(레코드) — baseId가 확정 레코드 전체의 수정판 사본을 만든다. 필드·성분 하나만 바꿔도 "문장 단위"로 복제(노드 복제) |
| 입장(stance) | 인식적·관점적 — 같은 확정된 사실, 기술만 다름("해방자" vs "점령자") | 양상적·가능세계 — 비확정 가상 미래들. 부하는 시제(과거→미래)가 아니라 이 입장 전환에 있다 |
| 기준 세계 | 없음 — View들은 서로 가리지 않는 대등한 peer, baseline·우선순위 없음 | 있음 — 정본 확정 세계에 대해 diff(확정−가림+델타). removed로 가리고, 참조는 수정판을 우선 |
| 해소 방향 | 구성적 비해소 — 관점을 영구히 열어 둠. '결정'은 의미론적으로 금지된 연산(네덜란드가 옳다고 정하지 않는다) | 붕괴 지향 — 승격(결정)을 위해 존재. worldview 폐루프(검토→결정→승격→이력) |
accordingTo(가장 세밀한 명제층 View)가 아니라 sem:Constraint의 named-graph(문맥층) 표현이다 — Scenario ≈ 문맥/named-graph이지 Scenario ≈ Authority가 아니다. accordingTo(1947 경찰행동 예)는 동기를 주는 사촌으로만 인용하고, 기술적 등식으로 쓰지 않는다. 그리고 결정 연산자는 '+ 더한 것'이 아니라 SEM의 구성적 비해소와 정반대로 도는 지배 동역학(영구 개방 vs 선택 폐쇄)이다 — 바로 이 대립이, 왜 친족이 동형이 아니라 family-resemblance에 머무는지의 근거다.baseId·removed·참조해석 우선), (iii) 해소·승격 연산자 — 은 전부 SEM Constraint 층에 없다. 시나리오 겹은 '친척이되 SEM보다 무거운 층'이다.6 · 다중 렌즈 색인 — 파셋 분류 + CQRS, 그리고 정직한 예외
렌즈 수만큼 데이터를 두지 않는다 — 중심 객체는 사건 하나, 렌즈는 필터+투영이다. 이건 도서관학의 파셋 분류(Ranganathan PMEST: 성분마다 축이 열린다)와 CQRS(쓰기 모델 하나, 읽기 투영 N개)의 결합이다. 원리는 옳다 — 다만 '모든 렌즈가 깨끗한 파셋 투영'이라는 전칭은 세 지점에서 정직하게 좁혀야 한다.
| 렌즈 | 사건 코어 위의 색인 | 성격 |
|---|---|---|
| 여정 · 1차 | start 있는 Event 필터 → 타임라인 투영 + '지금' 선 파생 | 깨끗한 필터+투영 |
| 수첩 · 1차 | projectId로 필터 + 상태·등장인물 롤업(파생) | 필터+투영 + L1 롤업 |
| 주소록·지도·사물함 · 2차 | 성분 역참조(person.involvements …) + now 대비 파생 | 집합소속 파셋 — 역인덱스 이득 |
| 여비 · 1차 | 점프(jumps) 필터 후 시간축 적분·복리 합성·근찾기 | L1 파생 시뮬레이션 — 파셋 아님 |
| 시나리오 · 겹 | 확정 세계 재합성(계산·비저장) | 렌즈 아님(겹) — CQRS 유의 |
정직한 예외 셋
- 여비 곡선 이것은 필터+투영이 아니라 L1 파생 시뮬레이션이다 — 사건에서 오는 부분은 점프 필터 하나뿐이고, 그 위에 run-rate의 시간축 적분·지갑 복리 점화식 합성·런웨이 근찾기가 얹히며, 사건 코어 하나가 아니라 네 쓰기 모델(사건-앵커
Flow·NetWorthSnapshot·run-rate·Pocket)을 조인한다. 적분은 분류가 아니다 — 곡선을 'PMEST 파셋 색인'이라 부르는 건 범주 오류다. (곡선이 fold라는 사실 자체는 CQRS 위반이 아니다 — 읽기 모델은 집계를 해도 된다. 위반은 오직 좁은 슬로건 '필터+투영'과 파셋 은유에 대해서만이다.) - 시나리오 겹 배포된 기본 겹은 읽기 필터가 아니라 전역 엔진 상태 변경이다 — 공유 엔진의 사건 배열을 통째로 재합성해 갈아끼우고(한 번에 하나), 읽기 렌즈들이 이 숨은 전역을 수동 반영한다. '어느 세계냐'가 투영 인자가 아니라 전역 상태인 것은 CQRS 읽기/쓰기 분리의 반대다. CQRS-정합한 경로는 여비의 N-곡선 비교(순수 재합성·엔진 미접촉)뿐이다.
- 단일 소스 오늘 프로토의 런타임 진실집합은 실은 세 소스(엔진 사건 ∪
dateless∪doneHidden)이고 소비자별 병합 규칙이 다르다 — 가려진 done span에만 등장하는 인물이 수첩 롤업엔 잡히지만 주소록에선 누락된다(실증적 렌즈 간 집계 불일치). 게다가 성분·상태·돈의 실거처는byEvent라는 네 번째 병렬 맵이다. 깨끗한 단일 소스는 '오늘 프로토'가 아니라 'SwiftData 포트(start==nil+ 게이트=술어)'가 비로소 배달한다.
byEvent id-배열 blob을 Involvement 1급 @Model로 승격해야 비로소 생기는 조건부, (iii) CloudKit에선 결과적 일관성이다. 곡선·겹에는 어떤 역인덱스도 무의미하고, 복합 파셋 질의(그 사람의 특정 기간·flow 조건 사건)는 SwiftData #Predicate의 to-many pushdown 한계로 별도 fetch가 필요하다. 그래서 "사건 중심 그래프가 [모든] 렌즈 인덱스를 native하게 제공"이라는 일반화는 철회하고, "집합소속 2차 렌즈에 한해 유지되는 역관계를 준다"로 좁힌다.결론. 다중 렌즈 색인은 원리상 가능하고(파셋+CQRS), 사건 중심 그래프는 집합소속 렌즈에 유지되는 역관계를 준다. 그러나 '모든 렌즈가 공짜 역인덱스를 가진 깨끗한 파셋 투영'은 아니다 — 곡선은 시뮬레이션, 겹은 재합성, 단일 소스는 이식이 배달한다.
7 · 확장성 — 순이익이 아니라 '집중된 마찰을 동반한 정합'
사건 중심성은 확장에서 congruent하다 — 다만 초안의 '순이익(net gain)'이라는 한 단어가 (a) 모든 축에서 균일 흑자라는 함의와 (b) 애그리게이트 무결성 이관의 총량 미집계로 과장됐다. 축별로 원장을 뜯는다.
벌린 흑자 — 모델 · MCP
- SwiftData:
Event=애그리게이트 루트,@Relationship역참조=2차 렌즈 인덱스, 파생-비저장으로@Model순수, 계산 겹. - MCP 쓰기 1:1: proto가 모든 변이를 단일 repository(
journey-data.js)로 깔때기했기에,createEvent·setAllocation·createScenario·compareScenarios가 MCP 툴에 ~1:1로 얹힌다 — 열망이 아니라 '이미 있는 seam을 읽는' 것. - 계산·비저장 겹: Claude가 가상 세계를 무쓰기로 탐색·비교 — LLM이 운전할 때 정확히 원하는 안전 속성. 킬러 유스케이스.
- 헤드리스 코어: 사건-파생 계산 코어를 UI 렌더러·MCP·export가 공유. export(zip)는 순수 read/serialize라 셋 중 가장 깨끗한 다리.
집중된 마찰 — CloudKit · 캘린더
- CloudKit 원자성: 교차레코드 원자성·
unique·cascade가 없어 '사건+성분' 애그리게이트가 원자적이지 않다 → 무결성이 앱 코드로 이동. - 기질 세금: 이 비용은 사건 중심성 탓이 아니라 어떤 관계형 모델이든 CloudKit에 얹으면 무는 세금이다. 오히려 사건 중심성이 이를 하나의 방어 가능한 애그리게이트 경계(Event 루트)로 집중시킨다.
- 화해 절이 아직 언명뿐: 캘린더 완충은 구체 설계됨(EKEvent.notes 구조화 블록 + UUID 왕복 + 우리 정본 우선)이나, CloudKit 무결성엔 대응 절이 없다.
Event+성분을 지우고 스냅샷에 동봉 → 레코드가 임의 순서로 기기 B에 동기화 → B는 잠시 성분 없는 Event, 또는 앵커 Event가 아직 안 온 Flow(시점이 앵커 start에서 파생되므로 시점 자체가 붕괴), 또는 전파 전의 삭제 스냅샷을 본다. 이 완충이 설계되기 전엔 CloudKit 다리의 '순이익'은 미획득이다. 추가할 절: 동시 다기기 병합 하 사건 애그리게이트 무결성 모델 + 화해 전략(검증-온-리드·고아 청소·참조 복구·동기화 순서 관용·앵커 미도착 Flow의 graceful 처리).accordingTo/RDF/reification을 단 한 번도 호출하지 않고(SEM/accordingTo 언급 0건), 시나리오 겹을 '소속 3필드(scenarioId·baseId·removed) + copy-on-write 델타'로 레코드-grain에 native하게 저장한다. 만약 진술-grain reification(필드마다 quad)을 저장했다면 모든 필드가 독립 CloudKit 레코드로 폭발해 원자성 부재와 곱셈으로 악화됐을 것 — 올바른 경량 표현을 이미 골랐다. '어휘로만 빌리고 저장 표현은 우리 것'이라는 경계가 실제 관행과 일치한다.남은 두 가지 정밀화
- MCP 읽기는 1:1이 아니다. '하나의 사건 코어 = 하나의 MCP 표면'은 쓰기 측 진술이다. 읽기/질의 표면은 아직 역인덱스 없는 선형 스캔이라, 코파일럿용 질의·집계 읽기 모델(CQRS 투영)은 별도 작업으로 명시한다. 그리고 킬러 유스케이스가 진짜 '무쓰기'이려면 코어가 저장된 Scenario 투영과 구별되는 인메모리 임시 델타 투영(project-with-ephemeral-overlay) 진입점을 노출해야 한다 —
createScenario/promoteScenario는 단일 소유자ModelContext를 거치는 변이이기 때문이다. - 헤드리스 코어의 입력 측 seam. 코어는 UI-프레임워크에서 자유롭지만 여전히 영속성-결합이다 — SwiftData의 main-actor 친화성 +
@Modelnon-Sendable +ModelActor경계가 진짜 통합 마찰이다.@Model → Sendable value-DTO매핑 층을 두어야 순수 계산(레이아웃·투영·겹 합성)이 액터-자유·테스트 가능해진다 — 디스플레이 리스트가 '출력'을 풀듯 이 매핑이 '입력'을 푼다.
8 · 우리 서비스의 열 가지 상황, 코어 위에서
앱 셸이 실제로 다루는 상황을 사건 코어 + 다섯 층으로 구성하면 이렇게 된다. 각 예시는 데이터 모델 정본과 대조해 다듬었고, 어느 층에 앉는지 칩으로 표시한다.
Scenario('제주 이주', exploring)·Scenario('서울 잔류', exploring) 두 겹. 각 겹에 소속 Event·Flow·Goal·Allocation(scenarioId 채움). 검토는 저장 없이 계산(확정−가림+델타), 여비에서 곡선 2벌을 나란히. '제주 이주'를 chosen하면 그 겹의 델타만 확정 세계로 승격, 형제는 discarded로 보존(가지 않은 길이 이력으로 남는다).Link 유무로 파생). '결정 시점 ≠ 실행 시점' — '제주 이주 결정'(지금 근처)과 '이사'(미래)는 별개 결절. 승격 순간부터 배정은 다시 하나.Event{start=null, status=backlog} + Involvement(person '아버지', place '제주'). 사건이 시간 없이 존재한다(정본: start는 선택값). 여정 타임라인엔 비노출, 수첩에만 산다(L4 필터). 이 backlog 사건에도 Allocation을 붙일 수 있어 — 여정엔 안 떠도 여비엔 뜬다.Event(kind=milestone) 생성 + "그때 누구와·어디였나요"가 Involvement(person/place)를 자연히 채움 → Reflection 기록 + Link(reflection↔event).Interview·Reflection 자체는 서사 층(L5)이다 — 초안의 L0~L4 도식이 이 층을 누락했기에 복원했다. 엔진(서사)이 데이터를 만든다.Flow{cadence=null, eventId=이사 사건} = 곡선의 점프. 시점은 앵커 사건의 start에서 파생(구간 사건이어도 시작일 — 보증금은 이사 '시작일'에 나간다). 사건을 옮기면 점프도 따라 움직인다. 돈은 SEM에 없는 경제 층(L2)이고 사건(L0)에 앵커된다.eventId 필수'엔 정본 예외 하나 — 만기 점프는 eventId 없이 시점이 Pocket.lockRule에서 파생. '항상 필수'로 쓰지 않는다.Person → 그를 targetId로 갖는 Involvement 역참조 → 그 사건들로 여정(1차 렌즈)을 필터 = "그 사람과의 여정". SwiftData면 person.involvements가 이 역인덱스를 준다(단 Involvement 1급 @Model 승격 조건부).role은 Involvement의 nullable 필드다. 같은 Person이 두 사건에 다른 역할로 등장 = 두 개의 Involvement 행, 각 행이 제 role을 지님. SEM의 reify된 sem:Role 노드에 대응하는 우리 것은 '별개 Role 노드'가 아니라 '별개 Involvement 행'이다 — Involvement 자체가 그 reification 자리를 겸한다.role을 '비워 두고 출발'(role=null), 어휘 확정은 phasing. 따라서 '다른 role'은 개념·미래형이며 현재 저장값은 null이다.Event{kind=index, start·end 필수} + Involvement(place). 정본: "index가 Place 성분을 가지면 거주·활동 무대 이력이 실린다". 지도 렌즈(L4)도 같은 성분을 역참조로 읽는다.Subject('비상금') + Goal{targetType=subject, targetAmount=300만} + Allocation{targetType=subject}. 충전 게이지 = Σ Allocation(대상) / Goal.targetAmount — 저장 안 하고 파생(L1). '필요(Goal)'와 '배정(Allocation)'은 다른 값, 게이지는 그 비율.Flow도 앵커 사건도 필요 없다 — 순수 Subject+Goal+Allocation. 사물함은 2차 렌즈(L4)로 이 돈 4역을 참조해 게이지를 보여줄 뿐 소유하지 않는다.Scenario(exploring) + 소속 Event/Flow/Goal/Allocation을 즉석 생성 → compareScenarios가 곡선 N벌을 계산(저장 안 함, 확정−가림+델타). MCP 표면 자체는 인터페이스/아키텍처이지 데이터 엔티티가 아니다(정본 밖 — 네이티브 이식 문서 소관).Flow{fromPocketId=생활비통장, toPocketId=적금, cadence=monthly}. from·to 둘 다 지갑 = 내부 이동 → 순자산 총합에서 상쇄(불변), 바뀌는 것은 유동성(어느 지갑에 있나)뿐. "주머닛돈이 쌈짓돈" — 적금 납입이 지출로 왜곡되지 않는 이유. 금융상품의 계약된 이동(납입 계단·만기 점프)은 별도 엔티티가 아니라 프리셋이 만드는 이 Flow들이다.NetWorthSnapshot이고 Pocket은 '스톡의 내부 구조(거처)'로 4역 밖이다 — 'Stock=NetWorthSnapshot, Pocket=거처'로 분리해 읽는다.9 · 경계와 리스크
이 문서가 스스로 인정하는 것들 — 판정이 '가능하다'로 끝나되 '무조건 이득'으로 끝나지 않는 이유.
- 재기술이지 채택이 아니다. '저장 표현은 우리 것, 표준은 어휘·검증·export로만'이라 못박은 순간, 이것은 정의상 '이미 도달한 것을 표준 계보로 정렬'하는 일이다. 런타임 이득은 0. 그래서 표제·판정에서 'SEM을 코어로'를 내렸다.
- 기본 시나리오 겹은 깨끗한 CQRS가 아니다. 배포된 겹은 전역 엔진 상태 변경(한 번에 하나)이고 읽기 렌즈가 수동 반영한다. '쓰기 하나·읽기 N' 서사는 N-곡선 비교(순수 재합성)에서만 성립한다.
- CloudKit 무결성 화해 절이 미설계. 동시 다기기 병합 하 사건 애그리게이트를 감싸는 트랜잭션 경계가 없고, 그 완충은 아직 언명뿐이다(§7). 설계 전엔 CloudKit 다리의 순이익 미획득.
- 오늘의 '단일 소스'는 세 소스다. 소비자별 병합 불일치가 실재한다(가려진 done의 인물이 렌즈마다 다르게 집계). 깨끗한 단일 소스는 SwiftData 포트가 배달한다.
- 역인덱스는 오늘 존재하지 않는다. 전방
byEvent맵의 선형 스캔뿐이고, SwiftData 이득도Involvement1급@Model승격을 전제로 한 조건부·집합소속 한정·복합 질의엔 별도 fetch·CloudKit 결과적 일관성이다. - L0~L5 도식은 정본을 대체하지 않는다. 정본 가드레일 7층(문장·성분·마스터·돈4역·겹·서사·계정) 위에 정합하는 재정초 어휘일 뿐이다(대응표는 §4).
- export·유형에서 SEM은 셋 중 최약체. 실효 대상은 schema.org/CIDOC다.
- 헤드리스 코어의 동시성 seam이 미해결.
@Modelnon-Sendable·main-actor 경계 →@Model→Sendable DTO매핑 층이 없으면 순수 계산이 진짜 테스트 가능해지지 않는다.
10 · SEM으로 정말 다시 세우는 것이 옳아지는 단 하나의 미래
제품이 '자서전'에서 '공존 연대기'로 피벗할 때
지금 우리 정체성은 단독 자서전이다 — 원칙 9의 암묵 주어 '나', 단일 소유 RLS. 그런데 대면 인터뷰가 깊어져 여러 화자가 같은 과거를 다르게 증언하고, 그 모두를 '공존하는 사실'로 보존하며 해소를 하지 않는 것이 가치 제안이 되는 순간 — 아버지가 기억하는 그날과 내가 기억하는 그날이 둘 다 참으로 남아야 하는 순간 — 이 온다면, 그때 sem:View/accordingTo/Authority는 '먼 친척'이 아니라 바로 그 저장 기제가 된다(1947 경찰행동 예와 동형: 한 사건, 두 Authority, 두 기술).
그 제품에서는 우리 시나리오 겹의 '결정 지향'(붕괴)과 정반대인 SEM의 '구성적 비해소'(영구 개방)가 오히려 정확히 옳은 기질이 된다. 그리고 그것을 단일 소유 RLS + copy-on-write 시나리오 위에 나중에 얹는 일이야말로 진짜 '갈아엎기'가 된다 — 모델의 phasing 항목(Person↔User.uid 소유권 이전, Publication 익명화)이 정확히 이 분기점을 가리킨다.
다음 액션 — '재아키텍처'가 아니라 조인 산출물
- 계보 노트 유지·보강. ontology.html이 이미 '검증 + 사전'을 적정 용량으로 수행 중 — 이 문서의 정밀 교정(≈ 수위, 세 어긋남, 차용/수렴 분리)을 그 노트에 역류.
- export 매핑 부록.
Event↔schema:Event(startDate/endDate/location/attendee)·Involvement↔P11/P7·roleType·Subject↔about대응표. 대상은 schema.org/Event + CIDOC(SEM 아님). 암묵 주어·시제 미저장·겹이 매핑에서 무엇을 잃는지가 진짜 설계 문제. Involvement.role어휘(액션 A2). Fillmore 의미역 × CIDOCP14.1× SEMroleType(장소 role 포함) × GEDCOM 역할의 대조표 → 최소 어휘(with/host/at/from/to/acquire/use). 열린 어휘 vs 통제 어휘는 원칙 8과 함께.- 헤드리스 사건 코어 추출. repository + 겹 합성 + 순자산 곡선 + 디스플레이 리스트를 UI·MCP·export 공용으로.
@Model→Sendable DTO매핑 층(§7)과 '인메모리 임시 델타 투영' 진입점(§8 예시9)을 계약으로 명시. Involvement1급@Model승격. 2차 렌즈 역인덱스의 전제(§6). id-배열 blob → event+target 관계.- 유형 도입은 가드레일 통과 후. 파생(제목·성분 추정)으로 충분한지 먼저. 필요 시 어휘는 CIDOC(
E67 Birth·E9 Move)·schema.org 하위타입에서.