Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .codex/skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- 전역 guard 정책은 `workflows/system/hooks.json`이 가진다.
- 내부 런타임 구현은 `scripts/workflow_runtime/`이 가진다.
- skill은 위 규칙을 복제하지 않고, 한 단계의 handoff와 CLI 진입점만 안내한다.
- `python3 scripts/workflow.py init`는 runtime surface를 source 기준으로 다시 동기화하고, `doctor`는 drift를 실패로 보고한다.

## Active Skill Set

Expand All @@ -27,10 +28,8 @@
1. `request-intake`
2. `socratic-spec-authoring`
3. `phase-planner`
4. `boundary-check`
5. `phase-executor`
6. `verification-runner`
7. `review-closeout`
8. `repair-reopen`
4. `boundary-check -> phase-executor -> verification-runner`를 active phase마다 반복
5. 모든 phase가 끝나면 `review-closeout`
6. 실패, block, follow-up이면 `repair-reopen`

새 workflow에서는 markdown spec과 JSON state를 분리한다. `spec.md`는 사람용 요구사항 초안이고, `approve`가 이를 `task.json.intake`로 잠근다. `task.json`/`phases.json`/`runs/*.json`은 자동화용 canonical source다.
3 changes: 2 additions & 1 deletion .codex/skills/repair-reopen/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ description: failed, blocked, review follow-up, 추가 요구사항이 들어온

1. 왜 reopen이 필요한지 note를 한 줄로 잠근다.
2. 필요하면 target phase를 정한다.
3. `python3 scripts/workflow.py reopen <task-id> --note "..." [--phase-id ...]`로 task를 `approved` 상태로 되돌린다.
3. `python3 scripts/workflow.py reopen <task-id> --note "..." [--phase-id ...]`로 task를 `approved` 상태로 되돌리고 target phase를 `pending`으로 복구한다.
4. 추가 요구사항이면 먼저 `spec.md`를 다시 잠근다.
5. spec이 바뀌었으면 `python3 scripts/workflow.py approve <task-id> --note "..."`로 `task.json.intake`를 다시 잠근다.
6. 필요하면 `plan`으로 phase를 다시 적재한다.
Expand All @@ -30,5 +30,6 @@ description: failed, blocked, review follow-up, 추가 요구사항이 들어온
## 결과

- `state=approved`인 `task.json`
- rerunnable target phase (`status=pending`)
- cleared `blocked_reason`
- new reopen run evidence
9 changes: 5 additions & 4 deletions .codex/skills/verification-runner/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: active phase의 acceptance command를 실행하고 `runs/*.json`에

## 언제 사용하나

- phase 구현이 끝났고 검증을 실행해야 한다.
- phase 구현이 끝났고 `run --complete`까지 닫은 뒤 검증을 실행해야 한다.

## 먼저 확인할 것

Expand All @@ -19,12 +19,13 @@ description: active phase의 acceptance command를 실행하고 `runs/*.json`에
## 작업 방식

1. `acceptance.commands`를 확인한다.
2. `python3 scripts/workflow.py verify <task-id>`를 실행한다.
3. `task.json.last_verified_run_id`가 현재 phase의 passed verification으로 갱신됐는지 확인한다.
2. 현재 active phase가 `completed` 상태인지 확인한다.
3. `python3 scripts/workflow.py verify <task-id>`를 실행한다.
4. `task.json.last_verified_run_id`가 현재 phase의 passed verification으로 갱신됐는지 확인한다.

## 결과

- `runs/<run-id>.json`
- `task.json.latest_run_id`
- `task.json.last_verified_run_id`
- 다음 stage: `review-closeout`
- 다음 stage: 다음 pending phase가 있으면 `phase-executor`, 없으면 `review-closeout`
6 changes: 2 additions & 4 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ python3 scripts/workflow.py doctor
python3 scripts/workflow.py hook pre_command --command-text "git push $*"

task_id="${WORKFLOW_TASK_ID:-}"
if [ -z "$task_id" ]; then
task_id="$(python3 scripts/workflow.py status --infer-active-task 2>/dev/null || true)"
fi

