<!-- task=03+execution_context_boundaries plan=0 tag=REFACTOR -->

# Execution Context Boundary Refactor

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

**경고: 구현 완료의 마지막 단계는 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 모두 채우는 것이다.**
구현 체크리스트를 기준으로 작업하고, plan과 review stub 양쪽의 체크리스트를 완료하며, 중간/최종 검증을 실행한 실제 출력으로 `CODE_REVIEW-*-G??.md`를 채운다.
리뷰 파일의 `이 파일을 읽는 리뷰 에이전트에게` 섹션에 있는 아카이브 지시는 실행하지 말고, `코드리뷰 전용 체크리스트`는 수정하거나 체크하지 않는다.

## 배경

`ExecutionContext`는 생겼지만 `TagSystem`, `Command`, `PipelineExecutor`가 여전히 `Application.instance`를 직접 읽는다.
AI가 실행 상태를 추적하려면 런타임 state의 소유자가 명확해야 하고, 전역 싱글턴 접근은 테스트와 병렬 실행의 추론을 어렵게 만든다.
이 작업은 public singleton을 제거하지 않고, 내부 실행 경계에 context를 전달하는 호환 단계로 제한한다.

## 의존 관계 및 구현 순서

- `agent-task/02+yaml_validation`의 `complete.log`가 선행되어야 한다. validation 실패 경로가 정리된 뒤 runtime context 이동을 한다.
- command 파일 분리 작업(`agent-task/04_build_ios_split`, `agent-task/05_git_split`)과 직접 의존은 없지만, 동일 파일 충돌을 줄이려면 이 task를 먼저 끝낸다.

## 분석 결과

### 읽은 파일

- `lib/oto/application.dart`
- `lib/oto/core/execution_context.dart`
- `lib/oto/core/tag_system.dart`
- `lib/oto/commands/command.dart`
- `lib/oto/pipeline/pipeline.dart`
- `lib/oto/pipeline/pipeline_exe.dart`
- `lib/oto/pipeline/pipeline_exe_handle.dart`
- `lib/oto/pipeline/pipeline_if.dart`
- `lib/oto/pipeline/pipeline_contain.dart`
- `lib/oto/pipeline/pipeline_foreach.dart`
- `lib/oto/pipeline/pipeline_switch.dart`
- `lib/oto/pipeline/pipeline_wait_until.dart`
- `lib/oto/pipeline/pipeline_while.dart`
- `test/oto_context_test.dart`
- `test/oto_command_runtime_test.dart`
- `test/oto_application_test.dart`
- `agent-ops/rules/project/domain/core/rules.md`
- `agent-ops/rules/project/domain/pipeline/rules.md`
- `agent-ops/rules/project/domain/command/rules.md`

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

- `test/oto_context_test.dart`는 `Application` compatibility accessor와 `TagSystem` 읽기/쓰기를 커버한다.
- `test/oto_command_runtime_test.dart`는 command runtime과 `Application.instance.context` 초기화를 커버한다.
- pipeline executor가 같은 process에서 명시 context를 공유하는지, concurrent build가 섞이지 않는지는 기존 테스트가 없다.
- 사용자가 유닛 테스트를 이후 별도 작업한다고 했으므로 신규 테스트는 작성하지 않는다.

### 심볼 참조

- renamed/removed symbol: none.
- `Application.instance.context` 직접 참조: `lib/oto/commands/command.dart:176`, `lib/oto/commands/command.dart:180`, `lib/oto/core/tag_system.dart:109`, `lib/oto/core/tag_system.dart:137`, `lib/oto/core/tag_system.dart:168`, tests at `test/oto_context_test.dart:8`, `test/oto_command_runtime_test.dart:76`.
- `Application.instance.dataCommandMap` 직접 참조: `lib/oto/pipeline/pipeline_exe.dart:58`, `lib/oto/pipeline/pipeline_exe.dart:69`, `lib/oto/pipeline/pipeline_exe_handle.dart:47`, `lib/oto/pipeline/pipeline_exe_handle.dart:75`.
- `Application.instance.updateCommandState` 직접 참조: `lib/oto/pipeline/pipeline_exe.dart:92`, `lib/oto/pipeline/pipeline_exe_handle.dart:94`.

