사용자는 새 기능을 먼저 구경하고 위치를 정한 뒤 필요할 때 실행하고 싶다. 등록 버튼을 눌렀는데 Python이 돌거나 외부 입력이 시작된다면 그 순서가 무너진다. 나는 설정을 다루는 경로와 일을 시작하는 경로를 실제 함수에서 나누고, 반환 문구도 그 구분에 맞췄다.

생성 완료라는 말부터 실행과 분리했다

TaskPluginService.create는 기존 programId를 연결하거나 제한된 recipe로 정의를 만든 뒤 등록한다. 두 입력을 함께 보내면 모호한 요청으로 거절한다. arbitrary shell 명령을 받는 등록기가 아니며 반환에는 executed=False가 명시돼 있다. 새 정의가 draft_unverified라면 등록됐다고 검증 완료로 부르지 않는다.

이 차이는 자동화 호출자에게도 필요하다. 생성 응답 하나를 보고 업무 완료라고 답하면 실제 입력과 결과 확인이 빠진다. 나는 무엇이 생겼는지와 무엇이 실행됐는지를 다른 결과로 읽도록 했다. 파일과 버튼이 준비된 상태를 정확하게 설명하는 것이 이후 실행 요청을 안전하게 이어 가는 시작이었다.

열기는 실행창을 준비하는 동작이다

pluginAction의 open은 현재 일반 프로필인지 확인하고 목록을 새로 읽는다. 등록과 enabled를 검사한 뒤 선택한 ID를 저장하고 launcher를 보여 준다. 이 분기에는 runner.start가 없다. 이름을 바꾸거나 고정·활성 설정을 저장하는 다른 동작도 프로그램을 시작하지 않는다.

설정 화면에서 계속 보이는 실행 버튼이 있더라도 같은 동작은 아니다. open은 사용자가 필요한 입력과 설명을 확인할 기회를 만든다. 이 경계가 있으면 관리 API도 준비 결과와 시작 결과를 구별할 수 있다. 공유 예제에서 prepared=True와 executed=False를 함께 돌려주는 이유도 그 차이를 직접 읽게 하기 위해서다.

실행 직전에는 현재 탭을 다시 묶는다

pluginRun은 시작 준비 중인 중복 요청을 먼저 막는다. 현재 프로필·프로젝트·탭을 잡고 전달된 세 값과 같은지 확인한다. 비공개 프로필과 다른 진행 작업, 바쁜 노트북도 거절한다. 프로그램 정의를 비동기로 읽은 뒤 같은 check를 다시 부르므로 읽는 동안 대상이 달라진 경우를 처음 검사만으로 통과시키지 않는다.

등록 시점의 대상은 실행 시점의 대상이 아닐 수 있다. 사용자가 탭을 바꾸거나 다른 프로젝트로 이동하는 것은 자연스럽다. 버튼이 오래 열려 있었다는 이유로 예전 탭을 지금 대상처럼 쓰면 안 된다. 나는 실행을 편하게 하는 것과 오래된 선택을 계속 믿는 것을 구별했다.

입력 개수가 맞는 것보다 키가 맞아야 한다

실행 입력은 전달된 사전에서 복사한다. 키의 개수와 recipe의 매개변수 개수를 비교하고 필요한 모든 키가 있는지 확인한다. 현재 페이지를 쓰는 일부 기능은 허용한 종류인지 확인한 뒤 현재 URL이나 소유자 값을 채운다. 임의의 프로그램에 현재 페이지 옵션을 붙이는 만능 우회는 아니다.

이것은 모든 하위 함수가 기본 입력을 절대 합치지 않는다는 주장이 아니다. 실제 하위 준비 함수에는 기본값을 합치는 경로도 있다. 이 글의 범위는 UI pluginRun 진입점이 현재 입력을 요구하는 방식이다. 어느 층에서 확인했는지까지 써야 구현 방법을 다른 서비스에 옮길 때 조건을 잃지 않는다.

끄기는 시작하지 않지만 진행 작업에는 영향을 준다

disable과 remove는 신규 실행을 시작하지 않는다. 하지만 실제 코드에서는 같은 프로필과 프로그램의 active 실행을 찾아 cancel을 요청한다. 선택 중이던 launcher도 지운다. 그래서 설정 버튼은 아무 실행에도 영향을 주지 않는다고 넓게 말하지 않았다. 신규 시작이 없다는 것과 기존 작업을 계속 둔다는 것은 다른 주장이다.

시작을 기다리는 동안 끈 경우를 위해 runner.start 뒤에도 enabled를 확인하는 부분이 있다. 이 점검은 취소 요청을 보내는 코드이며 외부 결과가 완전히 되돌아갔다는 증거는 아니다. 중단 이후 실제 결과와 기록을 읽는 책임은 남는다. 공유 모듈의 cancel_requested도 같은 이유로 완료 결과와 구분했다.

시험과 문구를 같은 경계로 읽는다

기존 등록 시험은 새 정의와 중복 재사용에서 executed=False를 확인하고 잘못된 입력이 등록을 남기지 않는지 본다. 설정 시험은 비활성 항목의 open을 거절한다. 나는 이 코드를 읽고 실제 분기의 runner 호출 위치를 대조했다. 원래 앱에서 새로운 업무를 실행해 본 결과로 적지는 않았다.

아래 독립 모듈은 등록과 열기를 실행 없이 시험하고 끄기·제거의 취소 의도를 자료로 돌려준다. 실제 프로필 인증과 작업 중단은 구현하지 않는다. 관리 화면과 에이전트 API 모두 저장했다·준비했다·시작했다·완료했다를 구분하면 기능이 많아져도 사용자가 지금 일어난 일을 이해하기 쉬워진다.