<a id="storage-topology"></a>
# 데이터 모델과 저장 계약

ARTEX는 PostgreSQL 선언상 51개 table, traffic SQLite 일반 table 3개와 선택적 FTS5 virtual table 1개, 여러 filesystem tree를 결합한다. PostgreSQL core `db/schema.sql`은 49 table·520 final column·97 index·4 function·22 trigger이고, `llm_records`와 `llm_usage`가 2 table·31 column·5 index를 더한다. 두 startup `Ensure*Table`은 best-effort라 core가 떠도 보조 ledger가 없을 수 있다. 이 숫자는 고정 source revision의 선언 분모이며 운영 DB 상태를 뜻하지 않는다. [Schema](evidence:schema-core) [LLM records schema](evidence:schema-db-commands) [LLM usage schema](evidence:schema-db-llm-usage)

```mermaid
flowchart LR
  Task[(tasks)] --> Exp[(explorations)]
  Exp --> Node[(exploration_nodes)]
  Node --> Edge[(exploration_edges)]
  Node --> Anchor[(exploration_anchors)]
  Anchor --> Asset[(assets)]
  Task --> Scope[(task_scope)]
  Task --> Link[(task_asset_links)]
  Link --> Asset
  Node --> Finding[(findings)]
  Finding --> Binding[(finding_traffic_bindings)]
  Binding --> Snap[(traffic_evidence_snapshots)]
  Snap -. copied from .-> Exchange[(Traffic SQLite exchanges)]
  Task --> Activity[(activity)]
  Task --> Profile[(task_llm_profiles)]
  Profile --> LLM[(llm_profiles)]

  click Task "db/task-exploration.md#table-tasks" "tasks"
  click Node "db/task-exploration.md#table-exploration-nodes" "exploration_nodes"
  click Asset "db/task-exploration.md#table-assets" "assets"
  click Finding "db/findings-operations.md#table-findings" "findings"
  click Snap "db/findings-operations.md#table-traffic-evidence-snapshots" "snapshots"
  click Exchange "db/traffic-sqlite.md#table-exchanges" "traffic exchanges"
  click LLM "db/agent-runtime.md#table-llm-profiles" "LLM profiles"
```

텍스트 관계: task는 exploration과 scope/asset/profile 연결을 소유한다. Exploration node는 edge와 asset anchor로 graph를 만들며 finding node는 별도 finding row와 이어진다. Finding traffic binding은 evidence snapshot을 가리키고, snapshot body의 원본은 capture 당시 Traffic store exchange다.

<a id="postgres-map"></a>
## PostgreSQL 독립 진입

