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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ node_modules/
.claude/handoffs/
.superpowers/
tests/fixtures/**/actual/
docs/*/*-handoff.md
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ SSOT는 여전히 네 파일이며, TCC는 거기서 파생된 현재 working se
- **경계 계약**: spec의 `## Boundary contracts` JSON 선언이 있으면 plan checkbox 완료 직전에
`harness-team boundary check`가 생산자·소비자 JSON Schema의 필수 필드와 기본 type을 대조한다.
선언이 없으면 `boundary: not-configured`으로 통과한다.
- **commit 시**: post-commit hook이 handoff 2파일 갱신 — 다음 커밋에 담는다(예외: harness-task)
- **commit 시**: post-commit hook이 task handoff를 갱신 — 다음 커밋에 담는다(예외: harness-task).
`<user>-handoff.md`도 함께 갱신되지만 워크트리 로컬(gitignore)이라 커밋하지 않는다
- **PR/MR 직전(ship)**: spec·plan·artifact를 최종 갱신하고 준비 완료를 보고한다 — 다이어그램
갱신은 옵트인이며, 절차·산출물 계약은 ship 명령 문서가 정본이다(PR/MR 생성은 별도 지시).
- **완료**: plan 전체 완료 감지 또는 사용자 신호 → AskUserQuestion → `harness-team done`
Expand Down Expand Up @@ -158,7 +159,7 @@ SSOT는 여전히 네 파일이며, TCC는 거기서 파생된 현재 working se
| `CLAUDE.md` | `@AGENTS.md` import + Claude 전용 (워크플로우·서브에이전트·advisor) |
| `docs/<user>/<name>/` | task별 작업 문서 4종 (spec·plan·handoff·artifact) |
| `.harness/active.json` | 현재 활성 task 포인터 |
| `docs/<user>/<user>-handoff.md` | 세션 시작 진입점 |
| `docs/<user>/<user>-handoff.md` | 세션 시작 진입점 (워크트리 로컬·gitignore — task 활성화·커밋 때 갱신) |
| `docs/decisions.md` | 팀 결정 로그 — 역할·프로토콜 규범의 근거·이력 정본 |
| `.claude/rules/*.md` | 영역별 코딩 규칙 |
<!-- harness:section="protocol" end -->
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ modified: 2026-09-13

## [Unreleased]

### Fixed
- **같은 user의 병렬 PR이 머지마다 `docs/<user>/<user>-handoff.md`에서 충돌하던 결함** (task `user-handoff-parallel-conflict`).
이 파일의 내용은 전부 gitignore된 `.harness/active.json`과 그 워크트리의 `git log -1`에서 나오는 워크트리 로컬 렌더링인데
추적 파일이라, post-commit 훅이 커밋마다 재작성한 값이 브랜치끼리 반드시 부딪혔다(실측 3회). 이제 추적하지 않는다.
- 하네스 관리 `.gitignore`에 `docs/*/*-handoff.md`(깊이 2만 — task handoff는 계속 추적) 추가. 소비자 도달은 `init`·`migrate`.
- `task <name>` 활성화(생성·재활성)가 user handoff를 곧바로 쓴다 — 새 워크트리·clone에서 첫 커밋 전 진입점 공백 해소.
- 아직 추적 중인 저장소: `doctor`가 경고하고 `migrate`는 `.gitignore` 줄을 보장한 뒤 `git rm --cached` 명령을 **안내만** 한다(인덱스 무변경).
- "handoff 2파일을 다음 커밋에 담는다" 규범을 task handoff 1파일로 정정(AGENTS.md 관리 절은 `init --yes`로 도달).
- 전환기 1회: 아직 이 파일을 수정한 구 브랜치를 머지하면 modify/delete 충돌이 한 번 난다 — 삭제 쪽을 취한다.

## [0.44.3] - 2026-09-27

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ harness-team --help
스택 옵션: `react-native` | `react` | `next` | `node` | `python` | `go` | `generic` — 목록 밖의 값은 exit 2로 거부합니다.
감지와 다른 스택은 `.harness/render-state.json`에 고정되어 이후 플래그 없는 `init`·`doctor`도 그 스택을 쓰며,
감지된 id를 다시 주면 고정이 풀립니다.
고정 여부는 `harness-team stack`으로 확인합니다 — text는 `pin:` 줄, `--json`은 `stackPin`(고정 없으면 `null`) 필드에 감지·고정·유효 스택과 해제 명령이 나옵니다.
React Native/Expo 전용 rules 4종은 유효 stack이 RN 계열일 때만 설치됩니다.

### `/harness-sync` — 내부 정합성 동기화
Expand Down
2 changes: 1 addition & 1 deletion commands/harness-ship.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Raw slash-command 인수:

5. **artifact 갱신** — `<name>-artifact.md`에 결과·검증 증거(실제 테스트 출력)·남은 리스크·
후속 작업을 기록한다. 리뷰를 돌렸다면 `## Reviews`에 날짜와 함께 남긴다 — 기록 없는 리뷰는
안 한 것이다. 이 문서 갱신을 커밋할 때 트리에 남은 handoff 2파일 변경도 **같은 커밋에** 담는다 —
안 한 것이다. 이 문서 갱신을 커밋할 때 트리에 남은 task handoff 변경도 **같은 커밋에** 담는다 —
handoff만의 커밋을 따로 만들지 않는다(`commands/harness-task.md` post-commit handoff 절).