### 범위 결정 근거

- public `Application.instance`와 기존 accessors는 제거하지 않는다. CLI, scheduler, tests의 호환성을 유지한다.
- 모든 command 구현 파일을 context-aware로 직접 수정하지 않는다. `Command` base와 pipeline dispatch에서 context를 주입해 하위 command는 기존 API를 유지한다.
- `DataComposerJenkins`의 외부 process 호출 구조는 제외한다. context 전달과 무관한 Jenkins 환경 합성이다.

### 빌드 등급

- build lane: `cloud-G08`. singleton 경계, pipeline dispatch, command base가 함께 바뀌는 넓은 API 영향이고 병렬 실행 테스트 공백이 있다.

## 구현 체크리스트

- [ ] `TagSystem` static API에 optional `ExecutionContext` 인자를 추가하고 기본 호환을 유지한다.
- [ ] `Command` base에 context 주입 지점을 추가하고 pipeline dispatch에서 설정한다.
- [ ] `Pipeline`과 `PipelineExecutor`가 context를 보유하도록 하되 기존 호출 API를 깨지 않는다.
- [ ] context 관련 기존 테스트와 runtime/application 테스트를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REFACTOR-1] TagSystem context parameter

#### 문제

`TagSystem`은 `lib/oto/core/tag_system.dart:109`, `137`, `168`에서 직접 `Application.instance.context`를 참조한다.
이 때문에 tag 치환의 입력 context가 함수 시그니처에 드러나지 않는다.

Before (`lib/oto/core/tag_system.dart:100`):

```dart
static Future setPropertyValue(String tag, dynamic value,
    {bool? showPrint}) async {
```

#### 해결 방법

`ExecutionContext? context` optional parameter를 `setPropertyValue`, `replaceAllTagsMap`, `replaceAllTagsList`, `replaceTagValue`, `replaceSingleTagValue`, `_getTagValue`, path helper에 전달한다.
기본값은 `Application.instance.context`로 두어 기존 호출을 유지한다.

After:

```dart
static ExecutionContext get _defaultContext => Application.instance.context;

static Future setPropertyValue(String tag, dynamic value,
    {bool? showPrint, ExecutionContext? context}) async {
  final runtimeContext = context ?? _defaultContext;
  ...
  runtimeContext.property[key] = value;
}
```

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

- [ ] `lib/oto/core/tag_system.dart`: optional context parameter 추가.
- [ ] `lib/oto/commands/command.dart`: TagSystem wrapper가 command context를 넘기도록 준비.

#### 테스트 작성

- 신규 유닛 테스트는 작성하지 않는다. 기존 `test/oto_context_test.dart`가 fallback compatibility를 확인한다.

#### 중간 검증

```bash
dart test test/oto_context_test.dart
```

기대 결과: 기존 context/tag 테스트가 통과한다.

### [REFACTOR-2] Command base context injection

#### 문제

`Command.property`와 `Command.commonData`는 `lib/oto/commands/command.dart:175`와 `179`에서 전역 Application context만 읽는다.
파이프라인 실행 중 어떤 context를 command가 사용하는지 class state에 드러나지 않는다.

Before (`lib/oto/commands/command.dart:175`):

```dart
Map<String, dynamic> get property {
  return Application.instance.context.property;
}
```

#### 해결 방법

`Command`에 `ExecutionContext? context` 필드를 추가하고, getter는 `context ?? Application.instance.context`를 사용한다.
`Command.replace*` static wrapper는 호환을 유지하되 instance method는 `context`를 전달한다.

After:

```dart
ExecutionContext? context;
ExecutionContext get runtimeContext => context ?? Application.instance.context;

Map<String, dynamic> get property => runtimeContext.property;
DataCommon? get commonData => runtimeContext.commonData;
```

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

- [ ] `lib/oto/commands/command.dart`: `ExecutionContext` import와 field/getter 추가.
- [ ] `lib/oto/commands/command.dart`: instance tag/path helpers가 `runtimeContext`를 사용하도록 조정.

#### 테스트 작성

