<!-- task=m-control-plane-jenkins-aligned-layout-pass/03+01,02_execution_artifact_views plan=0 tag=LAYOUT -->

# Plan - LAYOUT Execution And Artifact Views

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증을 실행하고 실제 변경 내용, 설계 결정, 명령 출력, 체크리스트를 채운 뒤 active 파일을 그대로 둔 상태로 리뷰 준비를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 전용이다.

선택된 Milestone `구현 잠금 > 결정 필요` 항목 때문에 막힌 경우에만 review stub의 `사용자 리뷰 요청` 섹션을 정확한 연결 근거와 함께 채우고 멈춘다. 구현 중 사용자에게 직접 묻거나, 선택지를 제시하거나, `request_user_input`을 호출하거나, `USER_REVIEW.md`/`complete.log`를 만들지 않는다. 환경/secret/service 차단, 일반 범위 변경, 검증 evidence 공백은 사용자 리뷰 요청이 아니라 검증 결과 또는 follow-up 대상이다.

## 배경

이 subtask는 shell foundation과 primary view rollout 후 남는 execution/artifact 중심 레이아웃을 코드에 반영한다. 현재 execution detail은 expandable card 내부에 actions/logs/artifacts가 섞여 있고, artifacts tab은 단순 icon/name/path row다. agent-ui는 Jenkins Build History + build page, Console Output, Pipeline Graph View, artifact browser 흐름을 러프 참조로 list/detail/pane 구조를 요구한다.

## 사용자 리뷰 요청 흐름