6. **다이어그램 (옵트인)** — `--no-diagram`이 없으면 `AskUserQuestion`으로 **한 번만** 묻는다:
Expand Down
11 changes: 9 additions & 2 deletions commands/harness-task.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,21 @@ created: docs/<user>/<name>/
## post-commit handoff — sweep 커밋에서는 침묵한다

`handoff`(post-commit 훅)는 커밋 **뒤에** `<name>-handoff.md`(항목 append)와 `<user>-handoff.md`(재작성)를
쓴다. 그래서 커밋 직후 트리는 dirty다.
쓴다. 커밋 대상은 **task handoff 하나**다 — `<user>-handoff.md`는 gitignore된 워크트리 로컬 파일이다
(내용이 전부 `.harness/active.json`과 그 워크트리의 `git log -1`에서 나온다. 추적하던 시절엔 같은 user의 병렬 PR이
머지마다 이 파일에서 충돌했다). 그래도 커밋 직후 트리는 task handoff 때문에 dirty다.

**그 변경만 담는 커밋을 따로 만들지 않는다.** 다음 작업 커밋에 함께 stage한다(`git add` 대상에
두 handoff 파일을 포함). 커밋마다 sweep을 붙이면 이력의 약 4분의 1이 "handoff 반영" 커밋이 된다
task handoff를 포함). 커밋마다 sweep을 붙이면 이력의 약 4분의 1이 "handoff 반영" 커밋이 된다
(2026-09-20~25 실측: 98커밋 중 23개). dirty handoff는 `done` 가드가 무시하므로 남겨 둬도 종결을 막지 않는다.
**예외** — 푸시·브랜치 전환·워크트리 정리 직전에 남은 변경이 handoff뿐이면 그때 단독 sweep 커밋 하나를
만든다. 보통 PR/MR당 한 번이지만 상한은 아니다 — 푸시 뒤 리뷰 대응 커밋이 생기면 다음 푸시 직전에 다시 한 번 쓴다.

`<user>-handoff.md`는 `task <name>` 활성화 때도 한 번 쓴다 — 추적하지 않으므로 새 워크트리·clone에는 없고,
첫 커밋까지 세션 진입점이 비지 않게 하기 위해서다. 아직 이 파일을 **추적 중인** 저장소(gitignore 줄 도입 전)는
무시 줄만으로는 충돌이 남는다 — `doctor`가 경고하고 `migrate`가 `.gitignore` 줄을 보장한 뒤 `git rm --cached` 명령을
**안내만** 한다(인덱스는 건드리지 않는다). 기본 브랜치에서 한 번 실행해 커밋한다.

