# Adaptive Guidebook 1차 동작 테스트 기획

## 기능의 가치

Adaptive Guidebook은 Coding Agent가 코드를 변경한 시점에 현재 코드와 관련된 개발 Guideline을 Context로 제공합니다.

팀의 개발 지침은 `Guideline` 단위로 관리합니다. Agent가 변경한 파일을 기준으로 관련 Guideline을 검색하고, 해당 `content`를 Agent의 다음 inference에 연결합니다.

이를 통해 개발 지침을 실제 코드 변경 시점과 연결할 수 있습니다.

```text id="8d1hpx"
Code Change
    ↓
Relevant Guideline
    ↓
Agent Context
    ↓
Next Inference
```

Adaptive Guidebook의 핵심 가치는 **현재 작업과 관련된 Guideline을 필요한 시점에 Agent Context로 제공하는 것**입니다.

## 기능의 목표

1차 동작 테스트에서는 Codex의 코드 변경과 Adaptive Guidebook 검색을 연결하는 전체 경로를 검증합니다.

검증할 경로는 다음과 같습니다.

```text id="yndbbg"
Codex apply_patch
        ↓
PostToolUse
        ↓
Hook Layer
        ↓
Adaptive Guidebook Search
        ↓
Matched Guidelines
        ↓
additionalContext
        ↓
Codex next inference
```

1차 테스트에서는 다음 동작을 완성합니다.

- Guideline은 로컬 `guideline.yaml`에서 읽습니다.
- Codex `apply_patch` 결과에서 변경 파일을 식별합니다.
- Hook Layer는 변경 파일을 절대 경로로 정규화합니다.
- Adaptive Guidebook은 변경된 파일의 현재 내용을 직접 읽습니다.
- Regex를 사용하여 관련 Guideline을 검색합니다.
- Match된 Guideline 전체를 Presentation합니다.
- Codex `session_id`를 기준으로 Presentation 상태를 유지합니다.
- Presentation 상태는 SQLite에 저장합니다.
- Presentation할 Guideline을 Codex `additionalContext`로 반환합니다.

## 시스템의 흐름

전체 동작은 다음 순서로 진행됩니다.

```text id="16x79h"
Codex
  ↓
apply_patch
  ↓
PostToolUse
  ↓
Hook Layer
  ↓
tool_response 파싱
  ↓
변경 path 추출
  ↓
절대 filepath 정규화
  ↓
SearchRequest
{
  sessionId,
  filepaths[]
}
  ↓
Adaptive Guidebook Application
  ↓
repository identity 계산
  ↓
Guideline 로드
  ↓
Applicable Guideline 계산
  ↓
변경 파일 내용 읽기
  ↓
Regex Match
  ↓
Presentation 상태 조회
  ↓
Guidelines to Present
  ↓
Presentation 상태 저장
  ↓
SearchResponse
[
  { id, content }
]
  ↓
Hook Layer
  ↓
Presentation Text
  ↓
additionalContext
  ↓
Codex next inference
```

### 파일 경로 추출

Codex의 `apply_patch` 결과는 다음과 같은 형태입니다.

```text id="mp7bj2"
Success. Updated the following files:
A src/foo.kt
M README.md
D docs/legacy.md
```

Hook Layer는 각 항목에서 파일 경로를 추출합니다.

각 경로는 절대 경로로 정규화합니다.

```text id="57eb93"
tool_response에서 path 추출
        ↓
absolute path?
   ├─ yes → 해당 path 사용
   └─ no  → cwd.resolve(path)
        ↓
normalized absolute filepath
```

예를 들어 `cwd`가 다음과 같다고 가정합니다.

```text id="fhp3q1"
/Users/user/projects/backend-order
```

변경 경로가 다음 값이면

```text id="l8jfe5"
src/main/kotlin/OrderService.kt
```

Search Request에는 다음 절대 경로를 전달합니다.

```text id="qmd63a"
/Users/user/projects/backend-order/src/main/kotlin/OrderService.kt
```

삭제된 파일은 1차 동작 테스트의 검색 대상에서 제외합니다.

### Search Request

Hook Layer와 Adaptive Guidebook Application 사이의 입력 계약은 다음과 같습니다.

```json id="11ol1c"
{
  "sessionId": "codex-session-id",
  "filepaths": [
    "/Users/user/projects/backend-order/src/main/kotlin/OrderService.kt",
    "/Users/user/projects/backend-order/README.md"
  ]
}
```

`sessionId`는 Codex의 `session_id`입니다.