if [ -n "$task_id" ]; then
phase_id="${WORKFLOW_PHASE_ID:-}"
if [ -z "$phase_id" ]; then
phase_id="$(python3 scripts/workflow.py status "$task_id" --field active_phase_id 2>/dev/null || true)"
fi
python3 scripts/workflow.py hook pre_push --task-id "$task_id" --phase-id "$phase_id" --command-text "git push $*"
else
python3 scripts/workflow.py hook pre_push --command-text "git push $*"
fi
9 changes: 7 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ NEVER: control plane 문서를 앱 source-of-truth보다 우선시하지 않는
5. `plan --from <json>` 또는 `plan --stdin`이 phase 초안을 읽어 `phases.json`으로 적재한다. `spec.md`와 `task.json.intake`가 어긋나 있으면 plan을 시작하지 않는다.
6. `run --start`가 active phase를 시작한다.
7. 구현 후 `run --complete --changed-path ...`가 write scope와 TDD guard를 통과해야 한다.
8. `verify`가 acceptance command를 실행하고 `runs/*.json`에 evidence를 남긴다.
8. `verify`가 completed phase의 acceptance command를 실행하고 `runs/*.json`에 evidence를 남긴다.
9. 모든 phase가 완료되면 `review`가 review readiness를 기록한다.
10. `review --close --user-validation-note ...`만 최종 완료를 닫을 수 있다.
11. 실패, block, 추가 요구사항이 생기면 `reopen`으로 repair loop를 재개한다.
11. 실패, block, 추가 요구사항이 생기면 `reopen`으로 target phase를 `pending`으로 되돌리고 repair loop를 재개한다.

## TDD Contract

