ํ๋ก์ ํธ ๋ฌด๊ด(project-agnostic) AI ์ฝ๋ฉ ํ๋ค์ค โ ์ด๋ repo ์๋ ๋๋กญ์ธ. ์คํยทํ์ผยทํ๋กฌํํธ ๋จ๊ณ์ ๋ผ์ด๋ค์ด ๊ท์น์ ๊ฐ์ ํ๊ณ , ๋ชจ๋ ๊ฒฐ๊ณผ๋ฅผ append-only JSONL ๋ก ๋จ๊ธด๋ค.
๐ง ํ๋ค์ค = "AI ์ฝ๋ฉ ๋ณด์๊ฒ์๋" โ AI ์์ด์ ํธ(Claude Code / Codex ๋ฑ)๊ฐ ๋ช ๋ น์ ์คํํ๊ฑฐ๋ ํ์ผ์ ๊ณ ์น๊ธฐ ์ง์ ยท์งํ์ ๊ฒ์ดํธ๋ฅผ ํต๊ณผ์์ผ, ์ํํ ๋์์ ๋ง๊ณ (block) ์ ๊ธ ํ์ผ ์์ ์ ๊ฒฝ๊ณ (warn)ํ๋ฉฐ ๊ฒ์ฆยท์ธ๊ณ๋ฅผ ์๋ํํ๋ค. ESLint ๊ฐ "์ฝ๋ ๋ฌธ๋ฒ"๋ง ๋ณธ๋ค๋ฉด, ํ๋ค์ค๋ ๋ช ๋ น ์คํยทํ์ผ ์ ๊ธยท๊ฒ์ฆยท์ธ์ ์ธ๊ณ๊น์ง ์์ ํ๋ฆ ์ ์ฒด๋ฅผ ๋จ์ํ๋ค.
์ด ์ ์ฅ์๋ dancinlab ์ ๋ชจ๋ repo(edge ยท anima ยท โฆ)๊ฐ ๊ณต์ ํ๋ ์์ง์ด๋ค. ํ๋ก์ ํธ๋ง๋ค ๋ฌ๋ผ์ง๋ ๊ฒ์ harness.config.json + .harness/*.json(๊ท์น ๋ฐ์ดํฐ)๋ฟ์ด๊ณ , .ts ์์ง ์ฝ๋๋ ์ ๋ถ ๊ณต์ ํ๋ค.
๐ ์ธ์ด ๋ฌด๊ด: ์น๋ฟ ์๋๋ผ Python ยท Rust ยท C/C++ ยท Go ยท Swift ยท hexa ๋ก์ปฌ/๋ชจ๋ฐ์ผ ์ฑ์๋ ์ด๋ค.
init์ด ์คํ์ ๊ฐ์งํด ๊ฒ์ฆ ๋ช ๋ น(cargo/pytest/swift build/โฆ)๊ณผ ๋ค๊ตญ์ด ์ฐํํจํด(# type: ignoreยท#[allow]ยทswiftlint:disableโฆ)์ ์๋ ์ ์ฉ. ์์ง ์คํ์ ๊ฐ๋ฐ๋จธ์ Node 1๊ฐ๋ง ํ์(ํ๊น ๋น๋์ ๋ฌด๊ด). โ docs/languages.md
[ edge repo ]โโโ
[ anima repo ]โโผโโโถ ๊ฐ์ .ts ์์ง (์ด repo)
[ ๋ค๋ฅธ repo ]โโโ โฒ
โ ๊ฐ์ harness.config.json + .harness/*.json ์ผ๋ก
โโ ๊ท์น๋ง ๋ค๋ฅด๊ฒ ์ฃผ์
| # | ์์น | ์๋ฏธ |
|---|---|---|
| H1 | ์ฑ๊ณต์ ์กฐ์ฉ, ์คํจ๋ ์๋๋ฝ๊ฒ | ํต๊ณผ ์ stdout ์นจ๋ฌต, ์คํจ๋ง stderr (JSON) |
| H2 | ์๋ ์์ ์ ํจ | ์ ์ยท์ฐจ๋จยท๊ฒฝ๊ณ ๋ง. ๊ณ ์น๋ ๊ฑด ์ฌ๋/์์ด์ ํธ |
| H3 | bitter-gate | ์ ๊ท์น ์ถ๊ฐ ์ , ์ ์ฐ๋(dormant) ๊ท์น ๋จผ์ ํ๊ธฐ |
| H4 | config ์ฃผ๋ | ๋ชจ๋ ํ๋ก์ ํธ ์์ฑ๋ ๋ฐ์ดํฐ(JSON)๋ก. ์์ง ์ฝ๋๋ ๋ถ๋ณ |
| H5 | AI-native | ๋ชจ๋ ์ฐ์ถ๋ฌผ JSONL append-only (.harness/logs/*.jsonl) |
harness/
โโโ bin/harness ์คํ ์
๊ตฌ (bash โ tsx ๋ฐํ์ ์๋ํ์)
โโโ cli/index.ts ๋์คํจ์ฒ (harness lint โ lint.ts)
โโโ lib/ ๊ณต์ฉ ๋ถํ
โ โโโ paths.ts repo-root ์๋ํ์ (harness.config.json / .git ์ํฅ ํ์)
โ โโโ config.ts harness.config.json ๋ก๋ + ๊ธฐ๋ณธ๊ฐ ๋จธ์ง
โ โโโ lockdown.ts L0(์ ๊ธ) ํ์ผ ๋ชฉ๋ก (config + ๐ด ๋งํฌ๋ค์ด ๋ธ๋ก ํ์ฑ)
โ โโโ log.ts json.ts exec.ts
โโโ modules/ ๊ธฐ๋ฅ 12์ข
(์๋ ํ)
โโโ config/ ๋ฒ๋ค ๊ธฐ๋ณธ ๊ท์น (๋๋ฉ์ธ-๋ฌด๊ด)
โ โโโ enforcement.json ์คํ/์ฐ๊ธฐ ์ฐจ๋จ ๊ท์น + ํ๋กฌํํธ ํํธ
โ โโโ keywords.json ํ๋กฌํํธ ํค์๋ ํธ๋ฆฌ๊ฑฐ
โ โโโ severity-map.json ์ค๋ฅ severity ๋ถ๋ฅ
โโโ harness.config.example.json
| ๋ช ๋ น | ์ญํ | hook ๋จ๊ณ |
|---|---|---|
pre bash / pre write |
enforcement ๊ท์น ๋งค์นญ โ block/warn | PreToolUse |
post bash <exit> / post edit <file> |
๊ฒฐ๊ณผ ๊ธฐ๋ก, 0โ exit ๋ผ์ฐํ , L0 ํธ์ง ๊ฒฝ๊ณ | PostToolUse |
prompt <text> |
ํค์๋ ํธ๋ฆฌ๊ฑฐ + ํ๋กฌํํธ ํํธ ์ฃผ์ | UserPromptSubmit |
architecture {inject|show} |
repo-root ARCHITECTURE.json(์ฐ์ )/.md ๋ฅผ ์ปจํ
์คํธ๋ก ์ฃผ์
โ CLAUDE.md ์ฒ๋ผ ์ฒซ ํด๋ถํฐ ์ค๊ณ SSOT ์์ฃผ (80KB ์ด๊ณผ ์ ์ ๋จ, ๋ถ์ฌ ์ ๋ฌด์) |
SessionStart |
lint [all|fast] |
staged-L0 + ์ ์ ๋ + CHANGELOG ๋๋ฝ + ์๋ ด ๋๋ฝ ์ฒดํฌ | commit ์ (git pre-commit hook) |
verify [all|fast|list] |
config ์ ๊ฒ์ฆ ๋ช ๋ น ๋ณ๋ ฌ ์คํ (์คํจ 1๊ฐ๋ผ๋ โ exit 1) | commit/push ์ |
errors {route|list|drain_check|mark_fixed} |
์ค๋ฅ severity ๋ถ๋ฅ + ํ | ์์ |
ledger {register|complete|list|gc|dup_check} |
๋ฐฑ๊ทธ๋ผ์ด๋ ์์ด์ ํธ ์์ ๋ฑ๋ก(์ค๋ณต ๋ฐฉ์ง) | Agent ์ /ํ |
bitter-gate audit [window] |
๊ท์น ํํธ ๋น๋ โ dormant ๊ท์น ํ๊ธฐ ๊ฒํ | ๊ท์น ์ถ๊ฐ ์ |
audit [full|summary|json] |
6์ถ ์๊ฐ ์ค์ฝ์ด์นด๋ (/60) | ์ฃผ๊ธฐ์ |
gc [scan|drift] |
๊ฐ์ด๋ ๋งํฌ๋ค์ด์ ๊นจ์ง ๋งํฌ ํ์ง | ์ฃผ๊ธฐ์ |
folders [scan|scaffold <dir>] |
์๋ธํด๋๋ณ CLAUDE.md ๋๋ฝ ํ์ง + ํ ํ๋ฆฟ ์์ฑ (ํธ์ง ์ ์๋ ๋์ง) | ์ฃผ๊ธฐ์ /์์ ์ค |
handoff [reason] |
์ธ์
์ค๋
์ท โ .harness/handoff/ |
์ธ์ ์ข ๋ฃ |
convergence {status|recompute|by-category} |
(์ ํ) incident ์๋ ด ์ถ์ | ๋ฒ๊ทธ ์์ ํ |
sync {run|diff} |
(์ ํ) repo ์์ฒด ๊ณต์ ํ์ผ sync ์คํฌ๋ฆฝํธ ์คํ | ๊ณต์ ํ์ผ ๋ณ๊ฒฝ ํ |
pool {list|add|rm|on <h> <cmd>|status|specs [h]} |
ํธ์คํธ ๋ก์คํฐ + ์๊ฒฉ ์คํ. shared:false ํธ์คํธ๋ ์ ํ ํธ์คํธ โ allow ํ๋ก์ ํธ ์ปจํ
์คํธ ๋ฐ์์ on ์ฐจ๋จ(๊ณต์ฉ ์ปดํจํธ๋ก ๋ชป ์). on ์ ssh ๋ฅผ ์ง์ spawn(argv)ํด cmd ์ $/$(...) ๊ฐ ๋ก์ปฌ์ด ์๋๋ผ ์๊ฒฉ ์
ธ์์ ์ ๊ฐ๋จ. specs ๋ ํธ์คํธ๋ณ ์ฝ์ด/๋ฉ๋ชจ๋ฆฌ/GPU ๋ฅผ ssh ํ๋ก๋ธํด ๋ก์คํฐ์ ์บ์(listยทstatus ์ ใ12c ยท 30G ยท GPU:โฆใ ์ธ๋ผ์ธ ํ๊ธฐ) โ ์ ํ ํธ์คํธ๋ ํ๋ก๋ธํ์ง ์์ |
์๊ฒฉ ์คํ ยท ์์ ํ์ธ ์ |
cd your-repo
git submodule add https://github.com/dancinlab/harness .harness-engine
# ๋๋ ๊ทธ๋ฅ clone / vendor ํด๋ ๋จbash .harness-engine/bin/harness init --hooks์ด ํ ์ค์ด ๋ง๋ ๋ค (๊ธฐ์กด ํ์ผ์ ๋ณด์กด, --force ๋ง ์์ธ ยท --dry-run ์ผ๋ก ๋ฏธ๋ฆฌ๋ณด๊ธฐ):
โ harness.config.json ํ๋ก์ ํธ๋ช
์๋๊ฐ์ง
โ .harness/enforcement.json ๋ฒ๋ค ๊ธฐ๋ณธ ๊ท์น ๋ณต์ฌ (repo ๊ฐ ์์ )
โ .harness/keywords.json
โ .harness/severity-map.json
โ .gitignore ๋ก๊ทธ/handoff ๋ฌด์ ์ถ๊ฐ
โ scripts/harness ์์ ๋ํผ
โ .claude/settings.json hook ๋ฐฐ์ (--hooks ์ผ ๋)
์์ฑ ํ harness.config.json ์ verify.checks ยท lockdown.files ๋ง repo ์ ๋ง๊ฒ ์ฑ์ฐ๋ฉด ๋๋ค.
์๋ ์ค์ ๋ ๊ฐ๋ฅ:
.harness/*.json์ ๋์ง ์์ผ๋ฉด ๋ฒ๋ค ๊ธฐ๋ณธ ๊ท์น(config/*.json)์ด ์๋ ์ ์ฉ๋๋ค.์ ๊ฑฐ:
harness uninstall(์ฃผ์ ๋ฌผ๋ง ์ ๊ฑฐ, ์ฌ์ฉ์ ์ฝํ ์ธ ๋ณด์กด ยท--dry-run๋ฏธ๋ฆฌ๋ณด๊ธฐ). ์์ธ docs/install.md.
bash .harness-engine/bin/harness audit
bash .harness-engine/bin/harness ci list.claude/settings.json:
ํ๊ฒฝ๋ณ์ ์ด๋ฆ(
CLAUDE_TOOL_INPUT๋ฑ)์ ๋ฐํ์ ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์๋ค. ํ๋ค์ค๋CLAUDE_TOOL_INPUT์CODEX_TOOL_INPUT๋ ๋ค ์ฝ๋๋ค. JSON ํ์:{"command":"...","file_path":"...","content":"..."}.
๐ก
harness install-hooks [--global|--repo]๋ hook ๋ฐฐ์ ๊ณผ ํจ๊ปsettings.json์env์CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1๋ ์ธํ ํ๋ค โ ๋ฐฑ๊ทธ๋ผ์ด๋ subagent ์SendMessage(agent-teams)๋ฅผ ๊ธฐ๋ณธ ํ์ฑํ. ์ด๋ฏธ ๊ทธ ํค๊ฐ ์์ผ๋ฉด ์ฌ์ฉ์ ๊ฐ์ ๋ณด์กดํ๋ค(๋ฎ์ด์ฐ์ง ์์). ๋๋ ค๋ฉด ๊ทธ ํค๋ฅผ"0"์ผ๋ก ๋๋ฉด ๋๋ค.
์ฌ์ฉ์ ํ๋กฌํํธ โโถ [prompt] ํค์๋ ํธ๋ฆฌ๊ฑฐ + ํํธ ์ฃผ์
์์ด์ ํธ Bash โโถ [pre bash] โ ๋งค์นญ? โโถ block(stdout JSON) / warn(stderr) / ํต๊ณผ(์นจ๋ฌต)
โ
โผ (์คํ ํ)
[post bash <exit>] โ 0โ exit โโถ errors ํ ๋ผ์ฐํ
์์ด์ ํธ Edit โโถ [pre write] โ ๊ฒฝ๋ก/๋ด์ฉ ๊ท์น โโถ block/warn
[post edit <file>] โ L0? โโถ ๊ฒฝ๊ณ
์ปค๋ฐ ์ โโถ [lint] + [verify]
์ธ์
์ข
๋ฃ โโถ [handoff]
๋ชจ๋ ๋จ๊ณ๋ .harness/logs/*.jsonl ์ ํ ์ค์ฉ ์์ธ๋ค โ audit ์ด ์ด๋ฅผ ์ฝ์ด ๊ฑด๊ฐ๋๋ฅผ ์ ์ํํ๋ค.
- docs/languages.md โ ์ธ์ด/ํ๋ซํผ ๋ฒ์ฉ์ฑ (PythonยทRustยทCยทGoยทSwiftยทhexa ํ๋ฆฌ์ + Node ๋ฐํ์ ์๊ตฌ)
- ARCHITECTURE.json โ ํ๋ค์ค ์ํคํ
์ฒ ํธ๋ฆฌ SSOT (์ปฌ๋ผํ ๋
ธ๋: ์ด๋ฆยท์ญํ ยท๊ตฌ๋ถยท์์ธ). ์ฌ๋์ฉ ๋ทฐ์ด๋ ARCHITECTURE.html โ ๋ก์ปฌ์
python3 serve.py(์๋ฒ + ๋ธ๋ผ์ฐ์ ์๋ ์คํ), ์๊ฒฉ์ raw.githack.com / GitHub Pages - docs/install.md โ repo ํตํฉ ์์ธ (submodule / vendor / ๋ฉํฐ repo)
- docs/extending.md โ ๊ท์น ์ถ๊ฐ, ๋๋ฉ์ธ ๋ชจ๋ ํ์ฅ ํจํด
์ด repo ์์ฒด๊ฐ ํ๋ค์ค๋ฅผ ์ด๋ค(dogfooding) โ harness.config.json(profile:default) + .claude/settings.json self hooks + pre-commit bin/harness lint. ์ฝ์ด(.ts) ๋ณ๊ฒฝ ์ CHANGELOG ๋์ ๊ฐฑ์ ์ด ๊ฐ์ ๋๊ณ , ๋ฒ๋ค enforcement(root-causeยทsecretยทforce-push)๊ฐ ์๊ธฐ ์ฝ๋์๋ ์ ์ฉ๋๋ค. hardcore ์๊ธฐ๋ชจ์(main ๋ณดํธยทno-verify ์ฐจ๋จ)๋ง ๋นผ์ ๊ฐ๋ฐ ํ๋ฆ์ ๋ง์ง ์๋๋ค.
๋งค ์ฌ์ดํด(harness pr-cycle)์ doc-gate ๋ ์๋ฏธ์๋ ๋ณ๊ฒฝ์ ๋ํด CHANGELOG.md(append) + (์กด์ฌ ์) ARCHITECTURE.mdยทREADME.md ํํํ๋ฅผ ์๊ตฌํ๋ค โ ์
์ค ๋ฏธ๊ฐฑ์ ์ด ์์ผ๋ฉด ๋จธ์ง๋ฅผ ๊ฑฐ๋ถํ๋ค(--no-doc ๋ ์ง์ง ๋ฌธ์ ๋ถํ์ํ ๋๋ง). ์ด README ๋ ๊ทธ ๋์์ด๋ฏ๋ก ๋งค ์ฌ์ดํด ์ต์ ์ํ๋ก ์ ์ง๋๋ค. (commons c14)
๊ฐ์ doc-gate ๊ฐ pre-commit harness lint ์์๋ ๋ฐํํ๋ค โ pr-cycle ์ ๊ฑฐ์น์ง ์๋ ์์
์ด๋ผ๋, ์๋ฏธ์๋ ์ฝ๋ ๋ณ๊ฒฝ์ด staged ์ธ๋ฐ CHANGELOG / (์กด์ฌ ์) ARCHITECTUREยทREADME ๊ฐ ๊ฐ์ด staged ์ ๋์ผ๋ฉด commit ์ ์ฐจ๋จํ๋ค(CHANGELOG-MISSINGยทARCHITECTURE-MISSINGยทREADME-MISSING, ๋ชจ๋ block). ์ฆ "๋ชจ๋ ์์
์ดํ" ๋ฌธ์ ํํํ๊ฐ ๊ฐ์ ๋๋ค. ์ง์ง ๋ฌธ์ ๋ถํ์ํ ๋ณ๊ฒฝ๋ง git commit --no-verify.
MIT
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [{ "type": "command", "command": "CLAUDE_TOOL_INPUT=\"$CLAUDE_TOOL_INPUT\" bash .harness-engine/bin/harness pre bash" }] }, { "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "CLAUDE_TOOL_INPUT=\"$CLAUDE_TOOL_INPUT\" bash .harness-engine/bin/harness pre write" }] } ], "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "bash .harness-engine/bin/harness post edit \"$CLAUDE_FILE_PATH\"" }] } ], "UserPromptSubmit": [ { "hooks": [{ "type": "command", "command": "bash .harness-engine/bin/harness prompt \"$CLAUDE_USER_PROMPT\"" }] } ] } }