<!-- task=m-plane-work-item-webhook-intake/04+01,02,03_live_smoke plan=0 tag=WEBHOOK_SMOKE -->

# Plan - WEBHOOK_SMOKE

## 이 파일을 읽는 구현 에이전트에게

구현 완료 전 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 설계 결정, 검증 출력으로 반드시 채운다. 구현 중 사용자만 결정할 수 있는 외부 환경, secret, scope 충돌이 생기면 리뷰 stub의 `사용자 리뷰 요청` 섹션에 증거와 재개 조건을 기록하고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

선행 작업들이 끝나면 사용자는 Plane UI에서 Backlog item을 AGENT에 assign하는 것만으로 NomadCode authoring flow가 시작되는지 확인해야 한다. 이 작업은 dev Core 배포, Plane webhook delivery, Core task id, authoring run 시작 또는 ignored reason을 secret redaction 상태로 남기는 검증 전용 작업이다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. 직접 사용자 프롬프트는 금지하며, code-review가 USER_REVIEW 작성 여부를 판단한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/plane-work-item-webhook-intake.md`
- Task ids:
  - `live-smoke`: Plane UI에서 상위 티켓을 만들고 Backlog 상태에서 AGENT에 assign하면 NomadCode task가 생성되고 Milestone authoring flow가 시작된다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-test/local/rules.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/contracts-smoke.md`
- `agent-test/plane-dev.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md`
- `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/plane-work-item-webhook-intake.md`
- `agent-ops/rules/project/domain/core/rules.md`
- `services/core/docker-compose.yml`
- `services/core/internal/http/router.go`
- `services/core/internal/http/handlers.go`
- `services/core/internal/http/plane_webhook.go`

### 테스트 환경 규칙

- `test_env=local`로 적용한다.
- `agent-test/local/rules.md`와 `agent-test/local/core-smoke.md`를 읽었다.
- live smoke의 표준 실행지는 원격 `toki@toki-labs.com`이다.
- Plane compose 위치는 `~/docker/agent-service/plane/compose`, 서비스는 `plane-api`, `plane-worker`, `postgres`로 확인했다.
- Core compose 위치는 `~/agent-work/nomadcode/services/core`, 서비스는 `nomadcode-core`로 확인했다.
- `<확인 필요>` 값은 없다.

### 테스트 커버리지 공백

- Unit tests는 webhook parsing/dispatch/idempotency를 검증하지만 실제 Plane delivery와 Core scheduler/authoring run 시작은 보장하지 않는다.
- 기존 webhook-only smoke는 `NOMAD-10`, `NOMAD-11` assign event가 Core receiver까지 202로 도달한 것만 확인했다. task 생성과 authoring run 시작 evidence는 아직 없다.

### 심볼 참조

- renamed/removed symbol 없음.

### 분할 판단

- split decision policy를 먼저 평가했다.
- shared task group: `m-plane-work-item-webhook-intake`
- 이 subtask directory `04+01,02,03_live_smoke`는 predecessors `01`, `02`, `03`을 요구한다.
- predecessor `01` 상태: `agent-task/m-plane-work-item-webhook-intake/01_payload_normalization/complete.log` 없음.
- predecessor `02` 상태: `agent-task/m-plane-work-item-webhook-intake/02+01_trigger_dispatch/complete.log` 없음.
- predecessor `03` 상태: `agent-task/m-plane-work-item-webhook-intake/03+02_idempotency/complete.log` 없음.
- 구현 시작 전 세 predecessor가 PASS/archive 또는 active complete.log를 가져야 한다.

### 범위 결정 근거

- 이 작업은 remote deploy/smoke evidence와 tracked non-secret 기록만 다룬다.
- 코드 수정은 선행 task의 누락을 발견한 경우에만 follow-up plan으로 분리한다.
- raw webhook secret, Plane API token, auth password를 출력하거나 tracked 파일에 기록하지 않는다.

### 빌드 등급

- `local-G05`: 구현보다는 deterministic remote smoke 절차와 evidence 기록이다. 외부 환경이 걸리지만 선행 구현 완료 후 명령 중심으로 검증 가능하다.

## 의존 관계 및 구현 순서

- `01_payload_normalization`, `02+01_trigger_dispatch`, `03+02_idempotency`가 각각 `complete.log`를 생성한 뒤 시작한다.
- 이 plan은 directory name의 predecessors `+01,02,03`만 의존한다.

## 구현 체크리스트

- [ ] 원격 Core checkout에 services/core, root `bin/nomadcode-sops-env`, `secrets/nomadcode.dev.sops.yaml`을 동기화하고 SOPS env로 compose를 재기동한다.
- [ ] Plane UI 또는 API로 새 top-level work item을 Backlog 상태에서 만들고 AGENT assignee를 설정해 webhook delivery를 발생시킨다. 검증: 테스트 Plane 티켓 id, Core task id, authoring run 시작 또는 ignored 사유를 secret redaction 상태로 기록한다.
- [ ] Plane webhook log와 Core log/API evidence를 수집해 `agent-test/plane-dev.md`와 milestone 작업 컨텍스트에 non-secret 결과만 남긴다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [WEBHOOK_SMOKE-1] Remote Deploy

### 문제

`services/core/docker-compose.yml`은 Core service만 정의하고 secret/env는 remote runtime에서 주입해야 한다. 현재 원격 root checkout에는 `bin/nomadcode-sops-env`가 없을 수 있으므로 live smoke 전에 helper와 encrypted secret file을 동기화해야 한다.

### 해결 방법

Sync needed files and restart Core with SOPS env. Do not print decrypted env.

