chore(merge): 최신 dev 변경을 반영한다
This commit is contained in:
commit
d23364ca94
6 changed files with 102 additions and 16 deletions
|
|
@ -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가 읽는 설명을 유지한다.
|
||||
|
||||
## 운영 동기화 흐름
|
||||
|
|
@ -112,6 +114,7 @@ agent-ui/
|
|||
- 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하지 않는다.
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ description: 정합화된 agent-ui 변경분을 코드에 직접 반영하거나
|
|||
- [ ] `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`에 존재하는지 확인한다.
|
||||
|
|
@ -211,6 +212,7 @@ python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py reconcile-mi
|
|||
- `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 변경분을 코드에 반영하는 구현 작업의 크기와 위험만 대상으로 한다.
|
||||
|
|
@ -250,10 +252,11 @@ python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py reconcile-mi
|
|||
|
||||
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-state.json`이 없으면 `agent-ops/skills/common/_templates/agent-ui/sync-state-template.json` 기준으로 새로 만든다.
|
||||
- 방금 만든 sync 결과 commit hash와 `.sync-state.json`의 최신 SHA-256을 bundled helper `record-sync`에 넘긴다.
|
||||
- helper가 `last_sync_mode`, `last_synced_head`, `last_synced_at`, `agent_ui_paths`, `code_paths`, `notes`를 갱신하고 기존 code/Milestone pending·reconciled 기록을 보존하게 한다.
|
||||
- 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 \
|
||||
|
|
@ -290,6 +293,7 @@ python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py record-sync
|
|||
- [ ] `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를 남겼는가
|
||||
- [ ] `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`로 분리했는가
|
||||
|
|
@ -346,6 +350,7 @@ python3 agent-ops/skills/common/sync-agent-ui/scripts/sync_state.py record-sync
|
|||
- `plan-required` 또는 `milestone-required`로 판정된 작업을 같은 `sync-agent-ui` 실행에서 직접 구현하지 않는다.
|
||||
- 일반 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하지 않는다.
|
||||
|
|
|
|||
|
|
@ -61,6 +61,16 @@ 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:
|
||||
|
|
@ -760,6 +770,25 @@ def _record_sync(args: argparse.Namespace) -> dict[str, Any]:
|
|||
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
|
||||
|
|
|
|||
|
|
@ -398,7 +398,13 @@ class SyncStateTests(unittest.TestCase):
|
|||
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"])
|
||||
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"])
|
||||
|
|
@ -417,15 +423,54 @@ class SyncStateTests(unittest.TestCase):
|
|||
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"])
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -12,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 안의 `검증:` 문구로 통합한다.
|
||||
|
||||
|
|
|
|||
|
|
@ -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