| 데이터 영역 | 객체 수 | 상세 |
|---|---:|---|
| 태스크·자산·탐색 | 18 | [task/exploration 객체와 최종 DDL](db/task-exploration.md#db-task) |
| Agent·LLM·도구·대화 | 18 | [agent runtime 객체와 최종 DDL](db/agent-runtime.md#db-agent) |
| Finding·승인·알림·운영 | 15 | [finding/operations 객체와 최종 DDL](db/findings-operations.md#db-ops) |
| function·trigger·migration | 4 function, 22 trigger | [table 밖 schema 동작](db/functions-triggers.md#db-functions-triggers) |
| fresh table-level constraint | 24 clause / 17 table | [composite 순서·invariant·delete/race 의미](db/table-constraints.md#db-table-constraints) |

각 객체 페이지는 CREATE 정의, 후속 ALTER, index와 저장소 경계를 원문 위치로 연결한다. Composite PK/UNIQUE와 cross-column CHECK는 [24/24 table-level constraint matrix](db/table-constraints.md#db-table-constraints)가 별도 분모로 소유한다. 51개 table·551개 field의 business meaning, type/null/default/FK, 민감도·encoding, production static SQL·정적으로 평가한 string expression·runtime template·닫힌 identifier allowlist의 reader/writer, index 비용과 retention/role 미확인은 [필드·접근 의미 카탈로그](db/semantic-catalog.md#db-semantic-catalog)가 별도로 소유한다. 임의 동적 identifier와 실제 call reachability는 U다. `DO` 블록의 조건부 constraint와 runtime transaction은 raw schema 및 해당 기능/모듈도 함께 대조한다.

<a id="traffic-map"></a>
## Traffic SQLite·file 독립 진입

| 객체/저장소 | 역할 | 상세 |
|---|---|---|
| `exchanges` | HTTP exchange 검색·목록 metadata | [정의](db/traffic-sqlite.md#table-exchanges) |
| `exchange_bodies` | inline body 또는 blob reference | [정의](db/traffic-sqlite.md#table-exchange-bodies) |
| `blob_refs` | exchange↔content-addressed blob | [정의](db/traffic-sqlite.md#table-blob-refs) |
| `ex_fts` | 선택적 contentless trigram FTS5 index | [정의와 fallback](db/traffic-sqlite.md#table-ex-fts) |
| CAS blob/CA + legacy host tree | 큰 body와 MITM 자원; host tree는 non-empty legacy `path` 호환만 | [Traffic 저장 계약](db/traffic-sqlite.md#filesystem) |

<a id="core-entities"></a>
## PostgreSQL 영역

| 논리 영역 | root | 공유/삭제 경계 |
|---|---|---|
| task 실행 | `tasks`→`explorations` | task archive/delete 대상; global config는 제외 |
| asset graph | `assets`, `companies` | 여러 task가 공유; task link 제거가 asset 삭제와 같지 않음 |
| exploration graph | node/edge/anchor/activity | task exploration에 종속, intent soft/hard delete 규칙 |
| agent configuration | profiles/agents/prompts/tools/MCP/visibility | global; task/profile binding으로 소비 |
| finding/evidence | finding/retest/snapshot/binding | task와 graph node에 연결, body files와 함께 보존 필요 |
| approval/notification | rule/pending/event/delivery | global/task 혼합, 외부 효과와 DB 상태 구분 |
| conversation | conversation/activity, side session/request | task main session과 별도 retention |

<a id="exploration-graph"></a>
## 탐색 그래프

Node kind는 goal, intent, fact, finding, hint, digest 등이며 state/priority/payload/source/content version/cold round를 갖는다. Edge rel은 goal proof, intent lineage와 yields/derived relationships를 표현한다. Anchor는 node를 typed asset id에 연결한다. [Exploration store](evidence:exploration-store)

Frontier는 `open` intent를 `priority DESC, id ASC`로 읽고 conditional state update로 claim한다. Node/edge row가 graph 의미를 전부 강제하지 않으며 parent kind, finding transaction, goal proof 같은 불변조건은 application tool handler에도 있다.

<a id="minimum-record"></a>
## 실행을 잇는 최소 상태 단위

장기 실행의 최소 단위는 단순 URL이 아니라 다음 식별자를 연결한 기록이다.

| 필드군 | 예 | 필요한 이유 |
|---|---|---|
| task/exploration | task id, exploration id, source task | 실행·상속·보존 경계 |
| hypothesis/work | intent node id, summary, priority, parents | 왜 이 branch를 시작했는가 |
| target | asset ids, task scope, company/source | 무엇을 어떤 허가 문맥에서 봤는가 |
| action | worker/session/tool-use id, input digest | 무엇을 실제 실행했는가 |
| observation | activity/result, traffic exchange | 무엇을 관찰했는가 |
| conclusion | fact/finding node, confidence/report status | 다음 planner가 무엇을 믿는가 |
| evidence | snapshot/binding/hash/version | 결론을 어떤 bytes로 검토하는가 |
| lifecycle | state, terminal reason, retry/delete/archive | 계속·중단·재개·보존 기준 |

ARTEX는 이 필드군을 한 table에 합치지 않고 graph/asset/activity/finding/evidence에 분산한다. 관계를 유지해야 중복 탐색과 근거 없는 결론을 구별할 수 있다.

<a id="finding-duality"></a>
## Finding의 이중 표현

Exploration finding node는 planner lineage와 goal proof를, `findings` row는 목록·status·severity·report·retest/evidence version을 소유한다. `RecordFinding` transaction이 둘과 source intent/yields/anchor를 함께 만든다. 한쪽만 읽으면 graph 문맥 또는 사용자 결과 metadata를 잃는다. [Finding flow](../features/findings-evidence.md#finding-transaction)

<a id="asset-scope"></a>
## Asset, task scope, coverage

`assets.task_ids`와 `task_asset_links`는 호환/세부 provenance가 겹치는 연결이고, `task_scope`는 authorization/coverage edge, `exploration_anchors`는 특정 node가 다룬 target이다. Company scope는 조직 귀속 규칙이다. 이들을 하나의 “scope”로 평탄화하지 않는다. [자산 기능](../features/assets-scope.md#scope-model)

<a id="activity-delivery"></a>
## Activity와 SSE cursor

Task `activity`와 standalone `conversation_activities`는 session event를 append하고 sequence id로 history/SSE cursor를 제공한다. `llm_records`는 raw request/response와 session/profile/task metadata, `llm_usage`는 token/비용 계량을 별도로 저장한다. Activity delivery 성공과 model provider 원본 기록/usage insert 성공은 별도다.

<a id="transaction-boundaries"></a>
## 주요 원자성 경계

| 동작 | 같은 transaction/lock에 포함 | 밖에 남는 효과 |
|---|---|---|
| task create | task/exploration/연결·일부 초기 상태 | goal LLM call, Engine start |
| intent claim | state conditional update | worker의 외부 tool |
| finding record | graph node/edge/anchor, finding, snapshot metadata/binding; notification event는 같은 transaction의 best-effort savepoint 시도 | event insert rollback이 격리되면 finding은 event 없이 commit 가능; body file stage/후속 reporter·delivery |
| task scope batch | 요청에 따라 asset/scope/link transaction | 후속 worker/enrichment |
| archive restore | 단계별 DB/file/traffic 설치와 journal | 전체 인스턴스 rollback |
| notification claim | delivery lease/state | remote channel이 받았는지 결과 불명 가능 |

<a id="retention"></a>
## 삭제·보존·복구 범위

Task archive는 선별된 task-owned rows와 workspace/transcript/traffic/evidence를 package로 만든다. Global 설정, profiles/tools/skills, JWT key, 모든 independent conversation과 archive query에 없는 기능 row는 포함되지 않을 수 있다. Task archive를 전체 backup으로 해석하지 않는다. [운영 archive](../operations.md#archives)

<a id="database-gaps"></a>
## Schema·환경 미확인

- 실제 PostgreSQL role/grant/RLS 선언은 저장소 schema에서 확인되지 않았다. DB 계정 권한은 배포 환경에 의존한다.
- 단일 migration ledger/version row가 없으므로 runtime schema 적용 여부는 catalog query로 별도 확인해야 한다.
- JSONB/array 내부 schema는 DB가 모두 강제하지 않으며 Go validator/merge logic이 권위인 필드가 많다.
- index 존재는 운영 규모의 query 성능을 보장하지 않는다.
- task archive와 DB backup의 restore rehearsal, RPO/RTO는 선언돼 있지 않다.