단독 sweep 커밋이 **핸드오프 파일만** 바꿨다면 훅은 아무것도 쓰지 않는다 — 기록할 작업이 없고, 쓰면
churn이 자기 자신을 먹여 트리가 깨끗해지는 지점이 사라지기 때문이다(0.38.1). 판정은 보수적이다:

Expand Down
8 changes: 4 additions & 4 deletions docs/ao-worker-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ gh api "repos/bd-makers/team-harness/check-runs/<ID>/annotations" --jq '.[] | "\
`docs/task_summary.md` 와 `docs/<user>/<user>-task.md` 는 **생성물**이고 기본 브랜치에서 `harness-team summary --write` 로만 갱신된다 —
PR 브랜치에서 절대 건드리지 않는다. AO 는 워커마다 별도 워크트리를 띄우므로, PR 브랜치에서 수정하면 **여러 워커가 같은 줄을
동시에 고쳐 충돌한다.** task SSOT 4파일(`spec`·`plan`·`handoff`·`artifact`)은 task 디렉터리에 격리되어 있으니 자유롭게 수정해도 된다.
단, `docs/<user>/<user>-handoff.md` 는 task 디렉터리 밖에 있고 post-commit 훅이 커밋마다 통째로 다시 쓴다 — 병렬 PR 끼리 반드시
충돌한다. 충돌하면 손으로 병합하지 말고 기본 브랜치 쪽을 취한다(다음 커밋에서 훅이 다시 쓴다).
`docs/<user>/<user>-handoff.md` 는 워크트리 로컬 파일이라 **추적하지 않는다**(gitignore) — 훅이 다시 써도 커밋·충돌 대상이 아니다.
`git add -f` 로 억지로 담지 마라.

## 3. 설치 단계는 없다

