sync: agent-ops from agentic-framework v1.1.177
Some checks are pending
ci / validate (push) Waiting to run
Some checks are pending
ci / validate (push) Waiting to run
This commit is contained in:
parent
23cb47627c
commit
a7379db957
27 changed files with 1871 additions and 240 deletions
|
|
@ -8,7 +8,7 @@
|
|||
- 코드 변경 전 관련 domain rule을 먼저 확인한다.
|
||||
- 요청 범위를 넘는 변경을 하지 않는다.
|
||||
- 불확실하면 단정하지 말고 후보를 제시한다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, 또는 plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우, 또는 `sync-agent-ui mode=reconcile-completion`에 exact `completion-log` 경로가 전달된 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다. UI 완료 정합화는 전달된 exact `complete.log` 한 건만 읽고 sibling archive log를 탐색하지 않는다.
|
||||
- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-ui/` 디렉터리가 있는 프로젝트에서도 `agent-ui/definition/archive/**`와 `agent-ui/archive/user-review/**`는 일반 작업에서 읽지 않는다. UI 과거 결정, 복원, 비교, 해결된 user review 확인이 필요한 경우에만 `agent-ops/rules/common/rules-agent-ui.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-spec/` 디렉터리가 있는 프로젝트에서 현재 구현 스펙 확인, 기존 기능 변경, 완료 검토, 구현 스펙 생성/갱신 요청은 세션 1회 `agent-ops/rules/common/rules-agent-spec.md`를 읽고, `agent-spec/index.md`와 매칭되는 spec 문서만 읽는다.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
- 코드 변경 전 관련 domain rule을 먼저 확인한다.
|
||||
- 요청 범위를 넘는 변경을 하지 않는다.
|
||||
- 불확실하면 단정하지 말고 후보를 제시한다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, 또는 plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우, 또는 `sync-agent-ui mode=reconcile-completion`에 exact `completion-log` 경로가 전달된 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다. UI 완료 정합화는 전달된 exact `complete.log` 한 건만 읽고 sibling archive log를 탐색하지 않는다.
|
||||
- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-ui/` 디렉터리가 있는 프로젝트에서도 `agent-ui/definition/archive/**`와 `agent-ui/archive/user-review/**`는 일반 작업에서 읽지 않는다. UI 과거 결정, 복원, 비교, 해결된 user review 확인이 필요한 경우에만 `agent-ops/rules/common/rules-agent-ui.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-spec/` 디렉터리가 있는 프로젝트에서 현재 구현 스펙 확인, 기존 기능 변경, 완료 검토, 구현 스펙 생성/갱신 요청은 세션 1회 `agent-ops/rules/common/rules-agent-spec.md`를 읽고, `agent-spec/index.md`와 매칭되는 spec 문서만 읽는다.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
- 코드 변경 전 관련 domain rule을 먼저 확인한다.
|
||||
- 요청 범위를 넘는 변경을 하지 않는다.
|
||||
- 불확실하면 단정하지 말고 후보를 제시한다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, 또는 plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우, 또는 `sync-agent-ui mode=reconcile-completion`에 exact `completion-log` 경로가 전달된 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다. UI 완료 정합화는 전달된 exact `complete.log` 한 건만 읽고 sibling archive log를 탐색하지 않는다.
|
||||
- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-ui/` 디렉터리가 있는 프로젝트에서도 `agent-ui/definition/archive/**`와 `agent-ui/archive/user-review/**`는 일반 작업에서 읽지 않는다. UI 과거 결정, 복원, 비교, 해결된 user review 확인이 필요한 경우에만 `agent-ops/rules/common/rules-agent-ui.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-spec/` 디렉터리가 있는 프로젝트에서 현재 구현 스펙 확인, 기존 기능 변경, 완료 검토, 구현 스펙 생성/갱신 요청은 세션 1회 `agent-ops/rules/common/rules-agent-spec.md`를 읽고, `agent-spec/index.md`와 매칭되는 spec 문서만 읽는다.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
- 코드 변경 전 관련 domain rule을 먼저 확인한다.
|
||||
- 요청 범위를 넘는 변경을 하지 않는다.
|
||||
- 불확실하면 단정하지 말고 후보를 제시한다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, 또는 plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우, 또는 `sync-agent-ui mode=reconcile-completion`에 exact `completion-log` 경로가 전달된 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다. UI 완료 정합화는 전달된 exact `complete.log` 한 건만 읽고 sibling archive log를 탐색하지 않는다.
|
||||
- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-ui/` 디렉터리가 있는 프로젝트에서도 `agent-ui/definition/archive/**`와 `agent-ui/archive/user-review/**`는 일반 작업에서 읽지 않는다. UI 과거 결정, 복원, 비교, 해결된 user review 확인이 필요한 경우에만 `agent-ops/rules/common/rules-agent-ui.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-spec/` 디렉터리가 있는 프로젝트에서 현재 구현 스펙 확인, 기존 기능 변경, 완료 검토, 구현 스펙 생성/갱신 요청은 세션 1회 `agent-ops/rules/common/rules-agent-spec.md`를 읽고, `agent-spec/index.md`와 매칭되는 spec 문서만 읽는다.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
- 코드 변경 전 관련 domain rule을 먼저 확인한다.
|
||||
- 요청 범위를 넘는 변경을 하지 않는다.
|
||||
- 불확실하면 단정하지 말고 후보를 제시한다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, 또는 plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우, 또는 `sync-agent-ui mode=reconcile-completion`에 exact `completion-log` 경로가 전달된 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다. UI 완료 정합화는 전달된 exact `complete.log` 한 건만 읽고 sibling archive log를 탐색하지 않는다.
|
||||
- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-ui/` 디렉터리가 있는 프로젝트에서도 `agent-ui/definition/archive/**`와 `agent-ui/archive/user-review/**`는 일반 작업에서 읽지 않는다. UI 과거 결정, 복원, 비교, 해결된 user review 확인이 필요한 경우에만 `agent-ops/rules/common/rules-agent-ui.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-spec/` 디렉터리가 있는 프로젝트에서 현재 구현 스펙 확인, 기존 기능 변경, 완료 검토, 구현 스펙 생성/갱신 요청은 세션 1회 `agent-ops/rules/common/rules-agent-spec.md`를 읽고, `agent-spec/index.md`와 매칭되는 spec 문서만 읽는다.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1.1.174
|
||||
1.1.177
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ sync_folder() {
|
|||
[[ -n "$exclude" && "$name" == "$exclude" ]] && continue
|
||||
rm -rf "$dst/$name"
|
||||
cp -r "$item" "$dst/"
|
||||
# 검증 실행 중 생긴 Python bytecode는 공통 산출물이 아니므로 전파하지 않는다.
|
||||
find "$dst/$name" -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete
|
||||
find "$dst/$name" -depth -type d -name '__pycache__' -empty -delete
|
||||
done
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
```text
|
||||
agent-ui/
|
||||
README.md
|
||||
.sync-state.json # 선택: agent-ui -> code 동기화 기준점
|
||||
.sync-state.json # 선택: agent-ui -> code 기준점과 plan/Milestone work 매핑
|
||||
USER_REVIEW.md # 선택: 사용자 판단이 필요한 활성 리뷰
|
||||
archive/
|
||||
user-review/
|
||||
|
|
@ -64,18 +64,20 @@ agent-ui/
|
|||
- component 문서는 `component_id`, `status`, `source_evidence`를 둔다.
|
||||
- view 문서의 `frame`은 frame-view 문서가 있으면 경로, 없으면 `null`로 둔다.
|
||||
- frame-view 문서는 visual source가 있을 때만 만들며 `view_id`, `definition`, `visual_source`, `regions`를 둔다.
|
||||
- `source_evidence`는 list이며 각 항목은 `type`, `path`, `notes`를 둔다.
|
||||
- `source_evidence.type`은 `code`, `docs`, `user` 중 하나다.
|
||||
- definition-index/view/component의 `source_evidence`는 list이며 각 항목은 `type`, `path`, `notes`를 둔다.
|
||||
- 이 문서들의 `source_evidence.type`은 `code`, `docs`, `user` 중 하나다.
|
||||
- 실제 파일 근거가 없으면 `path: null`을 사용한다. placeholder 문자열을 현재 근거처럼 남기지 않는다.
|
||||
- view에 visual source가 없으면 view 문서의 `frame`을 `null`로 두고 frame-view 문서를 만들지 않는다.
|
||||
- frame-view를 만들 때 `visual_source`는 실제 존재하는 visual source 경로여야 한다. 없는 `wire.excalidraw`를 기본 근거처럼 쓰지 않는다.
|
||||
- `status` 값은 `구현됨`, `계획`, `가정`, `불명확` 중 하나다.
|
||||
- view/component의 `status` 값은 `구현됨`, `계획`, `가정`, `불명확` 중 하나다. frame-view와 index 문서에는 `status`를 두지 않는다.
|
||||
- `구현됨`은 코드 반영 완료 상태다. 코드 근거가 있으면 존재하는 `code` evidence path를 함께 둔다.
|
||||
- `계획`은 정합화된 UI 정의지만 아직 코드 반영 전이거나 사용자 확인 후 재작업 대상인 상태다.
|
||||
- `가정`은 사용자 입력 또는 추정만 있고 확정 근거가 부족한 상태다.
|
||||
- `불명확`은 근거가 부족하거나 판단할 수 없는 상태다.
|
||||
- `가정`과 `불명확`은 코드 동기화 대상이 아니며, sync 전에 `agent-ui/USER_REVIEW.md`로 분리한다.
|
||||
- frontmatter의 `status`, `source_evidence`, `regions`와 본문 `Status`, `Source Evidence`, `Regions`는 같은 기준을 말해야 한다.
|
||||
- view는 frontmatter의 `status`, `source_evidence`, `regions`와 본문 `Status`, `Source Evidence`, `Regions`가 같은 기준을 말해야 한다.
|
||||
- component는 frontmatter의 `status`, `source_evidence`와 본문 `Status`, `Source Evidence`가 같은 기준을 말해야 한다.
|
||||
- frame-view는 frontmatter `regions`와 본문 `Required Regions`가 같은 기준을 말해야 하며, `status`나 `Source Evidence`를 요구하지 않는다.
|
||||
- frontmatter는 기계적 검증을 위한 최소 schema이고, 본문 Markdown은 사람과 agent가 읽는 설명을 유지한다.
|
||||
|
||||
## 운영 동기화 흐름
|
||||
|
|
@ -92,8 +94,12 @@ agent-ui/
|
|||
- 사용자가 "agent-ui와 코드 전체 동기화"처럼 전체 동기화를 명시한 경우에만 현재 agent-ui 전체를 코드와 대조한다.
|
||||
- `update-agent-ui` 또는 수동 `validate-agent-ui`가 코드 반영 필요성을 판단했으면 그 판단을 다음 단계로 전달한다. 전달된 판단이 있으면 뒤 단계는 문서 정합성과 sync intent 자체를 재판단하지 않지만, `sync-agent-ui`는 실제 코드 반영 방식 판정을 수행한다.
|
||||
- 직접 sync가 검증되면 반영된 view/component의 `status`를 `구현됨`으로 바꾼다.
|
||||
- `sync-agent-ui`가 코드 작업을 `plan-required`로 라우팅한 경우에는 plan이 `Agent UI Completion`을 남기고, code-review PASS 시점에 해당 view/component/frame만 `구현됨`으로 반영한다. 이 경로에서는 `sync-agent-ui`가 status를 직접 변경하지 않는다.
|
||||
- `sync-agent-ui`가 코드 작업을 `milestone-required`로 라우팅해 `agent-ui 상태 반영: 대기`를 남긴 Milestone만 종료 검토 통과 시 `구현됨` 반영 gate가 된다. 최종 검증과 code evidence가 확인된 view/component/frame만 반영하며, Milestone 완료만으로 agent-ui 전체 status를 일괄 변경하지 않는다.
|
||||
- `sync-agent-ui`가 코드 작업을 `plan-required`로 라우팅한 경우에는 일반 plan/review 문서에 agent-ui 전용 completion section을 추가하지 않는다.
|
||||
- plan pair 생성 뒤 `sync-agent-ui mode=prepare-code-work`가 `.sync-state.json.pending_code_work`에 task path, status 전환 대상 view/component, validation-only frame-view, 코드 후보, 검증 기준을 기록한다.
|
||||
- pending UI task가 plan refinement 또는 sibling reindex로 경로가 바뀌면 전체 status 대상을 닫는 child 하나로 기존 entry를 rebind한다. 이전 task path를 남기거나 같은 status/frame path를 여러 child에 중복 배정하지 않는다.
|
||||
- 일반 code-review PASS와 exact `complete.log` 생성 뒤 원래 `task-path`와 `completion-log`를 전달받은 `sync-agent-ui mode=reconcile-completion`이 matching pending entry만 사용해 `update-agent-ui`와 `validate-agent-ui`를 순서대로 실행한다. view/component만 `구현됨`으로 반영하고 frame-view에는 status를 추가하지 않는다.
|
||||
- `sync-agent-ui`가 코드 작업을 `milestone-required`로 라우팅하면 exact active Milestone과 UI/code evidence를 `.sync-state.json.pending_milestone_work`에 기록한다. 일반 Milestone 문서에는 agent-ui 전용 완료 필드를 추가하지 않는다.
|
||||
- Milestone 종료 요청에서는 `complete-milestone check-only`가 종료 가능 근거를 반환한 뒤에만 `sync-agent-ui reconcile-milestone-completion`을 실행한다. 최종 검증과 code evidence가 확인된 view/component만 status를 반영하고 연결된 frame은 정합성만 검증하며, 성공 뒤에만 caller/router가 Milestone close를 계속한다.
|
||||
- 사용자 확인 또는 후속 검증에서 불일치가 발견되면 해당 항목은 `계획`으로 되돌릴 수 있다.
|
||||
- `sync-agent-ui`가 검증 실패, 환경 차단, 구현 방향 충돌, 반복 실패를 스스로 해결하지 못하면 commit/push하지 않고 `agent-ui/USER_REVIEW.md`에 게이트를 남긴다.
|
||||
- 실패한 `sync-agent-ui`가 남긴 작업트리 변경은 사용자 결정과 충돌하지 않으면 보존하고, USER_REVIEW 해결 후 같은 변경을 이어서 재검증할 수 있다.
|
||||
|
|
@ -105,6 +111,14 @@ agent-ui/
|
|||
- 기존 방식으로 생성되어 `.sync-state.json`이 없는 agent-ui는 legacy 상태다. 이 경우 기본 변경분 sync를 시작하지 말고 validate 후 baseline migration으로 기준점을 먼저 만든다.
|
||||
- baseline은 `code-first`, `concept-first`, `blank` 모두에서 생성된 agent-ui 기준선을 뜻한다. `concept-first` baseline은 코드 구현 완료가 아니라 agent-ui 기준선 확정이다.
|
||||
- `last_synced_head`는 agent-ui 변경분과 코드 반영이 들어간 sync 결과 commit hash다. `.sync-state.json` 자체를 기록한 commit hash가 아니다.
|
||||
- schema version 4의 `pending_code_work`/`reconciled_code_work`는 `plan-required` task/UI 매핑과 완료 근거고, `pending_milestone_work`/`reconciled_milestone_work`는 `milestone-required` Milestone/UI 매핑과 완료 근거다. 일반 plan/review/complete.log/Milestone 문서는 이 schema를 알 필요가 없다.
|
||||
- `status_paths`에는 status schema가 있는 view/component만, `frame_paths`에는 validation-only frame-view만 기록한다.
|
||||
- pending entry의 `status_paths`와 `frame_paths`는 task와 Milestone을 통틀어 중복 소유할 수 없다. refinement/reindex에서는 helper의 `previous-task-path` rebind를 사용하고 scope/evidence 변경이 있을 때만 검증 후 replace한다.
|
||||
- direct sync의 agent-ui 경로 트리는 code/Milestone pending entry가 소유한 `status_paths`/`frame_paths`와 겹칠 수 없다. 코드 변경 전과 commit 전 확인하고, `record-sync`도 lock 안에서 동일·상위·하위 경로 겹침을 거부한다.
|
||||
- pending entry는 `prepare-code-work`만 생성/갱신하고 `reconcile-completion` 검증 통과 시에만 `reconciled_code_work`로 이동한다. 실패, ambiguous completion, evidence 부족에서는 유지한다.
|
||||
- Milestone pending entry는 `prepare-milestone-work`만 생성/갱신하고 `complete-milestone check-only`와 UI 검증이 모두 통과한 `reconcile-milestone-completion`에서만 `reconciled_milestone_work`로 이동한다. 실패하면 Milestone close를 진행하지 않는다.
|
||||
- `sync-agent-ui`의 prepare, reconcile, direct/baseline 기록은 모두 `sync-agent-ui/scripts/sync_state.py`의 agent-ui directory lock과 inspection 시점 SHA-256 검증으로 수행한다. shared state를 수동 read-modify-write하지 않는다.
|
||||
- `reconcile-completion`은 matching agent-ui/code 결과를 모두 포함하는 commit hash가 검증된 경우에만 `last_synced_head`를 갱신한다. 그렇지 않으면 기존 기준점을 보존하고 notes에 사유를 남긴다.
|
||||
- baseline 또는 sync 완료 시 commit은 두 단계로 남길 수 있다. 먼저 agent-ui/code 변경 commit을 만들고, 그 commit hash를 `.sync-state.json`에 기록한 별도 commit을 만든 뒤 push한다.
|
||||
- 변경분 판정에서는 `.sync-state.json` 자체 변경을 제외한다.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
- 코드 변경 전 관련 domain rule을 먼저 확인한다.
|
||||
- 요청 범위를 넘는 변경을 하지 않는다.
|
||||
- 불확실하면 단정하지 말고 후보를 제시한다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, 또는 plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다.
|
||||
- `agent-task/archive/**`는 일반 작업에서 읽지 않는다. 예외: 사용자가 과거 작업 확인, 복원, 비교, 특정 archive 경로 확인을 요청한 경우, active `PLAN-*.md` / `CODE_REVIEW-*.md` / `USER_REVIEW.md`가 특정 archive evidence 경로를 명시한 경우, plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인하는 경우, plan 스킬이 같은 task group의 새 index를 충돌 없이 할당하는 경우, 또는 `sync-agent-ui mode=reconcile-completion`에 exact `completion-log` 경로가 전달된 경우에만 필요한 범위를 좁게 읽는다. split 의존성 확인은 같은 task group의 후보 `complete.log`만 읽을 수 있다. index 할당은 같은 task group의 archived sibling directory basename만 열람하고 내부 파일은 읽지 않는다. UI 완료 정합화는 전달된 exact `complete.log` 한 건만 읽고 sibling archive log를 탐색하지 않는다.
|
||||
- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-ui/` 디렉터리가 있는 프로젝트에서도 `agent-ui/definition/archive/**`와 `agent-ui/archive/user-review/**`는 일반 작업에서 읽지 않는다. UI 과거 결정, 복원, 비교, 해결된 user review 확인이 필요한 경우에만 `agent-ops/rules/common/rules-agent-ui.md`의 archive 접근 규칙을 따른다.
|
||||
- `agent-spec/` 디렉터리가 있는 프로젝트에서 현재 구현 스펙 확인, 기존 기능 변경, 완료 검토, 구현 스펙 생성/갱신 요청은 세션 1회 `agent-ops/rules/common/rules-agent-spec.md`를 읽고, `agent-spec/index.md`와 매칭되는 spec 문서만 읽는다.
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ source_evidence:
|
|||
- frame은 definition을 대체하지 않는다.
|
||||
- `구현됨`, `계획`, `가정`, `불명확` 상태를 섞어 쓰지 않는다.
|
||||
- 기본 코드 동기화는 `.sync-state.json` 기준 이후 변경분만 대상으로 한다.
|
||||
- plan-required 코드 작업의 task/UI 매핑과 완료 정합화는 `.sync-state.json.pending_code_work`를 사용한다.
|
||||
- milestone-required 코드 작업의 Milestone/UI 매핑과 완료 정합화는 `.sync-state.json.pending_milestone_work`를 사용한다.
|
||||
- view/component status와 validation-only frame-view를 분리하고 완료 기록은 대응하는 `reconciled_*_work`에 둔다.
|
||||
- 전체 동기화는 사용자가 명시한 경우에만 수행한다.
|
||||
|
||||
## Decision History
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ surface_type: ops-dev
|
|||
|
||||
- `definition/`: 현재 UI 정의 source of truth
|
||||
- `frame/`: visual source가 있는 view의 wireframe 연결
|
||||
- `.sync-state.json`: agent-ui -> code 동기화 기준점
|
||||
- `.sync-state.json`: agent-ui -> code 동기화 기준점, plan/Milestone pending 매핑, 완료 정합화 기록
|
||||
- `USER_REVIEW.md`: 사용자 판단이 필요한 활성 질문
|
||||
- `archive/user-review/`: 해결된 사용자 리뷰 로그
|
||||
|
||||
|
|
@ -26,4 +26,7 @@ surface_type: ops-dev
|
|||
- visual wireframe은 visual source가 있을 때만 `frame/**`에 둔다.
|
||||
- `.excalidraw` 파일만으로 UI 기준을 확정하지 않는다.
|
||||
- 초기 생성 이후 기본 흐름은 `agent-ui -> code` 방향이다.
|
||||
- plan-required 코드 작업은 `.sync-state.json.pending_code_work`로 연결하고 일반 code-review 완료 뒤 `sync-agent-ui`가 정합화한다.
|
||||
- milestone-required 코드 작업은 `.sync-state.json.pending_milestone_work`로 연결하고 Milestone 종료 직전에 `sync-agent-ui`가 정합화한다.
|
||||
- `sync-agent-ui`의 state transition은 bundled helper가 담당하며 frame-view는 status 전환 대상이 아니다.
|
||||
- `definition/archive/**`와 `archive/user-review/**`는 과거 기록이며 일반 작업에서 읽지 않는다.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"schema_version": 1,
|
||||
"schema_version": 4,
|
||||
"surface_type": "ops-dev",
|
||||
"baseline_mode": "<code-first|concept-first|blank|migration>",
|
||||
"last_sync_mode": "baseline",
|
||||
|
|
@ -7,5 +7,9 @@
|
|||
"last_synced_at": "<ISO-8601>",
|
||||
"agent_ui_paths": [],
|
||||
"code_paths": [],
|
||||
"pending_code_work": [],
|
||||
"reconciled_code_work": [],
|
||||
"pending_milestone_work": [],
|
||||
"reconciled_milestone_work": [],
|
||||
"notes": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,15 +77,8 @@ Task 체크리스트는 Epic 바로 아래의 flat list로 유지하고, 구현
|
|||
- 요청일: <YYYY-MM-DD | 없음>
|
||||
- 완료 근거: <모든 기능 Task와 Task 안에 명시된 검증 충족 및 구현 잠금 해제 여부를 1~3줄로 요약>
|
||||
- 검토 항목: <없음 | 에이전트/런타임이 확인할 완료 근거 또는 archive 조건>
|
||||
- agent-ui 상태 반영: <해당 없음 | 대기 | 완료 | 차단: 사유>
|
||||
- 리뷰 코멘트: <없음 | 보완/보류/폐기 방향성>
|
||||
|
||||
<!--
|
||||
기본값은 `해당 없음`이다.
|
||||
`sync-agent-ui`가 코드 작업을 `milestone-required`로 라우팅했거나 사용자가 Milestone 종료 검토에서 agent-ui 구현 상태 반영을 명시한 경우에만 `대기`로 둔다.
|
||||
`대기`인 경우 Milestone 종료 검토에서 최종 검증과 code evidence가 확인된 agent-ui view/component/frame만 `구현됨`으로 반영한다.
|
||||
-->
|
||||
|
||||
## 범위 제외
|
||||
|
||||
- <이 Milestone에서 의도적으로 하지 않는 일>
|
||||
|
|
|
|||
|
|
@ -151,8 +151,6 @@ Count `agent-task/{task_name}/code_review_*.log` in the selected active task dir
|
|||
|
||||
The diff is the starting point, not the boundary. Follow behavior and API connections far enough to judge correctness.
|
||||
|
||||
If the active plan or review file contains `Agent UI Completion`, also read `agent-ops/rules/common/rules-agent-ui.md` and the listed active agent-ui documents. Do not read `agent-ui/definition/archive/**` or `agent-ui/archive/user-review/**` unless the review explicitly cites those archive paths.
|
||||
|
||||
Review scope control:
|
||||
|
||||
- Use the plan's commands and checkpoints as the primary evidence. Add one focused, possibly table-driven reproducer only when needed to prove a suspected blocking defect; do not build speculative exhaustive probe matrices.
|
||||
|
|
@ -165,12 +163,11 @@ Review scope control:
|
|||
Before writing the verdict:
|
||||
|
||||
- Compare actual source files against every planned checklist item.
|
||||
- Compare the plan `구현 체크리스트` and review stub `구현 체크리스트`; repair non-behavioral drift when implementation remains judgeable.
|
||||
- Compare the plan `Implementation Checklist` and review stub `Implementation Checklist` (legacy: `구현 체크리스트`); repair non-behavioral drift when implementation remains judgeable.
|
||||
- When the active artifacts have `Roadmap Targets`, check whether the referenced Milestone has `SDD: 필요`. When it does, read only that Milestone and its SDD, compare implementation evidence against the SDD Acceptance Scenarios/Evidence Map for the targeted task ids, and fail completeness or verification trust when evidence is insufficient. Do not require a separate SDD target section.
|
||||
- Directly repair obvious non-behavioral source nits when safe: typos, stale comments, docs, or formatting only, with no behavior/test/API contract change.
|
||||
- If a checklist item contains integrated verification for a feature, treat that feature item as incomplete until both implementation evidence and the matching verification output are present. Do not accept a separate unchecked completion-criteria item as a substitute.
|
||||
- Confirm the implementation marked the matching checklist items in the active review file, including the mandatory `CODE_REVIEW-*-G??.md` evidence item; repair clear artifact drift when evidence supports completion.
|
||||
- If the active plan or review file contains `Agent UI Completion`, verify the implementation-owned fields are filled before PASS: listed agent-ui docs exist, actual code evidence paths exist, requested status updates are explicit, and implementation verification evidence is present. Missing or untrusted evidence is a completeness or verification-trust issue.
|
||||
- Treat review artifact gaps as failures only when they prevent judging implementation correctness, tests, contracts, or verification trust.
|
||||
- Treat every generic `상태` field and implementation blocker record as ordinary evidence, never as a request to stop for the user. Evaluate the user-review gate independently from the current selected Milestone only after a WARN/FAIL finding requires a next state.
|
||||
- Grep renamed/removed symbols for stale references.
|
||||
|
|
@ -180,19 +177,19 @@ Before writing the verdict:
|
|||
|
||||
## Step 4 - Append Verdict
|
||||
|
||||
Append `코드리뷰 결과` to the active `CODE_REVIEW-*-G??.md`.
|
||||
Append the review result to the active `CODE_REVIEW-*-G??.md`. For a canonical English review file, append `## Code Review Result`. For a legacy active review file using Korean headings, append `## 코드리뷰 결과` using Korean field labels to preserve schema compatibility for running legacy dispatchers.
|
||||
|
||||
Before appending `PASS`, if all other PASS conditions are met and the active review file contains `Agent UI Completion`, perform the review-pass status update first: update the listed agent-ui docs to `구현됨`, add actual code evidence, run `validate-agent-ui`, and mark the section's review-agent-owned finalization items. If this update or validation fails, do not append `PASS`; classify the failure as `WARN` or `FAIL` and write the normal follow-up.
|
||||
Required fields for canonical English active pairs:
|
||||
|
||||
Required fields:
|
||||
- `Overall Verdict`: exactly `PASS`, `WARN`, or `FAIL`.
|
||||
- `Dimension Assessment`: Pass/Warn/Fail for correctness, completeness, test coverage, API contract, code quality, implementation deviation, verification trust. If SDD Evidence Map applies through `Roadmap Targets`, also include spec conformance.
|
||||
- `Findings`: `None`, or bullets using `Required`, `Suggested`, or `Nit` with `file:line` and a concrete fix.
|
||||
- `Routing Signals`: calculate once and append `review_rework_count=<N>` and `evidence_integrity_failure=true|false`. Set rework count to archived same-task `WARN|FAIL` verdicts plus one only when the current verdict is non-PASS. Set integrity failure to true only when a claimed test, command, exit code, or production path is absent, unexecuted, or contradicted by fresh reviewer evidence.
|
||||
- `Next Step`: keep only the matching PASS, WARN/FAIL follow-up, or USER_REVIEW line.
|
||||
|
||||
- `종합 판정`: exactly `PASS`, `WARN`, or `FAIL`.
|
||||
- `차원별 평가`: Pass/Warn/Fail for correctness, completeness, test coverage, API contract, code quality, implementation deviation, verification trust. If SDD Evidence Map applies through `Roadmap Targets`, also include spec conformance.
|
||||
- `발견된 문제`: `없음`, or bullets using `Required`, `Suggested`, or `Nit` with `file:line` and a concrete fix.
|
||||
- `라우팅 신호`: calculate once and append `review_rework_count=<N>` and `evidence_integrity_failure=true|false`. Set rework count to archived same-task `WARN|FAIL` verdicts plus one only when the current verdict is non-PASS. Set integrity failure to true only when a claimed test, command, exit code, or production path is absent, unexecuted, or contradicted by fresh reviewer evidence.
|
||||
- `다음 단계`: keep only the matching PASS, WARN/FAIL follow-up, or USER_REVIEW line.
|
||||
For legacy active pairs, use the equivalent legacy field labels: `종합 판정`, `차원별 평가`, `발견된 문제`, `라우팅 신호`, `다음 단계`.
|
||||
|
||||
Do not check archive/next-state items in `코드리뷰 전용 체크리스트` during Step 4. Complete the applicable dedicated checklist items in the archived `code_review_*.log` during Step 7, after archive, next-state writes, and PASS task-artifact moves are done.
|
||||
Do not check archive/next-state items in `Review-Only Checklist` (legacy: `코드리뷰 전용 체크리스트`) during Step 4. Complete the applicable dedicated checklist items in the archived `code_review_*.log` during Step 7, after archive, next-state writes, and PASS task-artifact moves are done.
|
||||
|
||||
Severity semantics:
|
||||
|
||||
|
|
@ -254,7 +251,6 @@ Complete log template:
|
|||
- Do not leave placeholders in `complete.log`.
|
||||
- If the task did not close through `USER_REVIEW.md`, remove the optional user-review row from the `루프 이력` table.
|
||||
- If the archived plan or review log contains `Roadmap Targets`, copy it into `complete.log` as `Roadmap Completion`. Include the Milestone path, completed Task ids, archived plan/review log paths, and verification evidence. If there is no `Roadmap Targets` section, remove the optional `Roadmap Completion` template section entirely and do not invent roadmap targets.
|
||||
- If the archived review log contains `Agent UI Completion`, copy the completed evidence into `complete.log`. If there is no `Agent UI Completion` section, remove the optional complete-log section entirely.
|
||||
- Use `없음` for empty `잔여 Nit` or `후속 작업`.
|
||||
- A PASS `complete.log` must not contain unresolved Required or Suggested issues. Nit-only leftovers may be recorded under `잔여 Nit`.
|
||||
|
||||
|
|
@ -276,7 +272,6 @@ After Step 6:
|
|||
- After moving a split subtask, remove the active parent `agent-task/{task_group}/` only when it is empty.
|
||||
- If verdict is `PASS` and `{task_group}` matches `m-<milestone-slug>`, do not resolve the roadmap target and do not call `update-roadmap`. Report completion event metadata after the task archive move: `origin-task=agent-task/{task_name}` from the original active task path, `task-group={task_group}`, `milestone-slug=<milestone-slug>`, final archive path, `complete.log` path, archived plan/review log paths, and `roadmap-completion=<Task ids from complete.log or none>`.
|
||||
- The runtime consumes that completion event, checks current state, and calls `update-roadmap` if needed. `update-roadmap` only checks Milestone Task ids when `complete.log` contains `Roadmap Completion`; if the section is absent, roadmap Task completion is a no-op even for `m-*` task groups.
|
||||
- If verdict is `PASS` and the archived review log contains `Agent UI Completion`, ensure the listed agent-ui status/evidence update and `validate-agent-ui` check are complete before writing or finalizing `complete.log`. Do not defer this to runtime or Milestone completion.
|
||||
- `WARN` and `FAIL` do not update the roadmap Milestone; the follow-up plan remains under the same `m-<milestone-slug>` task group when the original task was Milestone-linked.
|
||||
- `USER_REVIEW` does not update the roadmap Milestone and does not produce PASS completion metadata. Keep the active task directory in place with `USER_REVIEW.md` and archived plan/review logs until the linked Milestone lock decision is resolved.
|
||||
- If `USER_REVIEW.md` is later resolved as complete/PASS by linked Milestone decision and evidence, write `complete.log`, move the task artifacts to archive, and report `m-*` PASS completion metadata just like a normal `PASS`.
|
||||
|
|
@ -284,7 +279,7 @@ After Step 6:
|
|||
- For user-review-resolved PASS, confirm the moved archive contains the resolved `USER_REVIEW.md`, `complete.log`, and the existing archived `plan_*.log` / `code_review_*.log`; do not recreate an active review file only to add a new checklist item.
|
||||
- For `WARN` or `FAIL`, open `agent-task/{task_name}/{current_review_archive_name}`.
|
||||
- Run `git check-ignore -q --` on the generated task artifacts (`plan_*.log`, `code_review_*.log`, `user_review_*.log` when present, `complete.log` when present, and active follow-up `.md` files). If any are ignored, apply the Agent-Ops managed gitignore block and re-check before reporting.
|
||||
- Check every applicable item in `코드리뷰 전용 체크리스트`; leave mutually exclusive verdict items unchecked.
|
||||
- Check every applicable item in `Review-Only Checklist` (legacy: `코드리뷰 전용 체크리스트`); leave mutually exclusive verdict items unchecked.
|
||||
- If any applicable item cannot be checked, finish the missing archive, `complete.log`, task-artifact move, mandatory plan-skill follow-up, or `USER_REVIEW.md` write first.
|
||||
- Do not recreate an active review file just to update this checklist; update the archived `code_review_*.log`.
|
||||
- Only report after the archived review log has the verdict, applicable checked review-only checklist, required next-state files, for `PASS` or user-review-resolved PASS the final task archive move, for `m-*` PASS tasks the completion event metadata, and for unresolved `USER_REVIEW` the filled `USER_REVIEW.md`.
|
||||
|
|
@ -322,8 +317,6 @@ Report Required/Suggested counts, archive names, the final task archive path for
|
|||
- PASS milestone task group: `m-<milestone-slug>` completion event metadata was reported for runtime; roadmap was not modified by code-review.
|
||||
- PASS with `Roadmap Targets`: `complete.log` contains `Roadmap Completion` with Milestone path, Task ids, archived plan/review evidence, and verification evidence.
|
||||
- PASS without `Roadmap Targets`: `complete.log` omits `Roadmap Completion` and reported metadata says `roadmap-completion=none`.
|
||||
- PASS with `Agent UI Completion`: listed agent-ui docs were updated to `구현됨` with code evidence, `validate-agent-ui` passed, and `complete.log` contains `Agent UI Completion`.
|
||||
- PASS without `Agent UI Completion`: complete.log omits `Agent UI Completion`.
|
||||
- WARN/FAIL without user-review gate: the plan skill was invoked for the exact task path with verified `review_rework_count` and `evidence_integrity_failure`, completed `finalize-task-routing`, and created new active `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md` files matching the fresh routed output; no `complete.log`.
|
||||
- WARN/FAIL follow-up: the plan input omitted prior route fields, revalidated outcome/acceptance/exclusions from current evidence, used the completed in-memory PLAN as the packet, and copied identical `Archive Evidence Snapshot` sections into the new plan/review pair.
|
||||
- Follow-up plans and review stubs keep implementation agents limited to implementation/test/evidence and contain no implementation-owned user-review request section.
|
||||
|
|
|
|||
|
|
@ -33,15 +33,6 @@
|
|||
- `{task-id}`: PASS; evidence=`{archived-plan-log}`, `{archived-review-log}`; verification=`{command or saved output path}`
|
||||
- Not completed task ids: 없음
|
||||
|
||||
## Agent UI Completion
|
||||
|
||||
{optional; include only when archived review had Agent UI Completion. Remove this entire section when there is no Agent UI Completion.}
|
||||
|
||||
- Agent UI docs:
|
||||
- `{agent-ui/definition/views/<view-id>/index.md}`: `구현됨`; code evidence=`{code path}`; implementation verification=`{command or output path}`; validation=`{validate-agent-ui result or output path}`
|
||||
- Status updates: 완료
|
||||
- Remaining agent-ui issues: 없음
|
||||
|
||||
## 잔여 Nit
|
||||
|
||||
- 없음
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
name: complete-milestone
|
||||
version: 1.1.0
|
||||
description: "마일스톤 완료해도 될지 검토, 현 마일스톤 종료 검토, 현재 마일스톤 닫고 다음 마일스톤 지정, 검토중 Milestone 코드레벨 종료 감사 요청에 사용한다. 코드/테스트/계약/evidence를 점검하고 작은 보완은 처리하거나 큰 보완은 plan으로 넘기며, agent-spec이 있으면 update-spec을 필수 gate로 수행한 뒤 update-roadmap으로 완료/archive와 다음 Milestone 지정을 처리한다."
|
||||
---
|
||||
|
||||
|
|
@ -24,6 +23,7 @@ description: "마일스톤 완료해도 될지 검토, 현 마일스톤 종료
|
|||
- `next-milestone`: 종료 후 `current.md`에 둘 다음 Milestone 후보. 없으면 Phase 흐름에서 자동 후보를 찾되 모호하면 보고한다. (선택)
|
||||
- `mode`: `check-only` 또는 `close`. 기본값은 `close`다. (선택)
|
||||
- `evidence`: 완료 판단에 사용할 complete.log, 테스트 결과, 사용자 설명, PR/커밋 등. (선택)
|
||||
- `verification-context`: 환경, 출처, 명령, 기대 결과, precondition, 제약, gap, confidence를 담은 중립 검증 handoff. 있으면 사용하고 현재 checkout과 다시 대조한다. (선택)
|
||||
|
||||
## 먼저 확인할 것
|
||||
|
||||
|
|
@ -52,7 +52,8 @@ description: "마일스톤 완료해도 될지 검토, 현 마일스톤 종료
|
|||
- Milestone `기능` Task와 검증 문구, `완료 리뷰`, complete.log, SDD Evidence Map, 관련 코드/계약/테스트를 비교한다.
|
||||
- `agent-task/archive/**`는 같은 `m-<milestone-slug>` complete.log 후보처럼 규칙상 허용된 범위만 좁게 읽는다.
|
||||
- 코드/계약 변경에 닿으면 관련 domain rule과 `agent-contract/index.md` 라우팅을 따른다.
|
||||
- 필요한 검증 명령은 `agent-test/<env>/rules.md`와 관련 profile을 따른다. 환경 미지정은 local로 본다.
|
||||
- `verification-context`가 있으면 출처 경로, 명령, 기대 결과, precondition, 제약, gap, confidence가 현재 checkout에 맞는지 확인한다.
|
||||
- handoff가 없거나 불완전하면 repository manifest, script, workflow, domain rule, 관련 테스트와 안전한 read-only probe에서 부족한 검증 사실을 보완한다. 선택 handoff 누락만으로 종료 검토를 차단하지 않는다.
|
||||
|
||||
4. **보완 분기**
|
||||
- 작은 문서/코드/테스트 보완으로 바로 해결 가능한 이슈는 직접 수정하고 검증한다.
|
||||
|
|
@ -92,6 +93,7 @@ description: "마일스톤 완료해도 될지 검토, 현 마일스톤 종료
|
|||
- [ ] 대상 Milestone이 활성 경로에서 정확히 하나로 확정되었는가
|
||||
- [ ] Milestone 상태, 기능 Task, 구현 잠금, 완료 리뷰, SDD gate를 확인했는가
|
||||
- [ ] 코드/계약/테스트 evidence를 확인했거나 범위 불명확 사유를 보고했는가
|
||||
- [ ] 전달된 verification context와 repository-native fallback의 출처·gap·confidence를 구분했는가
|
||||
- [ ] 작은 보완은 검증까지 수행하고, 큰 보완은 plan으로 넘겼는가
|
||||
- [ ] `agent-spec/`가 있으면 `update-spec` 결과를 확인하고 완료 진행 가능 상태가 `Spec updated` 또는 `Spec update not needed`인지 판단했는가
|
||||
- [ ] `Spec blocked` 또는 `create-spec needed` 상태에서 Milestone 완료/archive를 하지 않았는가
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
name: create-roadmap
|
||||
version: 1.19.2
|
||||
description: AI-first 개인/소규모 프로젝트의 전체 목표, Phase scaffold, Phase 하위 Milestone 문서, 전역 priority-queue.md 실행 순서 문서, 로컬 current.md 활성 Phase/Milestone 창, archive Phase scaffold를 처음 생성하고, agent-ui 코드 동기화 Milestone의 완료 리뷰 상태 반영 항목을 포함하는 공통 스킬
|
||||
description: AI-first 개인/소규모 프로젝트의 전체 목표, Phase scaffold, Phase 하위 Milestone 문서, 전역 priority-queue.md 실행 순서 문서, 로컬 current.md 활성 Phase/Milestone 창, archive Phase scaffold를 처음 생성할 때 사용한다.
|
||||
---
|
||||
|
||||
# 로드맵 생성
|
||||
|
|
@ -127,9 +126,6 @@ agent-roadmap/
|
|||
- epic-id와 item-id는 공백 없는 짧은 ASCII 토큰이며, 해당 Milestone 안에서만 유일하면 된다.
|
||||
- 검증이 필요한 Task에만 같은 항목 안에 `검증: <명령/확인 방법/기대 결과>`를 붙인다. 검증이 필요 없는 기능에는 억지 검증을 붙이지 않는다.
|
||||
- `완료 리뷰`는 새 Milestone에서는 `상태: 없음`, `요청일: 없음`으로 두고, 모든 기능 Task와 Task 안에 명시된 검증이 충족되고 `구현 잠금`이 해제된 뒤 에이전트/런타임 완료 근거를 기록할 때 갱신한다. 에이전트가 확정할 수 없는 결정 항목은 완료 리뷰가 아니라 `구현 잠금 > 결정 필요` 또는 SDD `USER_REVIEW.md`로 분리한다.
|
||||
- `agent-ui 상태 반영` 항목은 모든 신규 Milestone의 `완료 리뷰`에 두고 기본값은 `해당 없음`으로 적는다.
|
||||
- `sync-agent-ui`의 `milestone-required` 코드 작업 라우팅으로 생성/갱신하는 Milestone, 또는 사용자가 Milestone 종료 검토에서 agent-ui 구현 상태 반영을 명시한 Milestone만 `agent-ui 상태 반영: 대기`로 둔다.
|
||||
- `대기` Milestone은 종료 검토에서 최종 검증과 code evidence가 확인된 agent-ui view/component/frame만 `구현됨`으로 반영한다.
|
||||
- `범위`, `범위 제외`, `작업 컨텍스트`는 설명 목록으로 작성하고, 실행해야 할 작업을 이 섹션에 숨기지 않는다.
|
||||
|
||||
## 먼저 확인할 것
|
||||
|
|
@ -191,7 +187,6 @@ agent-roadmap/
|
|||
- `check-gate` 결과는 `SDD gate` 항목에 `pass`, `review-required`, `blocked`, `invalid` 중 하나로 분리해 남긴다. `review-ready`로 만든 `USER_REVIEW.md` 때문에 막힌 경우는 `review-required`로 보고하고 형식 보완 실패로 보지 않는다.
|
||||
- `[검토중]` Milestone이 있다면 `구현 잠금`이 해제되어 있고 미완료 `결정 필요` 항목이 없는지 확인한다.
|
||||
- Milestone 문서에 `완료 리뷰` 섹션이 있는지 확인한다.
|
||||
- `sync-agent-ui`의 `milestone-required` 코드 작업 라우팅으로 생성/갱신한 Milestone이면 `완료 리뷰`에 `agent-ui 상태 반영: 대기`가 있고, 그 외 Milestone은 `agent-ui 상태 반영: 해당 없음`인지 확인한다.
|
||||
|
||||
## 출력 형식
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ Invocation modes:
|
|||
- `prepare-follow-up` is used only when code-review has appended WARN/FAIL but has not archived the active pair. This skill completes analysis, final routing, and exact PLAN/review-stub rendering in memory without mutating repository files. It returns `prepared_plan`, `prepared_review`, their routed basenames, the current pair's predicted archive names/numbers, and the post-archive log counts used by the new pair.
|
||||
- Both modes execute the same mandatory Step 3. `prepare-follow-up` is not a route-only shortcut.
|
||||
|
||||
Optional context:
|
||||
|
||||
- `verification_context` is a neutral handoff containing environment, source paths, commands, expected results, preconditions, read-only preflight results, constraints, gaps, and confidence. Consume it when supplied, but verify that its paths and commands still match the current checkout. The plan remains responsible for repository-native fallback analysis when it is absent or incomplete.
|
||||
|
||||
Task path terms:
|
||||
|
||||
- `{task_group}` is the top-level work category under `agent-task/`. Normal task groups use a short snake_case name such as `refactoring`.
|
||||
|
|
@ -51,10 +55,10 @@ Filename rules:
|
|||
Role boundary rules:
|
||||
|
||||
- Implementing agents fill implementation-owned `CODE_REVIEW-*-G??.md` sections, keep active files in place, and report ready for review.
|
||||
- If implementation cannot continue, implementing agents record the exact blocker, attempted commands/output, and resume condition only in `검증 결과` or `계획 대비 변경 사항`, then leave the active files in place for official review.
|
||||
- If implementation cannot continue, implementing agents record the exact blocker, attempted commands/output, and resume condition only in `Verification Results` or `Deviations from Plan` (legacy: `검증 결과` or `계획 대비 변경 사항`), then leave the active files in place for official review.
|
||||
- During implementation, do not ask the user directly, present choices, call user-input tools, or create control-plane stop files. The official reviewer owns all next-state classification.
|
||||
- Required UI evidence capture that needs a user-owned device, emulator, permission, secret, or interactive access unavailable to the agent is a verification blocker, not a user-review reason by itself. Record attempted commands and blocker evidence in `검증 결과` or `계획 대비 변경 사항` so code-review can write a normal follow-up or unresolved verification report.
|
||||
- Finalization (`코드리뷰 결과`, plan/review log rename, `complete.log`, task artifact archive moves, review-only checklist) is code-review-skill only.
|
||||
- Required UI evidence capture that needs a user-owned device, emulator, permission, secret, or interactive access unavailable to the agent is a verification blocker, not a user-review reason by itself. Record attempted commands and blocker evidence in `Verification Results` or `Deviations from Plan` (legacy: `검증 결과` or `계획 대비 변경 사항`) so code-review can write a normal follow-up or unresolved verification report.
|
||||
- Finalization (`Code Review Result` [legacy: `코드리뷰 결과`], plan/review log rename, `complete.log`, task artifact archive moves, review-only checklist) is code-review-skill only.
|
||||
|
||||
Split decision policy:
|
||||
|
||||
|
|
@ -144,7 +148,7 @@ Also note active user-review stops, excluding `agent-task/archive/**`:
|
|||
|
||||
The routed plan file is the loop entry point. A missing active plan normally means only that no plan has been started for a new task; do not create task files for casual analysis, status, or review requests unless the user explicitly asks for a plan.
|
||||
|
||||
If no active plan exists but one or more `USER_REVIEW.md` files exist, report that the linked Milestone decision is required and list the paths unless one path is explicitly selected for resolution or replanning. If a selected active task directory contains `USER_REVIEW.md`, read it before planning. Do not write a new follow-up plan unless the linked Milestone decision has been resolved or the new plan explicitly replans around that recorded decision. When planning resumes from `USER_REVIEW.md`, archive it to `user_review_N.log` in the same task directory before writing the new active plan/review pair, and record the resolved decision in the new plan `배경` or `분석 결과`.
|
||||
If no active plan exists but one or more `USER_REVIEW.md` files exist, report that the linked Milestone decision is required and list the paths unless one path is explicitly selected for resolution or replanning. If a selected active task directory contains `USER_REVIEW.md`, read it before planning. Do not write a new follow-up plan unless the linked Milestone decision has been resolved or the new plan explicitly replans around that recorded decision. When planning resumes from `USER_REVIEW.md`, archive it to `user_review_N.log` in the same task directory before writing the new active plan/review pair, and record the resolved decision in the new plan `Background` or `Analysis` (legacy: `배경` or `분석 결과`).
|
||||
|
||||
If a selected task directory contains both `USER_REVIEW.md` and active `PLAN-*-G??.md` or `CODE_REVIEW-*-G??.md`, report an inconsistent loop state and do not overwrite either state until a later explicit command selects either user-review resolution or the active plan/review path.
|
||||
|
||||
|
|
@ -183,17 +187,16 @@ Before choosing plan files or task directory names, apply the split decision pol
|
|||
|
||||
## Step 2 - Analyze Before Writing
|
||||
|
||||
Complete all items below before creating active plan/review files. Work through them in order; do not proceed to the next step until every checkbox is done. Keep the user request as the scope anchor and reconcile derived acceptance conditions before the split decision; do not create a separate routing summary. The only allowed file edits before writing plan/review files are local `agent-roadmap/current.md` creation or `.gitignore` block repair needed for roadmap routing, plus `create-test` or `update-test` edits when the repository already uses agent-test for the relevant scope or the user explicitly asked to maintain test rules.
|
||||
Complete all items below before creating active plan/review files. Work through them in order; do not proceed to the next step until every checkbox is done. Keep the user request as the scope anchor and reconcile derived acceptance conditions before the split decision; do not create a separate routing summary. The only allowed file edits before writing plan/review files are local `agent-roadmap/current.md` creation or `.gitignore` block repair needed for roadmap routing.
|
||||
|
||||
- [ ] **Load test environment rules** — because implementation plans include verification, determine `test_env` before choosing verification commands. Use the user-specified environment when provided; otherwise use `local`. Agent-test is optional: some repositories or tasks intentionally do not have or use `agent-test/`. Check `agent-test/<test_env>/rules.md`; read it in full when present, even if it appears blank or skeleton. If it is absent, record that no agent-test rule was applied and choose fallback verification sources from repository manifests, scripts, workflows, domain rules, or direct read-only environment probes. Do not create agent-test files merely because a plan has verification. Invoke `create-test` only when the user asked for test-rule creation/maintenance, the current task is itself test-rule work, or the repository already uses agent-test for this scope and the missing/blank rule is a real maintenance gap. If creation is not appropriate or possible in the current task context, record the gap and fallback source; absence is not a user-review blocker by itself.
|
||||
- [ ] **Resolve verification context** — because implementation plans include verification, consume supplied `verification_context` when present and confirm its source paths, commands, expected results, preconditions, constraints, gaps, and confidence still apply. When it is absent or incomplete, derive the missing facts from repository manifests, scripts, workflows, domain rules, related tests, user-provided environment facts, and safe read-only probes. Record which facts came from the handoff and which came from repository-native fallback evidence. A missing optional handoff is not a user-review blocker.
|
||||
- [ ] **Read all source files in full** — read every source file the change will touch, whole file. No partial reads.
|
||||
- [ ] **Resolve test profiles** — if env rules exist and contain `## 라우팅`, read every matching `agent-test/<test_env>/<test-profile>.md` in full. If agent-test is absent or intentionally unused for the task, skip profile resolution and record the fallback verification source. Use `create-test` for missing or structurally blank routes/profiles only when the repository already uses agent-test for this scope or the task is test-rule maintenance. Use `update-test` only when verified command or criteria facts are available or the user asked to maintain test rules. If a profile exists but leaves command/criteria values as `<확인 필요>`, record the incomplete values and choose fallback verification commands from repository manifests or workflows with lower confidence. Do not claim agent-test requires a command that is not written in the env rules or matched profiles.
|
||||
- [ ] **Preflight non-local test environments** — when any required verification leaves the current checkout, including remote runner, field/bootstrap, external provider, Docker/code-server, emulator/device, or shared long-running runtime, derive a read-only preflight before writing final verification commands. Use matched `agent-test` rules when they exist and apply; otherwise derive the preflight from repository manifests, scripts, workflows, domain rules, current task evidence, user-provided environment facts, and direct read-only probes. Record runner, repo root/workdir, branch/HEAD/dirty state, source sync status, binary/artifact paths, command help/version output needed by the verification, config path, runtime identity such as Edge id, ports/process state, external hosts, and OS/arch assumptions. If the preflight shows stale artifacts, dirty/divergent checkout, wrong identity, missing command, closed ports, host OS mismatch, or unsynced source, the plan must add an explicit setup/sync/rebuild step or report the blocker; do not write verification commands that silently assume profile or fallback values are already true.
|
||||
- [ ] **Read all test files in full** — read every test file that exercises the changed behavior, including files implied by matched agent-test profiles when any are used and by the repository test layout.
|
||||
- [ ] **Preflight external verification** — when any required verification leaves the current checkout, including remote runner, field/bootstrap, external provider, Docker/code-server, emulator/device, or shared long-running runtime, confirm or derive a read-only preflight before writing final verification commands. Record runner, repo root/workdir, branch/HEAD/dirty state, source sync status, binary/artifact paths, command help/version output needed by the verification, config path, runtime identity, ports/process state, external hosts, and OS/arch assumptions. If the preflight shows stale artifacts, dirty/divergent checkout, wrong identity, missing command, closed ports, host OS mismatch, or unsynced source, add an explicit setup/sync/rebuild step or report the blocker.
|
||||
- [ ] **Read all test files in full** — read every test file that exercises the changed behavior, including files identified by the verification context and repository test layout.
|
||||
- [ ] **Assess test coverage** — for each behavior change, explicitly record whether existing tests cover it.
|
||||
- [ ] **Assess split boundaries once** — reconcile request acceptance with source/tests, then split only where every child has a stable contract and independent PASS verification. Otherwise keep the invariant together; do not gather extra evidence solely to lower routing risk.
|
||||
- [ ] **Capture recovery signals once** — first-pass uses `review_rework_count=0` and `evidence_integrity_failure=false`. In `prepare-follow-up`, reuse the values already validated and appended by code-review; do not recount verdict history. For another isolated replan, derive them once from the same-task state already loaded for planning, without a routing-only log pass.
|
||||
- [ ] **Resolve split predecessor completion** — if the selected or proposed subtask directory has `NN+PP[,QQ...]_...`, resolve each predecessor index under the same task group. Check only the active and archive candidate patterns defined in the task directory naming rules. Record found active/archive paths, missing predecessors, or ambiguous matches in `분석 결과 > 분할 판단` and, when order matters, `의존 관계 및 구현 순서`.
|
||||
- [ ] **Resolve split predecessor completion** — if the selected or proposed subtask directory has `NN+PP[,QQ...]_...`, resolve each predecessor index under the same task group. Check only the active and archive candidate patterns defined in the task directory naming rules. Record found active/archive paths, missing predecessors, or ambiguous matches in `Analysis > Split Judgment` (legacy: `분석 결과 > 분할 판단`) and, when order matters, `Dependencies and Execution Order` (legacy: `의존 관계 및 구현 순서`).
|
||||
- [ ] **Grep all symbol references** — for any renamed or removed symbol, find every call site and import chain.
|
||||
- [ ] **Check dependency manifests** — before adding any new package, verify its presence in go.mod / package manifest.
|
||||
- [ ] **Pre-check compile issues** — identify missing interface implementations, type mismatches, and broken imports.
|
||||
|
|
@ -236,8 +239,8 @@ Header line must be exactly:
|
|||
Required sections:
|
||||
|
||||
- Title.
|
||||
- `이 파일을 읽는 구현 에이전트에게`: warn that filling implementation-owned `CODE_REVIEW-*-G??.md` sections is mandatory. Tell the implementer to run verification, fill actual notes/output, keep active files in place, and report ready for review; finalization is code-review-skill only. If blocked, the implementer records only exact blocker evidence, attempted commands/output, and resume conditions in implementation-owned evidence fields. It must not ask the user, call user-input tools, create control-plane stop files, classify the next state, archive logs, or write `complete.log`.
|
||||
- `배경`: 2-4 sentences explaining why the work is needed.
|
||||
- `For the Implementing Agent`: warn that filling implementation-owned `CODE_REVIEW-*-G??.md` sections is mandatory. Tell the implementer to run verification, fill actual notes/output, keep active files in place, and report ready for review; finalization is code-review-skill only. If blocked, the implementer records only exact blocker evidence, attempted commands/output, and resume conditions in implementation-owned evidence fields. It must not ask the user, call user-input tools, create control-plane stop files, classify the next state, archive logs, or write `complete.log`.
|
||||
- `Background`: 2-4 sentences explaining why the work is needed.
|
||||
- `Archive Evidence Snapshot`: include this section only when the plan resumes from `USER_REVIEW.md`, a prior archived review, or any archive evidence. Omit it for first-pass plans with no archive evidence. The section must contain only the archive facts needed to implement without rereading archive by default: prior task/archive paths, verdict, Required/Suggested/Nit summary, affected files, verification evidence, and any roadmap carryover. If exact prior context is still required, cite the specific archive file paths allowed to read; do not ask the implementer to search `agent-task/archive/**` broadly.
|
||||
- `Roadmap Targets`: include this section only when the plan is intended to complete one or more existing Milestone 기능 Task ids. Omit the section entirely for non-roadmap work or Milestone-adjacent work that should not check a Task on PASS. Format exactly:
|
||||
|
||||
|
|
@ -250,48 +253,31 @@ Required sections:
|
|||
- `<task-id>`: <Task text or concise label>
|
||||
- Completion mode: check-on-pass
|
||||
```
|
||||
- `Agent UI Completion`: include this section only when the plan implements agent-ui definition/frame/component changes in code and code-review PASS should move specific agent-ui documents to `status: 구현됨`. Omit it for non-agent-ui work, `direct-sync` work handled entirely by `sync-agent-ui`, and `milestone-required` work whose status update is intentionally deferred to Milestone 종료 검토. Format exactly:
|
||||
|
||||
```markdown
|
||||
## Agent UI Completion
|
||||
|
||||
- Mode: review-pass-status-update
|
||||
- Agent UI docs:
|
||||
- `agent-ui/definition/views/<view-id>/index.md`
|
||||
- Required code evidence:
|
||||
- `<code path>`: <what proves the UI definition is implemented>
|
||||
- Required implementation verification:
|
||||
- `<test or analysis command>`: PASS expected before review
|
||||
- Review finalization validation:
|
||||
- `validate-agent-ui scope=<scope>`: PASS expected after status/evidence update
|
||||
- Status updates on PASS:
|
||||
- `agent-ui/definition/views/<view-id>/index.md`: `계획` -> `구현됨`
|
||||
```
|
||||
- `분석 결과`: record the findings from Step 2 and the final routed output from Step 3. This section is the written output of the analysis — not a summary, but the actual findings that justify the plan's scope and decisions. Must include all of the following subsections:
|
||||
- `읽은 파일`: list every source and test file read during analysis, with path. List agent-test rule/profile files only when they were actually present and read.
|
||||
- `SDD 기준`: for `SDD: 필요` Milestones, list the SDD path, status, targeted Acceptance Scenario ids, their Milestone Task ids, and the Evidence Map rows that drive the plan. State explicitly how those rows shaped the implementation checklist and final verification. If the selected Milestone has `SDD: 불필요`, state the recorded reason. If the work is not Milestone-linked, state "not applicable".
|
||||
- `테스트 환경 규칙`: state the chosen `test_env`, whether `agent-test/<test_env>/rules.md` was present/read/missing/intentionally unused, every matched profile path read when any, the concrete rules/commands applied, any structural blank/skeleton or missing rules, any `<확인 필요>` values, and any fallback verification source. If any required verification leaves the current checkout, include a `테스트 환경 프리플라이트` record with runner, repo root/workdir, branch/HEAD/dirty state, source sync status, binary/artifact paths, required command help/version output, config path, runtime identity such as Edge id, ports/process state, external hosts, OS/arch assumptions, and the exact setup/sync/rebuild step or blocker derived from mismatches. If agent-test is absent or unusable, explicitly say no agent-test rule was applied, what fallback is used, and whether test-rule maintenance is actually needed or not needed for this task.
|
||||
- `테스트 커버리지 공백`: list each behavior change and whether existing tests cover it; explicitly note gaps.
|
||||
- `심볼 참조`: list renamed/removed symbols and every call site found, or state "none" if no symbols were changed.
|
||||
- `분할 판단`: for one plan, name the indivisible invariant or compact boundary; for split plans, list each child's stable contract, PASS evidence, and dependency. For dependent subtask plans, include each predecessor index and whether it is satisfied by an active or archived `complete.log`, missing, or ambiguous.
|
||||
- `범위 결정 근거`: state which files or areas were explicitly excluded from this change and why. This is the boundary justification — the implementing agent must not silently expand scope beyond what is recorded here.
|
||||
- `최종 라우팅`: record `evaluation_mode`, finalizer, both targets' closure/grade/route, `large_indivisible_context`, positive loop-risk names/count, recovery signals, capability-gap evidence, and canonical filenames. Do not include or compare a previous loop's lane/G.
|
||||
- `구현 체크리스트`: a top-level checklist the implementing agent must follow while coding. Include one item per implementation/verification unit; if the roadmap feature Task has `검증:`, keep that verification in the same checklist item instead of making a separate completion-criteria item. Include one item for whole-plan intermediate/final verification only when it is not already covered by the feature items. Make the last item exactly `- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.` Copy this checklist into the review stub's `구현 체크리스트` section with the same item text and order.
|
||||
- `Analysis`: record the findings from Step 2 and the final routed output from Step 3. This section is the written output of the analysis — not a summary, but the actual findings that justify the plan's scope and decisions. Must include all of the following subsections:
|
||||
- `Files Read`: list every source and test file read during analysis, with path. List verification-context source files only when they were actually present and read.
|
||||
- `SDD Criteria`: for `SDD: 필요` Milestones, list the SDD path, status, targeted Acceptance Scenario ids, their Milestone Task ids, and the Evidence Map rows that drive the plan. State explicitly how those rows shaped the implementation checklist and final verification. If the selected Milestone has `SDD: 불필요`, state the recorded reason. If the work is not Milestone-linked, state "not applicable".
|
||||
- `Verification Context`: state whether a handoff was supplied, every source path actually read, concrete commands/criteria applied, preconditions, constraints, gaps, confidence, and repository-native fallback evidence. If required verification leaves the current checkout, include an `External Verification Preflight` record with runner, repo root/workdir, branch/HEAD/dirty state, source sync status, binary/artifact paths, required command help/version output, config path, runtime identity, ports/process state, external hosts, OS/arch assumptions, and the exact setup/sync/rebuild step or blocker derived from mismatches.
|
||||
- `Test Coverage Gaps`: list each behavior change and whether existing tests cover it; explicitly note gaps.
|
||||
- `Symbol References`: list renamed/removed symbols and every call site found, or state "none" if no symbols were changed.
|
||||
- `Split Judgment`: for one plan, name the indivisible invariant or compact boundary; for split plans, list each child's stable contract, PASS evidence, and dependency. For dependent subtask plans, include each predecessor index and whether it is satisfied by an active or archived `complete.log`, missing, or ambiguous.
|
||||
- `Scope Rationale`: state which files or areas were explicitly excluded from this change and why. This is the boundary justification — the implementing agent must not silently expand scope beyond what is recorded here.
|
||||
- `Final Routing`: record `evaluation_mode`, finalizer, both targets' closure/grade/route, `large_indivisible_context`, positive loop-risk names/count, recovery signals, capability-gap evidence, and canonical filenames. Do not include or compare a previous loop's lane/G.
|
||||
- `Implementation Checklist`: a top-level checklist the implementing agent must follow while coding. Include one item per implementation/verification unit; if the roadmap feature Task has `검증:`, keep that verification in the same checklist item instead of making a separate completion-criteria item. Include one item for whole-plan intermediate/final verification only when it is not already covered by the feature items. Make the last item exactly `- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.` Copy this checklist into the review stub's `Implementation Checklist` section with the same item text and order.
|
||||
- One item per change: `### [TAG-1] Title`, `TAG-2`, etc.
|
||||
- `수정 파일 요약`: table mapping files to item ids.
|
||||
- `최종 검증`: runnable commands and expected outcome. Prefer commands from the matched agent-test env/profile rules. If agent-test is missing, blank, skeleton, or lacks a matching command, use repository manifests/workflows as fallback and record that fallback in `분석 결과 > 테스트 환경 규칙`. Commands must be exact and deterministic enough for the reviewer to rerun; use stable ordering for searches and state whether cached test output is acceptable. End this section with **"모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다."**
|
||||
- `Modified Files Summary`: table mapping files to item ids.
|
||||
- `Final Verification`: runnable commands and expected outcome. Prefer commands from verified handoff facts when supplied; fill missing coverage from repository manifests, scripts, workflows, domain rules, and related tests, and record the source in `Analysis > Verification Context`. Commands must be exact and deterministic enough for the reviewer to rerun; use stable ordering for searches and state whether cached test output is acceptable. End this section with **"After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`."**
|
||||
|
||||
Each plan item must include:
|
||||
|
||||
- `문제`: concrete problem with file:line references.
|
||||
- `해결 방법`: exact approach and before/after code block for non-trivial changes.
|
||||
- `수정 파일 및 체크리스트`: exhaustive file-level checklist.
|
||||
- `테스트 작성`: explicit write/skip decision. If writing tests, include path, test name, assertion goal, and fixtures. If skipping, justify.
|
||||
- `중간 검증`: runnable commands and expected result.
|
||||
- `Problem`: concrete problem with file:line references.
|
||||
- `Solution`: exact approach and before/after code block for non-trivial changes.
|
||||
- `Modified Files and Checklist`: exhaustive file-level checklist.
|
||||
- `Test Strategy`: explicit write/skip decision. If writing tests, include path, test name, assertion goal, and fixtures. If skipping, justify.
|
||||
- `Verification`: runnable commands and expected result.
|
||||
|
||||
Include `의존 관계 및 구현 순서` only when order matters.
|
||||
Include `Dependencies and Execution Order` only when order matters.
|
||||
|
||||
For split multi-plan work, the `{subtask_dir}` directory name is the runtime source of truth. If a plan has a `NN+PP[,QQ...]_...` subtask directory name, `의존 관계 및 구현 순서` must echo the decoded predecessor subtask directories under the same task group that must produce `complete.log` before implementation starts, and it must not add dependencies that are absent from the directory name. If a predecessor already completed, cite the active or archived `complete.log` path that satisfies it.
|
||||
For split multi-plan work, the `{subtask_dir}` directory name is the runtime source of truth. If a plan has a `NN+PP[,QQ...]_...` subtask directory name, `Dependencies and Execution Order` (legacy: `의존 관계 및 구현 순서`) must echo the decoded predecessor subtask directories under the same task group that must produce `complete.log` before implementation starts, and it must not add dependencies that are absent from the directory name. If a predecessor already completed, cite the active or archived `complete.log` path that satisfies it.
|
||||
|
||||
Quality rules:
|
||||
|
||||
|
|
@ -316,12 +302,12 @@ Test policy:
|
|||
Verification fidelity rules:
|
||||
|
||||
- Plan verification commands are a contract. The implementing agent must run them exactly as written.
|
||||
- If a command must be changed, the implementing agent must record the replacement command and reason in `계획 대비 변경 사항`, then paste the replacement command's actual stdout/stderr.
|
||||
- If a command must be changed, the implementing agent must record the replacement command and reason in `Deviations from Plan` (legacy: `계획 대비 변경 사항`), then paste the replacement command's actual stdout/stderr.
|
||||
- Before claiming a tool is unavailable, run and record `command -v <tool>` or the project-equivalent check.
|
||||
- Before a remote/field/external verification command assumes a checkout, binary, config, runtime identity, or listening port, the plan must include a preflight command that proves those assumptions or a setup command that makes them true.
|
||||
- Do not download, generate, or leave verification tools inside the repository. Temporary tools belong outside the repo, such as under `/tmp`, and must not become task artifacts.
|
||||
- For search commands whose output order may vary, specify deterministic options in the plan, for example `rg --sort path`.
|
||||
- `검증 결과` must contain actual stdout/stderr, not summarized or reconstructed output. If output is too long, record the saved output file path and the exact command used to create it.
|
||||
- `Verification Results` (legacy: `검증 결과`) must contain actual stdout/stderr, not summarized or reconstructed output. If output is too long, record the saved output file path and the exact command used to create it.
|
||||
- If mobile/UI verification has no progress for 2 minutes or times out, stop blind retries; collect focused stdout plus screenshot/window/UI-tree evidence when available, or record why capture is impossible.
|
||||
- If the plan's pass condition says all leftovers must be intentional exceptions, any `변경 필요` item forces FAIL until resolved or explicitly reclassified with evidence.
|
||||
- Decide in the plan whether Go test cache output is acceptable. If fresh execution matters, use `go test -count=1 ...`.
|
||||
|
|
@ -332,7 +318,7 @@ Read `agent-ops/skills/common/plan/templates/review-stub-template.md` in full on
|
|||
Replace every occurrence of each token below:
|
||||
|
||||
- Scalar tokens: `{date}`, `{task_group}`, `{task_name}`, `{plan_number}`, `{TAG}`, `{build_lane}`, `{build_grade}`, `{review_lane}`, `{review_grade}`, `{plan_log_number}`, `{review_log_number}`.
|
||||
- Plan-copy tokens: `{roadmap_targets_or_omit}`, `{archive_evidence_snapshot_or_omit}`, `{agent_ui_completion_or_omit}`, `{implementation_checklist}`, `{review_checkpoints}`.
|
||||
- Plan-copy tokens: `{roadmap_targets_or_omit}`, `{archive_evidence_snapshot_or_omit}`, `{implementation_checklist}`, `{review_checkpoints}`.
|
||||
- Generated row/section tokens: `{implementation_completion_rows}` contains one row for every plan item, and `{verification_result_sections}` contains the fixed verification instructions plus every intermediate/final command from the plan.
|
||||
|
||||
Use the routed build/review grades independently. Remove optional plan-copy content by replacing its token with an empty string, not by leaving template instructions.
|
||||
|
|
@ -362,16 +348,15 @@ Do not write or return a prepared pair when either routing target is not `routed
|
|||
- In `write` mode when resuming from `USER_REVIEW.md`, it was archived to the calculated `current_user_review_archive_name` and the resolved linked Milestone decision was recorded in the new plan.
|
||||
- `Roadmap Targets` exists only when PASS should check explicit Milestone Task ids, the task group is `m-<milestone-slug>` for the listed Milestone path, and every listed Task id exists in the selected active Milestone.
|
||||
- If `Roadmap Targets` exists in the plan, the review stub contains the identical section. If it does not exist in the plan, the review stub omits it too.
|
||||
- If `Agent UI Completion` exists in the plan, the review stub contains the matching section with implementation-owned evidence fields. If it does not exist in the plan, the review stub omits it too.
|
||||
- If the selected Milestone has `SDD: 필요`, the plan's `분석 결과 > SDD 기준` proves that the implementation checklist and final verification were derived from the approved SDD Acceptance Scenarios and Evidence Map. Missing SDD mapping blocks plan creation.
|
||||
- If the selected Milestone has `SDD: 필요`, the plan's `Analysis > SDD Criteria` (legacy: `분석 결과 > SDD 기준`) proves that the implementation checklist and final verification were derived from the approved SDD Acceptance Scenarios and Evidence Map. Missing SDD mapping blocks plan creation.
|
||||
- If the plan is a follow-up or resumes from prior archive evidence, it has `Archive Evidence Snapshot` and the review stub contains the identical section.
|
||||
- `분석 결과 > 테스트 환경 규칙` records the selected test env, env rules read/missing/structural-blank/intentionally-unused state, matched profiles read when any, and any fallback verification source.
|
||||
- `Analysis > Verification Context` records supplied handoff facts, source paths actually read, external preflight, gaps, confidence, and repository-native fallback evidence.
|
||||
- Dependent split plans record predecessor completion using active sibling `complete.log` or matching archived `complete.log`; ambiguous archive matches are not guessed.
|
||||
- Every plan item has problem, solution, checklist, test decision, and intermediate verification.
|
||||
- The plan and review stub have matching `구현 체크리스트` item text/order; their final checkbox is the mandatory `CODE_REVIEW-*-G??.md` evidence item.
|
||||
- The plan and review stub have matching `Implementation Checklist` (legacy: `구현 체크리스트`) item text/order; their final checkbox is the mandatory `CODE_REVIEW-*-G??.md` evidence item.
|
||||
- `finalize-task-routing` ran once after the PLAN body was complete, used no routing-only evidence pass, counted only positive packet-local risk, kept capability/grade basis from being relabeled by escalation signals, and produced matching filenames.
|
||||
- Review WARN/FAIL follow-ups entered through this plan skill and did not inherit or compare the archived lane/G.
|
||||
- The plan's implementer instructions and review stub limit local implementation agents to implementation/test/evidence work and keep user-review classification plus control-plane stop files out of their input and ownership.
|
||||
- The review stub has a clearly marked `코드리뷰 전용 체크리스트` owned only by the review agent.
|
||||
- The review stub has a clearly marked `Review-Only Checklist` (legacy: `코드리뷰 전용 체크리스트`) owned only by the review agent.
|
||||
- Routed review file completion table lists every plan item.
|
||||
- In `prepare-follow-up`, no repository file was mutated and the returned prepared basenames/bodies, `plan_number`, current archive names/numbers, post-archive log counts, and `gitignore_repair_needed` are complete; in `write`, prior active state was archived with its own parsed route and both new active files were written.
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Complete the `Implementation Checklist`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked, record the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields.
|
||||
> Do not ask the user directly, present choices, call user-input tools, create control-plane stop files, or classify the next state.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Finalization (`Code Review Result`, log rename, `complete.log`, archive moves, `Review-Only Checklist`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
## Overview
|
||||
|
||||
date={date}
|
||||
task={task_name}, plan={plan_number}, tag={TAG}
|
||||
|
|
@ -19,62 +19,60 @@ task={task_name}, plan={plan_number}, tag={TAG}
|
|||
{roadmap_targets_or_omit}
|
||||
{archive_evidence_snapshot_or_omit}
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
## For the Review Agent
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
> **[REVIEW AGENT ONLY]** The finalization steps below are review-agent only. Implementing agents must not execute this section.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
Compare implementation of each item against source files and verify that output in `Verification Results` matches code.
|
||||
Review completion means the following steps are finished:
|
||||
|
||||
1. 판정과 `review_rework_count` / `evidence_integrity_failure` 라우팅 신호를 append한다.
|
||||
2. `CODE_REVIEW-{review_lane}-{review_grade}.md` → `code_review_{review_lane}_{review_grade}_{review_log_number}.log`, `PLAN-{build_lane}-{build_grade}.md` → `plan_{build_lane}_{build_grade}_{plan_log_number}.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 code-review skill이 요구하는 다음 filesystem state를 완전히 작성한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
1. Append verdict and `review_rework_count` / `evidence_integrity_failure` routing signals.
|
||||
2. Archive `CODE_REVIEW-{review_lane}-{review_grade}.md` → `code_review_{review_lane}_{review_grade}_{review_log_number}.log` and `PLAN-{build_lane}-{build_grade}.md` → `plan_{build_lane}_{build_grade}_{plan_log_number}.log`.
|
||||
3. If PASS, write `complete.log` and move active task directory to `agent-task/archive/YYYY/MM/{task_name}/`. If WARN/FAIL, fully write the next filesystem state required by the code-review skill.
|
||||
4. If PASS and task group is `m-<milestone-slug>`, report completion event metadata. Roadmap state check and `update-roadmap` calls are runtime responsibilities.
|
||||
5. Check applicable `Review-Only Checklist` items at the final `.log` location before reporting.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
## Implementation Item Completion
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
| Item | Status |
|
||||
|------|---------|
|
||||
{implementation_completion_rows}
|
||||
|
||||
## 구현 체크리스트
|
||||
## Implementation Checklist
|
||||
|
||||
{implementation_checklist}
|
||||
|
||||
{agent_ui_completion_or_omit}
|
||||
## Review-Only Checklist
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
> **[REVIEW AGENT ONLY]** This checklist is used only by the review agent.
|
||||
> Implementing agents must not modify or check this section.
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
- [ ] Append one verdict of `PASS`, `WARN`, or `FAIL` and verified `review_rework_count`, `evidence_integrity_failure` to `Code Review Result`.
|
||||
- [ ] Verify that verdict, `Dimension Assessment`, and Required/Suggested/Nit classifications match.
|
||||
- [ ] Archive active `CODE_REVIEW-*-G??.md` to `code_review_{review_lane}_{review_grade}_{review_log_number}.log`.
|
||||
- [ ] Archive active `PLAN-*-G??.md` to `plan_{build_lane}_{build_grade}_{plan_log_number}.log`.
|
||||
- [ ] Verify that the Agent-Ops managed block in `.gitignore` unignores `agent-task/**/*.md` and `agent-task/**/*.log` and ignores `agent-roadmap/current.md`.
|
||||
- [ ] If PASS, write `complete.log` based on `agent-ops/skills/common/code-review/templates/complete-log-template.md` and leave no active `.md` files.
|
||||
- [ ] If PASS, move active task directory `agent-task/{task_name}/` to `agent-task/archive/YYYY/MM/{task_name}/` and update this checklist at the final archive path.
|
||||
- [ ] If PASS and task group is `m-<milestone-slug>`, report completion event metadata for runtime, without modifying roadmap or directly calling `update-roadmap`.
|
||||
- [ ] If PASS for split work, remove empty active parent `agent-task/{task_group}/` or verify it was kept due to remaining siblings/files.
|
||||
- [ ] If WARN/FAIL, write the next filesystem state matching code-review verdict and do not write `complete.log`.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정과 검증된 `review_rework_count`, `evidence_integrity_failure`를 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_{review_grade}_{review_log_number}.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_{build_grade}_{plan_log_number}.log`로 아카이브한다.
|
||||
- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이면 code-review skill의 판정에 맞는 다음 filesystem state를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
## Deviations from Plan
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
_Record any deviations from the plan and the rationale here._
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
## Key Design Decisions
|
||||
|
||||
## 주요 설계 결정
|
||||
_Record key design decisions here._
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
## Reviewer Checkpoints
|
||||
|
||||
{review_checkpoints}
|
||||
|
||||
## 검증 결과
|
||||
## Verification Results
|
||||
|
||||
{verification_result_sections}
|
||||
|
||||
|
|
@ -84,18 +82,17 @@ _구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
|||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
||||
## 섹션 소유권
|
||||
## Section Ownership
|
||||
|
||||
| Section | Owner | Note |
|
||||
|---------|-------|------|
|
||||
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
|
||||
| Header comment, Overview, Review Agent Instructions | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
|
||||
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS |
|
||||
| Archive Evidence Snapshot | Fixed at stub creation from plan when present | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required |
|
||||
| Agent UI Completion | Mixed | Present only for plan-required agent-ui code work; implementing agent fills actual evidence, review agent applies `구현됨` status/evidence update on PASS and copies the section into `complete.log` |
|
||||
| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
|
||||
| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only |
|
||||
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
|
||||
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
|
||||
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
|
||||
| 검증 결과 (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `계획 대비 변경 사항` entry |
|
||||
| 코드리뷰 결과 | Review agent appends | Not included in stub |
|
||||
| Implementation Item Completion (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
|
||||
| Implementation Checklist (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only |
|
||||
| Review-Only Checklist | Review agent only | Implementing agent must not modify or check this section |
|
||||
| Deviations from Plan, Key Design Decisions | Implementing agent | Replace placeholder text with actual content |
|
||||
| Reviewer Checkpoints | Fixed at stub creation | Pre-filled from plan |
|
||||
| Verification Results (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `Deviations from Plan` entry |
|
||||
| Code Review Result | Review agent appends | Not included in stub |
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ description: 현재 plan들 세분화해, 현재 plan 세분화, 기존 plan 더
|
|||
|
||||
4. **현재 PLAN/CODE_REVIEW 형식을 유지한다**
|
||||
- 각 child는 기존 PLAN을 복제한 뒤 자신의 scope만 남긴다. Header/task, title, background, `구현 체크리스트`, plan item, `수정 파일 요약`, `최종 검증`을 child 경계에 맞게 갱신한다. 기존 PLAN에 `분석 결과`가 있으면 읽은 파일·테스트 공백·심볼 참조·분할 판단·범위 결정 근거도 child 범위로 줄인다.
|
||||
- `Roadmap Targets`는 전체 결과를 닫는 closure child 하나에만 둔다. `Agent UI Completion`도 해당 구현과 PASS evidence를 소유하는 child 하나에만 둔다. 소유 child를 정할 수 없으면 분리하지 않는다.
|
||||
- `Roadmap Targets`는 전체 결과를 닫는 closure child 하나에만 둔다.
|
||||
- 각 review는 기존 CODE_REVIEW를 복제한 뒤 header/task, 완료표, 구현 checklist, checkpoint, 검증 section을 matching PLAN과 맞춘다. 고정 안내와 review 전용 section의 문구는 유지하되 child task path와 future archive suffix 참조는 갱신한다.
|
||||
- PLAN과 review는 입력에 이미 있는 section 구조를 유지하며 없는 section을 새로 만들지 않는다. 첫 task header 외의 HTML metadata comment는 출력에서 제거한다.
|
||||
- PLAN과 review의 첫 줄은 동일한 `task`, `plan`, `tag`를 사용한다. Checklist 문구·순서와 검증 명령을 서로 일치시킨다.
|
||||
|
|
|
|||
|
|
@ -5,11 +5,17 @@
|
|||
- SDD/spec gate 자체의 작성, 갱신, gate 확인, 사용자 리뷰, 잠금 해제는 `roadmap-sdd`로 보낸다.
|
||||
- 로드맵/마일스톤 생성 또는 갱신 요청 안에 SDD 필요 여부와 gate 연결이 포함되면 `create-roadmap` 또는 `update-roadmap`을 진입점으로 삼고, 해당 흐름에서 `roadmap-sdd` create/check를 처리한다.
|
||||
- agent-spec은 구현 후 현재 상태를 설명하는 living spec이다. SDD/spec gate와 구분하며, 현재 구현 스펙 생성은 `create-spec`, 갱신은 `update-spec`으로 보낸다.
|
||||
- "마일스톤 완료해도 될지 검토", "현 마일스톤 종료 검토", "현재 마일스톤 닫고 다음 마일스톤 지정"처럼 종료 판단, spec sync, roadmap archive, 다음 Milestone 지정을 함께 요구하는 요청은 `complete-milestone`으로 보낸다. 이 흐름 안에서 `update-spec`을 필수 gate로 수행한다.
|
||||
- "마일스톤 완료해도 될지 검토", "현 마일스톤 종료 검토", "현재 마일스톤 닫고 다음 마일스톤 지정"처럼 종료 판단, spec sync, roadmap archive, 다음 Milestone 지정을 함께 요구하는 요청은 `complete-milestone`으로 보낸다. 이 흐름 안에서 `update-spec`을 필수 gate로 수행한다. 사용할 테스트 환경 규칙이 있으면 먼저 `update-test mode=resolve-context`의 중립 `Verification Context`를 전달하고, 없거나 불완전하면 complete-milestone의 repository-native fallback을 사용한다.
|
||||
- 구현 계획 요청에서 선택 Milestone의 구현 잠금이 남아 있으면 `plan`은 구현 계획을 만들지 않고 잠금 차단을 보고한다.
|
||||
- SDD 생성/갱신/잠금 해제는 `roadmap-sdd` 또는 `update-roadmap` 요청으로 처리한다.
|
||||
- 런타임이 `origin-task`/`complete-log` 단건 완료 이벤트를 전달한 경우는 `update-roadmap`으로 처리한다.
|
||||
- active/archive `complete.log`, 관련 파일, git history를 종합해 Milestone 작업 상태를 복구하거나 확인하는 요청은 `sync-milestone-workstate`로 처리한다.
|
||||
- plan 요청에 사용할 테스트 환경 규칙이 있으면 `update-test mode=resolve-context`로 read-only `Verification Context`를 만든 뒤 `plan`에 전달한다. 규칙이 없거나 매칭되지 않으면 파일을 생성하지 않고 plan의 repository-native fallback을 사용한다.
|
||||
- `sync-agent-ui`가 `plan-required`로 라우팅한 작업은 plan pair 생성 뒤 `sync-agent-ui mode=prepare-code-work`로 task/UI 매핑을 기록한다. 일반 code-review PASS와 exact `complete.log` 생성 뒤에는 원래 `task-path`와 `completion-log`를 `sync-agent-ui mode=reconcile-completion`에 전달해 해당 매핑만 정합화한다.
|
||||
- pending UI task가 WARN/FAIL follow-up plan으로 교체되면 새 pair 생성 뒤 `prepare-code-work`를 다시 실행한다. 매핑 범위가 실제로 달라진 경우에만 검증 후 state helper의 `--replace`를 사용한다.
|
||||
- pending UI task를 `refine-local-plans`로 분할하거나 sibling reindex해 경로를 바꾼 경우에는 refine 완료 뒤 `sync-agent-ui mode=prepare-code-work`를 호출한다. 기존 경로는 `previous-task-path`, status 대상 전체를 닫는 child 하나는 새 `task-path`로 넘겨 매핑을 rebind하고, scope/evidence가 달라질 때만 검증 후 `--replace`를 사용한다.
|
||||
- `sync-agent-ui`가 `milestone-required`로 라우팅한 작업은 `update-roadmap`이 exact active Milestone을 확정한 뒤 `sync-agent-ui mode=prepare-milestone-work`로 Milestone/UI 매핑을 기록한다. 일반 Milestone 문서에는 agent-ui 전용 완료 필드를 넣지 않는다.
|
||||
- Milestone 종료 요청에서 exact target이 `.sync-state.json.pending_milestone_work`에 있으면 `complete-milestone mode=check-only`를 먼저 실행한다. 종료 가능 근거를 `sync-agent-ui mode=reconcile-milestone-completion`에 전달해 성공 또는 동일 evidence의 already-reconciled를 확인한 뒤에만 `complete-milestone mode=close`를 실행한다. UI 정합화가 실패하면 close하지 않는다.
|
||||
|
||||
| 요청 키워드 | SKILL.md |
|
||||
|------------|----------|
|
||||
|
|
@ -19,9 +25,9 @@
|
|||
| agent-ui 생성, UI 스캐폴드 생성, UI 정의 구조 생성, 화면 정의 구조 생성, agent-ui scaffold | `agent-ops/skills/common/create-agent-ui/SKILL.md` |
|
||||
| agent-ui 갱신, agent-ui 업데이트, view 추가, component 추가, frame 추가, wireframe 추가, 화면 정의 갱신, 화면 정의서 갱신, 화면정의서 수정, 화면 정의서에 추가, 화면 정의서에서 제거, 와이어프레임 수정, 와이어프레임에 추가, wireframe 수정, wireframe에 추가, agent-ui USER_REVIEW 해결, UIR 결정 반영 | `agent-ops/skills/common/update-agent-ui/SKILL.md` |
|
||||
| agent-ui 검증, agent-ui validate, UI 정의 정합성 확인, wireframe 정합성 확인, 화면정의서와 wireframe 맞춰, 화면 정의서와 와이어프레임 정합성 확인, 화면정의서와 코드가 안 맞아, UI 스캐폴드 검사 | `agent-ops/skills/common/validate-agent-ui/SKILL.md` |
|
||||
| agent-ui와 코드 동기화, agent-ui와 코드 전체 동기화, 화면정의서대로 코드 반영, 화면 정의서 변경사항 구현, wireframe 변경사항 구현 | `agent-ops/skills/common/sync-agent-ui/SKILL.md` |
|
||||
| agent-ui와 코드 동기화, agent-ui와 코드 전체 동기화, 화면정의서대로 코드 반영, 화면 정의서 변경사항 구현, wireframe 변경사항 구현, agent-ui 코드 작업 준비, agent-ui 완료 정합화 | `agent-ops/skills/common/sync-agent-ui/SKILL.md` |
|
||||
| create-test, 테스트 룰 작성, 테스트 룰 생성, 테스트 규칙 작성, 테스트 규칙 생성, 테스트 환경 생성, 상황별 테스트 문서 생성, 도메인별 테스트 문서 생성, 검증 시나리오별 테스트 문서 생성, test rule 생성, agent-test 생성 | `agent-ops/skills/common/create-test/SKILL.md` |
|
||||
| update-test, 테스트 룰 수정, 테스트 룰 갱신, 테스트 규칙 수정, 테스트 규칙 갱신, 테스트 환경 수정, 상황별 테스트 문서 수정, 도메인별 테스트 문서 수정, 검증 시나리오별 테스트 문서 수정, test rule 수정, agent-test 수정 | `agent-ops/skills/common/update-test/SKILL.md` |
|
||||
| update-test, 테스트 룰 수정, 테스트 룰 갱신, 테스트 규칙 수정, 테스트 규칙 갱신, 테스트 환경 수정, 상황별 테스트 문서 수정, 도메인별 테스트 문서 수정, 검증 시나리오별 테스트 문서 수정, test rule 수정, agent-test 수정, 테스트 컨텍스트 해석, Verification Context 생성 | `agent-ops/skills/common/update-test/SKILL.md` |
|
||||
| 계약 생성해, 프로젝트 계약 생성해, 프로젝트 계약 생성, contract 생성, agent-contract 생성, 계약 문서 생성, inner 계약 생성, outer 계약 생성 | `agent-ops/skills/common/create-contract/SKILL.md` |
|
||||
| 계약 업데이트해, 프로젝트 계약 업데이트해, 프로젝트 계약 업데이트, 계약 갱신해, 계약 갱신, 계약 수정, 계약 정리, contract update, agent-contract 갱신, inner 계약 갱신, outer 계약 갱신 | `agent-ops/skills/common/update-contract/SKILL.md` |
|
||||
| 스펙 생성, 구현 스펙 생성, 현재 구현 문서화, agent-spec 생성, living spec 생성, 완료된 기능 스펙 작성 | `agent-ops/skills/common/create-spec/SKILL.md` |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
name: sync-agent-ui
|
||||
version: 1.1.0
|
||||
description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나, 코드 작업 규모에 따라 plan 또는 roadmap/milestone 작업으로 라우팅한다. 직접 반영이 검증된 경우에만 .sync-state.json 기준점을 갱신한다. "agent-ui와 코드 동기화", "화면정의서대로 코드 반영" 요청 시 사용한다.
|
||||
description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나 plan/roadmap으로 라우팅하고 plan·Milestone 작업 매핑과 완료 후 상태 정합화를 소유한다. agent-ui와 코드 동기화, 화면정의서대로 코드 반영, agent-ui 코드 작업 준비·완료 정합화 요청에 사용한다.
|
||||
---
|
||||
|
||||
# sync-agent-ui
|
||||
|
|
@ -11,38 +10,199 @@ description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나
|
|||
정합화된 `agent-ui/` 변경분을 실제 UI 코드에 반영한다.
|
||||
기본 동작은 `.sync-state.json`의 `last_synced_head` 이후 agent-ui 변경분만 반영하며, 사용자가 전체 동기화를 명시한 경우에만 현재 agent-ui 전체를 코드와 대조한다.
|
||||
코드 반영 전에 코드 작업 규모를 판정해 작은 작업은 직접 반영하고, 중간 작업은 `plan` 루프로, Milestone이 필요한 큰 코드 작업은 `update-roadmap`의 Milestone/Epic/Task 갱신으로 넘긴다.
|
||||
`plan-required` 또는 `milestone-required`로 라우팅한 경우에는 이 스킬 실행 안에서 코드 구현, status 전환, sync state 갱신, commit/push를 하지 않는다.
|
||||
`plan-required` 작업은 일반 plan/code-review 문서에 agent-ui 전용 section을 넣지 않는다. 대신 plan pair 생성 직후 `prepare-code-work`로 task와 agent-ui 문서를 매핑하고, 일반 code-review PASS와 `complete.log` 생성 뒤 `reconcile-completion`으로 status/code evidence를 정합화한다.
|
||||
`milestone-required` 작업도 일반 Milestone 문서에 agent-ui 전용 완료 필드를 넣지 않는다. `update-roadmap`이 확정한 활성 Milestone을 `prepare-milestone-work`로 매핑하고, 종료 직전 `complete-milestone check-only` 근거를 받은 `reconcile-milestone-completion`이 status/code evidence를 정합화한다.
|
||||
|
||||
## 언제 호출할지
|
||||
|
||||
- 사용자가 "agent-ui와 코드 동기화", "화면정의서대로 코드 반영", "wireframe 변경사항 구현"을 요청할 때
|
||||
- `update-agent-ui` 또는 `validate-agent-ui`가 code sync 필요성을 판단했고 현재 범위에 미해결 USER_REVIEW가 없을 때
|
||||
- 사용자가 "agent-ui와 코드 전체 동기화"처럼 full sync를 명시할 때
|
||||
- `plan-required`로 생성된 task와 agent-ui 문서의 연결을 기록할 때
|
||||
- 일반 code-review PASS 뒤 생성된 `complete.log`를 agent-ui status/code evidence에 반영할 때
|
||||
- `milestone-required`로 확정된 활성 Milestone과 agent-ui 문서를 연결할 때
|
||||
- Milestone 종료 감사 통과 후보의 agent-ui 상태를 실제 종료 전에 정합화할 때
|
||||
|
||||
## 입력
|
||||
|
||||
- `mode`: `incremental` 또는 `full`. 기본값은 `incremental` (선택)
|
||||
- `mode`: `incremental`, `full`, `prepare-code-work`, `reconcile-completion`, `prepare-milestone-work`, `reconcile-milestone-completion` 중 하나. 기본값은 `incremental` (선택)
|
||||
- `scope`: `all`, `view:<view-id>`, `component:<component-id>` 중 하나. 기본값은 `all` (선택)
|
||||
- `validated`: `true` 또는 `false`. 기본값은 `false` (선택)
|
||||
- `sync-intent-source`: `manual`, `update-agent-ui`, `validate-agent-ui` 중 하나 (선택)
|
||||
- `execution-route`: `auto`, `direct-sync`, `plan-required`, `milestone-required` 중 하나. 기본값은 `auto` (선택)
|
||||
- `push`: `true` 또는 `false`. 기본값은 `true` (선택)
|
||||
- `task-path`: `agent-task/` 기준 원래 active task 경로. `prepare-code-work`와 `reconcile-completion`에서 필수
|
||||
- `previous-task-path`: plan refinement/reindex 전에 pending entry가 가리키던 active task 경로. `prepare-code-work` rebind에서만 선택
|
||||
- `milestone-path`: `agent-roadmap/phase/*/milestones/*.md`의 정확한 활성 Milestone 경로. Milestone prepare/reconcile에서 필수
|
||||
- `agent-ui-docs`: 완료 시 `구현됨`으로 전환할 활성 view/component 문서 목록. `prepare-code-work`에서 필수
|
||||
- `frame-docs`: 함께 검증할 활성 frame-view 문서 목록. `prepare-code-work`에서 선택
|
||||
- `code-paths`: 구현 후보 또는 완료 evidence 코드 경로 목록. `prepare-code-work`에서 필수
|
||||
- `verification-requirements`: 완료 정합화 전에 확인할 명령/기대 결과 목록. `prepare-code-work`에서 선택
|
||||
- `completion-log`: 일반 code-review가 만든 정확한 `complete.log` 경로. `reconcile-completion`에서 필수
|
||||
- `closure-evidence`: 같은 Milestone에 대해 `complete-milestone mode=check-only`가 반환한 종료 가능 근거와 검증 요약. `reconcile-milestone-completion`에서 필수
|
||||
- `validation-evidence`: `update-agent-ui` 후 실행한 `validate-agent-ui` PASS 근거. `reconcile-completion`에서 필수
|
||||
- `sync-result-head`: agent-ui와 코드 결과를 모두 포함한다고 검증된 commit hash. `reconcile-completion`에서 선택
|
||||
- `push`: `true` 또는 `false`. `incremental|full`에서만 사용하며 기본값은 `true` (선택)
|
||||
|
||||
## 먼저 확인할 것
|
||||
|
||||
- [ ] `agent-ops/rules/common/rules-agent-ui.md`를 읽는다.
|
||||
- [ ] `agent-ui/`와 `agent-ui/definition/index.md` 존재 여부를 확인한다.
|
||||
- [ ] `validated=true`가 아니면 먼저 `validate-agent-ui`를 실행한다.
|
||||
- [ ] `mode=incremental|full|prepare-code-work|prepare-milestone-work`이고 `validated=true`가 아니면 먼저 `validate-agent-ui`를 실행한다.
|
||||
- [ ] `agent-ui/USER_REVIEW.md`가 있으면 현재 sync 범위와 관련된 미해결 항목이 없는지 확인한다.
|
||||
- [ ] `agent-ui/.sync-state.json`을 읽는다. 없고 `mode=incremental`이면 legacy baseline migration 필요로 보고 code sync를 시작하지 않는다.
|
||||
- [ ] 수정할 UI 코드 경로에 해당하는 project/domain rule을 먼저 읽는다.
|
||||
- [ ] 코드 변경 검증이 필요하면 작업 환경의 `agent-test/<env>/rules.md`를 읽는다. 환경 미지정은 `local`로 본다.
|
||||
- [ ] `agent-ui/.sync-state.json`을 읽는다. 없고 `mode=incremental|prepare-code-work|reconcile-completion|prepare-milestone-work|reconcile-milestone-completion`이면 legacy baseline migration 필요로 보고 중단한다.
|
||||
- [ ] `schema_version: 1|2|3`이면 bundled state helper가 기존 필드와 pending 매핑을 보존·정규화하고 `schema_version: 4`로 올리게 한다.
|
||||
- [ ] `.sync-state.json`을 갱신하는 모든 모드에서 `agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py`를 사용한다. shared state를 수동 read-modify-write하지 않는다.
|
||||
- [ ] `mode=incremental|full`이면 현재 sync 대상 agent-ui 경로 또는 그 상·하위 경로가 code/Milestone pending entry의 `status_paths` 또는 `frame_paths`와 겹치지 않는지 확인한다. 겹치면 direct sync를 시작하지 않고 기존 pending owner 흐름으로 돌린다.
|
||||
- [ ] `mode=incremental|full`이면 수정할 UI 코드 경로에 해당하는 project/domain rule을 먼저 읽는다.
|
||||
- [ ] `mode=incremental|full`이고 코드 변경 검증이 필요하면 작업 환경의 `agent-test/<env>/rules.md`를 읽는다. 환경 미지정은 `local`로 본다.
|
||||
- [ ] `mode=prepare-code-work`이면 exact active `PLAN-*-G??.md`와 matching `CODE_REVIEW-*-G??.md`가 `task-path`에 존재하는지 확인한다.
|
||||
- [ ] `mode=reconcile-completion`이면 원래 `task-path`, exact `completion-log`가 존재하고 matching pending entry가 정확히 하나인지 확인한다.
|
||||
- [ ] `mode=prepare-milestone-work`이면 `update-roadmap`이 확정한 exact active `milestone-path`가 존재하는지 확인한다.
|
||||
- [ ] `mode=reconcile-milestone-completion`이면 같은 `milestone-path`의 pending entry와 `complete-milestone check-only` closure evidence가 있는지 확인한다.
|
||||
- [ ] `git status --short`와 현재 `HEAD`를 확인한다.
|
||||
- [ ] unrelated dirty worktree가 있으면 sync commit 전에 범위를 보고하고, agent-ui와 관련 코드 파일만 stage한다.
|
||||
- [ ] 이전 sync 실패로 남은 관련 작업트리 변경이 있으면 보존할지, 사용자 결정과 충돌하는지 확인한다.
|
||||
|
||||
## 실행 절차
|
||||
|
||||
### prepare-code-work 절차
|
||||
|
||||
1. **task 매핑 검증**
|
||||
- `task-path`의 active PLAN/review pair가 같은 task header를 갖고 아직 verdict가 없는지 확인한다.
|
||||
- `agent-ui-docs`는 현재 활성 view/component 문서이고 status가 `계획`인지 확인한다. `가정`, `불명확`, 이미 archive 된 문서는 매핑하지 않는다.
|
||||
- `frame-docs`는 visual source가 있는 활성 frame-view 문서인지 확인하고 대응 view가 `agent-ui-docs`에 포함됐는지 확인한다. frame-view 자체에는 status를 요구하거나 `구현됨` 전환을 적용하지 않는다.
|
||||
- `code-paths`는 실제 구현 후보로 좁혀진 경로만 기록한다. 존재하지 않는 새 파일 후보는 경로와 생성 의도가 plan에 명시된 경우에만 허용한다.
|
||||
- 기존 pending task가 `refine-local-plans`로 분할되거나 sibling reindex로 경로가 바뀌었으면 원래 매핑을 그대로 둘 수 없다. status 대상 전체를 닫는 child를 하나만 고르고 기존 경로를 `previous-task-path`, 그 child 경로를 `task-path`로 전달한다.
|
||||
|
||||
2. **pending entry 기록**
|
||||
- `agent-ui/.sync-state.json`의 SHA-256을 계산한 뒤 bundled helper의 `prepare` 명령에 `--expected-sha256`으로 넘긴다. helper는 agent-ui 디렉터리 잠금을 잡은 다음 digest를 다시 확인한다.
|
||||
- 같은 `task_path`와 같은 내용은 idempotent `already_prepared`로 허용한다.
|
||||
- 같은 `task_path`의 내용이 다르면 helper가 중단한다. follow-up plan으로 범위가 실제 바뀐 것을 확인한 경우에만 `--replace`를 사용한다.
|
||||
- `previous-task-path`를 전달하면 helper가 기존 pending entry를 새 `task-path`로 원자적으로 rebind한다. scope/status/frame/code/verification 매핑이 달라지면 전체 결과를 닫는 child인지 검증한 뒤에만 `--replace`를 함께 사용한다.
|
||||
- 하나의 `status_path` 또는 `frame_path`를 둘 이상의 pending task가 소유하려 하면 helper가 중단한다.
|
||||
|
||||
```json
|
||||
{
|
||||
"task_path": "agent-task/<task-name>",
|
||||
"scope": "<scope>",
|
||||
"status_paths": ["agent-ui/definition/views/.../index.md"],
|
||||
"frame_paths": ["agent-ui/frame/views/.../index.md"],
|
||||
"code_paths": ["<code path>"],
|
||||
"verification_requirements": ["<command>: <expected result>"],
|
||||
"prepared_at": "<ISO-8601>",
|
||||
"state": "pending"
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py prepare \
|
||||
--state agent-ui/.sync-state.json \
|
||||
--expected-sha256 <sha256> \
|
||||
--task-path agent-task/<task-name> \
|
||||
--scope <scope> \
|
||||
--status-path agent-ui/definition/views/<view-id>/index.md \
|
||||
--frame-path agent-ui/frame/views/<view-id>/index.md \
|
||||
--code-path <code-path> \
|
||||
--verification-requirement '<command>: <expected result>' \
|
||||
--prepared-at <ISO-8601>
|
||||
```
|
||||
|
||||
- `--status-path`와 `--code-path`는 필요한 만큼 반복하고, frame이 없으면 `--frame-path`를 생략한다.
|
||||
- refinement/reindex rebind에서는 `--previous-task-path agent-task/<old-task-name>`을 추가한다.
|
||||
|
||||
3. **state 경계 유지**
|
||||
- helper가 `schema_version`을 `4`로 올리고 code/Milestone pending·reconciled 기록과 baseline 필드를 보존하게 한다.
|
||||
- 이 모드는 `last_synced_head`, agent-ui status, code evidence를 바꾸지 않고 commit/push하지 않는다.
|
||||
- 일반 plan/review 문서에는 agent-ui 전용 completion section이나 review 전용 status 전환 지시를 추가하지 않는다.
|
||||
|
||||
### reconcile-completion 절차
|
||||
|
||||
1. **완료 이벤트 매칭**
|
||||
- exact `completion-log`가 `agent-task/archive/**/complete.log`에 있고 header task가 원래 `task-path`와 일치하며 loop history에 terminal PASS/RESOLVED가 있는지 확인한다.
|
||||
- 입력된 원래 `task-path`를 `pending_code_work[].task_path`와 exact match한다. archive destination suffix나 `completion-log` 경로에서 원래 task path를 역추정하지 않는다.
|
||||
- zero/multiple match이면 agent-ui 문서를 추측해 갱신하지 않는다.
|
||||
|
||||
2. **완료 evidence 검증**
|
||||
- pending entry의 모든 `code_paths`가 실제 존재하고 agent-ui 정의를 구현한 근거인지 확인한다.
|
||||
- `completion-log`의 최종 검증과 `verification_requirements`가 충족됐는지 확인한다.
|
||||
- generic `complete.log`에 agent-ui 전용 section이 없어도 정상으로 본다. 필요한 매핑은 pending entry에서만 읽는다.
|
||||
- evidence가 부족하면 entry를 `pending`으로 유지하고 일반 plan/review artifact를 수정하지 않는다.
|
||||
|
||||
3. **agent-ui 상태 반영**
|
||||
- pending entry의 `status_paths`에 있는 view/component 문서마다 `update-agent-ui`를 `status=구현됨`, 실제 code evidence, `post-validate=false`, `post-sync=false`로 실행한다.
|
||||
- `frame_paths`에는 status를 쓰지 않는다. 대응 definition과 visual source 연결을 검증 대상으로만 사용한다.
|
||||
- 모든 문서 갱신 뒤 `validate-agent-ui`를 matching scope, `sync-intent=skip`으로 실행하고 PASS 근거를 `validation-evidence`로 고정한다.
|
||||
- 갱신이나 검증이 실패하면 pending entry를 제거하지 않고 `agent-ui/USER_REVIEW.md`에 `Source Skill: sync-agent-ui`, `Stage: completion-reconcile`로 차단 근거를 남긴다.
|
||||
|
||||
4. **pending entry 종료**
|
||||
- 갱신과 검증이 모두 통과한 뒤 `.sync-state.json`의 최신 SHA-256을 다시 계산하고 bundled helper의 `reconcile` 명령을 실행한다.
|
||||
- helper가 matching pending entry를 제거하고 `reconciled_code_work`에 `task_path`, exact `completion_log`, scope, status/frame/code paths, validation evidence를 기록하게 한다.
|
||||
- helper가 `last_sync_mode`을 `plan-reconcile`, `last_synced_at`, `agent_ui_paths`, `code_paths`, `notes`를 완료 근거로 갱신하게 한다.
|
||||
- `sync-result-head`가 실제 commit이고 matching agent-ui/code 결과를 모두 포함하는지 검증된 경우에만 `last_synced_head`를 그 값으로 갱신한다. 그렇지 않으면 기존 `last_synced_head`를 보존하고 notes에 기준점 미갱신 사유를 남긴다.
|
||||
- 이미 같은 `task_path`와 `completion-log`가 `reconciled_code_work`에 있으면 helper의 idempotent `already_reconciled` 결과를 사용한다.
|
||||
- 이 모드는 일반 plan/review/complete.log를 수정하거나 commit/push하지 않는다.
|
||||
|
||||
```bash
|
||||
python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py reconcile \
|
||||
--state agent-ui/.sync-state.json \
|
||||
--expected-sha256 <sha256> \
|
||||
--task-path agent-task/<task-name> \
|
||||
--completion-log agent-task/archive/<YYYY>/<MM>/<task-path>/complete.log \
|
||||
--validation-evidence '<validate-agent-ui PASS evidence>' \
|
||||
--reconciled-at <ISO-8601>
|
||||
```
|
||||
|
||||
- 검증된 결과 commit이 있을 때만 `--sync-result-head <commit>`을 추가한다.
|
||||
|
||||
### Milestone 매핑과 완료 정합화 절차
|
||||
|
||||
1. **Milestone 작업 준비**
|
||||
- `execution-route=milestone-required`이면 코드 구현을 시작하지 않고 먼저 `update-roadmap`으로 활성 Milestone을 생성하거나 정확히 하나를 선택한다.
|
||||
- 일반 Milestone 문서에는 agent-ui 전용 완료 필드나 status 전환 지시를 쓰지 않는다.
|
||||
- `agent-ui-docs`, `frame-docs`, `code-paths`, `verification-requirements`를 `prepare-code-work`와 같은 기준으로 검증한다.
|
||||
- `.sync-state.json`의 최신 SHA-256과 exact active `milestone-path`를 bundled helper의 `prepare-milestone`에 전달한다.
|
||||
- 같은 Milestone과 같은 매핑은 `already_prepared`로 허용하고, 실제 scope/evidence 변경을 확인한 경우에만 `--replace`를 사용한다.
|
||||
|
||||
```bash
|
||||
python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py prepare-milestone \
|
||||
--state agent-ui/.sync-state.json \
|
||||
--expected-sha256 <sha256> \
|
||||
--milestone-path agent-roadmap/phase/<phase>/milestones/<milestone>.md \
|
||||
--scope <scope> \
|
||||
--status-path agent-ui/definition/views/<view-id>/index.md \
|
||||
--frame-path agent-ui/frame/views/<view-id>/index.md \
|
||||
--code-path <code-path> \
|
||||
--verification-requirement '<command>: <expected result>' \
|
||||
--prepared-at <ISO-8601>
|
||||
```
|
||||
|
||||
2. **종료 직전 정합화**
|
||||
- caller/router는 Milestone 종료 요청에서 먼저 `complete-milestone mode=check-only`를 실행한다. 종료 가능 판정과 실제 검증 근거가 없으면 이 모드를 실행하지 않는다.
|
||||
- `closure-evidence`에는 exact `milestone-path`, check-only 판정, 코드레벨 감사, 실행한 검증 결과, spec check-only 결과를 식별 가능하게 요약한다. 재시도 idempotency를 위해 실행 시각처럼 의미 없는 가변 값은 넣지 않는다.
|
||||
- pending entry의 모든 `code_paths`와 `verification_requirements`가 check-only 근거와 현재 checkout에서 충족되는지 다시 확인한다.
|
||||
- `status_paths`의 view/component만 `update-agent-ui status=구현됨`과 실제 code evidence로 갱신하고, `frame_paths`는 연결 정합성만 검증한다.
|
||||
- 모든 문서 갱신 뒤 `validate-agent-ui sync-intent=skip`을 실행한다. 실패하면 pending entry를 유지하고 Milestone close를 호출하지 않는다.
|
||||
|
||||
3. **Milestone pending 종료**
|
||||
- 갱신과 검증이 통과한 뒤 최신 state SHA-256과 closure/validation evidence를 helper의 `reconcile-milestone`에 전달한다.
|
||||
- helper가 matching entry를 `pending_milestone_work`에서 `reconciled_milestone_work`로 이동하고 완료 근거를 기록한다.
|
||||
- 성공 또는 같은 evidence의 `already_reconciled` 뒤에만 caller/router가 `complete-milestone mode=close`를 실행한다. close가 다시 감사를 통과하지 못하면 roadmap 상태를 추측해 바꾸지 않는다.
|
||||
- 이 모드는 일반 Milestone 문서와 complete-milestone 결과를 수정하지 않고 commit/push하지 않는다.
|
||||
|
||||
```bash
|
||||
python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py reconcile-milestone \
|
||||
--state agent-ui/.sync-state.json \
|
||||
--expected-sha256 <sha256> \
|
||||
--milestone-path agent-roadmap/phase/<phase>/milestones/<milestone>.md \
|
||||
--closure-evidence '<complete-milestone check-only closure evidence>' \
|
||||
--validation-evidence '<validate-agent-ui PASS evidence>' \
|
||||
--reconciled-at <ISO-8601>
|
||||
```
|
||||
|
||||
- 검증된 결과 commit이 있을 때만 `--sync-result-head <commit>`을 추가한다.
|
||||
|
||||
### incremental/full 절차
|
||||
|
||||
1. **동기화 범위 확정**
|
||||
- `.sync-state.json`이 없고 `mode=incremental`이면 현재 agent-ui를 legacy baseline 후보로 본다.
|
||||
- legacy baseline 후보는 `validate-agent-ui` 통과 후 agent-ui baseline commit과 `.sync-state.json` commit을 남기는 migration으로 처리하고, 같은 실행에서 코드 반영은 하지 않는다.
|
||||
|
|
@ -52,6 +212,7 @@ description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나
|
|||
- `mode=full`에서 `.sync-state.json`이 없으면 code sync 성공 후 새 sync state를 만든다.
|
||||
- `가정` 또는 `불명확` 상태 항목은 코드 반영 대상에서 제외하고 USER_REVIEW로 남긴다.
|
||||
- 현재 범위에 미해결 USER_REVIEW가 있으면 중단한다.
|
||||
- 확정된 sync 대상 agent-ui 경로 트리가 `pending_code_work` 또는 `pending_milestone_work`의 `status_paths`/`frame_paths`와 하나라도 겹치면 direct sync 대상으로 가져오지 않는다. 해당 pending task/Milestone의 완료 정합화 흐름을 사용한다.
|
||||
|
||||
2. **코드 반영 방식 판정**
|
||||
- 이 판정은 agent-ui 문서 작업의 크기가 아니라, 정합화된 agent-ui 변경분을 코드에 반영하는 구현 작업의 크기와 위험만 대상으로 한다.
|
||||
|
|
@ -64,17 +225,19 @@ description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나
|
|||
- `plan-required`는 목표와 범위는 명확하지만 독립 리뷰 가능한 구현 루프가 필요할 때 사용한다.
|
||||
- 여러 view/component에 걸친 반영, shared component 추출, route/shell 조정, 테스트/검증 보강, UI 상태와 코드 변경을 함께 다루는 작업이 여기에 속한다.
|
||||
- 이 경우 직접 코드 반영을 시작하지 않고 `plan` 스킬로 전환한다.
|
||||
- plan에는 반영 대상 agent-ui 문서, 코드 후보, 최종 `validate-agent-ui`, code evidence 기준을 포함한다.
|
||||
- plan이 만드는 `CODE_REVIEW-*-G??.md`에는 `Agent UI Completion` 섹션을 넣어 code-review PASS 때 listed agent-ui 문서를 `구현됨`으로 전환하게 한다.
|
||||
- plan에는 일반 구현 범위, 코드 후보, 검증 기준만 넘긴다.
|
||||
- plan pair가 생성되면 이 스킬의 `prepare-code-work`를 실행해 반영 대상 agent-ui 문서, 코드 후보, verification requirement를 task에 매핑한다.
|
||||
- 일반 code-review PASS와 `complete.log` 생성 뒤 caller/router/runtime가 `reconcile-completion`을 실행해 mapped agent-ui 문서를 `구현됨`으로 전환한다.
|
||||
- `milestone-required`는 코드 구현이 제품 UI 구조나 장기 작업 단위까지 바꾸는 경우 사용한다.
|
||||
- navigation/information architecture 재정렬, 외부 제품 UI 분석 기반 재설계, 여러 workflow/role/surface를 묶는 작업, 활성 Milestone 범위 밖 작업이 여기에 속한다.
|
||||
- 이 경우 직접 코드 반영과 plan 생성을 시작하지 않고 `update-roadmap`으로 Milestone/Epic/Task 배치를 먼저 남긴다.
|
||||
- 이 Milestone에는 `완료 리뷰`의 `agent-ui 상태 반영: 대기`를 남기고, `status: 구현됨` 전환은 plan 완료가 아니라 Milestone 종료 검토 항목으로 둔다.
|
||||
- `plan-required` 또는 `milestone-required`로 판정하면 agent-ui 문서 status를 `구현됨`으로 바꾸지 않고, sync state와 commit/push도 갱신하지 않는다.
|
||||
- exact active Milestone이 확정되면 `prepare-milestone-work`로 UI 매핑을 `.sync-state.json`에 남긴다. Milestone 문서에는 agent-ui 전용 완료 필드를 추가하지 않는다.
|
||||
- `plan-required` 판정 시 `prepare-code-work` pending entry 외에는 agent-ui 문서 status, baseline sync state, commit/push를 갱신하지 않는다.
|
||||
- `milestone-required` 판정 시 agent-ui 문서 status와 baseline sync state, commit/push를 갱신하지 않고 `pending_milestone_work` 매핑만 기록한다.
|
||||
|
||||
3. **코드 반영**
|
||||
- 이 단계는 `execution-route=direct-sync`일 때만 수행한다. `plan-required` 또는 `milestone-required` 판정이면 Step 2의 라우팅 결과를 보고하고 해당 스킬 흐름으로 전환한다.
|
||||
- `계획` 상태의 view/component/frame 요구사항을 UI 코드에 반영한다.
|
||||
- `계획` 상태의 view/component 요구사항과 연결된 frame의 layout 근거를 UI 코드에 반영한다.
|
||||
- existing component, route, shell, widget 구조를 우선하고 새 구조를 임의로 만들지 않는다.
|
||||
- definition이 요구하는 정보 우선순위, region, action, state, component 참조를 코드 반영 기준으로 삼는다.
|
||||
- wireframe/frame은 layout, density, visual relationship 보조 근거로만 사용하고 definition을 대체하지 않는다.
|
||||
|
|
@ -89,10 +252,26 @@ description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나
|
|||
|
||||
5. **sync state와 commit/push**
|
||||
- 이 단계는 `execution-route=direct-sync`에서 코드 반영, 검증, agent-ui status/code evidence 갱신이 모두 통과한 경우에만 수행한다.
|
||||
- commit 전에 state를 다시 읽어 direct sync 대상이 새 pending entry의 소유 경로와 겹치지 않는지 재확인한다. 겹치면 commit하지 않고 충돌 경로와 pending owner를 보고한다.
|
||||
- 검증이 통과하면 agent-ui/code 변경을 먼저 commit한다.
|
||||
- 방금 만든 sync 결과 commit hash를 `agent-ui/.sync-state.json`의 `last_synced_head`에 기록한다.
|
||||
- `.sync-state.json`이 없으면 `agent-ops/skills/common/_templates/agent-ui/sync-state-template.json` 기준으로 새로 만든다.
|
||||
- `.sync-state.json`에는 `last_sync_mode`, `last_synced_at`, `agent_ui_paths`, `code_paths`, `notes`를 갱신한다.
|
||||
- 방금 만든 sync 결과 commit hash와 `.sync-state.json`의 최신 SHA-256을 bundled helper `record-sync`에 넘긴다.
|
||||
- helper가 lock 안에서 pending 소유 경로와의 겹침을 다시 거부한 뒤 `last_sync_mode`, `last_synced_head`, `last_synced_at`, `agent_ui_paths`, `code_paths`, `notes`를 갱신하고 기존 code/Milestone pending·reconciled 기록을 보존하게 한다.
|
||||
|
||||
```bash
|
||||
python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py record-sync \
|
||||
--state agent-ui/.sync-state.json \
|
||||
--expected-sha256 <sha256> \
|
||||
--sync-mode <incremental|full> \
|
||||
--sync-result-head <sync-result-commit> \
|
||||
--synced-at <ISO-8601> \
|
||||
--agent-ui-path <agent-ui-path> \
|
||||
--code-path <code-path> \
|
||||
--note '<sync evidence>'
|
||||
```
|
||||
|
||||
- agent-ui/code 경로와 note는 필요한 만큼 옵션을 반복한다.
|
||||
- legacy baseline migration은 `--sync-mode migration`을 사용하고 code 반영이 없으면 `--code-path`를 생략한다.
|
||||
- `.sync-state.json` 변경을 별도 commit한다.
|
||||
- commit message는 예를 들어 `sync: apply agent-ui to code`와 `sync: record agent-ui sync state`처럼 목적이 분리되게 쓴다.
|
||||
- `push=true`이면 두 commit을 push한다.
|
||||
|
|
@ -111,9 +290,24 @@ description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나
|
|||
- [ ] `validate-agent-ui`가 먼저 실행되었거나 `validated=true` 근거가 있는가
|
||||
- [ ] 현재 sync 범위에 미해결 USER_REVIEW가 없는가
|
||||
- [ ] 코드 반영 전에 `direct-sync`, `plan-required`, `milestone-required` 중 하나로 판정했는가
|
||||
- [ ] `plan-required` 또는 `milestone-required` 판정에서 직접 코드 변경, status 구현됨 전환, sync state 갱신, commit/push를 하지 않았는가
|
||||
- [ ] `plan-required` 판정에서 직접 코드 변경, status 구현됨 전환, baseline sync state 갱신, commit/push를 하지 않고 pending entry만 기록했는가
|
||||
- [ ] `milestone-required` 판정에서 직접 코드 변경, status 구현됨 전환, baseline sync state 갱신, commit/push를 하지 않고 exact Milestone pending mapping만 기록했는가
|
||||
- [ ] `direct-sync` 판정에서 코드 반영과 검증이 통과한 항목은 같은 실행 안에서 agent-ui 문서 status를 `구현됨`으로 바꾸고 code evidence를 남겼는가
|
||||
- [ ] `plan-required` 판정에서 생성될 `CODE_REVIEW-*-G??.md`에 `Agent UI Completion` 섹션으로 status 전환 규칙을 넘겼는가
|
||||
- [ ] `direct-sync` 대상이 code/Milestone pending owner의 status/frame path와 겹치지 않는지 코드 변경 전과 commit 전 확인했는가
|
||||
- [ ] `prepare-code-work`가 exact task path와 활성 agent-ui 문서를 unique pending entry로 기록했는가
|
||||
- [ ] refinement/reindex된 pending task는 전체 결과를 닫는 child 하나로 rebind되어 이전 task path가 남지 않았는가
|
||||
- [ ] view/component는 `status_paths`, frame-view는 validation-only `frame_paths`로 분리했는가
|
||||
- [ ] 하나의 status/frame path가 둘 이상의 pending task에 배정되지 않았는가
|
||||
- [ ] state helper에 inspection 시점 SHA-256을 전달해 directory lock 안에서 concurrent/stale write를 차단했는가
|
||||
- [ ] `prepare-code-work`가 일반 plan/review 문서에 agent-ui 전용 completion section을 요구하지 않았는가
|
||||
- [ ] `reconcile-completion`이 exact completion log, code path, verification requirement를 확인했는가
|
||||
- [ ] `reconcile-completion`이 `update-agent-ui` 다음 `validate-agent-ui sync-intent=skip` 순서로 실행했는가
|
||||
- [ ] reconcile 실패 시 pending entry를 유지하고 성공 시에만 제거했는가
|
||||
- [ ] reconcile 성공과 idempotency를 `reconciled_code_work`로 판정했는가
|
||||
- [ ] Milestone mapping은 roadmap 문서가 아니라 `pending_milestone_work`에만 기록했는가
|
||||
- [ ] Milestone reconcile 전에 `complete-milestone check-only`의 closure-ready 근거를 확인했는가
|
||||
- [ ] Milestone reconcile 성공 뒤에만 caller/router가 `complete-milestone close`로 진행하는가
|
||||
- [ ] 검증되지 않은 `sync-result-head`로 `last_synced_head`를 바꾸지 않았는가
|
||||
- [ ] `가정` 또는 `불명확` 항목을 코드로 반영하지 않았는가
|
||||
- [ ] `direct-sync`로 코드 반영된 항목의 status가 `구현됨`이고 code evidence가 있는가
|
||||
- [ ] `direct-sync` 관련 검증이 통과했는가
|
||||
|
|
@ -121,23 +315,29 @@ description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나
|
|||
- [ ] 실패 또는 차단 시 재개 진입점과 남은 변경 파일이 USER_REVIEW에 기록되었는가
|
||||
- [ ] `direct-sync` 성공 시 sync 결과 commit과 `.sync-state.json` commit이 분리되었는가
|
||||
- [ ] `direct-sync` 성공 시 `.sync-state.json.last_synced_head`가 sync 결과 commit hash를 가리키는가
|
||||
- [ ] direct-sync/baseline state 갱신도 helper를 사용해 code/Milestone pending·reconciled 기록을 보존했는가
|
||||
- [ ] unrelated dirty file이 sync commit에 포함되지 않았는가
|
||||
- 검증 실패 시: 에이전트가 해결할 수 없으면 `agent-ui/USER_REVIEW.md`에 남기고 중단한다.
|
||||
|
||||
## 출력 형식
|
||||
|
||||
```md
|
||||
## agent-ui 코드 동기화 결과: <PASS|PLAN_REQUIRED|MILESTONE_REQUIRED|USER_REVIEW|FAIL>
|
||||
## agent-ui 코드 동기화 결과: <PASS|PLAN_REQUIRED|MILESTONE_REQUIRED|PREPARED|RECONCILED|ALREADY_RECONCILED|USER_REVIEW|FAIL>
|
||||
|
||||
- Mode: <incremental|full>
|
||||
- Mode: <incremental|full|prepare-code-work|reconcile-completion|prepare-milestone-work|reconcile-milestone-completion>
|
||||
- Scope: <scope>
|
||||
- Execution Route: <direct-sync|plan-required|milestone-required>
|
||||
- Execution Route: <direct-sync|plan-required|milestone-required|not-applicable>
|
||||
- Task Mapping: <task-path와 pending 상태 또는 없음>
|
||||
- Milestone Mapping: <milestone-path와 pending 상태 또는 없음>
|
||||
- Completion Log: <path 또는 없음>
|
||||
- Closure Evidence: <Milestone check-only 근거 또는 없음>
|
||||
- Agent UI Changes: <파일 목록 또는 없음>
|
||||
- Code Changes: <파일 목록 또는 없음>
|
||||
- Status Updates: <구현됨 전환 목록 또는 없음>
|
||||
- Verification: <명령과 결과>
|
||||
- Sync Commit: <hash 또는 없음>
|
||||
- Sync State Commit/Push: <완료/생략/실패>
|
||||
- State Transition: <prepared|already_prepared|replaced|rebound|reconciled|already_reconciled|없음>
|
||||
- USER_REVIEW: <생성/갱신/없음>
|
||||
- Remaining Issues: <목록 또는 없음>
|
||||
```
|
||||
|
|
@ -148,7 +348,16 @@ description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나
|
|||
- 미해결 USER_REVIEW가 있는 범위를 코드에 반영하지 않는다.
|
||||
- `가정` 또는 `불명확` 상태 항목을 코드로 구현하지 않는다.
|
||||
- `plan-required` 또는 `milestone-required`로 판정된 작업을 같은 `sync-agent-ui` 실행에서 직접 구현하지 않는다.
|
||||
- `execution-route=milestone-required`로 라우팅된 agent-ui 코드 작업의 일반 plan 완료만으로 agent-ui status를 일괄 `구현됨`으로 바꾸지 않는다. 종료 검토 통과와 code evidence가 있는 항목만 반영한다.
|
||||
- 일반 plan/review/complete.log에 agent-ui 전용 completion schema나 status 전환 책임을 추가하지 않는다.
|
||||
- 일반 Milestone 문서나 roadmap 스킬에 agent-ui 전용 완료 필드나 status 전환 책임을 추가하지 않는다.
|
||||
- code/Milestone pending entry가 소유한 status/frame path를 direct sync로 반영하거나 `record-sync`하지 않는다.
|
||||
- matching pending entry 없이 `complete.log`만 보고 agent-ui 문서를 추측해 갱신하지 않는다.
|
||||
- frame-view 문서에 `status`를 추가하거나 `구현됨` 전환을 적용하지 않는다.
|
||||
- `.sync-state.json`을 bundled helper 밖에서 수동 read-modify-write하지 않는다.
|
||||
- refinement/reindex 뒤 이전 task path의 pending entry를 남기거나 같은 status/frame path를 여러 pending task에 중복 배정하지 않는다.
|
||||
- reconcile 실패 시 pending entry를 제거하거나 `last_synced_head`를 추정하지 않는다.
|
||||
- `complete-milestone check-only` 종료 가능 근거 없이 Milestone pending entry를 reconcile하거나 close를 계속하지 않는다.
|
||||
- `execution-route=milestone-required`로 라우팅된 agent-ui 코드 작업의 일반 plan 완료만으로 agent-ui status를 일괄 `구현됨`으로 바꾸지 않는다. 종료 감사와 code evidence가 있는 항목만 반영한다.
|
||||
- 검증 실패나 사용자 판단 차단 상태에서 commit/push하지 않는다.
|
||||
- `.sync-state.json`을 agent-ui 변경분 판정 대상으로 삼지 않는다.
|
||||
- 정의서와 충돌하는 wireframe만을 근거로 코드 구현을 확정하지 않는다.
|
||||
|
|
|
|||
903
agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py
Normal file
903
agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py
Normal file
|
|
@ -0,0 +1,903 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Atomically manage plan- and Milestone-required agent-ui work mappings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import fcntl
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
from pathlib import Path, PurePosixPath
|
||||
from typing import Any
|
||||
|
||||
|
||||
SCHEMA_VERSION = 4
|
||||
SHA_PATTERN = re.compile(r"^(?:[0-9a-f]{40}|[0-9a-f]{64})$")
|
||||
STATUS_PATH_PATTERN = re.compile(
|
||||
r"^agent-ui/definition/(?:views|components)/[a-z0-9][a-z0-9/-]*/index\.md$"
|
||||
)
|
||||
FRAME_PATH_PATTERN = re.compile(
|
||||
r"^agent-ui/frame/views/[a-z0-9][a-z0-9/-]*/index\.md$"
|
||||
)
|
||||
SCOPE_PATTERN = re.compile(r"^(?:all|view:[a-z0-9][a-z0-9-]*|component:[a-z0-9][a-z0-9/-]*)$")
|
||||
MILESTONE_PATH_PATTERN = re.compile(
|
||||
r"^agent-roadmap/phase/[a-z0-9][a-z0-9-]*/milestones/"
|
||||
r"[a-z0-9][a-z0-9-]*\.md$"
|
||||
)
|
||||
|
||||
|
||||
class StateError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _state_lock(path: Path):
|
||||
directory_fd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
|
||||
try:
|
||||
fcntl.flock(directory_fd, fcntl.LOCK_EX)
|
||||
yield
|
||||
finally:
|
||||
fcntl.flock(directory_fd, fcntl.LOCK_UN)
|
||||
os.close(directory_fd)
|
||||
|
||||
|
||||
def _locked_state_command(handler):
|
||||
@wraps(handler)
|
||||
def wrapped(args: argparse.Namespace) -> dict[str, Any]:
|
||||
with _state_lock(args.state):
|
||||
return handler(args)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def _stable_unique(values: list[str]) -> list[str]:
|
||||
return list(dict.fromkeys(values))
|
||||
|
||||
|
||||
def _path_trees_overlap(left: str, right: str) -> bool:
|
||||
left_path = PurePosixPath(left)
|
||||
right_path = PurePosixPath(right)
|
||||
return (
|
||||
left_path == right_path
|
||||
or left_path in right_path.parents
|
||||
or right_path in left_path.parents
|
||||
)
|
||||
|
||||
|
||||
def _require_relative_path(value: str, *, prefix: str | None = None) -> str:
|
||||
path = PurePosixPath(value)
|
||||
if not value or path.is_absolute() or ".." in path.parts or "." in path.parts:
|
||||
raise StateError(f"invalid repository-relative path: {value!r}")
|
||||
normalized = path.as_posix()
|
||||
if prefix and not normalized.startswith(prefix):
|
||||
raise StateError(f"path must start with {prefix!r}: {value!r}")
|
||||
return normalized
|
||||
|
||||
|
||||
def _require_task_path(value: str) -> str:
|
||||
normalized = _require_relative_path(value, prefix="agent-task/")
|
||||
if normalized == "agent-task" or normalized.startswith("agent-task/archive/"):
|
||||
raise StateError(f"task path must be the original active path: {value!r}")
|
||||
return normalized.rstrip("/")
|
||||
|
||||
|
||||
def _require_milestone_path(value: str) -> str:
|
||||
normalized = _require_relative_path(value, prefix="agent-roadmap/phase/")
|
||||
if not MILESTONE_PATH_PATTERN.fullmatch(normalized):
|
||||
raise StateError(f"milestone path must be an active Milestone document: {value!r}")
|
||||
return normalized
|
||||
|
||||
|
||||
def _require_timestamp(value: str) -> str:
|
||||
try:
|
||||
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||
except ValueError as exc:
|
||||
raise StateError(f"invalid ISO-8601 timestamp: {value!r}") from exc
|
||||
if parsed.tzinfo is None:
|
||||
raise StateError(f"timestamp must include a timezone: {value!r}")
|
||||
return value
|
||||
|
||||
|
||||
def _require_string_list(value: Any, *, field: str) -> list[str]:
|
||||
if not isinstance(value, list) or not all(isinstance(item, str) and item for item in value):
|
||||
raise StateError(f"{field} must be a list of non-empty strings")
|
||||
return _stable_unique(value)
|
||||
|
||||
|
||||
def _require_object_list(value: Any, *, field: str) -> list[dict[str, Any]]:
|
||||
if not isinstance(value, list) or not all(isinstance(item, dict) for item in value):
|
||||
raise StateError(f"{field} must be a list of objects")
|
||||
return value
|
||||
|
||||
|
||||
def _normalize_pending_entry(raw: Any) -> dict[str, Any]:
|
||||
if not isinstance(raw, dict):
|
||||
raise StateError("pending_code_work entries must be objects")
|
||||
|
||||
legacy_paths = _require_string_list(raw.get("agent_ui_paths", []), field="agent_ui_paths")
|
||||
status_paths = _require_string_list(raw.get("status_paths", []), field="status_paths")
|
||||
frame_paths = _require_string_list(raw.get("frame_paths", []), field="frame_paths")
|
||||
if legacy_paths:
|
||||
status_paths.extend(path for path in legacy_paths if not path.startswith("agent-ui/frame/"))
|
||||
frame_paths.extend(path for path in legacy_paths if path.startswith("agent-ui/frame/"))
|
||||
|
||||
status_paths = [
|
||||
_require_relative_path(path, prefix="agent-ui/definition/")
|
||||
for path in _stable_unique(status_paths)
|
||||
]
|
||||
frame_paths = [
|
||||
_require_relative_path(path, prefix="agent-ui/frame/")
|
||||
for path in _stable_unique(frame_paths)
|
||||
]
|
||||
if not status_paths:
|
||||
raise StateError("pending_code_work requires at least one status_path")
|
||||
if not all(STATUS_PATH_PATTERN.fullmatch(path) for path in status_paths):
|
||||
raise StateError("status_paths must be active view/component index.md paths")
|
||||
if not all(FRAME_PATH_PATTERN.fullmatch(path) for path in frame_paths):
|
||||
raise StateError("frame_paths must be active frame-view index.md paths")
|
||||
if set(status_paths) & set(frame_paths):
|
||||
raise StateError("status_paths and frame_paths must be disjoint")
|
||||
|
||||
code_paths = [
|
||||
_require_relative_path(path)
|
||||
for path in _require_string_list(raw.get("code_paths", []), field="code_paths")
|
||||
]
|
||||
if not code_paths:
|
||||
raise StateError("pending_code_work requires at least one code_path")
|
||||
if any(path.startswith(("agent-ui/", "agent-task/")) for path in code_paths):
|
||||
raise StateError("code_paths must not point to agent-ui or agent-task artifacts")
|
||||
|
||||
scope = str(raw.get("scope", "")).strip() or "all"
|
||||
if not SCOPE_PATTERN.fullmatch(scope):
|
||||
raise StateError(f"invalid sync scope: {scope!r}")
|
||||
|
||||
return {
|
||||
"task_path": _require_task_path(str(raw.get("task_path", ""))),
|
||||
"scope": scope,
|
||||
"status_paths": status_paths,
|
||||
"frame_paths": frame_paths,
|
||||
"code_paths": code_paths,
|
||||
"verification_requirements": _require_string_list(
|
||||
raw.get("verification_requirements", []),
|
||||
field="verification_requirements",
|
||||
),
|
||||
"prepared_at": _require_timestamp(str(raw.get("prepared_at", ""))),
|
||||
"state": "pending",
|
||||
}
|
||||
|
||||
|
||||
def _normalize_pending_milestone_entry(raw: Any) -> dict[str, Any]:
|
||||
if not isinstance(raw, dict):
|
||||
raise StateError("pending_milestone_work entries must be objects")
|
||||
entry = _normalize_pending_entry(
|
||||
{
|
||||
**raw,
|
||||
"task_path": "agent-task/milestone-placeholder",
|
||||
}
|
||||
)
|
||||
return {
|
||||
"milestone_path": _require_milestone_path(str(raw.get("milestone_path", ""))),
|
||||
"scope": entry["scope"],
|
||||
"status_paths": entry["status_paths"],
|
||||
"frame_paths": entry["frame_paths"],
|
||||
"code_paths": entry["code_paths"],
|
||||
"verification_requirements": entry["verification_requirements"],
|
||||
"prepared_at": entry["prepared_at"],
|
||||
"state": "pending",
|
||||
}
|
||||
|
||||
|
||||
def _normalize_reconciled_entry(raw: Any) -> dict[str, Any]:
|
||||
if not isinstance(raw, dict):
|
||||
raise StateError("reconciled_code_work entries must be objects")
|
||||
scope = str(raw.get("scope", "")).strip() or "all"
|
||||
if not SCOPE_PATTERN.fullmatch(scope):
|
||||
raise StateError(f"invalid reconciled sync scope: {scope!r}")
|
||||
entry = {
|
||||
"task_path": _require_task_path(str(raw.get("task_path", ""))),
|
||||
"completion_log": _require_relative_path(
|
||||
str(raw.get("completion_log", "")),
|
||||
prefix="agent-task/archive/",
|
||||
),
|
||||
"scope": scope,
|
||||
"status_paths": [
|
||||
_require_relative_path(path, prefix="agent-ui/definition/")
|
||||
for path in _require_string_list(raw.get("status_paths", []), field="status_paths")
|
||||
],
|
||||
"frame_paths": [
|
||||
_require_relative_path(path, prefix="agent-ui/frame/")
|
||||
for path in _require_string_list(raw.get("frame_paths", []), field="frame_paths")
|
||||
],
|
||||
"code_paths": [
|
||||
_require_relative_path(path)
|
||||
for path in _require_string_list(raw.get("code_paths", []), field="code_paths")
|
||||
],
|
||||
"validation_evidence": str(raw.get("validation_evidence", "")).strip(),
|
||||
"reconciled_at": _require_timestamp(str(raw.get("reconciled_at", ""))),
|
||||
"sync_result_head": raw.get("sync_result_head"),
|
||||
}
|
||||
if not entry["status_paths"] or not entry["code_paths"] or not entry["validation_evidence"]:
|
||||
raise StateError("reconciled_code_work entry is incomplete")
|
||||
if not all(STATUS_PATH_PATTERN.fullmatch(path) for path in entry["status_paths"]):
|
||||
raise StateError("reconciled status_paths must be active view/component index.md paths")
|
||||
if not all(FRAME_PATH_PATTERN.fullmatch(path) for path in entry["frame_paths"]):
|
||||
raise StateError("reconciled frame_paths must be active frame-view index.md paths")
|
||||
if any(path.startswith(("agent-ui/", "agent-task/")) for path in entry["code_paths"]):
|
||||
raise StateError("reconciled code_paths must not point to agent-ui or agent-task artifacts")
|
||||
if entry["sync_result_head"] is not None and not SHA_PATTERN.fullmatch(
|
||||
str(entry["sync_result_head"])
|
||||
):
|
||||
raise StateError("sync_result_head must be a 40- or 64-character lowercase hex hash")
|
||||
return entry
|
||||
|
||||
|
||||
def _normalize_reconciled_milestone_entry(raw: Any) -> dict[str, Any]:
|
||||
if not isinstance(raw, dict):
|
||||
raise StateError("reconciled_milestone_work entries must be objects")
|
||||
synthetic = _normalize_reconciled_entry(
|
||||
{
|
||||
**raw,
|
||||
"task_path": "agent-task/milestone-placeholder",
|
||||
"completion_log": "agent-task/archive/2000/01/milestone-placeholder/complete.log",
|
||||
}
|
||||
)
|
||||
closure_evidence = str(raw.get("closure_evidence", "")).strip()
|
||||
if not closure_evidence:
|
||||
raise StateError("reconciled_milestone_work requires closure_evidence")
|
||||
return {
|
||||
"milestone_path": _require_milestone_path(str(raw.get("milestone_path", ""))),
|
||||
"closure_evidence": closure_evidence,
|
||||
"scope": synthetic["scope"],
|
||||
"status_paths": synthetic["status_paths"],
|
||||
"frame_paths": synthetic["frame_paths"],
|
||||
"code_paths": synthetic["code_paths"],
|
||||
"validation_evidence": synthetic["validation_evidence"],
|
||||
"reconciled_at": synthetic["reconciled_at"],
|
||||
"sync_result_head": synthetic["sync_result_head"],
|
||||
}
|
||||
|
||||
|
||||
def _normalize_state(raw: Any) -> dict[str, Any]:
|
||||
if not isinstance(raw, dict):
|
||||
raise StateError("sync state must be a JSON object")
|
||||
version = raw.get("schema_version")
|
||||
if version not in {1, 2, 3, 4}:
|
||||
raise StateError(f"unsupported schema_version: {version!r}")
|
||||
|
||||
state = dict(raw)
|
||||
state["schema_version"] = SCHEMA_VERSION
|
||||
state["pending_code_work"] = [
|
||||
_normalize_pending_entry(entry)
|
||||
for entry in _require_object_list(
|
||||
raw.get("pending_code_work", []),
|
||||
field="pending_code_work",
|
||||
)
|
||||
]
|
||||
state["reconciled_code_work"] = [
|
||||
_normalize_reconciled_entry(entry)
|
||||
for entry in _require_object_list(
|
||||
raw.get("reconciled_code_work", []),
|
||||
field="reconciled_code_work",
|
||||
)
|
||||
]
|
||||
state["pending_milestone_work"] = [
|
||||
_normalize_pending_milestone_entry(entry)
|
||||
for entry in _require_object_list(
|
||||
raw.get("pending_milestone_work", []),
|
||||
field="pending_milestone_work",
|
||||
)
|
||||
]
|
||||
state["reconciled_milestone_work"] = [
|
||||
_normalize_reconciled_milestone_entry(entry)
|
||||
for entry in _require_object_list(
|
||||
raw.get("reconciled_milestone_work", []),
|
||||
field="reconciled_milestone_work",
|
||||
)
|
||||
]
|
||||
pending_keys = [entry["task_path"] for entry in state["pending_code_work"]]
|
||||
if len(pending_keys) != len(set(pending_keys)):
|
||||
raise StateError("pending_code_work contains duplicate task_path values")
|
||||
milestone_keys = [
|
||||
entry["milestone_path"] for entry in state["pending_milestone_work"]
|
||||
]
|
||||
if len(milestone_keys) != len(set(milestone_keys)):
|
||||
raise StateError("pending_milestone_work contains duplicate milestone_path values")
|
||||
pending_entries = state["pending_code_work"] + state["pending_milestone_work"]
|
||||
pending_status_paths = [
|
||||
path for entry in pending_entries for path in entry["status_paths"]
|
||||
]
|
||||
if len(pending_status_paths) != len(set(pending_status_paths)):
|
||||
raise StateError("pending work assigns one status_path to multiple owners")
|
||||
pending_frame_paths = [
|
||||
path for entry in pending_entries for path in entry["frame_paths"]
|
||||
]
|
||||
if len(pending_frame_paths) != len(set(pending_frame_paths)):
|
||||
raise StateError("pending work assigns one frame_path to multiple owners")
|
||||
reconciled_keys = [
|
||||
(entry["task_path"], entry["completion_log"])
|
||||
for entry in state["reconciled_code_work"]
|
||||
]
|
||||
if len(reconciled_keys) != len(set(reconciled_keys)):
|
||||
raise StateError("reconciled_code_work contains duplicate task/completion pairs")
|
||||
reconciled_milestone_keys = [
|
||||
(entry["milestone_path"], entry["closure_evidence"])
|
||||
for entry in state["reconciled_milestone_work"]
|
||||
]
|
||||
if len(reconciled_milestone_keys) != len(set(reconciled_milestone_keys)):
|
||||
raise StateError(
|
||||
"reconciled_milestone_work contains duplicate milestone/evidence pairs"
|
||||
)
|
||||
notes = raw.get("notes", [])
|
||||
state["notes"] = _require_string_list(notes, field="notes")
|
||||
return state
|
||||
|
||||
|
||||
def _load_state(path: Path, expected_sha256: str) -> tuple[dict[str, Any], str]:
|
||||
if not path.is_file():
|
||||
raise StateError(f"sync state does not exist: {path}")
|
||||
if not re.fullmatch(r"[0-9a-f]{64}", expected_sha256):
|
||||
raise StateError("expected_sha256 must be a 64-character lowercase hex digest")
|
||||
payload = path.read_bytes()
|
||||
actual_sha256 = hashlib.sha256(payload).hexdigest()
|
||||
if actual_sha256 != expected_sha256:
|
||||
raise StateError(
|
||||
"sync state changed after inspection: "
|
||||
f"expected={expected_sha256} actual={actual_sha256}"
|
||||
)
|
||||
try:
|
||||
raw = json.loads(payload)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise StateError(f"invalid sync state JSON: {exc}") from exc
|
||||
return _normalize_state(raw), actual_sha256
|
||||
|
||||
|
||||
def _write_state(path: Path, state: dict[str, Any]) -> str:
|
||||
payload = (json.dumps(state, ensure_ascii=False, indent=2) + "\n").encode()
|
||||
mode = stat.S_IMODE(path.stat().st_mode)
|
||||
temporary: Path | None = None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(dir=path.parent, delete=False) as handle:
|
||||
temporary = Path(handle.name)
|
||||
handle.write(payload)
|
||||
handle.flush()
|
||||
os.fsync(handle.fileno())
|
||||
os.chmod(temporary, mode)
|
||||
os.replace(temporary, path)
|
||||
temporary = None
|
||||
directory_fd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
|
||||
try:
|
||||
os.fsync(directory_fd)
|
||||
finally:
|
||||
os.close(directory_fd)
|
||||
finally:
|
||||
if temporary is not None:
|
||||
temporary.unlink(missing_ok=True)
|
||||
return hashlib.sha256(payload).hexdigest()
|
||||
|
||||
|
||||
def _pending_entry_from_args(args: argparse.Namespace) -> dict[str, Any]:
|
||||
return _normalize_pending_entry(
|
||||
{
|
||||
"task_path": args.task_path,
|
||||
"scope": args.scope,
|
||||
"status_paths": args.status_path,
|
||||
"frame_paths": args.frame_path,
|
||||
"code_paths": args.code_path,
|
||||
"verification_requirements": args.verification_requirement,
|
||||
"prepared_at": args.prepared_at,
|
||||
"state": "pending",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _pending_milestone_entry_from_args(args: argparse.Namespace) -> dict[str, Any]:
|
||||
return _normalize_pending_milestone_entry(
|
||||
{
|
||||
"milestone_path": args.milestone_path,
|
||||
"scope": args.scope,
|
||||
"status_paths": args.status_path,
|
||||
"frame_paths": args.frame_path,
|
||||
"code_paths": args.code_path,
|
||||
"verification_requirements": args.verification_requirement,
|
||||
"prepared_at": args.prepared_at,
|
||||
"state": "pending",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@_locked_state_command
|
||||
def _prepare(args: argparse.Namespace) -> dict[str, Any]:
|
||||
state, before_sha256 = _load_state(args.state, args.expected_sha256)
|
||||
requested = _pending_entry_from_args(args)
|
||||
previous_task_path = (
|
||||
_require_task_path(args.previous_task_path) if args.previous_task_path else None
|
||||
)
|
||||
if previous_task_path == requested["task_path"]:
|
||||
raise StateError("previous_task_path must differ from task_path")
|
||||
matches = [
|
||||
index
|
||||
for index, entry in enumerate(state["pending_code_work"])
|
||||
if entry["task_path"] == requested["task_path"]
|
||||
]
|
||||
if len(matches) > 1:
|
||||
raise StateError(f"multiple pending entries for {requested['task_path']}")
|
||||
|
||||
if previous_task_path is not None:
|
||||
previous_matches = [
|
||||
index
|
||||
for index, entry in enumerate(state["pending_code_work"])
|
||||
if entry["task_path"] == previous_task_path
|
||||
]
|
||||
if len(previous_matches) != 1:
|
||||
raise StateError(
|
||||
f"expected exactly one previous pending entry for {previous_task_path}, "
|
||||
f"found {len(previous_matches)}"
|
||||
)
|
||||
if matches:
|
||||
raise StateError(f"new task_path already has a pending entry: {requested['task_path']}")
|
||||
existing = state["pending_code_work"][previous_matches[0]]
|
||||
semantic_fields = (
|
||||
"scope",
|
||||
"status_paths",
|
||||
"frame_paths",
|
||||
"code_paths",
|
||||
"verification_requirements",
|
||||
"state",
|
||||
)
|
||||
changed = any(existing[field] != requested[field] for field in semantic_fields)
|
||||
if changed and not args.replace:
|
||||
raise StateError(
|
||||
"rebound mapping changes scope or evidence; verify the closure child "
|
||||
"and rerun with --replace"
|
||||
)
|
||||
del state["pending_code_work"][previous_matches[0]]
|
||||
state["pending_code_work"].append(requested)
|
||||
status = "rebound"
|
||||
elif matches:
|
||||
index = matches[0]
|
||||
existing = state["pending_code_work"][index]
|
||||
semantic_fields = (
|
||||
"task_path",
|
||||
"scope",
|
||||
"status_paths",
|
||||
"frame_paths",
|
||||
"code_paths",
|
||||
"verification_requirements",
|
||||
"state",
|
||||
)
|
||||
if all(existing[field] == requested[field] for field in semantic_fields):
|
||||
status = "already_prepared"
|
||||
elif args.replace:
|
||||
state["pending_code_work"][index] = requested
|
||||
status = "replaced"
|
||||
else:
|
||||
raise StateError(
|
||||
f"pending entry differs for {requested['task_path']}; "
|
||||
"verify the new plan scope and rerun with --replace"
|
||||
)
|
||||
else:
|
||||
state["pending_code_work"].append(requested)
|
||||
status = "prepared"
|
||||
|
||||
state = _normalize_state(state)
|
||||
after_sha256 = _write_state(args.state, state)
|
||||
return {
|
||||
"status": status,
|
||||
"task_path": requested["task_path"],
|
||||
"previous_task_path": previous_task_path,
|
||||
"before_sha256": before_sha256,
|
||||
"after_sha256": after_sha256,
|
||||
}
|
||||
|
||||
|
||||
@_locked_state_command
|
||||
def _prepare_milestone(args: argparse.Namespace) -> dict[str, Any]:
|
||||
state, before_sha256 = _load_state(args.state, args.expected_sha256)
|
||||
requested = _pending_milestone_entry_from_args(args)
|
||||
if not Path(requested["milestone_path"]).is_file():
|
||||
raise StateError(
|
||||
f"active Milestone does not exist: {requested['milestone_path']}"
|
||||
)
|
||||
matches = [
|
||||
index
|
||||
for index, entry in enumerate(state["pending_milestone_work"])
|
||||
if entry["milestone_path"] == requested["milestone_path"]
|
||||
]
|
||||
if len(matches) > 1:
|
||||
raise StateError(
|
||||
f"multiple pending entries for {requested['milestone_path']}"
|
||||
)
|
||||
|
||||
if matches:
|
||||
index = matches[0]
|
||||
existing = state["pending_milestone_work"][index]
|
||||
semantic_fields = (
|
||||
"milestone_path",
|
||||
"scope",
|
||||
"status_paths",
|
||||
"frame_paths",
|
||||
"code_paths",
|
||||
"verification_requirements",
|
||||
"state",
|
||||
)
|
||||
if all(existing[field] == requested[field] for field in semantic_fields):
|
||||
status = "already_prepared"
|
||||
elif args.replace:
|
||||
state["pending_milestone_work"][index] = requested
|
||||
status = "replaced"
|
||||
else:
|
||||
raise StateError(
|
||||
f"pending entry differs for {requested['milestone_path']}; "
|
||||
"verify the Milestone scope and rerun with --replace"
|
||||
)
|
||||
else:
|
||||
state["pending_milestone_work"].append(requested)
|
||||
status = "prepared"
|
||||
|
||||
state = _normalize_state(state)
|
||||
after_sha256 = _write_state(args.state, state)
|
||||
return {
|
||||
"status": status,
|
||||
"milestone_path": requested["milestone_path"],
|
||||
"before_sha256": before_sha256,
|
||||
"after_sha256": after_sha256,
|
||||
}
|
||||
|
||||
|
||||
def _completion_log_location(path: Path) -> tuple[Path, str]:
|
||||
workspace = Path.cwd().resolve()
|
||||
resolved = path.resolve()
|
||||
try:
|
||||
relative = resolved.relative_to(workspace)
|
||||
except ValueError as exc:
|
||||
raise StateError(f"completion log is outside the current workspace: {path}") from exc
|
||||
normalized = _require_relative_path(relative.as_posix(), prefix="agent-task/archive/")
|
||||
return resolved, normalized
|
||||
|
||||
|
||||
def _validate_completion_log(path: Path, task_path: str) -> str:
|
||||
resolved, normalized = _completion_log_location(path)
|
||||
if not resolved.is_file():
|
||||
raise StateError(f"completion log does not exist: {path}")
|
||||
|
||||
archive_parts = PurePosixPath(normalized).parts
|
||||
if len(archive_parts) < 6 or archive_parts[-1] != "complete.log":
|
||||
raise StateError("completion log must be under agent-task/archive/YYYY/MM/<task>/")
|
||||
if not re.fullmatch(r"[0-9]{4}", archive_parts[2]) or not re.fullmatch(
|
||||
r"(?:0[1-9]|1[0-2])",
|
||||
archive_parts[3],
|
||||
):
|
||||
raise StateError("completion log archive path must contain YYYY/MM")
|
||||
archived_task_parts = archive_parts[4:-1]
|
||||
original_task_parts = PurePosixPath(task_path.removeprefix("agent-task/")).parts
|
||||
same_parent = archived_task_parts[:-1] == original_task_parts[:-1]
|
||||
final_name = archived_task_parts[-1] if archived_task_parts else ""
|
||||
original_final_name = original_task_parts[-1]
|
||||
suffix_match = re.fullmatch(re.escape(original_final_name) + r"(?:_[0-9]+)?", final_name)
|
||||
if not same_parent or suffix_match is None:
|
||||
raise StateError("completion log archive path does not match task_path")
|
||||
|
||||
text = resolved.read_text()
|
||||
task_name = task_path.removeprefix("agent-task/")
|
||||
if f"# Complete - {task_name}" not in text.splitlines()[:3]:
|
||||
raise StateError("completion log task header does not match task_path")
|
||||
if "## 루프 이력" not in text:
|
||||
raise StateError("completion log has no loop history")
|
||||
terminal = re.compile(r"^\|[^|]*\|[^|]*\|\s*(?:PASS|RESOLVED|PASS/RESOLVED)\s*\|", re.MULTILINE)
|
||||
if not terminal.search(text):
|
||||
raise StateError("completion log has no terminal PASS/RESOLVED row")
|
||||
return normalized
|
||||
|
||||
|
||||
@_locked_state_command
|
||||
def _reconcile(args: argparse.Namespace) -> dict[str, Any]:
|
||||
state, before_sha256 = _load_state(args.state, args.expected_sha256)
|
||||
task_path = _require_task_path(args.task_path)
|
||||
completion_log = _validate_completion_log(args.completion_log, task_path)
|
||||
|
||||
completed = [
|
||||
entry
|
||||
for entry in state["reconciled_code_work"]
|
||||
if entry["task_path"] == task_path and entry["completion_log"] == completion_log
|
||||
]
|
||||
if completed:
|
||||
state = _normalize_state(state)
|
||||
after_sha256 = _write_state(args.state, state)
|
||||
return {
|
||||
"status": "already_reconciled",
|
||||
"task_path": task_path,
|
||||
"before_sha256": before_sha256,
|
||||
"after_sha256": after_sha256,
|
||||
}
|
||||
|
||||
matches = [
|
||||
index
|
||||
for index, entry in enumerate(state["pending_code_work"])
|
||||
if entry["task_path"] == task_path
|
||||
]
|
||||
if len(matches) != 1:
|
||||
raise StateError(f"expected exactly one pending entry for {task_path}, found {len(matches)}")
|
||||
pending = state["pending_code_work"][matches[0]]
|
||||
|
||||
sync_result_head = args.sync_result_head
|
||||
if sync_result_head is not None and not SHA_PATTERN.fullmatch(sync_result_head):
|
||||
raise StateError("sync_result_head must be a 40- or 64-character lowercase hex hash")
|
||||
reconciled = _normalize_reconciled_entry(
|
||||
{
|
||||
"task_path": task_path,
|
||||
"completion_log": completion_log,
|
||||
"scope": pending["scope"],
|
||||
"status_paths": pending["status_paths"],
|
||||
"frame_paths": pending["frame_paths"],
|
||||
"code_paths": pending["code_paths"],
|
||||
"validation_evidence": args.validation_evidence,
|
||||
"reconciled_at": args.reconciled_at,
|
||||
"sync_result_head": sync_result_head,
|
||||
}
|
||||
)
|
||||
|
||||
del state["pending_code_work"][matches[0]]
|
||||
state["reconciled_code_work"].append(reconciled)
|
||||
state["last_sync_mode"] = "plan-reconcile"
|
||||
state["last_synced_at"] = args.reconciled_at
|
||||
state["agent_ui_paths"] = pending["status_paths"] + pending["frame_paths"]
|
||||
state["code_paths"] = pending["code_paths"]
|
||||
if sync_result_head is not None:
|
||||
state["last_synced_head"] = sync_result_head
|
||||
baseline_evidence = f"baseline=updated:{sync_result_head}"
|
||||
else:
|
||||
baseline_evidence = "baseline=preserved:no validated sync_result_head"
|
||||
state["notes"].append(
|
||||
f"plan reconcile: task={task_path}; completion={completion_log}; "
|
||||
f"validation={args.validation_evidence}; {baseline_evidence}"
|
||||
)
|
||||
|
||||
state = _normalize_state(state)
|
||||
after_sha256 = _write_state(args.state, state)
|
||||
return {
|
||||
"status": "reconciled",
|
||||
"task_path": task_path,
|
||||
"before_sha256": before_sha256,
|
||||
"after_sha256": after_sha256,
|
||||
}
|
||||
|
||||
|
||||
@_locked_state_command
|
||||
def _reconcile_milestone(args: argparse.Namespace) -> dict[str, Any]:
|
||||
state, before_sha256 = _load_state(args.state, args.expected_sha256)
|
||||
milestone_path = _require_milestone_path(args.milestone_path)
|
||||
closure_evidence = args.closure_evidence.strip()
|
||||
if not closure_evidence:
|
||||
raise StateError("closure_evidence must be non-empty")
|
||||
|
||||
matches = [
|
||||
index
|
||||
for index, entry in enumerate(state["pending_milestone_work"])
|
||||
if entry["milestone_path"] == milestone_path
|
||||
]
|
||||
if not matches:
|
||||
completed = [
|
||||
entry
|
||||
for entry in state["reconciled_milestone_work"]
|
||||
if entry["milestone_path"] == milestone_path
|
||||
and entry["closure_evidence"] == closure_evidence
|
||||
]
|
||||
if completed:
|
||||
state = _normalize_state(state)
|
||||
after_sha256 = _write_state(args.state, state)
|
||||
return {
|
||||
"status": "already_reconciled",
|
||||
"milestone_path": milestone_path,
|
||||
"before_sha256": before_sha256,
|
||||
"after_sha256": after_sha256,
|
||||
}
|
||||
if len(matches) != 1:
|
||||
raise StateError(
|
||||
f"expected exactly one pending entry for {milestone_path}, "
|
||||
f"found {len(matches)}"
|
||||
)
|
||||
if not Path(milestone_path).is_file():
|
||||
raise StateError(f"active Milestone does not exist: {milestone_path}")
|
||||
pending = state["pending_milestone_work"][matches[0]]
|
||||
|
||||
sync_result_head = args.sync_result_head
|
||||
if sync_result_head is not None and not SHA_PATTERN.fullmatch(sync_result_head):
|
||||
raise StateError("sync_result_head must be a 40- or 64-character lowercase hex hash")
|
||||
reconciled = _normalize_reconciled_milestone_entry(
|
||||
{
|
||||
"milestone_path": milestone_path,
|
||||
"closure_evidence": closure_evidence,
|
||||
"scope": pending["scope"],
|
||||
"status_paths": pending["status_paths"],
|
||||
"frame_paths": pending["frame_paths"],
|
||||
"code_paths": pending["code_paths"],
|
||||
"validation_evidence": args.validation_evidence,
|
||||
"reconciled_at": args.reconciled_at,
|
||||
"sync_result_head": sync_result_head,
|
||||
}
|
||||
)
|
||||
|
||||
del state["pending_milestone_work"][matches[0]]
|
||||
state["reconciled_milestone_work"].append(reconciled)
|
||||
state["last_sync_mode"] = "milestone-reconcile"
|
||||
state["last_synced_at"] = args.reconciled_at
|
||||
state["agent_ui_paths"] = pending["status_paths"] + pending["frame_paths"]
|
||||
state["code_paths"] = pending["code_paths"]
|
||||
if sync_result_head is not None:
|
||||
state["last_synced_head"] = sync_result_head
|
||||
baseline_evidence = f"baseline=updated:{sync_result_head}"
|
||||
else:
|
||||
baseline_evidence = "baseline=preserved:no validated sync_result_head"
|
||||
state["notes"].append(
|
||||
f"milestone reconcile: milestone={milestone_path}; "
|
||||
f"closure={closure_evidence}; validation={args.validation_evidence}; "
|
||||
f"{baseline_evidence}"
|
||||
)
|
||||
|
||||
state = _normalize_state(state)
|
||||
after_sha256 = _write_state(args.state, state)
|
||||
return {
|
||||
"status": "reconciled",
|
||||
"milestone_path": milestone_path,
|
||||
"before_sha256": before_sha256,
|
||||
"after_sha256": after_sha256,
|
||||
}
|
||||
|
||||
|
||||
@_locked_state_command
|
||||
def _record_sync(args: argparse.Namespace) -> dict[str, Any]:
|
||||
state, before_sha256 = _load_state(args.state, args.expected_sha256)
|
||||
if not SHA_PATTERN.fullmatch(args.sync_result_head):
|
||||
raise StateError("sync_result_head must be a 40- or 64-character lowercase hex hash")
|
||||
synced_at = _require_timestamp(args.synced_at)
|
||||
agent_ui_paths = [
|
||||
_require_relative_path(path, prefix="agent-ui/")
|
||||
for path in _stable_unique(args.agent_ui_path)
|
||||
]
|
||||
code_paths = [
|
||||
_require_relative_path(path) for path in _stable_unique(args.code_path)
|
||||
]
|
||||
if not agent_ui_paths:
|
||||
raise StateError("record-sync requires agent_ui_paths")
|
||||
if args.sync_mode in {"incremental", "full"} and not code_paths:
|
||||
raise StateError("incremental/full record-sync requires code_paths")
|
||||
if any(path.startswith(("agent-ui/", "agent-task/")) for path in code_paths):
|
||||
raise StateError("record-sync code_paths must not point to agent-ui or agent-task artifacts")
|
||||
if args.sync_mode in {"incremental", "full"}:
|
||||
pending_paths = {
|
||||
path
|
||||
for entry in state["pending_code_work"] + state["pending_milestone_work"]
|
||||
for path in entry["status_paths"] + entry["frame_paths"]
|
||||
}
|
||||
overlapping_paths = sorted(
|
||||
pending_path
|
||||
for pending_path in pending_paths
|
||||
if any(
|
||||
_path_trees_overlap(pending_path, sync_path)
|
||||
for sync_path in agent_ui_paths
|
||||
)
|
||||
)
|
||||
if overlapping_paths:
|
||||
raise StateError(
|
||||
"incremental/full record-sync overlaps pending work paths: "
|
||||
+ ", ".join(overlapping_paths)
|
||||
)
|
||||
|
||||
state["last_sync_mode"] = args.sync_mode
|
||||
state["last_synced_head"] = args.sync_result_head
|
||||
state["last_synced_at"] = synced_at
|
||||
state["agent_ui_paths"] = agent_ui_paths
|
||||
state["code_paths"] = code_paths
|
||||
state["notes"] = _stable_unique(state["notes"] + args.note)
|
||||
state = _normalize_state(state)
|
||||
after_sha256 = _write_state(args.state, state)
|
||||
return {
|
||||
"status": "sync_recorded",
|
||||
"before_sha256": before_sha256,
|
||||
"after_sha256": after_sha256,
|
||||
"sync_result_head": args.sync_result_head,
|
||||
}
|
||||
|
||||
|
||||
def _parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
prepare = subparsers.add_parser("prepare")
|
||||
prepare.add_argument("--state", type=Path, required=True)
|
||||
prepare.add_argument("--expected-sha256", required=True)
|
||||
prepare.add_argument("--task-path", required=True)
|
||||
prepare.add_argument("--previous-task-path")
|
||||
prepare.add_argument("--scope", default="all")
|
||||
prepare.add_argument("--status-path", action="append", default=[], required=True)
|
||||
prepare.add_argument("--frame-path", action="append", default=[])
|
||||
prepare.add_argument("--code-path", action="append", default=[], required=True)
|
||||
prepare.add_argument("--verification-requirement", action="append", default=[])
|
||||
prepare.add_argument("--prepared-at", required=True)
|
||||
prepare.add_argument("--replace", action="store_true")
|
||||
prepare.set_defaults(handler=_prepare)
|
||||
|
||||
prepare_milestone = subparsers.add_parser("prepare-milestone")
|
||||
prepare_milestone.add_argument("--state", type=Path, required=True)
|
||||
prepare_milestone.add_argument("--expected-sha256", required=True)
|
||||
prepare_milestone.add_argument("--milestone-path", required=True)
|
||||
prepare_milestone.add_argument("--scope", default="all")
|
||||
prepare_milestone.add_argument(
|
||||
"--status-path",
|
||||
action="append",
|
||||
default=[],
|
||||
required=True,
|
||||
)
|
||||
prepare_milestone.add_argument("--frame-path", action="append", default=[])
|
||||
prepare_milestone.add_argument(
|
||||
"--code-path",
|
||||
action="append",
|
||||
default=[],
|
||||
required=True,
|
||||
)
|
||||
prepare_milestone.add_argument(
|
||||
"--verification-requirement",
|
||||
action="append",
|
||||
default=[],
|
||||
)
|
||||
prepare_milestone.add_argument("--prepared-at", required=True)
|
||||
prepare_milestone.add_argument("--replace", action="store_true")
|
||||
prepare_milestone.set_defaults(handler=_prepare_milestone)
|
||||
|
||||
reconcile = subparsers.add_parser("reconcile")
|
||||
reconcile.add_argument("--state", type=Path, required=True)
|
||||
reconcile.add_argument("--expected-sha256", required=True)
|
||||
reconcile.add_argument("--task-path", required=True)
|
||||
reconcile.add_argument("--completion-log", type=Path, required=True)
|
||||
reconcile.add_argument("--validation-evidence", required=True)
|
||||
reconcile.add_argument("--reconciled-at", required=True)
|
||||
reconcile.add_argument("--sync-result-head")
|
||||
reconcile.set_defaults(handler=_reconcile)
|
||||
|
||||
reconcile_milestone = subparsers.add_parser("reconcile-milestone")
|
||||
reconcile_milestone.add_argument("--state", type=Path, required=True)
|
||||
reconcile_milestone.add_argument("--expected-sha256", required=True)
|
||||
reconcile_milestone.add_argument("--milestone-path", required=True)
|
||||
reconcile_milestone.add_argument("--closure-evidence", required=True)
|
||||
reconcile_milestone.add_argument("--validation-evidence", required=True)
|
||||
reconcile_milestone.add_argument("--reconciled-at", required=True)
|
||||
reconcile_milestone.add_argument("--sync-result-head")
|
||||
reconcile_milestone.set_defaults(handler=_reconcile_milestone)
|
||||
|
||||
record_sync = subparsers.add_parser("record-sync")
|
||||
record_sync.add_argument("--state", type=Path, required=True)
|
||||
record_sync.add_argument("--expected-sha256", required=True)
|
||||
record_sync.add_argument(
|
||||
"--sync-mode",
|
||||
choices=("incremental", "full", "baseline", "migration"),
|
||||
required=True,
|
||||
)
|
||||
record_sync.add_argument("--sync-result-head", required=True)
|
||||
record_sync.add_argument("--synced-at", required=True)
|
||||
record_sync.add_argument("--agent-ui-path", action="append", default=[], required=True)
|
||||
record_sync.add_argument("--code-path", action="append", default=[])
|
||||
record_sync.add_argument("--note", action="append", default=[])
|
||||
record_sync.set_defaults(handler=_record_sync)
|
||||
return parser
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = _parser().parse_args()
|
||||
try:
|
||||
result = args.handler(args)
|
||||
except (OSError, StateError) as exc:
|
||||
print(json.dumps({"status": "error", "error": str(exc)}, ensure_ascii=False))
|
||||
return 2
|
||||
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
490
agent-ops/skills/common/sync-agent-ui/tests/test_sync_state.py
Normal file
490
agent-ops/skills/common/sync-agent-ui/tests/test_sync_state.py
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SCRIPT = Path(__file__).parents[1] / "scripts" / "sync_state.py"
|
||||
SPEC = importlib.util.spec_from_file_location("sync_state", SCRIPT)
|
||||
sync_state = importlib.util.module_from_spec(SPEC)
|
||||
assert SPEC.loader is not None
|
||||
SPEC.loader.exec_module(sync_state)
|
||||
|
||||
|
||||
def write_json(path: Path, value: dict) -> str:
|
||||
payload = (json.dumps(value, indent=2) + "\n").encode()
|
||||
path.write_bytes(payload)
|
||||
return hashlib.sha256(payload).hexdigest()
|
||||
|
||||
|
||||
class SyncStateTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
self.root = Path(self.temporary.name)
|
||||
self.original_cwd = Path.cwd()
|
||||
os.chdir(self.root)
|
||||
self.state_path = self.root / "agent-ui" / ".sync-state.json"
|
||||
self.state_path.parent.mkdir(parents=True)
|
||||
self.base_state = {
|
||||
"schema_version": 1,
|
||||
"surface_type": "ops-dev",
|
||||
"baseline_mode": "migration",
|
||||
"last_sync_mode": "baseline",
|
||||
"last_synced_head": "a" * 40,
|
||||
"last_synced_at": "2026-01-01T00:00:00Z",
|
||||
"agent_ui_paths": [],
|
||||
"code_paths": [],
|
||||
"notes": [],
|
||||
}
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.original_cwd)
|
||||
self.temporary.cleanup()
|
||||
|
||||
def prepare_args(self, digest: str, **overrides):
|
||||
values = {
|
||||
"state": self.state_path,
|
||||
"expected_sha256": digest,
|
||||
"task_path": "agent-task/ui_work",
|
||||
"previous_task_path": None,
|
||||
"scope": "view:dashboard",
|
||||
"status_path": ["agent-ui/definition/views/dashboard/index.md"],
|
||||
"frame_path": ["agent-ui/frame/views/dashboard/index.md"],
|
||||
"code_path": ["src/dashboard.py"],
|
||||
"verification_requirement": ["python -m unittest: PASS"],
|
||||
"prepared_at": "2026-07-29T00:00:00Z",
|
||||
"replace": False,
|
||||
}
|
||||
values.update(overrides)
|
||||
return type("Args", (), values)
|
||||
|
||||
def reconcile_args(self, digest: str, completion_log: Path, **overrides):
|
||||
values = {
|
||||
"state": self.state_path,
|
||||
"expected_sha256": digest,
|
||||
"task_path": "agent-task/ui_work",
|
||||
"completion_log": completion_log,
|
||||
"validation_evidence": "validate-agent-ui scope=view:dashboard PASS",
|
||||
"reconciled_at": "2026-07-29T01:00:00Z",
|
||||
"sync_result_head": None,
|
||||
}
|
||||
values.update(overrides)
|
||||
return type("Args", (), values)
|
||||
|
||||
def prepare_milestone_args(self, digest: str, **overrides):
|
||||
values = {
|
||||
"state": self.state_path,
|
||||
"expected_sha256": digest,
|
||||
"milestone_path": (
|
||||
"agent-roadmap/phase/product/milestones/dashboard-refresh.md"
|
||||
),
|
||||
"scope": "view:dashboard",
|
||||
"status_path": ["agent-ui/definition/views/dashboard/index.md"],
|
||||
"frame_path": ["agent-ui/frame/views/dashboard/index.md"],
|
||||
"code_path": ["src/dashboard.py"],
|
||||
"verification_requirement": ["python -m unittest: PASS"],
|
||||
"prepared_at": "2026-07-29T00:00:00Z",
|
||||
"replace": False,
|
||||
}
|
||||
values.update(overrides)
|
||||
return type("Args", (), values)
|
||||
|
||||
def reconcile_milestone_args(self, digest: str, **overrides):
|
||||
values = {
|
||||
"state": self.state_path,
|
||||
"expected_sha256": digest,
|
||||
"milestone_path": (
|
||||
"agent-roadmap/phase/product/milestones/dashboard-refresh.md"
|
||||
),
|
||||
"closure_evidence": "complete-milestone check-only: closure-ready",
|
||||
"validation_evidence": "validate-agent-ui scope=view:dashboard PASS",
|
||||
"reconciled_at": "2026-07-29T01:00:00Z",
|
||||
"sync_result_head": None,
|
||||
}
|
||||
values.update(overrides)
|
||||
return type("Args", (), values)
|
||||
|
||||
def record_sync_args(self, digest: str, **overrides):
|
||||
values = {
|
||||
"state": self.state_path,
|
||||
"expected_sha256": digest,
|
||||
"sync_mode": "incremental",
|
||||
"sync_result_head": "b" * 40,
|
||||
"synced_at": "2026-07-29T02:00:00Z",
|
||||
"agent_ui_path": ["agent-ui/definition/views/dashboard/index.md"],
|
||||
"code_path": ["src/dashboard.py"],
|
||||
"note": ["direct sync"],
|
||||
}
|
||||
values.update(overrides)
|
||||
return type("Args", (), values)
|
||||
|
||||
def test_prepare_migrates_v1_and_records_typed_paths(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
result = sync_state._prepare(self.prepare_args(digest))
|
||||
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual("prepared", result["status"])
|
||||
self.assertEqual(4, state["schema_version"])
|
||||
self.assertEqual(
|
||||
["agent-ui/definition/views/dashboard/index.md"],
|
||||
state["pending_code_work"][0]["status_paths"],
|
||||
)
|
||||
self.assertEqual(
|
||||
["agent-ui/frame/views/dashboard/index.md"],
|
||||
state["pending_code_work"][0]["frame_paths"],
|
||||
)
|
||||
self.assertEqual([], state["reconciled_code_work"])
|
||||
self.assertEqual([], state["pending_milestone_work"])
|
||||
self.assertEqual([], state["reconciled_milestone_work"])
|
||||
|
||||
def test_prepare_is_idempotent_and_requires_replace_for_scope_change(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
first = sync_state._prepare(self.prepare_args(digest))
|
||||
second = sync_state._prepare(
|
||||
self.prepare_args(
|
||||
first["after_sha256"],
|
||||
prepared_at="2026-07-29T00:05:00Z",
|
||||
)
|
||||
)
|
||||
self.assertEqual("already_prepared", second["status"])
|
||||
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._prepare(
|
||||
self.prepare_args(
|
||||
second["after_sha256"],
|
||||
code_path=["src/other.py"],
|
||||
)
|
||||
)
|
||||
|
||||
replaced = sync_state._prepare(
|
||||
self.prepare_args(
|
||||
second["after_sha256"],
|
||||
code_path=["src/other.py"],
|
||||
replace=True,
|
||||
)
|
||||
)
|
||||
self.assertEqual("replaced", replaced["status"])
|
||||
|
||||
def test_prepare_rejects_stale_digest(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._prepare(self.prepare_args("0" * 64))
|
||||
self.assertEqual(digest, hashlib.sha256(self.state_path.read_bytes()).hexdigest())
|
||||
|
||||
def test_prepare_rejects_malformed_digest(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._prepare(self.prepare_args("not-a-sha256"))
|
||||
self.assertEqual(digest, hashlib.sha256(self.state_path.read_bytes()).hexdigest())
|
||||
|
||||
def test_prepare_rebinds_refined_task_path(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
prepared = sync_state._prepare(self.prepare_args(digest))
|
||||
rebound = sync_state._prepare(
|
||||
self.prepare_args(
|
||||
prepared["after_sha256"],
|
||||
previous_task_path="agent-task/ui_work",
|
||||
task_path="agent-task/ui_work/01_closure",
|
||||
)
|
||||
)
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual("rebound", rebound["status"])
|
||||
self.assertEqual(
|
||||
"agent-task/ui_work/01_closure",
|
||||
state["pending_code_work"][0]["task_path"],
|
||||
)
|
||||
|
||||
def test_prepare_rejects_status_path_owned_by_another_task(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
prepared = sync_state._prepare(self.prepare_args(digest))
|
||||
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._prepare(
|
||||
self.prepare_args(
|
||||
prepared["after_sha256"],
|
||||
task_path="agent-task/other_ui_work",
|
||||
frame_path=[],
|
||||
code_path=["src/other.py"],
|
||||
)
|
||||
)
|
||||
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual(1, len(state["pending_code_work"]))
|
||||
self.assertEqual(
|
||||
"agent-task/ui_work",
|
||||
state["pending_code_work"][0]["task_path"],
|
||||
)
|
||||
|
||||
def test_prepare_rebind_requires_replace_for_changed_scope(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
prepared = sync_state._prepare(self.prepare_args(digest))
|
||||
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._prepare(
|
||||
self.prepare_args(
|
||||
prepared["after_sha256"],
|
||||
previous_task_path="agent-task/ui_work",
|
||||
task_path="agent-task/ui_work/01_closure",
|
||||
code_path=["src/closure.py"],
|
||||
)
|
||||
)
|
||||
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual(
|
||||
"agent-task/ui_work",
|
||||
state["pending_code_work"][0]["task_path"],
|
||||
)
|
||||
|
||||
def test_prepare_milestone_records_mapping_and_blocks_shared_ui_owner(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
milestone = self.root / (
|
||||
"agent-roadmap/phase/product/milestones/dashboard-refresh.md"
|
||||
)
|
||||
milestone.parent.mkdir(parents=True)
|
||||
milestone.write_text("# Milestone: Dashboard refresh\n")
|
||||
prepared = sync_state._prepare_milestone(
|
||||
self.prepare_milestone_args(digest)
|
||||
)
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual("prepared", prepared["status"])
|
||||
self.assertEqual(1, len(state["pending_milestone_work"]))
|
||||
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._prepare(
|
||||
self.prepare_args(
|
||||
prepared["after_sha256"],
|
||||
frame_path=[],
|
||||
)
|
||||
)
|
||||
|
||||
def test_reconcile_milestone_moves_mapping_and_is_idempotent(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
milestone = self.root / (
|
||||
"agent-roadmap/phase/product/milestones/dashboard-refresh.md"
|
||||
)
|
||||
milestone.parent.mkdir(parents=True)
|
||||
milestone.write_text("# Milestone: Dashboard refresh\n")
|
||||
prepared = sync_state._prepare_milestone(
|
||||
self.prepare_milestone_args(digest)
|
||||
)
|
||||
|
||||
reconciled = sync_state._reconcile_milestone(
|
||||
self.reconcile_milestone_args(prepared["after_sha256"])
|
||||
)
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual("reconciled", reconciled["status"])
|
||||
self.assertEqual([], state["pending_milestone_work"])
|
||||
self.assertEqual(1, len(state["reconciled_milestone_work"]))
|
||||
self.assertEqual("a" * 40, state["last_synced_head"])
|
||||
|
||||
milestone.unlink()
|
||||
repeated = sync_state._reconcile_milestone(
|
||||
self.reconcile_milestone_args(reconciled["after_sha256"])
|
||||
)
|
||||
self.assertEqual("already_reconciled", repeated["status"])
|
||||
|
||||
def test_v2_legacy_pending_paths_are_typed_during_migration(self):
|
||||
state = dict(self.base_state)
|
||||
state["schema_version"] = 2
|
||||
state["pending_code_work"] = [
|
||||
{
|
||||
"task_path": "agent-task/ui_work",
|
||||
"scope": "all",
|
||||
"agent_ui_paths": [
|
||||
"agent-ui/definition/views/dashboard/index.md",
|
||||
"agent-ui/frame/views/dashboard/index.md",
|
||||
],
|
||||
"code_paths": ["src/dashboard.py"],
|
||||
"verification_requirements": [],
|
||||
"prepared_at": "2026-07-29T00:00:00Z",
|
||||
"state": "pending",
|
||||
}
|
||||
]
|
||||
normalized = sync_state._normalize_state(state)
|
||||
entry = normalized["pending_code_work"][0]
|
||||
self.assertEqual(
|
||||
["agent-ui/definition/views/dashboard/index.md"],
|
||||
entry["status_paths"],
|
||||
)
|
||||
self.assertEqual(
|
||||
["agent-ui/frame/views/dashboard/index.md"],
|
||||
entry["frame_paths"],
|
||||
)
|
||||
|
||||
def test_normalize_rejects_non_list_and_duplicate_pending_entries(self):
|
||||
invalid = dict(self.base_state)
|
||||
invalid["pending_code_work"] = {}
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._normalize_state(invalid)
|
||||
|
||||
entry = {
|
||||
"task_path": "agent-task/ui_work",
|
||||
"scope": "all",
|
||||
"status_paths": ["agent-ui/definition/views/dashboard/index.md"],
|
||||
"frame_paths": [],
|
||||
"code_paths": ["src/dashboard.py"],
|
||||
"verification_requirements": [],
|
||||
"prepared_at": "2026-07-29T00:00:00Z",
|
||||
"state": "pending",
|
||||
}
|
||||
duplicate = dict(self.base_state)
|
||||
duplicate["schema_version"] = 3
|
||||
duplicate["pending_code_work"] = [entry, dict(entry)]
|
||||
duplicate["reconciled_code_work"] = []
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._normalize_state(duplicate)
|
||||
|
||||
second_owner = dict(entry)
|
||||
second_owner["task_path"] = "agent-task/other"
|
||||
duplicate_owner = dict(self.base_state)
|
||||
duplicate_owner["schema_version"] = 3
|
||||
duplicate_owner["pending_code_work"] = [entry, second_owner]
|
||||
duplicate_owner["reconciled_code_work"] = []
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._normalize_state(duplicate_owner)
|
||||
|
||||
def test_reconcile_moves_pending_entry_and_is_idempotent(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
prepared = sync_state._prepare(self.prepare_args(digest))
|
||||
completion_log = Path("agent-task/archive/2026/07/ui_work/complete.log")
|
||||
(self.root / completion_log).parent.mkdir(parents=True)
|
||||
(self.root / completion_log).write_text(
|
||||
"# Complete - ui_work\n\n"
|
||||
"## 루프 이력\n\n"
|
||||
"| Plan | Review | Verdict | 메모 |\n"
|
||||
"|---|---|---|---|\n"
|
||||
"| `plan.log` | `review.log` | PASS | done |\n"
|
||||
)
|
||||
|
||||
reconciled = sync_state._reconcile(
|
||||
self.reconcile_args(prepared["after_sha256"], self.root / completion_log)
|
||||
)
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual("reconciled", reconciled["status"])
|
||||
self.assertEqual([], state["pending_code_work"])
|
||||
self.assertEqual(1, len(state["reconciled_code_work"]))
|
||||
self.assertEqual("a" * 40, state["last_synced_head"])
|
||||
self.assertIn(
|
||||
"baseline=preserved:no validated sync_result_head",
|
||||
state["notes"][-1],
|
||||
)
|
||||
|
||||
repeated = sync_state._reconcile(
|
||||
self.reconcile_args(reconciled["after_sha256"], self.root / completion_log)
|
||||
)
|
||||
self.assertEqual("already_reconciled", repeated["status"])
|
||||
|
||||
def test_reconcile_rejects_completion_log_for_another_task(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
prepared = sync_state._prepare(self.prepare_args(digest))
|
||||
completion_log = Path("agent-task/archive/2026/07/other/complete.log")
|
||||
(self.root / completion_log).parent.mkdir(parents=True)
|
||||
(self.root / completion_log).write_text(
|
||||
"# Complete - other\n\n"
|
||||
"## 루프 이력\n\n"
|
||||
"| Plan | Review | Verdict | 메모 |\n"
|
||||
"|---|---|---|---|\n"
|
||||
"| `plan.log` | `review.log` | PASS | done |\n"
|
||||
)
|
||||
with self.assertRaises(sync_state.StateError):
|
||||
sync_state._reconcile(
|
||||
self.reconcile_args(prepared["after_sha256"], completion_log)
|
||||
)
|
||||
|
||||
def test_record_sync_preserves_pending_and_reconciled_state(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
prepared = sync_state._prepare(self.prepare_args(digest))
|
||||
recorded = sync_state._record_sync(
|
||||
self.record_sync_args(
|
||||
prepared["after_sha256"],
|
||||
agent_ui_path=[
|
||||
"agent-ui/definition/views/other-dashboard/index.md"
|
||||
],
|
||||
code_path=["src/other_dashboard.py"],
|
||||
)
|
||||
)
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual("sync_recorded", recorded["status"])
|
||||
self.assertEqual("incremental", state["last_sync_mode"])
|
||||
self.assertEqual("b" * 40, state["last_synced_head"])
|
||||
self.assertEqual(1, len(state["pending_code_work"]))
|
||||
self.assertEqual([], state["reconciled_code_work"])
|
||||
|
||||
def test_record_sync_preserves_pending_milestone_state(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
milestone = self.root / (
|
||||
"agent-roadmap/phase/product/milestones/dashboard-refresh.md"
|
||||
)
|
||||
milestone.parent.mkdir(parents=True)
|
||||
milestone.write_text("# Milestone: Dashboard refresh\n")
|
||||
prepared = sync_state._prepare_milestone(
|
||||
self.prepare_milestone_args(digest)
|
||||
)
|
||||
state_before = self.state_path.read_bytes()
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
sync_state.StateError,
|
||||
"overlaps pending work paths",
|
||||
):
|
||||
sync_state._record_sync(
|
||||
self.record_sync_args(prepared["after_sha256"])
|
||||
)
|
||||
self.assertEqual(state_before, self.state_path.read_bytes())
|
||||
|
||||
recorded = sync_state._record_sync(
|
||||
self.record_sync_args(
|
||||
prepared["after_sha256"],
|
||||
agent_ui_path=[
|
||||
"agent-ui/definition/views/other-dashboard/index.md"
|
||||
],
|
||||
code_path=["src/other_dashboard.py"],
|
||||
)
|
||||
)
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual("sync_recorded", recorded["status"])
|
||||
self.assertEqual(1, len(state["pending_milestone_work"]))
|
||||
self.assertEqual([], state["reconciled_milestone_work"])
|
||||
|
||||
def test_record_sync_rejects_paths_owned_by_pending_work(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
prepared = sync_state._prepare(self.prepare_args(digest))
|
||||
state_before = self.state_path.read_bytes()
|
||||
|
||||
for agent_ui_path in (
|
||||
"agent-ui/definition/views/dashboard/index.md",
|
||||
"agent-ui/definition/views/dashboard",
|
||||
):
|
||||
with self.subTest(agent_ui_path=agent_ui_path):
|
||||
with self.assertRaisesRegex(
|
||||
sync_state.StateError,
|
||||
"overlaps pending work paths",
|
||||
):
|
||||
sync_state._record_sync(
|
||||
self.record_sync_args(
|
||||
prepared["after_sha256"],
|
||||
agent_ui_path=[agent_ui_path],
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(state_before, self.state_path.read_bytes())
|
||||
|
||||
def test_record_migration_allows_no_code_paths(self):
|
||||
digest = write_json(self.state_path, self.base_state)
|
||||
recorded = sync_state._record_sync(
|
||||
self.record_sync_args(
|
||||
digest,
|
||||
sync_mode="migration",
|
||||
code_path=[],
|
||||
)
|
||||
)
|
||||
state = json.loads(self.state_path.read_text())
|
||||
self.assertEqual("sync_recorded", recorded["status"])
|
||||
self.assertEqual("migration", state["last_sync_mode"])
|
||||
self.assertEqual([], state["code_paths"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
name: update-roadmap
|
||||
version: 1.24.2
|
||||
description: 로드맵 업데이트, 로드맵에 추가, 마일스톤 추가/갱신, phase/페이즈 변경, 전역 Milestone 실행 순서 갱신 요청에 사용한다. Roadmap-priority-queue-Phase-Milestone scaffold에서 target 없는 신규 작업의 규모를 판정하고 기존 Phase/Milestone/Epic/Task를 검색해 upsert한 뒤, 없을 때만 새 항목을 만들고 priority-queue.md 순서 동기화, 로컬 current.md 동기화, runtime m-task 완료 이벤트 반영, 완료 후보 검토중 전환, agent-ui 코드 동기화 Milestone의 종료 검토 시 구현됨 상태 반영, 완료 근거 충족 archive 이동, workspace 외부 의존 잠금 양방향 동기화를 처리한다.
|
||||
description: 로드맵 업데이트, 로드맵에 추가, 마일스톤 추가·갱신, phase 변경, 전역 Milestone 실행 순서 갱신 요청에 사용한다. 기존 항목 upsert, runtime 완료 반영, 완료 후보 전환과 archive, workspace 외부 의존 잠금 동기화를 처리한다.
|
||||
---
|
||||
|
||||
# 로드맵 업데이트
|
||||
|
|
@ -13,7 +12,6 @@ description: 로드맵 업데이트, 로드맵에 추가, 마일스톤 추가/
|
|||
archive도 같은 Phase scaffold를 유지하며 `archive/phase/<phase-slug>/...` 아래에 둔다.
|
||||
로드맵 전체를 매 작업마다 읽지 않도록 유지하면서, 브랜치별 로컬 `current.md`의 활성 Phase와 활성 Milestone 창이 실제 작업 후보 목록으로 동작하게 한다.
|
||||
`priority-queue.md`는 Phase를 가로지르는 실행 순서만 담당하며, Milestone 상세 정보는 복제하지 않는다.
|
||||
|
||||
Milestone은 구현 계획이 아니라 방향성, 범위, 위험, 확인 필요 사항을 기록하는 협업 문서로 유지한다.
|
||||
Epic과 Task는 별도 파일로 분리하지 않고 Milestone 문서의 `기능` 안에서 관리한다. 별도 `완료 기준` 섹션은 만들지 않고, 검증이 필요한 기능에만 같은 Task 안의 `검증:` 문구로 통합한다.
|
||||
|
||||
|
|
@ -219,21 +217,6 @@ agent-roadmap/
|
|||
- 보류 또는 폐기 근거가 있으면 `완료 리뷰`와 Milestone 상태를 각각 `보류`/`[보류]`, `폐기`/`[폐기]`로 맞춘다. `[폐기]`는 archive 대상이 될 수 있다.
|
||||
- Phase는 하위 Milestone이 모두 `[완료]` 또는 `[폐기]`로 정리되고 Phase 목표도 충족된 것으로 보일 때 `[완료]` 또는 `[폐기]`로 archive한다.
|
||||
|
||||
### agent-ui 코드 동기화 Milestone 완료 리뷰
|
||||
|
||||
- 이 절은 `sync-agent-ui`가 코드 작업을 `milestone-required`로 라우팅했거나 사용자가 명시해 Milestone `완료 리뷰`에 `agent-ui 상태 반영: 대기`가 있는 경우에만 적용한다.
|
||||
- agent-ui 문서만 갱신한 Milestone, 작은 `direct-sync` 작업, 일반 `plan-required` 작업, `agent-ui 상태 반영: 해당 없음`인 Milestone에는 적용하지 않는다.
|
||||
- 기존 Milestone에 `agent-ui 상태 반영` 항목이 없으면 `해당 없음`으로 취급한다. 문서 구조 보정 또는 해당 Milestone 갱신 범위에 포함될 때만 `agent-ui 상태 반영: 해당 없음`을 보강한다.
|
||||
- 사용자가 "이 Milestone을 종료해도 될지 검토", "마일스톤 완료 검토", "종료 검토"처럼 완료 리뷰를 요청했거나 `review-state=통과`로 갱신할 때 적용한다.
|
||||
- agent-ui 코드 동기화 Milestone이 완료 리뷰 `통과` 조건을 충족하면 `[완료]` 전환 또는 archive 전에 관련 agent-ui view/component/frame 문서의 `status`를 `구현됨`으로 반영할 수 있다.
|
||||
- 상태 반영은 완료 evidence가 직접 가리키는 agent-ui 활성 문서에만 적용한다. Milestone 전체 완료만을 근거로 `agent-ui/definition/**` 전체를 일괄 `구현됨`으로 바꾸지 않는다.
|
||||
- 각 대상 문서는 실제 존재하는 code evidence path와 최종 검증 근거가 있어야 `구현됨`으로 바꾼다. 근거가 없으면 `계획` 또는 기존 상태를 유지하고 완료 리뷰를 `보완 필요`로 둔다.
|
||||
- agent-ui 상태 반영은 `update-agent-ui`의 문서 갱신 규칙을 따르고, 반영 후 `validate-agent-ui`로 정합성을 확인한다. 이 단계는 이미 완료된 코드 반영의 문서 상태 갱신이므로 `sync-agent-ui`를 새로 실행하지 않는다.
|
||||
- status/code evidence 반영과 `validate-agent-ui`가 모두 통과하면 Milestone `완료 리뷰`의 `agent-ui 상태 반영`을 `완료`로 바꾼다.
|
||||
- 근거 부족, 미해결 USER_REVIEW, 또는 `validate-agent-ui` 실패로 반영하지 못하면 Milestone `완료 리뷰`의 `agent-ui 상태 반영`을 `차단: <사유>`로 바꾸고 `상태: 보완 필요`를 남긴다.
|
||||
- `validate-agent-ui`가 FAIL이거나 현재 범위에 미해결 `agent-ui/USER_REVIEW.md`가 있으면 Milestone을 `[완료]`로 전환하지 않고 완료 리뷰를 `보완 필요`로 둔다.
|
||||
- `agent-ui 상태 반영: 대기`가 아닌 Milestone에서는 완료 리뷰 중 agent-ui status를 변경하지 않는다.
|
||||
|
||||
## Milestone task group 연동
|
||||
|
||||
- 런타임 완료 이벤트의 `origin-task`에서 `agent-task/` 다음 첫 path segment가 `m-<milestone-slug>`이면 Milestone 기반 plan/review 완료에서 온 요청으로 본다. `origin-task`는 archive 이동 전 active task 경로 또는 런타임이 그 형태로 정규화한 경로를 사용한다.
|
||||
|
|
@ -346,7 +329,6 @@ target 없는 신규 추가 요청은 append가 아니라 upsert로 처리한다
|
|||
- 구조 전환, 템플릿 보정, current 동기화는 `sync`로 본다.
|
||||
- `priority-queue.md` 생성, 순서 조정, 깨진 링크 복구, archive/폐기/경로 변경/split/merge 후 큐 정리는 `sync` 또는 `replan`으로 본다.
|
||||
- 완료/폐기 근거가 충족된 이동은 `archive`로 본다.
|
||||
- 완료 리뷰 요청에서 대상 Milestone의 `완료 리뷰`가 `agent-ui 상태 반영: 대기`인지 확인한다. 항목이 없거나 `대기`가 아니면 agent-ui 상태 반영 단계를 적용하지 않는다.
|
||||
- 새 기능 배치, Epic/Task 추가는 `milestone` 또는 `phase`로 본다.
|
||||
- "로드맵에 추가"처럼 target이 없는 신규 작업 요청은 `placement=auto`, `placement-unit=auto`, `new-feature=<요청 내용>`으로 본다.
|
||||
- 외부 의존 잠금 요청이면 `workspace-lock` 갱신으로 본다.
|
||||
|
|
@ -401,8 +383,6 @@ target 없는 신규 추가 요청은 append가 아니라 upsert로 처리한다
|
|||
- 모든 기능 Task와 Task 안에 명시된 검증이 evidence와 함께 `[x]`이어도 `구현 잠금`이 `해제`가 아니거나 미완료 `결정 필요` 항목이 있으면 Milestone 상태를 `[검토중]`으로 바꾸지 않는다. `완료 리뷰` 또는 `작업 컨텍스트`에 잠금 차단 항목을 남긴다.
|
||||
- 모든 기능 Task와 Task 안에 명시된 검증이 evidence와 함께 `[x]`이고 `구현 잠금`도 해제되어 있으면 Milestone 상태를 `[검토중]`으로 바꾸고 `완료 리뷰`에 완료 근거와 남은 차단 항목을 남긴다.
|
||||
- `[검토중]` 전환만으로 archive 이동, 로컬 `current.md` 제거, archive 링크 변경을 수행하지 않는다.
|
||||
- `agent-ui 상태 반영: 대기`인 Milestone의 완료 리뷰가 `통과` 후보이면 `[완료]` 전환 전에 관련 agent-ui 문서의 `status`를 `구현됨`으로 갱신할 수 있는지 확인한다. 대상 문서, code evidence, 최종 검증 근거, `validate-agent-ui` 결과가 모두 충족될 때만 반영하고 `agent-ui 상태 반영: 완료`로 바꾼다.
|
||||
- agent-ui 상태 반영이 필요한데 근거가 부족하거나 `validate-agent-ui`가 통과하지 않으면 Milestone 상태를 `[진행중]` 또는 `[검토중]`으로 유지하고 `완료 리뷰: 보완 필요`와 `agent-ui 상태 반영: 차단: <사유>`를 남긴다.
|
||||
- 기능 Task, 검증, 구현 잠금, 완료 근거가 모두 충족되면 `[검토중]`을 `[완료]`로 전환하고 archive 모드를 수행할 수 있다.
|
||||
- `locks.yaml`이 있으면 갱신 대상 Milestone identity로 `--find-milestone "<identity>" both "<locks-file>"`를 실행해 이 Milestone이 `locked`인지, `rely-on.target`인지, 관련 lock이 없는지 확인한다.
|
||||
- archive 모드이면 파일 이동 전 active Milestone identity를 보존하고, 그 identity로 `--find-milestone "<identity>" both "<locks-file>"`를 먼저 실행한다.
|
||||
|
|
@ -434,8 +414,6 @@ target 없는 신규 추가 요청은 append가 아니라 upsert로 처리한다
|
|||
- `[검토중]` Milestone이 archive 경로로 이동되지 않았는지 확인한다.
|
||||
- 모든 기능 Task와 Task 안에 명시된 검증이 `[x]`인 Milestone은 `구현 잠금`이 해제되어야 `[검토중]`이 될 수 있다. 잠금이 남아 있으면 `완료 리뷰` 또는 `작업 컨텍스트`에 잠금 차단 항목이 있는지 확인한다.
|
||||
- `[검토중]` Milestone에는 `완료 리뷰` 섹션과 완료 근거/남은 차단 항목이 있는지 확인한다.
|
||||
- `agent-ui 상태 반영: 대기`인 Milestone을 `[완료]`로 전환하는 경우, 완료 evidence가 가리키는 agent-ui 문서의 status/code evidence 반영 여부와 `validate-agent-ui` 결과가 완료 리뷰에 남았는지 확인한다.
|
||||
- `agent-ui 상태 반영: 대기`가 아닌 Milestone 완료 리뷰에서 agent-ui 문서 status를 변경하지 않았는지 확인한다.
|
||||
- 각 Milestone의 `구현 잠금`에 SDD 필요 여부와 사유가 있는지 확인한다.
|
||||
- `SDD: 필요` Milestone은 SDD 문서 링크, SDD 파일 존재, 잠금 해제 조건, SDD 사용자 리뷰 상태가 일관되는지 확인한다. 사용자가 명시적으로 SDD 생성을 뒤로 미루지 않았는데 SDD 파일이 없으면 검증 실패로 본다.
|
||||
- Epic heading과 Task id 형식이 맞고, 새 Epic과 갱신 범위에 포함된 기존 Epic의 기능 Task 수가 최대 5개인지 확인한다. 6개 이상이면 검증 실패로 보고하고 Epic 분리를 요구한다.
|
||||
|
|
@ -462,7 +440,6 @@ target 없는 신규 추가 요청은 append가 아니라 upsert로 처리한다
|
|||
- 전역 실행 순서 변경 사항
|
||||
- 로컬 current.md 활성 창 변경 사항
|
||||
- 완료 리뷰 상태와 남은 차단 항목
|
||||
- `agent-ui 상태 반영: 대기`인 Milestone이면 agent-ui status 반영 여부와 validate-agent-ui 결과
|
||||
- SDD gate 상태와 사용자 리뷰 필요 여부
|
||||
- 런타임 완료 이벤트의 `origin-task`가 `m-<milestone-slug>`이면 원래 active task 경로와 매칭된 target Milestone, `Roadmap Completion` Task ids 또는 no-op 사유
|
||||
- archive 모드이면 이동 경로와 남긴 링크
|
||||
|
|
@ -474,21 +451,11 @@ target 없는 신규 추가 요청은 append가 아니라 upsert로 처리한다
|
|||
## 업데이트 완료
|
||||
|
||||
- 모드: <status | milestone | phase | replan | sync | concretize | archive>
|
||||
- 수정 파일:
|
||||
- [ROADMAP.md](agent-roadmap/ROADMAP.md)
|
||||
- [priority-queue.md](agent-roadmap/priority-queue.md)
|
||||
- [current.md](agent-roadmap/current.md) (local)
|
||||
- [PHASE.md](agent-roadmap/phase/<phase-slug>/PHASE.md)
|
||||
- [<milestone-slug>.md](agent-roadmap/phase/<phase-slug>/milestones/<milestone-slug>.md)
|
||||
- [SDD.md](agent-roadmap/sdd/<phase-slug>/<milestone-slug>/SDD.md) (SDD 작성/갱신 시)
|
||||
- [USER_REVIEW.md](agent-roadmap/sdd/<phase-slug>/<milestone-slug>/USER_REVIEW.md) (SDD 사용자 리뷰 요청 시)
|
||||
- [archive PHASE.md](agent-roadmap/archive/phase/<phase-slug>/PHASE.md) 또는 [archive Milestone](agent-roadmap/archive/phase/<phase-slug>/milestones/<milestone-slug>.md) (archive 모드)
|
||||
- [archive SDD.md](agent-roadmap/archive/sdd/<phase-slug>/<milestone-slug>/SDD.md) (SDD archive 시)
|
||||
- 수정 파일: <실제로 변경한 ROADMAP/queue/current/Phase/Milestone/SDD/archive Markdown 링크 목록>
|
||||
|
||||
## 변경 사항
|
||||
|
||||
- Phase: <변경 없음 | 요약>
|
||||
- Milestone: <변경 없음 | 요약>
|
||||
- Phase / Milestone: <변경 없음 | 요약>
|
||||
- 삽입 단위: <Phase | Milestone | Epic | Task | 하위 작업 | 작업 컨텍스트/TODO | 변경 없음>
|
||||
- 규모 판정: <phase | milestone | epic | task | subtask | context | 변경 없음> - <근거>
|
||||
- 탐색 경로: <Phase 후보 -> Milestone 후보 -> Epic 후보 -> Task 후보 | 해당 없음>
|
||||
|
|
@ -501,7 +468,6 @@ target 없는 신규 추가 요청은 append가 아니라 upsert로 처리한다
|
|||
- SDD gate: <불필요 | 필요-작성 전 | 필요-잠금 | 필요-사용자 리뷰 | 필요-승인됨 | 변경 없음>
|
||||
- 승격 조건: <해당 없음 | 추가/수정/미충족 유지/충족 요약>
|
||||
- 완료 리뷰: <변경 없음 | 검토중 | 통과 | 보완 필요 | 보류 | 폐기>
|
||||
- agent-ui 상태 반영: <해당 없음 | 대기 | 완료 | 차단: 사유 | 변경 없음>
|
||||
- runtime m-task 라우팅: <해당 없음 | origin-task -> target Milestone | target 불명확>
|
||||
- Workspace 잠금: <변경 없음 | 관련 lock 없음 | entry 생성/갱신 | rely-on enable | rely-on disable | 미충족 | 런타임 해제 대기>
|
||||
- 활성 항목: <변경 없음 | Phase/Milestone 추가/제거 요약>
|
||||
|
|
@ -532,8 +498,6 @@ target 없는 신규 추가 요청은 append가 아니라 upsert로 처리한다
|
|||
- Phase와 Milestone 이름 또는 파일명에 순번을 강제하지 않는다.
|
||||
- 에이전트가 확정할 수 없는 결정 항목이 남아 있는데 Milestone의 `구현 잠금`을 `해제`로 바꾸지 않는다.
|
||||
- `구현 잠금`이 남아 있는 Milestone을 `[검토중]`, `[완료]`, 또는 완료 archive 대상으로 전환하지 않는다. 명시적인 폐기 근거가 있는 `[폐기]` archive는 허용한다.
|
||||
- `agent-ui 상태 반영: 대기`가 아닌 Milestone 완료 리뷰에서 agent-ui 문서 status를 변경하지 않는다.
|
||||
- `agent-ui 상태 반영: 대기`인 Milestone이라도 최종 검증과 실제 code evidence 없이 agent-ui status를 `구현됨`으로 바꾸지 않는다.
|
||||
- 사용자가 지정한 Phase/Milestone/Epic/Task anchor를 무시하지 않는다.
|
||||
- 사용자가 명시하지 않은 기존 epic-id나 item-id를 바꾸지 않는다.
|
||||
- `rely-on.status=enable`만으로 다른 프로젝트 Milestone의 `구현 잠금`을 직접 해제하지 않는다.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
name: update-test
|
||||
version: 1.1.0
|
||||
description: 기존 agent-test 환경 rules.md 또는 도메인/검증 시나리오별 테스트 rule 문서를 수정하는 스킬
|
||||
description: 기존 agent-test 환경 rules.md 또는 테스트 profile을 수정하고 파일 수정 없는 Verification Context를 제공한다. 테스트 규칙 수정·갱신, 테스트 컨텍스트 해석, Verification Context 생성 요청에 사용한다.
|
||||
---
|
||||
|
||||
# update-test
|
||||
|
|
@ -10,6 +9,7 @@ description: 기존 agent-test 환경 rules.md 또는 도메인/검증 시나리
|
|||
|
||||
기존 `agent-test/<env>/rules.md` 또는 `agent-test/<env>/<test-profile>.md`를 최신 테스트 기준에 맞게 갱신한다.
|
||||
라우팅은 3홉 안에 유지하고, 도메인/검증 시나리오별 문서는 자체 완결되게 보완한다.
|
||||
`resolve-context` 모드에서는 해당 문서를 수정하지 않고 현재 task에 적용할 명령, 판정 기준, 제약, 외부 환경 preflight를 중립 `Verification Context`로 반환한다.
|
||||
|
||||
`test-case`는 기존 호출과의 호환을 위한 alias이며, 새 문서 기준 이름은 `test-profile`이다.
|
||||
|
||||
|
|
@ -18,30 +18,42 @@ description: 기존 agent-test 환경 rules.md 또는 도메인/검증 시나리
|
|||
- 기존 테스트 환경 규칙을 수정할 때
|
||||
- 도메인/검증 시나리오별 테스트 기준, 명령, 판정 기준을 보완할 때
|
||||
- 테스트 라우팅을 추가, 제거, 정리할 때
|
||||
- plan 또는 다른 소비자가 구현 범위에 맞는 테스트 환경/profile 정보를 필요로 할 때
|
||||
|
||||
## 입력
|
||||
|
||||
- `env`: `local`, `dev`, `qa`, `prod` 중 하나 (필수)
|
||||
- `mode`: `update` 또는 `resolve-context`. 기본값은 `update` (선택)
|
||||
- `env`: `local`, `dev`, `qa`, `prod` 중 하나. 기본값은 `local` (선택)
|
||||
- `test-profile`: 수정할 도메인/검증 시나리오별 테스트 문서 이름, kebab-case (선택)
|
||||
- `test-case`: `test-profile`의 호환 alias (선택)
|
||||
- `domain`: 대상 도메인 이름, kebab-case (선택)
|
||||
- `change`: 수정할 내용 요약 (필수)
|
||||
- `change`: 수정할 내용 요약. `mode=update`에서 필수 (선택)
|
||||
- `task-summary`: 검증할 동작과 완료 조건 요약. `mode=resolve-context`에서 필수 (선택)
|
||||
- `scope-paths`: 변경 후보 source/test 경로 목록. `mode=resolve-context`에서 선택
|
||||
- `verification-type`: 예: `unit`, `smoke`, `integration`, `e2e`. `mode=resolve-context`에서 선택
|
||||
|
||||
## 먼저 확인할 것
|
||||
|
||||
- [ ] `agent-ops/rules/common/rules.md`의 스킬 규칙과 테스트 규칙이 분리되어 있는지 확인한다.
|
||||
- [ ] `agent-ops/skills/common/router.md`에 `update-test` 라우팅이 있는지 확인한다.
|
||||
- [ ] `test-case`가 있고 `test-profile`이 없으면 `test-profile`로 취급한다.
|
||||
- [ ] `agent-test/<env>/rules.md` 존재 여부를 확인하고, 없으면 `create-test` 대상으로 보고 중단한다.
|
||||
- [ ] `mode=update`이면 `change`가 있는지, `mode=resolve-context`이면 `task-summary`가 있는지 확인한다.
|
||||
- [ ] `agent-test/<env>/rules.md` 존재 여부를 확인한다. `mode=update`에서 없으면 `create-test` 대상으로 보고 중단하고, `mode=resolve-context`에서는 `rules_state: missing`으로 반환한다.
|
||||
- [ ] `agent-test/<env>/rules.md`가 있으면 읽는다.
|
||||
- [ ] `test-profile`이 있으면 `agent-test/<env>/<test-profile>.md` 존재 여부를 확인하고, 없으면 `create-test` 대상으로 보고 중단한다.
|
||||
- [ ] `test-profile`이 있으면 `agent-test/<env>/<test-profile>.md` 존재 여부를 확인한다. `mode=update`에서 없으면 `create-test` 대상으로 보고 중단하고, `mode=resolve-context`에서는 gap으로 기록한다.
|
||||
- [ ] `test-profile` 문서가 있으면 읽는다.
|
||||
- [ ] `test-profile`이 없고 `domain` 또는 `change`가 특정 도메인/검증 시나리오를 가리키면 env rules의 라우팅에서 대상 문서를 찾는다.
|
||||
- [ ] `test-profile`이 없으면 `domain`, `change`, `task-summary`, `scope-paths`, `verification-type`을 env rules의 `## 라우팅`과 대조해 모든 matching profile을 찾는다.
|
||||
- [ ] 템플릿 구조 확인이 필요하면 `agent-ops/rules/common/_templates/`의 테스트 템플릿을 읽는다.
|
||||
- [ ] 기존 라우팅이 3홉 안에 있는지 확인한다.
|
||||
|
||||
## 실행 절차
|
||||
|
||||
1. **모드 확정**
|
||||
- `mode=resolve-context`이면 아래 `resolve-context 절차`만 수행하고 파일, `.gitignore`, `last_rule_updated_at`을 수정하지 않는다.
|
||||
- `mode=update`이면 아래 `update 절차`를 수행한다.
|
||||
|
||||
### update 절차
|
||||
|
||||
1. **대상 확정**
|
||||
- 환경 공통 규칙 변경이면 `agent-test/<env>/rules.md`만 수정한다.
|
||||
- 특정 도메인/검증 기준 변경이면 해당 `test-profile` 문서를 수정한다.
|
||||
|
|
@ -69,6 +81,34 @@ description: 기존 agent-test 환경 rules.md 또는 도메인/검증 시나리
|
|||
- 보존한 환경값
|
||||
- 남은 확인 필요 항목
|
||||
|
||||
### resolve-context 절차
|
||||
|
||||
1. **환경 규칙 상태 판정**
|
||||
- env rules를 `missing`, `blank-skeleton`, `structured-incomplete`, `usable` 중 하나로 판정한다.
|
||||
- `missing` 또는 `blank-skeleton`이어도 생성하거나 보완하지 않는다.
|
||||
- 실제 파일에 없는 명령, endpoint, credential, 판정 기준을 추측하지 않는다.
|
||||
|
||||
2. **matching profile 해석**
|
||||
- 명시된 `test-profile` 또는 env rules의 `## 라우팅`에서 `domain / verification-type / scope`가 맞는 모든 profile을 선택한다.
|
||||
- 매칭 근거가 없는 profile은 읽거나 반환하지 않는다.
|
||||
- route가 가리키는 profile이 없거나 구조적으로 비어 있으면 gap으로 기록한다.
|
||||
|
||||
3. **검증 사실 추출**
|
||||
- env rules와 matching profile에서 실행 위치, 명령, 필수 순서, 기대 결과, 차단 기준, cache 허용 여부, 외부 서비스/secret 요구 여부를 원문 의미를 바꾸지 않고 추출한다.
|
||||
- 명령이 여러 profile에 걸치면 profile별 출처와 적용 범위를 유지한다.
|
||||
- `<확인 필요>` 또는 서로 충돌하는 값은 확정하지 않고 gap으로 기록한다.
|
||||
- env rules와 matching profile이 usable이고 적용 명령·판정 기준에 gap이 없으면 confidence를 `high`, 일부 값에 repository-native 확인이 더 필요하면 `medium`, rules가 missing/blank이거나 핵심 값이 unresolved이면 `low`로 판정한다.
|
||||
|
||||
4. **외부 환경 preflight 구성**
|
||||
- 현재 checkout 밖의 runner, field/bootstrap, external provider, Docker/code-server, emulator/device, shared runtime이 필요한 검증만 read-only preflight 대상으로 삼는다.
|
||||
- env/profile에 적힌 범위 안에서 repo root/workdir, branch/HEAD/dirty state, source sync, binary/artifact, command help/version, config path, runtime identity, ports/process, external host, OS/arch 확인 항목을 구성한다.
|
||||
- 안전한 read-only probe를 현재 환경에서 실행할 수 있으면 실제 결과를 반환하고, 실행할 수 없으면 필요한 probe와 blocker를 구분해 반환한다.
|
||||
- secret, token, credential 원문은 읽거나 출력하지 않는다.
|
||||
|
||||
5. **중립 handoff 반환**
|
||||
- 소비 스킬 이름이나 문서 section 이름에 결합하지 않고 아래 출력 형식을 그대로 반환한다.
|
||||
- test rule 유지보수는 사용자가 요청했거나 확인된 구조 결함이 있을 때만 `create-test` 또는 `update-test` 후보로 표시한다. `resolve-context` 실행 중 직접 호출하거나 수정하지 않는다.
|
||||
|
||||
## 실행 결과 검증
|
||||
|
||||
- [ ] 수정 대상 문서가 여전히 필수 섹션을 포함하는가
|
||||
|
|
@ -76,6 +116,10 @@ description: 기존 agent-test 환경 rules.md 또는 도메인/검증 시나리
|
|||
- [ ] env rules 라우팅이 3홉 제한을 넘기지 않는가
|
||||
- [ ] 도메인/검증 시나리오별 문서가 다른 테스트 문서로 라우팅하지 않는가
|
||||
- [ ] local 수정 시 `.gitignore`에 local 경로가 반영되었는가
|
||||
- [ ] `resolve-context`에서 파일과 `.gitignore`를 수정하지 않았는가
|
||||
- [ ] `resolve-context`가 읽은 env/profile 경로와 각 명령의 출처를 정확히 반환하는가
|
||||
- [ ] 외부 검증이 있으면 read-only preflight 결과 또는 실행 불가 blocker가 구분되어 있는가
|
||||
- [ ] 누락, `<확인 필요>`, 충돌 값을 확정된 명령이나 기준으로 반환하지 않았는가
|
||||
- [ ] secret, token, 개인 endpoint 원문이 tracked 파일에 추가되지 않았는가
|
||||
- 검증 실패 시: 문제 문서만 다시 보완한다.
|
||||
|
||||
|
|
@ -91,9 +135,36 @@ description: 기존 agent-test 환경 rules.md 또는 도메인/검증 시나리
|
|||
- 확인 필요: <항목 또는 없음>
|
||||
```
|
||||
|
||||
`mode=resolve-context`:
|
||||
|
||||
```md
|
||||
## Verification Context
|
||||
|
||||
- Environment: <env>
|
||||
- Rules State: <missing|blank-skeleton|structured-incomplete|usable>
|
||||
- Sources Read:
|
||||
- <env rules/profile path 또는 없음>
|
||||
- Scope Match:
|
||||
- <domain / verification-type / scope와 매칭 근거 또는 없음>
|
||||
- Commands:
|
||||
- `<command>`: source=`<path>`; scope=`<scope>`; expected=`<result>`; cache=`<allowed|fresh|required|unspecified>`
|
||||
- Preconditions:
|
||||
- <필수 순서, 실행 위치, config/runtime 요구 또는 없음>
|
||||
- Read-only Preflight:
|
||||
- `<probe>`: <PASS|BLOCKED|NOT_RUN>; <actual result or reason>
|
||||
- Constraints:
|
||||
- <금지 사항, 외부 서비스/secret 요구 여부 또는 없음>
|
||||
- Gaps:
|
||||
- <missing profile, blank value, `<확인 필요>`, conflict 또는 없음>
|
||||
- Confidence: <high|medium|low>; <판정 근거>
|
||||
- Maintenance: <not-needed|create-test-candidate|update-test-candidate>; <근거>
|
||||
```
|
||||
|
||||
## 금지 사항
|
||||
|
||||
- 기존 local 환경값을 추측으로 바꾸지 않는다.
|
||||
- 확인되지 않은 테스트 명령을 필수 검증으로 단정하지 않는다.
|
||||
- 도메인/검증 시나리오별 문서를 router처럼 쓰지 않는다.
|
||||
- `resolve-context`에서 테스트 문서 생성, 수정, `.gitignore` 갱신을 수행하지 않는다.
|
||||
- `resolve-context` 출력에 특정 소비 스킬 전용 필드나 파일명을 강제하지 않는다.
|
||||
- secret, token, 개인 endpoint 원문을 tracked 파일에 기록하지 않는다.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
name: validate-agent-ui
|
||||
version: 1.1.0
|
||||
description: agent-ui scaffold, definition/frame/component/wireframe/source evidence/status 정합성을 맞추고 필요한 USER_REVIEW와 code sync intent를 분리하는 스킬
|
||||
---
|
||||
|
||||
|
|
@ -46,7 +45,11 @@ definition, component, frame, visual source, USER_REVIEW, archive가 서로 뒤
|
|||
- `definition/views/**/index.md`에서 view 목록을 만든다.
|
||||
- `definition/components/**/index.md`에서 component 목록을 만든다.
|
||||
- `frame/views/**/index.md`에서 frame 목록을 만든다. frame이 없는 view는 visual source가 없는 정상 상태로 본다.
|
||||
- 활성 Markdown 문서의 frontmatter에서 `ui_doc_type`, id, status, source_evidence, regions를 수집한다.
|
||||
- 활성 Markdown 문서의 frontmatter에서 `ui_doc_type`과 문서 유형별 필드를 수집한다.
|
||||
- view: `view_id`, `status`, `frame`, `source_evidence`, `regions`
|
||||
- component: `component_id`, `status`, `source_evidence`
|
||||
- frame-view: `view_id`, `definition`, `visual_source`, `regions`
|
||||
- definition-index: `surface_type`, `source_evidence`
|
||||
- 같은 레벨의 `<name>.md`와 `<name>/` 혼재 여부를 확인한다.
|
||||
|
||||
2. **구조 검증과 자동 보정**
|
||||
|
|
@ -62,15 +65,17 @@ definition, component, frame, visual source, USER_REVIEW, archive가 서로 뒤
|
|||
- `ui_doc_type`이 경로와 맞는지 확인한다. 예: `definition/views/<view-id>/index.md`는 `view`다.
|
||||
- view/component/frame id가 경로 id와 일치하는지 확인한다.
|
||||
- view/component 문서에 `Source Evidence`와 `Status`가 있는지 확인한다.
|
||||
- frontmatter `source_evidence`는 list이고 각 항목은 `type`, `path`, `notes`를 가져야 한다.
|
||||
- `source_evidence.type`은 `code`, `docs`, `user` 중 하나여야 한다.
|
||||
- view/component/definition-index의 frontmatter `source_evidence`는 list이고 각 항목은 `type`, `path`, `notes`를 가져야 한다.
|
||||
- 이 문서들의 `source_evidence.type`은 `code`, `docs`, `user` 중 하나여야 한다.
|
||||
- 근거 파일이 없으면 `path: null`이어야 하며, placeholder 문자열을 근거로 보지 않는다.
|
||||
- frontmatter의 `status`, `source_evidence`, `regions`와 본문 `Status`, `Source Evidence`, `Regions`가 충돌하는지 확인한다.
|
||||
- `Status`는 `구현됨`, `계획`, `가정`, `불명확` 중 하나여야 한다.
|
||||
- view는 frontmatter의 `status`, `source_evidence`, `regions`와 본문 `Status`, `Source Evidence`, `Regions`가 충돌하는지 확인한다.
|
||||
- component는 frontmatter의 `status`, `source_evidence`와 본문 `Status`, `Source Evidence`가 충돌하는지 확인한다.
|
||||
- frame-view는 frontmatter `regions`와 본문 `Required Regions`가 충돌하는지 확인한다. frame-view에는 `status`나 `Source Evidence`를 요구하지 않는다.
|
||||
- view/component의 `Status`는 `구현됨`, `계획`, `가정`, `불명확` 중 하나여야 한다.
|
||||
- `구현됨` 항목은 코드 반영 완료 상태여야 한다. 코드 근거가 있으면 실제 존재하는 code evidence path를 가져야 한다.
|
||||
- `구현됨`의 code evidence 경로가 명시되어 있고 실제 존재하지 않으면 `repair=true`일 때 `계획`으로 낮추고 USER_REVIEW를 남길 수 있다.
|
||||
- `가정`, `불명확` 항목이 코드 동기화 가능한 정의처럼 설명되면 자동 의미 변경하지 않고 USER_REVIEW로 남긴다.
|
||||
- region id가 `<view-id>.<region>` 또는 `<view-id>.<region>.<subregion>` 형식인지 확인한다.
|
||||
- view와 frame-view의 region id가 `<view-id>.<region>` 또는 `<view-id>.<region>.<subregion>` 형식인지 확인한다.
|
||||
- view 문서에서 참조한 component id가 component 목록에 있는지 확인한다.
|
||||
- 없는 component가 단순 누락이고 생성 의도가 명확하면 `repair=true`일 때 component skeleton을 만들고 `Status`와 `Source Evidence`를 채운다.
|
||||
- 새 component로 만들지 기존 component로 바꿀지 판단이 필요하면 USER_REVIEW로 남긴다.
|
||||
|
|
@ -133,7 +138,7 @@ definition, component, frame, visual source, USER_REVIEW, archive가 서로 뒤
|
|||
- 명확한 단일 후보 상대 링크 보정
|
||||
- visual source가 있는 frame index의 region 누락 보완
|
||||
- 빈 archive/user-review 디렉터리 생성
|
||||
- 템플릿 필수 섹션과 frontmatter 누락 보완. `ui_doc_type`, `Source Evidence`, `Status` 누락 포함
|
||||
- 템플릿 필수 섹션과 frontmatter 누락 보완. 모든 문서의 `ui_doc_type`, view/component의 `Source Evidence`와 `Status`, 문서 유형별 필수 필드 누락 포함
|
||||
- 명시된 code evidence 경로가 실제 존재하지 않는 `구현됨`을 `계획`으로 낮추고 USER_REVIEW를 남기는 보정
|
||||
|
||||
## USER_REVIEW 대상
|
||||
|
|
|
|||
Loading…
Reference in a new issue