`filepaths`는 정규화된 절대 경로입니다.

### Repository 식별

Adaptive Guidebook은 검색 대상 파일이 속한 Git repository의 `origin`을 repository identity로 사용합니다.

기준 명령은 다음과 같습니다.

```bash id="ffuh5q"
git remote get-url origin
```

예:

```text id="rtxtag"
git@github.com:acme/backend-order.git
```

또는

```text id="tp8sxs"
https://github.com/acme/backend-order.git
```

Remote URL을 분리하여 다음 형식으로 정규화합니다.

```text id="mi3wbz"
acme/backend-order
```

Repository identity는 Guideline의 `scope.repositories`를 평가할 때 사용합니다.

```text id="rnz7jf"
absolute filepath
        ↓
Git repository
        ↓
git remote get-url origin
        ↓
remote URL split
        ↓
owner/repository
        ↓
scope.repositories
```

### Guideline 검색

Adaptive Guidebook은 Search Request의 절대 경로를 사용하여 파일 내용을 직접 읽습니다.

```text id="qcnoua"
absolute filepath
        ↓
source read
        ↓
Applicable Guidelines
        ↓
Regex Match
        ↓
Matched Guidelines
```

하나의 파일 내용은 하나의 Search에서 1회 읽습니다.

### Presentation 처리

Matched Guideline은 SQLite의 Presentation 상태와 결합합니다.

```text id="w2l9hl"
Matched Guideline
        +
sessionId
        ↓
Presentation 조회
        ↓
Guideline to Present
```

현재 Codex Session에서 처음 Presentation되는 Guideline을 Search Response에 포함합니다.

Presentation 대상으로 결정된 Guideline은 SQLite에 기록합니다.

### Search Response

Adaptive Guidebook은 Hook Layer에 `id`와 `content`를 반환합니다.

```json id="ewv5a5"
[
  {
    "id": "service-depends-on-port",
    "content": "Services should depend on repository abstractions."
  },
  {
    "id": "preserve-required-config",
    "content": "Required configuration files must remain available."
  }
]
```

### Codex Context 생성

Hook Layer는 Search Response를 하나의 문자열로 구성합니다.

```text id="rbuxqn"
Adaptive Guidebook guidelines relevant to the latest changes:

[service-depends-on-port]
Services should depend on repository abstractions.

[preserve-required-config]
Required configuration files must remain available.
```

이 문자열을 `additionalContext`로 반환합니다.

```json id="my1xwo"
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "..."
  }
}
```

## 핵심 원리

### Guideline은 현재 코드와 함께 평가합니다

Guideline 검색의 시작점은 Codex가 변경한 파일입니다.

Adaptive Guidebook은 전달받은 절대 경로에서 현재 파일 내용을 읽고 Guideline의 `match` 조건을 평가합니다.

```text id="2ms0pr"
filepath
    ↓
current source
    ↓
Guideline match
```

### Regex Match는 관련성을 나타냅니다

POC에서 `match`는 Regex로 평가합니다.

Regex가 일치하면 해당 Guideline을 현재 변경과 관련된 Guideline으로 판단합니다.

```text id="rk4qkd"
Applicable Guideline
        ↓
Regex
        ↓
Matched Guideline
```

### Match된 Guideline 전체를 Presentation합니다

1차 동작 테스트에서는 Match된 Guideline 전체가 Presentation 대상입니다.

```text id="isguj8"
Matched Guidelines
        ↓
Presentation State
        ↓
Guidelines to Present
```

### Presentation은 Session 단위로 관리합니다

Codex `session_id`가 Presentation 상태의 Session 경계입니다.

하나의 Guideline은 같은 Session에서 1회 Presentation합니다.

고유성은 다음 조합으로 정의합니다.

```text id="1cd658"
session_id + guideline_id
```

### Guideline과 Presentation 상태를 분리합니다

Guideline 정의는 로컬 파일 시스템에 저장합니다.

```text id="a6qd2w"
guideline.yaml
```

Presentation 상태는 SQLite에 저장합니다.

```text id="by16yc"
~/.adaptive-guidebook/adaptive-guidebook.db
```

두 저장소는 다음 책임을 가집니다.

```text id="to3x52"
guideline.yaml
→ Guideline 정의

SQLite
→ Presentation 상태
```

### Hook Layer는 Protocol Adapter입니다

Hook Layer는 Codex의 `PostToolUse` 입력을 Adaptive Guidebook Search 계약으로 변환하고, Search Response를 Codex `additionalContext`로 변환합니다.