구현 중 직접 사용자에게 묻지 않는다. 선택된 Milestone의 `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 active review stub의 `사용자 리뷰 요청`에 기록하고, code-review가 검증 후 실제 `USER_REVIEW.md` 생성을 맡는다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/control-plane-product-surface/milestones/control-plane-jenkins-aligned-layout-pass.md`
- Task ids:
  - `view-layout`: `overview`, `runners`, `pipelines`, `executions`, `artifacts`, `settings`를 Target Layout Delta에 맞춰 재배치
  - `component-layout`: `record-list`, `state-panel`, `status-badge`, `action-feedback`, `execution-action-toolbar`, `log-preview`, `artifact-list`, create/update form의 reusable layout pattern 정리
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/sync-agent-ui/SKILL.md`
- `agent-test/local/rules.md`
- `agent-test/local/framework-smoke.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/control-plane-product-surface/PHASE.md`
- `agent-roadmap/phase/control-plane-product-surface/milestones/control-plane-jenkins-aligned-layout-pass.md`
- `agent-ui/definition/views/executions/index.md`
- `agent-ui/definition/views/artifacts/index.md`
- `agent-ui/definition/components/record-list/index.md`
- `agent-ui/definition/components/status-badge/index.md`
- `agent-ui/definition/components/state-panel/index.md`
- `agent-ui/definition/components/action-feedback/index.md`
- `agent-ui/definition/components/execution-action-toolbar/index.md`
- `agent-ui/definition/components/log-preview/index.md`
- `agent-ui/definition/components/artifact-list/index.md`
- `packages/flutter/oto_console/lib/src/oto_console_contract.dart`
- `packages/flutter/oto_console/lib/src/oto_executions_surface.dart`
- `packages/flutter/oto_console/lib/src/oto_artifacts_surface.dart`
- `apps/client/lib/src/app/oto_client_app.dart`
- `packages/flutter/oto_console/test/oto_console_test.dart`
- `apps/client/test/widget_test.dart`

### SDD 기준

선택 Milestone은 `SDD: 불필요`다. 이 plan은 UI layout, action toolbar 배치, log/artifact preview anatomy만 바꾸며 write API contract와 audit source of truth를 바꾸지 않는다. 기존 SDD에서 언급된 raw log/artifact path 민감도는 UI display-safe copy와 ellipsis 유지 기준으로만 반영한다.

### 테스트 환경 규칙

- `test_env`: local.
- `agent-test/local/rules.md`와 `framework-smoke.md`를 읽었다.
- Flutter package/app 변경이므로 원격 runner `/Users/toki/agent-work/oto`에서 `packages/flutter/oto_console`와 `apps/client`의 `flutter analyze`, `flutter test`가 필요하다.
- 원격 preflight에서 checkout은 접근되지만 `command -v flutter`가 실패했다. 구현자는 원격 Flutter PATH/source sync가 준비되지 않으면 해당 출력과 blocker를 review stub에 남긴다.

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

- Executions: 기존 tests가 loading/empty/error/data, expand detail, action callbacks, disabled unknown runner, success refresh, logs/artifacts empty/error를 커버한다. list/detail 재배치 후에도 tap target과 callbacks가 보존되는지 갱신 필요.
- Artifacts: 기존 tests가 loading/empty/error/data와 `Path: ...` 표시를 커버한다. artifact browser anatomy로 바꿔도 name/path assertion을 유지해야 한다.
- Component layout: 공통 public widget을 새로 만들지 않으면 private helper 수준이라 기존 widget tests가 behavior 회귀를 잡는다. reusable pattern이 문서와 충돌하지 않는지 code inspection이 필요하다.

### 심볼 참조

삭제/rename 예정 symbol은 없다. `OtoExecutionsSurface`, `OtoArtifactsSurface`, `OtoActionViewState`, `OtoExecution*Draft`, `OtoArtifactViewModel` public contract는 유지한다. 호출부는 `apps/client/lib/src/app/oto_client_app.dart`, package tests, app tests다.

### 분할 판단

multi-plan output이다. 공유 task group은 `m-control-plane-jenkins-aligned-layout-pass`.

- `01_definition_shell_foundation`: predecessor `01`, 현재 작성 시점 complete log missing.
- `02+01_primary_views`: predecessor `02`, 현재 작성 시점 complete log missing.
- `03+01,02_execution_artifact_views`: 현재 plan. directory dependency상 01과 02 둘 다 PASS 후 구현해야 한다.

### 범위 결정 근거

Stage graph 데이터 모델은 없으므로 Pipeline Graph View의 full stage graph 구현은 제외하고 selected execution summary placeholder까지만 허용한다. Artifact download/content preview, full console follow/search/copy도 제외한다. Core API, proto, auth/audit persistence는 변경하지 않는다. agent-ui `구현됨` status 전환은 milestone-required completion review로 남긴다.

### 빌드 등급

`local-G07`. Execution detail은 action toolbar, confirmation dialog, write callbacks, logs/artifacts detail loading을 함께 건드려 blast radius가 primary views보다 크다. 그래도 범위는 Flutter package/app 위젯과 기존 tests로 bounded된다.

## 구현 체크리스트

- [ ] `01_definition_shell_foundation`과 `02+01_primary_views`의 `complete.log`가 active/archive predecessor로 존재하는지 확인하고 없으면 구현하지 않고 dependency missing을 기록한다.
- [ ] `OtoExecutionsSurface`를 execution list + selected run detail 관계로 재배치하고, run header/action toolbar/summary placeholder/log pane/artifact pane 순서를 구현한다.
- [ ] execution action toolbar에서 cancel/report/log/artifact guard, confirmation dialog, callback payload, submitting/success/failed feedback을 유지한다.
- [ ] logs preview를 selected execution detail의 독립 bounded pane으로 재배치하고 loading/empty/error/data states를 유지한다.
- [ ] artifacts preview와 artifacts tab을 같은 artifact-list anatomy로 정리하고 name/path ellipsis, empty/error/data states를 유지한다.
- [ ] record-list/status-badge/state-panel/action-feedback private reusable pattern을 surface 간 충돌 없이 정리한다.
- [ ] package/app widget tests를 갱신해 executions/artifacts state, action, navigation, detail preview를 검증한다.
- [ ] 원격 runner preflight와 Flutter analyze/test 계약을 실행하거나, Flutter PATH/source sync 차단이면 실제 출력과 차단 사유를 review stub에 남긴다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

디렉터리명 `03+01,02_execution_artifact_views`의 predecessors는 `01_definition_shell_foundation`, `02+01_primary_views`다. 구현 시작 전 같은 task group active/archive에서 각 predecessor `complete.log`를 확인한다. 현재 작성 시점에는 둘 다 missing이다.

### [LAYOUT-1] Execution List And Selected Detail

#### 문제

[packages/flutter/oto_console/lib/src/oto_executions_surface.dart](/config/workspace/oto/packages/flutter/oto_console/lib/src/oto_executions_surface.dart:185)는 execution별 card list를 만들고 [lines 277-288](/config/workspace/oto/packages/flutter/oto_console/lib/src/oto_executions_surface.dart:277)에서 expanded card 안에 actions/logs/artifacts를 순서대로 넣는다. agent-ui는 Jenkins Build History + build page처럼 list/detail 관계와 run detail panes를 요구한다.

#### 해결 방법

List row와 selected detail을 시각적으로 분리한다. stage graph 데이터가 없으므로 selected detail에 "Stage summary unavailable" 같은 짧은 placeholder region을 두고, action toolbar/log/artifact pane을 뒤에 배치한다.

Before:

```dart
// oto_executions_surface.dart:277
if (isExpanded) ...[
  const Divider(height: 1, thickness: 1),
  Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      children: [
        _buildActionsSection(context, theme, exec),
        _buildLogsSection(theme),
        _buildArtifactsSection(theme),
      ],
    ),
  ),
]
```

After:

```dart
return _ExecutionWorkspace(
  list: _buildExecutionList(context, theme, executions),
  detail: selected == null
      ? _RunDetailPlaceholder(theme: theme)
      : _buildExecutionDetail(context, theme, selected),
);
```

`_buildExecutionDetail`은 run header, `_buildActionsSection`, stage summary placeholder, `_buildLogsSection`, `_buildArtifactsSection` 순서를 갖는다.

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

- [ ] `packages/flutter/oto_console/lib/src/oto_executions_surface.dart`: list/detail workspace 구현.
- [ ] `packages/flutter/oto_console/test/oto_console_test.dart`: expand/detail assertions 갱신.
- [ ] `apps/client/test/widget_test.dart`: app execution detail tests 갱신.

#### 테스트 작성

기존 execution tests를 갱신한다. `Exec ID`, `Job ID`, `Runner ID`, status, `Actions`, `Logs Preview`, artifact name, empty/error detail strings를 검증한다.

#### 중간 검증

```bash
cd packages/flutter/oto_console && flutter test
```

예상: package execution widget tests PASS 또는 원격 Flutter blocker 기록.

### [LAYOUT-2] Execution Action Toolbar And Feedback

#### 문제

[packages/flutter/oto_console/lib/src/oto_executions_surface.dart](/config/workspace/oto/packages/flutter/oto_console/lib/src/oto_executions_surface.dart:300)의 `_buildActionsSection`은 expanded card 내부 `Wrap` button group이다. agent-ui는 destructive/report/append/navigation action grouping이 selected run detail header/action area에 붙는 구조를 요구한다.

#### 해결 방법

Button callback logic은 유지하고, toolbar layout만 group화한다. `canAct` guard, cancel confirmation, action state feedback 조건은 보존한다.

Before:

```dart
// oto_executions_surface.dart:317
Wrap(
  spacing: 8,
  runSpacing: 8,
  children: [
    _ActionButton(label: 'Cancel', ...),
    _ActionButton(label: 'Report', ...),
    _ActionButton(label: 'Log', ...),
    _ActionButton(label: 'Artifact', ...),
  ],
)
```

After:

```dart
_ExecutionActionToolbar(
  destructive: [_ActionButton(label: 'Cancel', ...)],
  reporting: [_ActionButton(label: 'Report', ...)],
  append: [
    _ActionButton(label: 'Log', ...),
    _ActionButton(label: 'Artifact', ...),
  ],
)
```

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

- [ ] `packages/flutter/oto_console/lib/src/oto_executions_surface.dart`: toolbar grouping.
- [ ] `packages/flutter/oto_console/test/oto_console_test.dart`: action callbacks/state tests 갱신.
- [ ] `apps/client/test/widget_test.dart`: action wiring tests 갱신.

#### 테스트 작성

Report/Log/Artifact callbacks, cancel dialog, disabled unknown runner, success refresh, failed no-refresh tests를 유지한다.

#### 중간 검증

```bash
cd apps/client && flutter test
```

예상: app execution action tests PASS 또는 원격 Flutter blocker 기록.

### [LAYOUT-3] Log And Artifact Panes

#### 문제

[packages/flutter/oto_console/lib/src/oto_executions_surface.dart](/config/workspace/oto/packages/flutter/oto_console/lib/src/oto_executions_surface.dart:459)의 log preview와 [artifacts preview](/config/workspace/oto/packages/flutter/oto_console/lib/src/oto_executions_surface.dart:533)는 card 내부 sections다. agent-ui는 selected execution detail의 독립 panes로 두고 future full console/action slot을 열어둘 것을 요구한다.

#### 해결 방법

Log preview는 bounded monospace pane으로 유지하되 header/action slot을 가진 helper로 감싼다. Artifact preview는 artifacts tab과 같은 row anatomy helper를 공유한다.

Before:

```dart
// oto_executions_surface.dart:477
Container(
  width: double.infinity,
  padding: const EdgeInsets.all(12),
  decoration: BoxDecoration(color: Colors.black, ...),
  constraints: const BoxConstraints(maxHeight: 180),
  child: _buildLogsContent(theme),
)
```

After:

```dart
_DetailPane(
  title: 'Logs Preview',
  action: null,
  child: _LogPreviewBox(theme: theme, child: _buildLogsContent(theme)),
)
```

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

- [ ] `packages/flutter/oto_console/lib/src/oto_executions_surface.dart`: `_DetailPane`, log preview layout, artifact preview layout.
- [ ] `packages/flutter/oto_console/test/oto_console_test.dart`: log/artifact detail tests 갱신.
- [ ] `apps/client/test/widget_test.dart`: expanded empty/error detail tests 갱신.

#### 테스트 작성

`Log is empty.`, `Error loading logs: ...`, `No artifacts produced.`, `Error loading artifacts: ...`, artifact row name/path assertions를 유지한다.

#### 중간 검증

```bash
cd packages/flutter/oto_console && flutter test
```

예상: package detail preview tests PASS 또는 원격 Flutter blocker 기록.

### [LAYOUT-4] Artifacts Browser Anatomy

#### 문제

[packages/flutter/oto_console/lib/src/oto_artifacts_surface.dart](/config/workspace/oto/packages/flutter/oto_console/lib/src/oto_artifacts_surface.dart:165)는 simple icon/name/path row만 렌더링한다. agent-ui는 Jenkins artifact browser처럼 file identity, path/location, future action slot을 분리한 anatomy를 요구한다.

#### 해결 방법

Artifacts tab row와 execution detail compact artifact row가 같은 private helper를 쓰게 한다. Full path는 ellipsis를 유지하고 download/content preview는 넣지 않는다.

Before:

```dart
// oto_artifacts_surface.dart:170
return Container(
  padding: const EdgeInsets.all(16),
  decoration: BoxDecoration(...),
  child: Row(
    children: [
      Icon(Icons.insert_drive_file_outlined, ...),
      Expanded(child: Column(children: [Text(artifact.name), Text('Path: ${artifact.path}')]))
    ],
  ),
);
```

After:

```dart
return _ArtifactRecordRow(
  artifact: artifact,
  theme: theme,
  density: _ArtifactRowDensity.full,
);
```

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

- [ ] `packages/flutter/oto_console/lib/src/oto_artifacts_surface.dart`: artifact browser row anatomy.
- [ ] `packages/flutter/oto_console/lib/src/oto_executions_surface.dart`: compact preview helper alignment.
- [ ] `packages/flutter/oto_console/test/oto_console_test.dart`: artifact tab tests 갱신.
- [ ] `apps/client/test/widget_test.dart`: artifact app tests 갱신.

#### 테스트 작성

Existing artifact loading/empty/error/data tests를 유지한다. `release.tar.gz`, `Path: /out/release.tar.gz` 또는 새 label이 있다면 동일 의미의 path text assertion으로 갱신한다.

#### 중간 검증

```bash
cd apps/client && flutter test
```

예상: app artifact wiring tests PASS 또는 원격 Flutter blocker 기록.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `packages/flutter/oto_console/lib/src/oto_executions_surface.dart` | LAYOUT-1, LAYOUT-2, LAYOUT-3, LAYOUT-4 |
| `packages/flutter/oto_console/lib/src/oto_artifacts_surface.dart` | LAYOUT-4 |
| `packages/flutter/oto_console/test/oto_console_test.dart` | LAYOUT-1, LAYOUT-2, LAYOUT-3, LAYOUT-4 |
| `apps/client/test/widget_test.dart` | LAYOUT-1, LAYOUT-2, LAYOUT-3, LAYOUT-4 |

## 최종 검증

```bash
git diff --check
```

예상: PASS.

```bash
python3 - <<'PY'
from pathlib import Path
targets = [
  'agent-ui/definition/views/overview/index.md',
  'agent-ui/definition/views/runners/index.md',
  'agent-ui/definition/views/pipelines/index.md',
  'agent-ui/definition/views/executions/index.md',
  'agent-ui/definition/views/artifacts/index.md',
  'agent-ui/definition/views/settings/index.md',
  'agent-ui/definition/components/record-list/index.md',
  'agent-ui/definition/components/state-panel/index.md',
  'agent-ui/definition/components/status-badge/index.md',
  'agent-ui/definition/components/action-feedback/index.md',
  'agent-ui/definition/components/execution-action-toolbar/index.md',
  'agent-ui/definition/components/log-preview/index.md',
  'agent-ui/definition/components/artifact-list/index.md',
]
for p in targets:
    text = Path(p).read_text()
    if 'status: 계획' not in text and 'Status: `계획`' not in text:
        raise SystemExit(f'agent-ui status should remain 계획 until milestone completion review: {p}')