- 신규 유닛 테스트는 작성하지 않는다. `test/oto_command_runtime_test.dart`가 command helper를 통한 workspace resolve를 간접 검증한다.

#### 중간 검증

```bash
dart test test/oto_command_runtime_test.dart
```

기대 결과: shell/git/process runtime 테스트가 통과한다.

### [REFACTOR-3] Pipeline carries ExecutionContext

#### 문제

`Pipeline`은 `lib/oto/pipeline/pipeline.dart:51`에서 executor list만 보관한다.
`PipelineExe`와 `PipelineExeHandle`은 `Application.instance.dataCommandMap`과 command state를 직접 사용한다.

Before (`lib/oto/pipeline/pipeline.dart:49`):

```dart
final List<PipelineExecutor> _exeList;

Pipeline(this._exeList);
```

#### 해결 방법

`Pipeline.pipelineInitialize(List<dynamic> list, {ExecutionContext? context})`를 추가하고, executor 생성 직후 `exe.context = runtimeContext`를 설정한다.
`Pipeline` constructor도 context를 보유하며 하위 pipeline validate 호출은 같은 context를 전달한다.

After:

```dart
final ExecutionContext context;
final List<PipelineExecutor> _exeList;

Pipeline(this._exeList, this.context);

static PipelineValidateResult pipelineInitialize(List<dynamic> list,
    {ExecutionContext? context}) {
  final runtimeContext = context ?? Application.instance.context;
  ...
  exe.context = runtimeContext;
}
```

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

- [ ] `lib/oto/pipeline/pipeline.dart`: `ExecutionContext` 전달 추가.
- [ ] `lib/oto/pipeline/pipeline_exe.dart`: `PipelineExecutor.context` field 추가, dataCommandMap/state 접근 전환.
- [ ] `lib/oto/pipeline/pipeline_exe_handle.dart`: dataCommandMap/state 접근 전환.
- [ ] `lib/oto/pipeline/pipeline_if.dart`, `pipeline_contain.dart`, `pipeline_foreach.dart`, `pipeline_switch.dart`, `pipeline_while.dart`: nested validation이 context를 전달하는지 확인.
- [ ] `lib/oto/application.dart`: top-level `Pipeline.pipelineInitialize(build.pipeline!.workflow, context: context)`로 호출.

#### 테스트 작성

- 신규 유닛 테스트는 작성하지 않는다. 기존 application tests가 pipeline 실행 contract를 확인한다.

#### 중간 검증

```bash
dart test test/oto_application_test.dart test/oto_core_test.dart
```

기대 결과: application/core 테스트가 통과한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/oto/core/tag_system.dart` | REFACTOR-1 |
| `lib/oto/commands/command.dart` | REFACTOR-1, REFACTOR-2 |
| `lib/oto/pipeline/pipeline.dart` | REFACTOR-3 |
| `lib/oto/pipeline/pipeline_exe.dart` | REFACTOR-3 |
| `lib/oto/pipeline/pipeline_exe_handle.dart` | REFACTOR-3 |
| `lib/oto/pipeline/pipeline_if.dart` | REFACTOR-3 |
| `lib/oto/pipeline/pipeline_contain.dart` | REFACTOR-3 |
| `lib/oto/pipeline/pipeline_foreach.dart` | REFACTOR-3 |
| `lib/oto/pipeline/pipeline_switch.dart` | REFACTOR-3 |
| `lib/oto/pipeline/pipeline_wait_until.dart` | REFACTOR-3 확인 |
| `lib/oto/pipeline/pipeline_while.dart` | REFACTOR-3 |
| `lib/oto/application.dart` | REFACTOR-3 |
| `test/oto_context_test.dart` | 검증 대상 |
| `test/oto_command_runtime_test.dart` | 검증 대상 |
| `test/oto_application_test.dart` | 검증 대상 |

## 최종 검증

```bash
dart analyze
dart test test/oto_context_test.dart test/oto_command_runtime_test.dart test/oto_application_test.dart test/oto_core_test.dart
dart run bin/main.dart exe -f assets/yaml/sample/01_basic.yaml
```

기대 결과: analyze와 지정 테스트가 통과하고, `01_basic` 샘플 실행이 `Build Successfully Complete`를 출력한다.

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