```text id="fpcikl"
Codex Protocol
      ↓
Hook Layer
      ↓
Guidebook Search Contract
```

Adaptive Guidebook Application은 Codex Hook의 응답 형식을 직접 다루지 않습니다.

## 시스템 구성요소별 기능과 책임

### Codex Hook Layer

Hook Layer는 Codex와 Adaptive Guidebook Application을 연결합니다.

주요 기능은 다음과 같습니다.

```text id="5dl6sh"
PostToolUse 수신
        ↓
apply_patch 결과 파싱
        ↓
path 추출
        ↓
삭제 항목 제외
        ↓
absolute filepath 정규화
        ↓
SearchRequest 생성
        ↓
Adaptive Guidebook 호출
        ↓
SearchResponse 수신
        ↓
Presentation Text 생성
        ↓
additionalContext 반환
```

Hook Layer가 Application에 전달하는 데이터는 `sessionId`와 `filepaths`입니다.

### Adaptive Guidebook Application

Adaptive Guidebook Application은 Guideline 검색과 Presentation 상태를 관리합니다.

주요 기능은 다음과 같습니다.

```text id="bc03yo"
SearchRequest 수신
        ↓
Repository 식별
        ↓
Guideline 로드
        ↓
Applicable Guideline 계산
        ↓
파일 읽기
        ↓
Regex Match
        ↓
Presentation 상태 조회
        ↓
Presentation 기록
        ↓
SearchResponse 반환
```

Application 내부에는 Guideline Loader, Repository Resolver, Guideline Resolver, Regex Matcher, Presentation Repository가 위치합니다.

### Local Guideline Store

Local Guideline Store는 Guideline 정의를 보관합니다.

각 Guideline은 하나의 `guideline.yaml`로 저장합니다.

```text id="n275xd"
adaptive-guidebook/
├── architecture/
│   └── service-depends-on-port/
│       └── guideline.yaml
├── domain/
│   └── prefer-domain-behavior/
│       └── guideline.yaml
└── schema/
    └── guideline.schema.json
```

Guidebook Root 아래의 `guideline.yaml`을 재귀적으로 탐색합니다.

### Presentation Store

Presentation Store는 SQLite를 사용합니다.

SQLite 파일 위치는 다음과 같습니다.

```text id="ppavj8"
~/.adaptive-guidebook/adaptive-guidebook.db
```

`presentation` 테이블은 Codex Session에서 Guideline을 Presentation한 사실을 저장합니다.

```sql id="sw8k4x"
CREATE TABLE presentation (
    session_id TEXT NOT NULL,
    guideline_id TEXT NOT NULL,
    presented_at TEXT NOT NULL,
    PRIMARY KEY (session_id, guideline_id)
);
```

`presented_at`은 UTC 기준 RFC 3339 형식입니다.

예:

```text id="itocwg"
2026-09-23T09:41:18Z
```

## 시스템 간 연결 방식

### Codex → Hook Layer

Codex `PostToolUse`가 Hook Layer를 호출합니다.

Hook Layer는 `apply_patch`의 `tool_response`, Codex `session_id`, `cwd`를 사용합니다.

```text id="eydsze"
PostToolUse
├── session_id
├── cwd
└── apply_patch tool_response
```

### Hook Layer → Adaptive Guidebook Application

연결 계약은 `SearchRequest`입니다.

```text id="i399pz"
SearchRequest
├── sessionId
└── filepaths[]
```

`filepaths`는 절대 경로입니다.

예:

```json id="dmpjh5"
{
  "sessionId": "codex-session-id",
  "filepaths": [
    "/Users/user/projects/backend-order/src/main/kotlin/OrderService.kt"
  ]
}
```

### Adaptive Guidebook Application → Local Source File

Application은 `filepaths`의 절대 경로를 직접 읽습니다.

```text id="0f9d8w"
absolute filepath
        ↓
source
```

### Adaptive Guidebook Application → Git

Application은 파일이 속한 Git repository에서 다음 명령을 실행합니다.

```bash id="57pzct"
git remote get-url origin
```

Remote URL을 `owner/repository` 형식의 `RepositoryIdentity`로 변환합니다.

### Adaptive Guidebook Application → Local Guideline Store

Application은 Guidebook Root 아래의 `guideline.yaml`을 읽습니다.

```text id="dpo9wf"
Guidebook Root
      ↓
**/guideline.yaml
      ↓
Guideline[]
```

### Adaptive Guidebook Application → Presentation Store

Application은 `session_id + guideline_id`로 Presentation 상태를 조회합니다.