Expand Down Expand Up @@ -71,8 +71,8 @@ task 다이어그램은 **옵트인 단계**이지 상시 의무가 아니다.
PR 단위 슬라이드 덱이며, 오케스트레이터가 지정한 상시 단계다.
- **호출은 PR 이 생긴 뒤, 번호를 명시해서:** `/mr-change-diagram <PR번호>`. 스킬은 번호가 없으면 멈추고 되묻는다 — 현재 브랜치로 추측하게 두지 않는다.
- **저장 경로: `docs/diagrams/pr/pr-<번호>-<슬러그>.html`.** 스킬은 폴더를 스스로 정하지 않으므로, 경로를 주지 않으면 되묻고 멈춘다.
- **이 산출물에 한해 커밋이 선승인되어 있다.** opt-in 을 다시 묻지 말고 덱 HTML 과, 트리에 남아 있는 handoff 2파일
(`<name>-handoff.md`·`<user>-handoff.md`) 변경만 함께 스테이징한다 — 그 밖의 변경은 담지 않는다(`commands/harness-task.md`
- **이 산출물에 한해 커밋이 선승인되어 있다.** opt-in 을 다시 묻지 말고 덱 HTML 과, 트리에 남아 있는 task handoff
(`<name>-handoff.md`) 변경만 함께 스테이징한다 — 그 밖의 변경은 담지 않는다(`commands/harness-task.md`
post-commit handoff 절). PR 소스 브랜치에 푸시한다(`docs(diagram): PR <번호> 변경 슬라이드 추가`). `--amend`·`--force` 는 쓰지 않는다.
- **건너뛰어도 되는 경우**: 문서·설정만 바뀌어 제품 동작 변화가 없는 PR. 근거를 한 줄 남긴다.
- **스킬이 없는 머신**이면 실패로 처리하지 말고 건너뛰되, 보고에 `리뷰 덱 미실행(스킬 없음)` 이라고 남긴다.
Expand Down
11 changes: 0 additions & 11 deletions docs/chad/chad-handoff.md

This file was deleted.

5 changes: 5 additions & 0 deletions docs/harness-overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -2892,6 +2892,11 @@ <h3>병합 마커 포맷</h3>
<td><span class="tag tag-blue" style="font-size:0.7rem;padding:1px 7px;">Test</span></td>
<td>자동 회귀 검증</td>
</tr>
<tr>
<td><code>tests/user-handoff-untracked.test.mjs</code></td>
<td><span class="tag tag-blue" style="font-size:0.7rem;padding:1px 7px;">Test</span></td>
<td>자동 회귀 검증</td>
</tr>
<tr>
<td><code>tests/user-handoff.test.mjs</code></td>
<td><span class="tag tag-blue" style="font-size:0.7rem;padding:1px 7px;">Test</span></td>
Expand Down
11 changes: 0 additions & 11 deletions docs/hslee/hslee-handoff.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# user-handoff-parallel-conflict — Artifact

*최종 결과물과 학습 내용을 기록한다.*

## 결과
**2026-09-27** — 선택지 A 구현(사용자 결정: A · 소비자 migrate는 안내만).
- `src/task-paths.mjs` `USER_HANDOFF_IGNORE` = `docs/*/*-handoff.md` → `src/harness.mjs` `appendGitignore`(export) 관리 목록에 추가.
- `src/commands/task.mjs`: `writeActivationHandoff`(생성·재활성 시 활성 형태 1회), `trackedUserHandoffs`(`git ls-files :(glob)…`).
- `src/commands/doctor.mjs` `checkTrackedUserHandoffs`(warn-only), `src/commands/migrate.mjs` `migrateUserHandoffUntrack`(gitignore 보장 + `git rm --cached` 안내, 인덱스 무변경).
- 이 레포 사본 `docs/chad/chad-handoff.md`·`docs/hslee/hslee-handoff.md` 추적 해제 + `.gitignore` 줄.
- 규범: AGENTS.md·템플릿·harness-task.md·harness-ship.md·ao-worker-rules §2·§7·templates/docs/README.md·docs/index.html.
- 검증: `tests/user-handoff-untracked.test.mjs` 7케이스(무시 줄을 빼면 4개 실패 — 병렬 머지 충돌 재현 확인),
`npm test` 1058 tests / 0 fail, `npm run docs:check` 최신. 기존 `tests/summary.test.mjs` 병렬 원장 테스트가
활성화 기록 때문에 실패 → fixture에 설치본과 같은 무시 줄 추가(전환 전 저장소는 종전과 같은 충돌 프로파일).
- 남은 리스크: 전환기 구 브랜치 머지 시 modify/delete 충돌 1회. 새 main을 pull한 워크트리에서 로컬 user handoff가
한 번 지워졌다가 다음 활성화·커밋에 재생성.


## Reviews
*Codex 등 리뷰 실행 시 결과(요약·발견·조치)를 날짜와 함께 남긴다. 남기지 않은 리뷰는 "안 한 것"으로 간주.*
*기계 판독용 마커를 함께 남긴다: `<!-- harness:review kind=codex scope=worktree tip=<sha|none> at=<ISO8601> -->`*

### 2026-09-27T14:33:44.091Z — codex (harness-team review)

- engine: codex · scope: worktree · tip: fd37fa34b6ee510a077be456e2b3a297d02c1531 · exit 0 · 1288 B

```text
전하, **P2 두 건이 있어 수정 후 진행을 권합니다.** 작업 트리와 staged 삭제를 모두 확인했으며 파일은 변경하지 않았습니다.

- **P2 — [src/commands/migrate.mjs:1087](/Users/hsonpro/.ao/data/worktrees/harness-aijient-team-plugin/harness-aijient-team-plugin-6/src/commands/migrate.mjs:1087):** 추적 중인 handoff가 없으면 `.gitignore` 갱신도 건너뛰므로, 기존 설치에서 이후 생성한 user handoff가 다시 커밋될 수 있습니다. `doctor`도 이 경우 경고하지 않습니다.
- **P2 — [src/task-paths.mjs:28](/Users/hsonpro/.ao/data/worktrees/harness-aijient-team-plugin/harness-aijient-team-plugin-6/src/task-paths.mjs:28), [src/commands/task.mjs:712](/Users/hsonpro/.ao/data/worktrees/harness-aijient-team-plugin/harness-aijient-team-plugin-6/src/commands/task.mjs:712):** glob은 깊이 2만 제한하고 파일명이 실제 `<user>-handoff.md`인지 확인하지 않습니다. 같은 위치의 다른 `*-handoff.md`도 무시되며 `git rm --cached` 안내 대상에 들어갑니다.

**판정:** should-fix. 활성화 시점 기록과 task handoff를 제외하는 깊이 제한은 확인했습니다. `git diff --check`는 통과했으며, 읽기 전용 리뷰라 테스트는 실행하지 않았습니다.
```

<!-- harness:review kind=codex scope=worktree tip=fd37fa34b6ee510a077be456e2b3a297d02c1531 at=2026-09-27T14:33:44.091Z -->

**조치 (작성 세션 판별):**
- P2-1 **수용** — 추적 해제만 되고 무시 줄이 없으면 다음 `git add -A`가 재추적한다(재현 테스트로 확인). `migrate`가 추적
사본 유무와 무관하게 `.gitignore` 줄을 보장(`appendGitignore`가 변경 여부 반환), `doctor`가 `userHandoffIgnored`(probe
경로 `git check-ignore`)로 무시 줄 누락도 경고.
- P2-2 **부분 수용** — gitignore 문법은 "파일명 = 디렉터리명-handoff.md"를 표현할 수 없어 같은 깊이의 다른 `*-handoff.md`
신규 파일도 무시되는 것은 설계 한계로 남기고 `USER_HANDOFF_IGNORE` 주석에 명시(이미 추적 중인 파일은 영향 없음).
추적 해제 **안내** 대상은 `<u>/<u>-handoff.md`만 남기도록 `trackedUserHandoffs`에 필터 추가.
- 회귀 테스트 2개 추가(9케이스), `npm test` 1060 tests / 0 fail, `docs:check` 최신.

## Learnings
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# user-handoff-parallel-conflict — Context Card
<!-- working set only; UTF-8 <= 6 KiB, nonblank lines <= 100 -->

## Now
- Goal: 병렬 PR 사이 `docs/<user>/<user>-handoff.md` 충돌을 구조적으로 제거(진입점 기능 유지)
- Current atomic step: 구현·테스트 완료 → codex 리뷰 기록 → 커밋·push·PR·CI
- Stop / human-decision condition: 머지 금지(사람 지시로만). 0.44.3이 main에 먼저 들어가면 갱신(CHANGELOG 양쪽 유지).

## Constraints and settled decisions
- 결정(09-27): 선택지 A, 소비자 migrate는 안내만(인덱스 무변경)
- 코드 읽기 소비자 0 — session-context는 active.json을 직접 읽는다(확인됨)
- `handoffRelPaths`는 변경하지 않는다(전환기 churn 방지)
- gitignore 패턴 `docs/*/*-handoff.md`는 깊이 2만 매치(임시 repo로 확인)

## JIT retrieval map
- Identifiers / symbols: renderUserHandoff, runHandoffAuto, runDone, handoffRelPaths, appendGitignore, userHandoffRel
- Narrow globs: src/commands/task.mjs, src/harness.mjs, src/task-paths.mjs, tests/user-handoff.test.mjs, tests/handoff-hook-churn.test.mjs
- Read next: spec `## 설계 / 접근` A의 세부 설계
- Verification command: npm test && npm run docs:check

## Failure capsules (max 3 unresolved)
- (none)

## Resume checklist
- 결정 내용을 spec Ambiguity 게이트에 반영 → plan 3단계 체크 → 실패 테스트부터
Loading
Loading