Archyl Harness: 시작하기 전에 자기 작업을 선언하는 코딩 에이전트
지난주 저는 세 에이전트, 세 개의 pull request, 그리고 하나의 일관성 없는 시스템에 대해 썼습니다. 그 글은 연습 문제로 끝났습니다. 당신 팀이 에이전트가 작성한 pull request를 두 개 이상 병합한 가장 최근 한 주를 골라, 나란히 놓고 읽고, 지금의 구성에서 그것들이 서로 어긋난다는 사실을 무엇이 알려 주었을지 물어보라는 것이었습니다.
저는 우리 저장소에 대해 그걸 해 봤고, 답은 아무것도 없다였습니다. "리뷰어가 결국 알아챘다"도 아니고, "CI가 절반은 잡았다"도 아닙니다. 아무것도 없었습니다. 어떤 에이전트도 자기가 무엇을 하려는지 말한 적이 없기 때문입니다. 각자 저장소를 읽고, 코드를 쓰고, pull request를 열었습니다. 사람이 둘이 같은 서비스에서 일하고 있다는 걸 볼 수 있는 첫 순간은 리뷰였고, 그건 마지막 순간이며, 그때쯤이면 둘 다 이미 확신을 마친 상태입니다.
그래서 빠져 있던 단계를 만들었습니다. Archyl Harness가 이번 주에 나왔습니다. 이건 또 하나의 코딩 에이전트가 아닙니다. 당신이 이미 돌리고 있는 에이전트들 위에 앉아서, 각각이 무언가를 건드리기 전에 문서화된 아키텍처를 기준으로 작업 단위를 선언하게 만듭니다.
작업 세션, 안쪽에서 본 모습
루프는 네 번의 호출로 이루어지고, MCP tool로 노출됩니다. 에이전트는 계획하고, 세션을 열고, heartbeat를 보내며 작업하고, 실제로 일어난 일을 담아 세션을 닫습니다.
아래는 그중 두 번째 호출로, Archyl 프로젝트 자체에서의 실제 세션에서 잘라 온 것입니다:
▶ start_work_session(
task: "rank recalled memories by freshness so stale facts stop winning",
agentName: "claude-code/vincent")
# Harness Session
- **Session ID**: `24643fa6…`
- **Gate**: warn
- 1 target element(s) are being worked on by other active sessions — coordinate before changing them
- **Leased elements** (you are the announced worker on these):
- component `Harness Service`
- container `MCP Server`
- **Conflicts** (someone else is already working here):
- MCP Server — held by claude-code/memory-ui: render the memory graph with element clusters
**Protocol**: call `heartbeat_work_session` at least every 30 minutes while working,
and `finish_work_session` with a summary (and decisions worth recording) when done.
## Most relevant elements
- **Harness Service** (component) — `backend/internal/service/harness`
Work sessions, leases, preflight gate, element memory.
## Related decisions (respect these)
- ADR-5: Agents propose, humans merge [accepted]
## What previous sessions did here
- **MCP Server** [PITFALL] (claude-code/vincent, 1 day ago): every new ID argument
name must be added to idArgumentResolvers in authz.go, or the cross-org check
silently skips it.
그 한 번의 호출에서 네 가지 일이 일어났고, 그중 어느 것도 룰 파일이 할 수 있는 일이 아닙니다.
작업은 C4 모델을 기준으로 해소되었고, 그래서 에이전트는 아키텍처 전체가 아니라 중요한 조각을 받았습니다. 곧 바꾸게 될 요소에는 어드바이저리 리스(advisory lease)가 걸렸고, 다음 에이전트는 그것을 통해 이 에이전트의 존재를 알게 됩니다. 프리플라이트 게이트가 판정을 돌려주었습니다. 그리고 브리핑은 이 작업을 제약하는 결정들과, 여기에 마지막으로 서 있었던 에이전트가 호되게 배운 것을 함께 들고 왔습니다.
그 마지막 줄이 memory이고, 이건 이 글의 한 문단이 아니라 자기 글을 가질 자격이 있습니다. 짧게 말하면: 세션은 메모, 관례, 함정을 아키텍처 요소에 붙여 남기고, 다음 세션은 그것을 자동으로 돌려받습니다.
게이트의 판정은 셋이고, deny는 드문 쪽입니다
프리플라이트 게이트는 의도적으로 작습니다. 작업이 시작되기 전에 하나의 질문에 답하고, 에이전트가 그것을 근거로 행동할 수 있는 판정을 냅니다.
allow는 당신의 대상 요소에 대해 다른 어떤 세션도 리스를 쥐고 있지 않고, error 수준의 guardrail이 이 작업에 적용되지 않는다는 뜻입니다. 진행하십시오.
warn은 흔한 쪽이고, 이유가 함께 옵니다. 당신이 곧 바꾸려는 요소에서 다른 세션이 이미 작업 중이거나, 심각도가 error인 적합성 규칙이 이 작업을 덮고 있습니다. 첫 번째 경우의 정확한 문자열은 위에서 본 그것입니다: N target element(s) are being worked on by other active sessions — coordinate before changing them. 에이전트는 진행하지만, 나열된 모든 이유를 처리해야 하고, 그 이유들은 이름을 짚어 줍니다.
deny는 세션이 그것을 요청할 때만 일어납니다. exclusive: true를 넘기면, 리스 충돌은 세션에 경고하는 대신 세션을 멈춥니다. 그건 누구와도 경쟁해서는 안 되는 작업을 위한 플래그입니다. 스키마 마이그레이션, contract 변경, 모든 호출부를 건드리는 rename 같은 것들 말입니다. 세션은 아예 열리지 않고, 에이전트는 우회하는 대신 사용자에게 보고하라는 말을 듣습니다.
이 부분에서 정확한 것이, 게이트를 영리해 보이게 만드는 것보다 더 중요합니다. deny는 정책 엔진이 아닙니다. 당신의 계획을 읽고 원칙적으로 거부하지 않습니다. 당신이 어떤 요소를 배타적이라고 말했을 때 두 에이전트가 같은 요소를 주장하는 것을 거부할 뿐이고, 나머지는 모두 에이전트가 답해야 하는 경고입니다.
Guard는 쓰기를 지켜봅니다
세션은 의도를 다룹니다. Guard는 실제로 쓰이는 것을 다룹니다.
이건 Claude Code용 PreToolUse 훅이고, 플러그인과 함께 설치됩니다. 에이전트가 파일을 쓰거나 편집하기 전에, 훅은 편집 후의 파일을 그대로 재구성해서 당신 프로젝트의 적합성 규칙에 보내고, 판정을 읽습니다. critical 위반은 쓰기를 막고 그 이유를 에이전트에게 돌려줍니다:
Archyl Guard: this change violates the project's architecture rules for
backend/internal/adapter/http/handlers/report.go:
- [critical] No direct database access from HTTP handlers — move the query
behind a service
Adjust the change to respect these rules, or ask the user whether to override them.
에이전트는 그걸 읽고, 레이어링을 고치고, 하던 일을 계속합니다. 사람은 아무도 방해받지 않았고, 위반은 브랜치에 한 번도 닿지 않았습니다.
두 가지 설계 선택은 분명히 말해 둘 만합니다. ARCHYL_GUARD_BLOCK이 임계값을 제어합니다. 기본은 critical, 더 많이 막고 싶으면 high, 경고만 하려면 off. 그리고 훅은 어디서든 fail-open입니다. API 키가 없거나, 네트워크가 없거나, jq가 설치되어 있지 않거나, 응답이 느리면: 편집은 손대지 않은 채로 진행됩니다. 누군가의 편집 세션을 망가뜨릴 수 있는 governance 도구는 일주일이면 제거되므로, 망가뜨릴 수 없게 만들었습니다.
루프 닫기
finish_work_session은 정직한 결과를 받습니다. 요약, 기록할 만한 결정, 하지 못하고 남긴 후속 작업. 리스는 해제되고, 요약은 그 세션이 쥐고 있던 요소들에 고정되며, 작업이 아키텍처를 바꿨다면 createChangeRequest: true가 Architecture Change Request(아키텍처 변경 요청) 초안을 엽니다.
이 부분이 모델이 조용히 drift 하는 것을 막습니다. 서비스를 재구성한 에이전트가 C4 모델을 슬그머니 편집하지 않습니다. 제안을 제출하고, 문서가 어떻게 따라잡아야 하는지를 사람이 읽고, 병합은 지난주에 쓴 버전 검사를 통과합니다. 에이전트는 제안합니다. 사람이 병합합니다. 그 경계를 없앨 계획은 없습니다.
그 모든 것 위에서, Agent Hub의 Fleet console은 조직 안의 모든 세션을 실시간으로 보여 줍니다. 누가, 무엇을, 어떤 요소를 쥔 채로, 어떤 게이트 뒤에서 작업 중인지, 마지막 heartbeat가 얼마나 최근인지. 활성 리스가 걸린 요소는 C4 다이어그램 위에도 작업 중 표시가 바로 뜹니다. "여기 안에 다른 누가 있다"가 실제로 쓸모 있는 화면이 바로 거기이기 때문입니다.
그것 자신 아래에서 만들었습니다
Harness는 Harness 아래에서 일하는 에이전트들이, Archyl을 문서화하는 Archyl 프로젝트 위에서 만들었습니다.
그건 데모가 아니었습니다. 루프가 진짜 작업과의 접촉에서 살아남는지 알아낼 유일한 방법이었고, 그 과정에서 제품이 여러 번 바뀌었습니다. 세션은 진짜 충돌에서 진짜로 warn을 받았습니다. 두 에이전트가 같은 시간에 정말로 같은 container를 편집하고 있었기 때문입니다. 위 transcript에 있는 함정은 어떤 세션이 오후 하나를 통째로 날린 뒤에 쓴 memory이고, 나중 세션이 같은 파일을 건드리기 전에 브리핑에서 그것을 돌려받았습니다. 그 세션들에서 Architecture Change Request 세 건이 나왔고, 각각은 에이전트가 방금 한 일에 모델이 어떻게 따라잡아야 하는지를 사람이 검토한 것이었습니다.
도그푸딩에서만 드러나는 더 작은 수정들도 나왔습니다. 콘솔의 게이트 배지는 예전에 allow에 대해 중립적인 칩을 렌더링했는데, 모든 줄에 "아무 문제 없음"이라고 표시하는 배지는 소음이라는 지적이 나왔습니다. 이제는 판정이 이유 없는 allow일 때 아무것도 렌더링하지 않고, 그 판단의 근거는 프로젝트의 관례로 저장되었습니다. 다음에 그 component를 건드리는 에이전트가 친절하게 그걸 다시 넣어 놓지 않도록 말입니다.
설치는 명령 하나
저장소 루트에서:
curl -fsSL https://raw.githubusercontent.com/archyl-com/agent-skills/main/templates/setup.sh | bash
프로젝트와 API 키를 물어본 다음 세 가지를 씁니다. ?profile=coding이 붙은 Archyl의 MCP 서버를 가리키는 .mcp.json, 저장소를 프로젝트에 묶는 커밋 가능한 .archyl.json(키는 당신의 환경에 남습니다), 그리고 CLAUDE.md와 AGENTS.md 뒤에 덧붙는 harness 루프:
# Architecture — Archyl Harness
This project's architecture is documented in Archyl. Work under the harness loop:
1. For any non-trivial task, call `plan_work` first — it returns an implementation
plan grounded in the documented architecture.
2. BEFORE changing code, call `start_work_session` (task + your agent name).
Read the briefing: gate verdict, leased elements, conflicts, decisions, guardrails.
...
그다음 Claude Code에서 /plugin marketplace add archyl-com/agent-skills와 /plugin install archyl-developer@archyl-marketplace를 실행하면 archyl-harness 스킬과 Guard 훅이 들어옵니다. 플러그인 버전 0.7.0이 나와 있습니다.
?profile=coding은 나머지를 작동하게 만드는 작은 디테일입니다. Archyl의 MCP 서버는 189개의 tool을 노출하는데, 그건 아키텍처를 관리하기에는 맞는 숫자이고 rate limiting을 추가하려는 에이전트 앞에 놓기에는 틀린 숫자입니다. coding 프로필이 광고하는 것은 16개입니다. 오리엔테이션, 작업 범위의 컨텍스트, 네 개의 세션 tool, memory, 그리고 적합성과 diff 검사. 모델을 직접 편집하는 것은 하나도 없습니다. 그 경로는 Change Request를 통하기 때문입니다. 우리 테스트에서, 전체 카탈로그를 건네받은 에이전트는 카탈로그를 탐색합니다. 열여섯 개의 tool을 건네받은 에이전트는 루프를 따릅니다.
이것이 하지 않는 일
리스는 어드바이저리입니다. 아무것도 잠그지 않습니다. 리스는 두 번째 에이전트에게 첫 번째가 거기 있다고 알려 줍니다. 브리핑에서, 콘솔에서, 다이어그램 위에서. 막지는 않습니다. 지금은 의도적입니다. 당신 아키텍처의 모델에 대한 강한 락은, 에이전트가 세션 중간에 죽었을 때 팀의 작업을 멈추게 하는 아주 효과적인 방법이기 때문입니다. 다만 팀에게 리스를 상호 배제라고 설명해서는 안 됩니다.
세션을 한 번도 열지 않는 에이전트는 보이지 않습니다. 여기의 모든 보장은 에이전트가 start_work_session을 호출하는 데서 시작합니다. 프로토콜에는 그 호출을 강제하는 것이 없습니다. 스킬과 CLAUDE.md 스니펫이 그것을 기본 동작으로 만들 뿐입니다. 작정한 에이전트나 harness 스킬 없이 연결된 에이전트는 늘 하던 대로 코드를 씁니다. 협조 없이 발동하는 유일한 부분이 Guard 훅이고, 그것도 Claude Code 안에서만입니다.
deny는 당신이 쓴 것만큼만 좋습니다. 게이트는 당신의 적합성 규칙과 리스를 읽습니다. 비어 있는 규칙 집합과 에이전트 하나는 영원히 allow를 만들어 냅니다. 기술적으로는 맞고, 정보로서는 완전히 무의미합니다.
계획은 근거를 갖지만, 옳지는 않습니다. plan_work는 당신의 C4 모델, ADR, guardrail로부터 만들어진 AI 계획이고, AI 제공자가 설정되어 있지 않거나 모델이 쓸 수 없는 것을 돌려줄 때는 정렬된 기본 사실을 반환하는 결정적 폴백이 붙어 있습니다. 문서화된 아키텍처를 존중합니다. 문서화된 아키텍처가 좋은 생각인지는 모릅니다.
Change Request에는 알려진 작성자가 필요합니다. 사용자에 묶여 있지 않은 자격 증명으로 시작된 세션은 하나도 열 수 없고, finish_work_session은 실패하는 대신 응답에서 그렇게 말합니다. CI 봇의 키가 조직 범위라면, 그 결과는 memory로는 남지만 제안으로는 남지 않습니다.
어디서 시작할까
이미 문서화된 Archyl 프로젝트를 대상으로 에이전트를 돌리고 있다면, 위의 설치 명령은 5분쯤 걸리고 첫 세션이 당신에게 무언가를 말해 줄 겁니다. 에이전트 둘이 돌아가는 오후에 Fleet console을 지켜보십시오. 흥미로운 순간은 첫 warn입니다. 예전에는 리뷰 전까지 보이지 않던 충돌에 이름을 붙여 주기 때문입니다.
아직 문서화된 아키텍처가 없다면, 그게 진짜 전제 조건이고, 늘 같은 조건입니다. Harness는 모델을 사용해 중재하므로, 빈 모델은 아무것도 중재하지 않습니다.
Harness는 archyl의 일부입니다: 작업 세션, 프리플라이트 게이트, Fleet console, 그리고 memory. 플러그인, 스킬, Guard 훅과 GitHub Actions는 오픈 소스입니다. 전체 설치 방법은 Harness 가이드에 있습니다. 함께 읽기: 여러 에이전트, 하나의 아키텍처, 왜 당신의 에이전트에게는 룰 파일이 있고 모델은 없는가, 그리고 그 뒤에 있는 MCP 서버.