print('agent-ui implementation targets remain plan-state for milestone review')
PY
```

예상: `agent-ui implementation targets remain plan-state for milestone review`.

```bash
ssh -o BatchMode=yes -o ConnectTimeout=10 toki@toki-labs.com 'cd /Users/toki/agent-work/oto && printf "workdir=%s\n" "$PWD" && printf "branch=" && git rev-parse --abbrev-ref HEAD && printf "head=" && git rev-parse --short HEAD && printf "status-start\n" && git status --short && printf "status-end\n" && command -v flutter && flutter --version | head -n 1'
```

예상: 원격 runner Flutter 경로와 source sync 상태 확인. 현재 사전 확인에서는 Flutter PATH 실패 상태다.

```bash
ssh -o BatchMode=yes -o ConnectTimeout=10 toki@toki-labs.com 'cd /Users/toki/agent-work/oto/packages/flutter/oto_console && flutter analyze && flutter test'
```

예상: 원격 runner Flutter 준비 및 source sync 후 PASS.

```bash
ssh -o BatchMode=yes -o ConnectTimeout=10 toki@toki-labs.com 'cd /Users/toki/agent-work/oto/apps/client && flutter analyze && flutter test'
```

예상: 원격 runner Flutter 준비 및 source sync 후 PASS.

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