Presentation 대상으로 결정된 Guideline은 `presented_at`과 함께 저장합니다.

### Adaptive Guidebook Application → Hook Layer

Search Response는 다음 구조입니다.

```text id="wiuqxd"
SearchResponse
└── Guideline[]
      ├── id
      └── content
```

JSON 표현은 다음과 같습니다.

```json id="6uylrb"
[
  {
    "id": "service-depends-on-port",
    "content": "Services should depend on repository abstractions."
  }
]
```

### Hook Layer → Codex

Hook Layer는 Search Response를 Presentation Text로 변환하고 `additionalContext`에 넣습니다.

```text id="ccfrzm"
SearchResponse
      ↓
Presentation Text
      ↓
additionalContext
      ↓
Codex
```

## Guideline 정의

Guideline은 다음 구조를 사용합니다.

```yaml id="ulcje1"
id: service-depends-on-port

scope:
  repositories:
    - acme/backend-*
  languages:
    - kotlin

when:
  - file_changed

match:
  any:
    - regex:
        pattern: 'RepositoryImpl'

content: |
  Services should depend on repository abstractions.
```

### `id`

Guidebook 안에서 Guideline을 식별하는 고유 값입니다.

```text id="az0rkd"
service-depends-on-port
```

### `scope.repositories`

Guideline이 적용되는 repository를 정의합니다.

```yaml id="5ih47q"
scope:
  repositories:
    - acme/backend-*
```

비교 대상은 Git `origin`에서 계산한 `owner/repository`입니다.

### `scope.languages`

1차 동작 테스트에서는 Kotlin을 사용합니다.

```yaml id="nlw6j4"
languages:
  - kotlin
```

Kotlin source 확장자는 `.kt`, `.kts`입니다.

### `when`

1차 동작 테스트의 Guidebook Event는 `file_changed`입니다.

```yaml id="m5ar6i"
when:
  - file_changed
```

Codex `PostToolUse`가 이 event를 발생시킵니다.

### `match`

관련성 검색은 Regex를 사용합니다.

```yaml id="x6tv2m"
match:
  any:
    - regex:
        pattern: 'RepositoryImpl'
    - regex:
        pattern: 'JpaRepository'
```

`any`에 포함된 Regex 중 하나가 일치하면 해당 Guideline은 Matched Guideline이 됩니다.

### `content`

`content`는 Codex Context에 Presentation할 개발 지침 본문입니다.

```yaml id="m20x7c"
content: |
  Services should depend on repository abstractions.
```

## Application 내부 도메인

### `Guideline`

```text id="lkqp5u"
Guideline
├── id
├── scope
├── when
├── match
└── content
```

### `RepositoryIdentity`

현재 source가 속한 remote repository를 나타냅니다.

```text id="t20cyr"
owner/repository
```

예:

```text id="d77ujt"
acme/backend-order
```

### `ApplicableGuideline`

현재 `RepositoryIdentity`, language, event에 적용되는 Guideline입니다.

### `MatchedGuideline`

현재 source가 Guideline의 `match` 조건을 만족한 상태입니다.

### `Presentation`

특정 Codex Session에서 특정 Guideline을 Presentation한 사실입니다.

```text id="sw4la4"
Presentation
├── sessionId
├── guidelineId
└── presentedAt
```

### `SearchRequest`

```text id="aqom62"
SearchRequest
├── sessionId
└── filepaths[]
```

### `SearchResult`

```text id="aqreti"
SearchResult
└── Guideline[]
      ├── id
      └── content
```

## 1차 동작 테스트 범위

1차 테스트의 실행 경로는 다음과 같습니다.

```text id="0v7zzg"
Local guideline.yaml
        +
Codex apply_patch
        ↓
PostToolUse
        ↓
Hook Layer
        ↓
sessionId + absolute filepaths[]
        ↓
Adaptive Guidebook Application
        ↓
Regex Match
        ↓
SQLite Presentation State
        ↓
{id, content}[]
        ↓
additionalContext
        ↓
Codex next inference
```

삭제된 파일은 검색 대상에서 제외하는 것으로 임시 적용합니다.

## [미결정]

### Guidebook Root 설정 방식

Adaptive Guidebook Application이 Local Guideline Store의 root 디렉터리를 전달받는 방식을 결정해야 합니다.

필요한 값은 다음 하나입니다.

```text id="spzh2k"
guidebookRoot
```

설정 방식이 결정되면 1차 동작 테스트의 외부 입력 계약과 로컬 저장 위치가 모두 확정됩니다.