```bash
tar -C services/core -czf - . | ssh toki@toki-labs.com 'mkdir -p ~/agent-work/nomadcode/services/core && tar -C ~/agent-work/nomadcode/services/core -xzf -'
tar -czf - bin/nomadcode-sops-env secrets/nomadcode.dev.sops.yaml | ssh toki@toki-labs.com 'mkdir -p ~/agent-work/nomadcode && tar -C ~/agent-work/nomadcode -xzf -'
ssh toki@toki-labs.com 'chmod +x ~/agent-work/nomadcode/bin/nomadcode-sops-env'
ssh toki@toki-labs.com 'cd ~/agent-work/nomadcode/services/core && SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt ../../bin/nomadcode-sops-env /usr/local/bin/docker compose up -d --build'
```

### 수정 파일 및 체크리스트

- [ ] No source edit required unless deploy evidence reveals a bug.
- [ ] `agent-test/plane-dev.md`: record deploy date and Core URL only.

### 테스트 작성

- No unit test. This is deployment verification.

### 중간 검증

```bash
curl -fsS http://toki-labs.com:18010/healthz
ssh toki@toki-labs.com 'cd ~/agent-work/nomadcode/services/core && /usr/local/bin/docker compose logs --tail=80 nomadcode-core'
```

Expected: health endpoint returns success; logs show server listening and no secret values.

## [WEBHOOK_SMOKE-2] Plane Event Delivery

### 문제

Prior evidence proved webhook-only delivery to receiver. After dispatch/idempotency, delivery must create or intentionally ignore a task with a clear reason.

### 해결 방법

Use a new test Plane top-level work item when possible. If Plane UI action is unavailable to the implementing agent, use Plane API with `.env.plane.local` on the repo root if present; otherwise record a user-review blocker with exact missing prerequisite. Never print `PLANE_TOKEN`.

Plane webhook log query:

```bash
ssh toki@toki-labs.com "cd ~/docker/agent-service/plane/compose && /usr/local/bin/docker compose exec -T postgres sh -lc 'psql -U plane -d plane -Atc \"select created_at, event_type, response_status, request_body::jsonb->>''action'', request_body::jsonb#>>''{data,id}'', request_body::jsonb#>>''{activity,field}'' from webhook_logs order by created_at desc limit 10;\"'"
```

### 수정 파일 및 체크리스트

- [ ] `agent-test/plane-dev.md`: record smoke item key/id and non-secret delivery result.
- [ ] milestone file: record smoke evidence in 작업 컨텍스트.

### 테스트 작성

- No unit test. This verifies remote integration.

### 중간 검증

```bash
ssh toki@toki-labs.com "cd ~/docker/agent-service/plane/compose && /usr/local/bin/docker compose exec -T postgres sh -lc 'psql -U plane -d plane -Atc \"select created_at, event_type, response_status, request_body::jsonb->>''action'', request_body::jsonb#>>''{data,id}'', request_body::jsonb#>>''{activity,field}'' from webhook_logs order by created_at desc limit 10;\"'"
```

Expected: latest relevant issue event has response status 202.

## [WEBHOOK_SMOKE-3] Core Task And Authoring Evidence

### 문제

Milestone pass requires task id and authoring run started or ignored reason. Receiver accepted alone is insufficient.

### 해결 방법

Collect Core logs around the Plane work item id. Prefer structured log lines emitted by dispatch/idempotency implementation. If API auth is available through remote env without printing password, use `/api/tasks` to confirm `external_provider=plane` and `external_id=<work item id>`.

```bash
ssh toki@toki-labs.com 'cd ~/agent-work/nomadcode/services/core && /usr/local/bin/docker compose logs --tail=300 nomadcode-core | grep -E "plane webhook|work item|task|authoring|ignored"'
```

If Core exposes task evidence only through protected API, run the command via a secret-safe script that does not echo credentials, and paste redacted output only.

### 수정 파일 및 체크리스트

- [ ] `agent-test/plane-dev.md`: add task id / authoring run evidence or ignored reason.
- [ ] milestone file: update `live-smoke` context with test item id and result.

### 테스트 작성

- No unit test.

### 중간 검증

```bash
ssh toki@toki-labs.com 'cd ~/agent-work/nomadcode/services/core && /usr/local/bin/docker compose logs --tail=300 nomadcode-core | grep -E "plane webhook|work item|task|authoring|ignored"'
```

Expected: logs identify the smoke work item and either task creation/authoring start or an intentional ignored reason.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `agent-test/plane-dev.md` | WEBHOOK_SMOKE-1, WEBHOOK_SMOKE-2, WEBHOOK_SMOKE-3 |
| `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/plane-work-item-webhook-intake.md` | WEBHOOK_SMOKE-2, WEBHOOK_SMOKE-3 |

## 최종 검증

```bash
ssh toki@toki-labs.com 'zsh -lc "cd ~/agent-work/nomadcode/services/core && go test -count=1 ./..."'
curl -fsS http://toki-labs.com:18010/healthz
ssh toki@toki-labs.com "cd ~/docker/agent-service/plane/compose && /usr/local/bin/docker compose exec -T postgres sh -lc 'psql -U plane -d plane -Atc \"select created_at, event_type, response_status, request_body::jsonb->>''action'', request_body::jsonb#>>''{data,id}'', request_body::jsonb#>>''{activity,field}'' from webhook_logs order by created_at desc limit 10;\"'"
ssh toki@toki-labs.com 'cd ~/agent-work/nomadcode/services/core && /usr/local/bin/docker compose logs --tail=300 nomadcode-core | grep -E "plane webhook|work item|task|authoring|ignored"'
git diff --check
```

Expected: remote tests pass, Core health succeeds, Plane log shows 202 for smoke event, Core evidence shows task id plus authoring start or explicit ignored reason, diff check is clean.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