Expand Down Expand Up @@ -74,9 +74,14 @@ python3 scripts/workflow.py review <task-id> --note "..."
python3 scripts/workflow.py review <task-id> --close --user-validation-note "..."
python3 scripts/workflow.py reopen <task-id> --note "..."
python3 scripts/workflow.py status <task-id>
python3 scripts/workflow.py hook pre_command --command-text "..."
python3 scripts/workflow.py hook pre_commit --staged
python3 scripts/workflow.py hook pre_push --command-text "..."
```

- `scripts/workflow.py`는 영구적인 CLI entrypoint다.
- hook enforcement의 공식 surface도 `python3 scripts/workflow.py hook ...` 하나다.
- `init`는 `.githooks/*`와 `workflows/system/hooks.json`을 source 기준으로 다시 동기화하고, `doctor`는 drift를 실패로 취급한다.
- 실제 실행 엔진은 `scripts/workflow_runtime/cli.py`, `engine.py`, `models.py`, `guards.py`, `doctor.py`, `git_ops.py`가 분담한다.
- `workflow.py`를 유지하는 이유는 사람과 AI가 하나의 안정적인 명령 surface만 기억하면 되기 때문이다.

Expand Down
16 changes: 13 additions & 3 deletions docs/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
## Canonical Hook Config

- 설정 파일: [workflows/system/hooks.json](../workflows/system/hooks.json)
- 구현 파일: [scripts/hooks](../scripts/hooks/)
- 구현 파일: [scripts/workflow_runtime/guards.py](../scripts/workflow_runtime/guards.py), [scripts/workflow_runtime/cli.py](../scripts/workflow_runtime/cli.py)
- trigger surface: workflow CLI, `.githooks/`, CI

## Hook Events

- `pre_command`
- `pre_commit`
- `pre_phase_start`
- `post_change`
- `pre_phase_complete`
- `pre_review`
Expand Down Expand Up @@ -38,7 +37,14 @@
### Latest Verification Guard

- `pre_review`에서는 active phase에 대한 latest passed verification을 요구한다.
- `pre_push`에서는 unpushed diff가 active phase scope와 겹칠 때 latest passed verification을 요구한다.
- `pre_push`에서는 unpushed diff를 기준으로 task를 먼저 해석하고, 그 scope와 겹칠 때 latest passed verification을 요구한다.
- active task가 없더라도 unpushed diff가 정확히 하나의 completed task scope에 매핑되면 그 task를 push gate context로 재사용한다.
- unpushed diff가 어느 task에도 단일하게 매핑되지 않으면 `pre_push`는 fail-closed 한다.

### User Validation Guard

- `pre_complete`에서는 non-empty user validation note를 요구한다.
- `review --close --user-validation-note ...`만 최종 완료를 닫을 수 있다.

### Circuit Breaker

Expand All @@ -55,3 +61,7 @@

git hook만으로는 충분하지 않다. 로컬 CLI와 phase runner가 같은 hook 설정을 읽어야 guard가 유지된다.
`python3 scripts/workflow.py init`는 로컬 git 저장소의 `core.hooksPath`를 `.githooks`로 맞추고, `doctor`는 이 설정이 빠지면 실패한다.
`init`는 `.githooks/pre-commit`, `.githooks/pre-push`, `workflows/system/hooks.json`을 source 기준으로 다시 동기화한다.
`doctor`는 위 runtime surface가 source와 drift하면 실패하고 `python3 scripts/workflow.py init`를 다시 요구한다.
`pre_commit`은 active task가 하나로 추론되지 않으면 fail-closed 한다. 이때는 `WORKFLOW_TASK_ID` 또는 `--task-id`로 task binding을 명시해야 한다.
`pre_push`는 먼저 unpushed diff를 task scope에 매핑한다. scope가 단일 task로 정해지지 않으면 역시 fail-closed 한다.
9 changes: 7 additions & 2 deletions docs/runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ python3 scripts/workflow.py doctor
python3 -m unittest discover -s tests -v
```

`init`가 로컬 git 저장소의 `core.hooksPath`를 `.githooks`로 맞춘다. `doctor`는 이 값이 어긋나면 실패한다.
`init`가 로컬 git 저장소의 `core.hooksPath`를 `.githooks`로 맞추고 `.githooks/*`, `workflows/system/hooks.json`을 source 기준으로 다시 동기화한다. `doctor`는 이 값이나 runtime surface가 어긋나면 실패한다.

## 새 작업 시작

Expand Down Expand Up @@ -41,6 +41,8 @@ cat /tmp/task-001-phases.json | python3 scripts/workflow.py plan task-001 --stdi

## 실행과 검증

`verify`는 `run --complete`로 닫힌 phase에 대해서만 실행한다.

```bash
python3 scripts/workflow.py run task-001 --start
python3 scripts/workflow.py run task-001 --complete --changed-path scripts/workflow.py --changed-path tests/test_workflow_cli.py
Expand All @@ -51,7 +53,7 @@ python3 scripts/workflow.py review task-001 --close --user-validation-note "vali

## Repair / Reopen

실패, block, review follow-up, 추가 요구사항이 들어오면 `reopen`으로 다시 `approved` 상태로 되돌린다. 추가 요구사항이면 `spec.md`를 다시 잠근 뒤 `approve`를 다시 실행하고, 그 다음 `plan`으로 phase를 갱신한다.
실패, block, review follow-up, 추가 요구사항이 들어오면 `reopen`으로 다시 `approved` 상태로 되돌린다. `reopen`은 target phase를 `pending`으로 복구해서 바로 `run --start`로 재개할 수 있게 만든다. 추가 요구사항이면 `spec.md`를 다시 잠근 뒤 `approve`를 다시 실행하고, 그 다음 `plan`으로 phase를 갱신한다.

```bash
python3 scripts/workflow.py reopen task-001 --note "추가 요구사항 반영"
Expand All @@ -68,3 +70,6 @@ python3 scripts/workflow.py hook pre_command --command-text "git push origin fea
python3 scripts/workflow.py hook pre_commit --staged
python3 scripts/workflow.py hook pre_push --command-text "git push origin feature"
```

`pre_commit`은 active task가 여러 개라면 자동 추론을 중단하고 실패한다. 이 경우 `WORKFLOW_TASK_ID` 또는 `--task-id`로 명시적으로 task binding을 넣는다.
`pre_push`는 먼저 unpushed diff를 task scope에 매핑한다. active task가 없어도 completed task scope 하나로 매핑되면 그 task를 재사용하고, 단일 task로 정해지지 않으면 실패한다.
9 changes: 5 additions & 4 deletions docs/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
5. `plan`으로 `phases.json` 적재
6. active phase 실행
7. `run --complete`로 phase closure
8. `verify`로 acceptance command 실행
8. `verify`로 completed phase의 acceptance command 실행
9. 모든 phase 완료 후 `review`
10. `review --close`로 user validation과 완료 고정
11. 필요하면 `reopen`으로 repair loop 재개
Expand Down Expand Up @@ -41,7 +41,7 @@
- `plan`은 `task.json.intake`와 현재 `spec.md`가 일치할 때만 허용한다.
- `status`는 `spec.md`를 읽어 `ready_for_approval`, `clarification_count`, `unresolved_clarifications`를 JSON으로 노출한다.
- `run --complete`는 `allowed_write_paths`와 `workflows/tasks/<task-id>/` 밖 변경을 차단한다.
- `verify`는 active phase 또는 지정 phase의 `acceptance.commands`를 실행해 run evidence를 남긴다.
- `verify`는 completed active phase 또는 지정된 completed phase의 `acceptance.commands`를 실행해 run evidence를 남긴다.
- phase completion 뒤 verification이 통과하기 전까지 `last_verified_run_id`는 비어 있어야 한다.
- `review`는 `last_verified_run_id`가 active phase와 일치하는 passed verification일 때만 허용한다.
- task-level review는 모든 phase가 `completed`일 때만 연다.
Expand All @@ -58,13 +58,14 @@
- `scripts/workflow_runtime/git_ops.py`: staged/unpushed path 조회

`workflow.py`를 유지하는 이유는 사람과 AI가 단일 고정 명령 surface를 기억하는 편이 가장 효율적이기 때문이다.
`init`는 로컬 git 저장소의 `core.hooksPath`를 `.githooks`로 맞추고, `doctor`는 이 설정이 어긋나면 실패한다.
`init`는 로컬 git 저장소의 `core.hooksPath`를 `.githooks`로 맞추고, `.githooks/*`와 `workflows/system/hooks.json`을 source 기준으로 다시 동기화한다.
`doctor`는 이 설정이 어긋나거나 runtime surface가 source와 drift하면 실패한다.

## Retry / Blocked Semantics

- phase 실패 시 `retry_count`를 증가시킨다.
- verification 실패도 retry에 포함한다.
- 동일한 `error_fingerprint`가 짧은 시간 안에 반복되면 Circuit Breaker가 `blocked`로 전이한다.
- `blocked`는 외부 입력, spec 재정의, phase 재계획이 필요한 상태다.
- `reopen`은 `failed`, `blocked`, `review_ready`, `completed` task를 다시 `approved`로 되돌리고 repair loop를 재시작한다.
- `reopen`은 `failed`, `blocked`, `review_ready`, `completed` task를 다시 `approved`로 되돌리고 target phase를 `pending`으로 복구한 뒤 repair loop를 재시작한다.
- 추가 요구사항이면 `reopen` 뒤 `spec.md`를 다시 잠그고 `approve`를 재실행한 뒤 `plan`으로 phase를 다시 적재한다.
27 changes: 0 additions & 27 deletions scripts/hooks/circuit_breaker.py

This file was deleted.

27 changes: 0 additions & 27 deletions scripts/hooks/dangerous_cmd_guard.py

This file was deleted.

40 changes: 0 additions & 40 deletions scripts/hooks/tdd_guard.py

This file was deleted.

21 changes: 13 additions & 8 deletions scripts/workflow_runtime/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,26 +213,31 @@ def main(argv: list[str] | None = None) -> int:
return 0

if args.command == "hook":
task = service.load_task(args.task_id) if args.task_id else None
phase = None
if args.task_id and args.phase_id:
_, _, phase = service.current_phase(args.task_id, args.phase_id)
elif args.task_id and task and task["active_phase_id"]:
_, _, phase = service.current_phase(args.task_id, task["active_phase_id"])

changed_paths = list(args.changed_path)
if args.staged:
changed_paths.extend(service.staged_or_worktree_paths(staged=True))
if args.worktree:
changed_paths.extend(service.staged_or_worktree_paths(staged=False))
if args.event == "pre_push" and not changed_paths:
changed_paths.extend(service.unpushed_paths())
changed_paths = service.normalize_changed_paths(changed_paths)

task_id = args.task_id
if not task_id and args.event in {"pre_commit", "pre_push"}:
task_id = service.infer_hook_task(args.event, changed_paths)

task = service.load_task(task_id) if task_id else None
phase = None
if task_id and args.phase_id:
_, _, phase = service.current_phase(task_id, args.phase_id)
elif task_id and task and task["active_phase_id"]:
_, _, phase = service.current_phase(task_id, task["active_phase_id"])

result = service.run_hooks(
args.event,
task=task,
phase=phase,
changed_paths=service.normalize_changed_paths(changed_paths),
changed_paths=changed_paths,
command=args.command_text,
user_validation_note=args.user_validation_note,
)
Expand Down
5 changes: 3 additions & 2 deletions scripts/workflow_runtime/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"in_progress": {"completed", "failed", "blocked"},
"failed": {"pending", "in_progress", "blocked"},
"blocked": {"pending", "in_progress"},
"completed": {"failed", "blocked"},
"completed": {"pending", "failed", "blocked"},
}

SPEC_REQUIRED_SECTIONS = [
Expand Down Expand Up @@ -64,7 +64,7 @@
"docs/README.md": ["AGENTS.md", "docs/runtime.md", "docs/artifact-model.md"],
"docs/artifact-model.md": ["task.json", "intake", "clarifications"],
"docs/runtime.md": ["소크라테스 질문", "approve", "plan", "review --close"],
"docs/hooks.md": [".githooks/", "TDD Guard", "Dangerous Command Guard", "Circuit Breaker"],
"docs/hooks.md": [".githooks/", "TDD Guard", "Dangerous Command Guard", "Circuit Breaker", "workflow.py hook"],
"docs/runbook.md": ["사용자가 현재 spec 초안에 명시적으로 동의하면 approve 한다.", "approve", "plan"],
}

Expand All @@ -77,6 +77,7 @@
"workflows/config/",
"workflows/runtime/",
"workflows/schemas/",
"scripts/hooks/",
"workflow_lib.py",
]

Expand Down
Loading
Loading