이 노트에 대하여
이 노트는 힣 하네스의 코드 작업면(forge-config 레이어)을 정의한다. 봇멘트가 정원 댓글이라는 공통면을 만들었다면, 포지 레이어는 이슈·PR·라벨·CI를 공통면으로 만든다. 핵심은 공장식 병렬 코딩이 아니라 공유 컨텍스트의 지속성 — repo 경계가 작업 경계가 아니라는 것.
히스토리
- @pi@oracle [claude-opus-4-7] — 세션 매듭: GitHub PAT 분리(GITHUB_PERSONAL_TOKEN / GITHUB_WORK_TOKEN, 의도된 GITHUB_TOKEN unset). agent-config thin pointer 박힘(별도 세션 결과 회수). 두 NEXT.md 정렬 — nixos-config / forge-config 정합. 오늘 8 commits, 봇로그 SSOT 9줄 — 다음 세션은 denotecli read 20260527T073823 한 줄로 시작.
- @pi@oracle [claude-opus-4-7] — work forge(=alskdjf) 가동 검증. glg-bot 토큰 두 자리에 이중화: pass api/forge/work/glg-bot + ~/.env.local FORGE_WORK_*. forge path prefix 구조. v15.0.2 / glg-bot 응답 OK. bin/forge multi-host 지원(—host work)은 다음 spike.
- @glg-bot@openclaw [claude-opus-4-7] — 공개 가동 확인. OpenClaw 컨테이너에서 forge.junghanacs.com 외부 가시(HTTP/2 200, Caddy via, Forgejo 15.0.2). glg-bot/sandbox + glg-bot/forge-config 두 repo 노출. forge-config README/AGENTS/NEXT 가 일차 리뷰의 세 권고를 그대로 흡수 — 라벨 v1 5개, 단일 glg-bot + footer 서명, Spike 0 봇멘트 fork. agent-config #13 일차 정리가 가동 단계에 깨끗하게 흡수됨. 댓글의 코드 버전이 실제로 섰다 — 봇멘트 부모 패턴의 자식 표면 분화 완료. 다음: bin/forge 동사 6개 완성 + 첫 검증 repo의 .forgejo/workflows/ci.yml.
- @pi@oracle [claude-opus-4-7] — 매듭 갱신: OpenClaw verboseDefault on→full, glg-bot/forge-config (Forgejo 운영면) 박힘 — GitHub junghan0611/forge-config의 짝. 두 NEXT.md 진척 반영 commit (forge-config 382a17b, nixos-config 5ee3c03). alskdjf 인프라는 힣이 직접 구축 중.
- @pi@oracle [claude-opus-4-7] — 매듭: gpt-5.5 담당자 두 사이클 commit/push 박음 (forge-config 1bd7adf). 자기 문서 수정 루프 검증 완료 — 작업면 이관 첫 단계.
- @glg-bot@oracle [gpt-5.5] — Turn 2: AGENTS.md 운영 사실 반영(라벨 /forge cmd/footer/secret), bin/forge label-add 검증(sandbox#2 agent:running→done), 자기 문서 수정 루프 두 번째 사이클.
- @glg-bot@oracle [gpt-5.5] — forge-config 담당자 첫 사이클 완료: NEXT.md 결정 6항목 업데이트, bin/forge minimal 4-command(sh+curl+jq) 박힘, sandbox#1 state/list/comment round-trip 검증.
- @pi@oracle — 인프라 가동 + 첫 round-trip 검증. Forgejo 15.0.2 + postgres 16 on Oracle (forge.junghanacs.com), Caddy + Let’s Encrypt 30초 발급. forge-config 공개 repo 분리 (junghan0611/forge-config), nixos-config는 인프라만. glg-bot user + token (~/.env.local + pass), 라벨 5개 + 이슈 #1 + 봇 footer 코멘트 검증 완료. 함정 3개 박제: INSTALL_LOCK env 함정, write:user scope 누락, Caddy bind mount inode caching.
- 생성 — 이슈 #13 일차 정리 후 시리즈 문서로 박제. 봇멘트의 다음 시리즈.
왜 포지 레이어인가
GitHub는 셀프호스트의 결여로 두 가지 비용이 생긴다.
- Latency 비용. webhook → runner spin-up → 첫 step 까지 분 단위. 에이전트 사용자 경험 측면에서 이 지연은 “흐름이 끊긴다”는 뜻이다.
- 주체성 비용. GitHub Copilot Workspace, Sweep, Devin은 “내가 모르는 모델이 내 코드에 코멘트한다.” 힣 하네스는 이 구조를 거부한다 — 내 에이전트가 리뷰하고 구현한다.
힣 하네스는 이미 셀프호스트 정체성을 갖는다 — Doomemacs / NixOS / Denote / 디지털 가든 / remark42(봇멘트). 코드면도 같은 자리에 와야 한다.
봇멘트와의 연속
봇멘트는 정원 페이지에 댓글을 남기는 패턴이다.
봇멘트:
garden page comment
-> 힣 agent reply
-> durable marginal note on public knowledge surface
포지 레이어 (forge-config):
issue / PR / label / CI / review comment
-> 힣 agent interpretation and action
-> durable work trace on code surface두 시스템은 같은 부모 패턴 에서 자란다. 한 쪽은 정원, 한 쪽은 코드. 둘 다 공유 작업면이고, 둘 다 비동기 코멘트로 움직인다.
비-목표: 공장 모델 거부
흔히 빠지는 함정.
repository
-> spawn 30 agents
-> split worktrees
-> implement in parallel
-> merge queue
-> industrialized coding factory이건 힣의 의도가 아니다. 의도는:
shared harness memory + current work surface
-> 힣 agent reads situation across repos and logs
-> agent claims or assists a task
-> agent implements/reviews/tests with context
-> forge records the trace through issue/PR/CI/comment/label
-> other 힣 agents can later recover the state and continue핵심 가치는 에이전트 수가 아니라 연속성과 공유 컨텍스트. repo는 작업의 진입점이지 컨텍스트 경계가 아니다.
아키텍처 — 역할 분리
Forgejo
- Git origin (셀프호스트)
- issue / PR / label / comment surface
- webhook/API source
- GitHub mirror source
CI runner
- deterministic verification
- test / lint / build / artifact
- isolated from agent identity
agent bus
- receives webhook events
- normalizes issue/PR/comment/label/CI state
- routes tasks to appropriate 힣 agents
- records job state
- writes labels/comments back to forge
agent worker
- reads issue + repo + AGENTS.md + wider harness memory
- creates branch/worktree only when needed
- calls pi-shell-acp / Claude Code / Codex / Gemini CLI / OpenClaw agents
- pushes branch, opens PR, comments summary규칙:
- CI = verifier (재현 가능한 검증)
- agent worker = implementer/reviewer (컨텍스트 있는 작업)
- Forgejo = durable work surface (사라지지 않는 상태면)
- memory = wider than any single repo (semantic-memory / botlog / notes)
라벨 프로토콜 — v1은 5개로
초기 이슈 #13 초안은 20개 라벨을 제안했지만, 운영 안 되면 라벨 묘지가 된다. v1 권장은 5개.
| 라벨 | 의미 |
|---|---|
agent:ready | 에이전트가 잡아도 됨 |
agent:running | 잡힘 — 작업 중 |
agent:done | 완료 |
human:needs-review | 사람 판단 필요 |
ci:failed | CI 깨짐 |
봇멘트도 처음엔 read/reply 두 동작뿐이었다. 운영하면서 부족하면 추가한다.
에이전트 식별 — 단일 glg-bot + footer 서명
여러 에이전트가 forge에 코멘트할 때, 각자 Forgejo 사용자를 따로 만들면 토큰/권한 관리가 폭발한다.
v1 권장:
- 단일
glg-botForgejo 사용자 (forge user) - 코멘트 본문 마지막에 footer 서명:
— glg-bot [claude-opus-4-7 / openclaw]— glg-bot [claude-code / nuc]— glg-bot [pi-codex / oracle]
봇멘트와 일관 (힣봇 단일 신원). 미래에 신원 분리가 필요하면 footer → user로 승격하면 된다.
OpenClaw 힣봇 접근성
forge-config 스킬은 HTTP curl 레시피집 형태로 가야 한다 — 명령어 모음이 아니라.
이유: OpenClaw 힣봇 컨테이너는 ~/org, ~/repos/gh read-only 접근만 있고 shell exec이 제한적이다. forge API를 직접 호출하려면 토큰 + curl + jq 가 가용 도구의 전부다.
봇멘트 스킬 패턴 그대로:
forge list-open # 열린 이슈 목록
forge comment <issue-id> <body> # 코멘트 작성
forge label-add <issue-id> <label> # 라벨 부착
forge state <issue-id> # 현재 상태 + 최근 코멘트각 명령은 얇은 curl wrapper. 환경변수 FORGE_URL, FORGE_TOKEN 만 있으면 동작.
진행 — 이슈 #13 일차 정리
agent-config #13 에 일차 설계가 정리됐다. 7개 spike:
- Spike 1: Forgejo base — 외부 서버 배포, Caddy/HTTPS/PostgreSQL, latency 검증
- Spike 2: GitHub mirror — Forgejo origin, GitHub push mirror
- Spike 3: Actions runner — Forgejo Runner,
./scripts/ciconvention - Spike 4: Label/comment protocol — 라벨, 이슈 템플릿,
/agent명령 - Spike 5: Agent bus MVP — webhook 수신, 라벨 전이, dummy 코멘트
- Spike 6: Agent worker MVP — clone/worktree, 브랜치/PR/코멘트
- Spike 7: OpenClaw 힣봇 skill 통합 —
skills/forge/SKILL.md
Spike 0 — 봇멘트 코드 fork
이슈 #13에 없지만 OpenClaw 측에서 제안한 추가 spike: 봇멘트 스킬을 fork → API endpoint만 Forgejo로 swap.
봇멘트의 list/reply/label 코어 로직은 거의 그대로 옮겨진다. 새 design부터 시작하지 말고 기존 코드 변형 으로 시작하면 첫 protocol round-trip이 며칠 안에 나온다. “botment는 forge-config의 부모 패턴”을 코드와 문서에 명시.
보안과 권한
- 어떤 에이전트도
main에 직접 push하지 않는다. - 모든 구현은 branch + PR을 거친다.
- CI runner와 agent worker는 분리된다. CI는 신뢰 불가 코드를 실행, agent worker는 신뢰된 하네스 로직을 실행.
- Fork/public PR에는 특권 시크릿을 주지 않는다.
- 에이전트 토큰은 역할별 scope.
- Merge는 첫 버전에선 사람 게이트. 자동 merge는 v1 non-goal.
요약
중요한 점은 Git을 셀프호스트한다는 것이 아니다.
중요한 점은 forge가 지속적이고, 낮은 지연을 갖고, 에이전트에 보이는 작업면 이 되어, 힣 에이전트들이 컨텍스트를 회수하고, 이슈 PR 코멘트/라벨로 조율하고, 검증을 실행하고, 미래 에이전트가 이해할 수 있는 자취를 남기는 자리가 되는 것이다.
이건 하네스 메모리/워크플로 레이어이지, 코딩 공장이 아니다.
관련 노트
- 봇멘트: 힣의 분신과 댓글로 소통하라 — 부모 패턴. 정원 댓글면.
- 하네스 엔지니어링: 돌도끼에서 인공지능까지 — forge/도구 은유의 상위 시리즈.
- agent-config 이슈 #13: https://github.com/junghan0611/agent-config/issues/13
agent-config 쪽 구상안 — skills/forge fork from botment
자리 정렬: 인프라/배치(docker caddy + Forgejo 컨테이너)는 nixos-config 담당자. agent-config 쪽 본질은 에이전트가 forge 위에서 일하는 손 — 즉 skills/forge/. GLG가 이 자리에 우리를 부른 이유.
botment 코드를 손으로 한 번 읽고 박는 mental model
skills/botment/scripts/botment.sh (277라인) 정독 결과: fork → endpoint swap이 진짜 거리.
botment → forge 동사 매핑:
| botment (remark42) | forge (Forgejo API) | 비고 |
|---|---|---|
unread | unread | mention/assigned 미응답 |
list | list-issues / list-prs | |
read <url> | read <#> | thread 평탄화로 더 단순 |
reply <bot> <cid> <url> | comment <#> | parent thread 없음 |
comment (독립) | issue-create <repo> | |
| (없음) | label-add / label-remove | v1 5개 |
| (없음) | pr-create | merge는 human-gated |
| Dev auth 3-step OAuth | Authorization: token <T> | 인증 ~50줄 → 1줄 |
코드 추정: botment 277라인 → forge 200라인 이하. OAuth 댄스가 빠진다.
폴더 골격 — botment 동형
skills/forge/
├── SKILL.md (LSP 패턴: description + API 테이블 + 노트, <100 라인)
└── scripts/
└── forge.sh (단일 스크립트, 동사 6개)skills/botment 옆에 skills/forge. 디렉토리 동형이 부모/자식 명명을 영구화한다. 미래 에이전트가 폴더만 봐도 “댓글의 코드 버전” 즉시 이해.
동사 6개 (v1)
unread— 라벨agent:ready또는 mention 있는 미응답list— 열린 이슈/PR 목록read <#>— 이슈/PR 본문 + 코멘트 + 라벨 + CI 상태comment <#> <text>— 코멘트 작성 (footer 서명 자동)label add <#> <label>/label removeissue create <repo> <title> <body>
PR 동사(pr create, pr merge)는 v2. merge는 첫 버전 사람 게이트.
단일 glg-bot + footer 서명 — 1KB 정체성 일관성
- Forgejo 사용자:
glg-bot하나 - 토큰:
~/.env.local의FORGE_TOKEN— 모든 백엔드(OpenClaw/pi/Claude Code) 공유 - footer 서명: 본문 끝에
— glg-claude [claude-sonnet-4-6]/— glg-codex [gpt-5.5]/— glg-gemini [gemini-2.5-pro] - 분신이 형제이지 부속품이 아니다 (agent-config/AGENTS.md 첫 자리) — 신원은 하나, 모델은 footer로 식별
환경 변수와 closed-loop 정책 — botment에서 그대로 차용
FORGE_BASE_URL(e.g.https://forge.junghanacs.com) — caddy 뜨면 결정FORGE_TOKEN—~/.env.local- closed-loop write: botment처럼 외부 read OK / write는 oracle 내부에서만 또는 token이 있으면 어디서든 write — 결정 필요
Spike 0 — 오전 작업 단계
skills/forge/scripts/forge.sh— botment.sh를 복사하여 endpoint/ 인증 부분만 Forgejo로 교체SKILL.md— botment SKILL.md 구조 그대로 차용, 동사 표만 갈아끼움- caddy + Forgejo 떠 있으면: 첫 round-trip 실험 — glg-bot으로 “Hello forge” 이슈 생성 + 코멘트 + 라벨 부착
- 검증되면 첫 검증 대상 repo(
denotecli후보)에./scripts/ci+.forgejo/workflows/ci.yml미리 깔기 (Spike 3 준비)
대기 결정 (caddy 뜨기 전)
-
FORGE_BASE_URL도메인:forge.junghanacs.com/git.junghanacs.com - closed-loop write 여부 (oracle 내부 한정 vs 토큰 인증만)
- 첫 검증 repo:
denotecli/forge-config자체
forge-config repo 정신 보호 — 다음 자리
이슈 #13 본문은 영문 spec체로 정리돼 있다. forge-config 새 repo 만들 때 AGENTS.md 첫 자리에 한글로 “이 집은 무엇인가” 박는 것 — 본문이 spec으로 읽히지 않게 하는 보호 장치. agent-config/AGENTS.md “담당자의 자리” 섹션 패턴 차용.
repo 후보 URL: https://github.com/junghan0611/forge-config (아직 생성 전 — 이름 박아둠)
첫 round-trip — 인프라부터 봇 코멘트까지
인프라 가동 (Oracle)
forge.junghanacs.com— Forgejo 15.0.2 LTS- DB:
postgres:16-alpine별도 인스턴스 (forge-internalbridge 격리) - Caddy 리버스 프록시 + Let’s Encrypt 자동 (ACME http-01 30초 발급)
- SSH 비활성 (HTTPS git만),
DISABLE_REGISTRATION=true닫힌 계
설계 결정 두 가지:
- DB는 PostgreSQL부터 — SQLite로 시작 추천했으나 멀티 에이전트 동시 write contention 회피 위해 처음부터 postgres. umami-db와 같은 패턴, 별도 인스턴스 격리. 리소스 ~150MB 추가는 작은 비용.
- Forgejo 15 LTS — 13은 이미 EOL, 15가 현재 stable.
codeberg.org/forgejo/forgejo:15.
책임 경계 — repo 셋
| Layer | 위치 | 책임 |
|---|---|---|
| 인프라 | nixos-config/docker/forge/ | Docker compose, Caddy, host-specific |
| 운영 ownership | forge-config repo (신규 공개) | 라벨/footer/봇 행동 규약 + bin/forge + agent skill SSOT |
| agent surface | agent-config/skills/forge/ | thin pointer (앞으로) |
forge-config README/AGENTS/NEXT 박힘. 코드는 같이 논의 후 — 봇멘트가 부모 패턴이라 fork-then-swap이 첫 spike.
운영 함정 — 세 개 박제
INSTALL_LOCK=false env 함정
처음에 docker-compose.yml 에 FORGEJO__security__INSTALL_LOCK=false 박았다 — setup wizard 첫 진입을 위해. 그런데:
- wizard 통과 →
app.ini에INSTALL_LOCK = true박힘 - 다음 부팅 → env가 매번
false로 덮어씀 - Forgejo: “이미 설치됐는데 lock=false 모순” → fatal → 무한 재시작 (crash loop)
해결: env에서 그 줄 제거. wizard가 자동 박는 값을 env로 덮지 말 것.
→ 일반 원칙: setup wizard가 자동으로 박는 값은 env에 박지 않는다. SECRET_KEY, INTERNAL_TOKEN, JWT_SECRET 도 같은 원칙.
Token scope — Forgejo가 GitHub와 다른 점
첫 토큰 발급 시 write:issue, write:repository, read:user, write:organization 만 체크. 검증 시도:
POST /user/repos
→ 403: token does not have at least one of required scope(s): [write:user]GitHub은 user repo 생성에 repo scope만 필요. Forgejo는 write:user 별도 요구. 재발급 후 통과.
→ forge-config/AGENTS.md token 발급 절차에 필수 5개 scope 명시: write:user, write:repository, write:issue, write:organization, read:user.
단일 파일 bind mount inode caching (Caddy)
Caddyfile 수정 후 docker exec caddy caddy reload 했는데 새 forge 도메인 인식 안 됨. 원인: ./Caddyfile:/etc/caddy/Caddyfile 같은 파일 단위 bind mount는 컨테이너가 처음 mount된 inode를 캐싱. Edit=/=sed -i 가 atomic rename으로 새 inode 만들면 컨테이너는 옛 inode 계속 가리킴.
# host file: inode 2623087, size 934, mtime 2026-05-27
# container view: inode 2624603, size 709, mtime 2026-05-17 ← 옛 inode!해결: docker compose restart caddy. reload만으로는 부족. 또는 디렉토리 단위 bind mount.
Round-trip 검증 — glg-bot/sandbox
검증용 repo 생성 후 5단계 protocol round-trip:
| 단계 | 결과 |
|---|---|
1. POST /user/repos | glg-bot/sandbox |
2. POST /repos/.../labels × 5 | agent:ready/running/done, human:needs-review, ci:failed |
3. GET /repos/.../labels | 5개 확인 |
4. POST /repos/.../issues (with labels) | 이슈 #1 — “Spike 0 — 봇멘트 fork → forge API endpoint swap” |
5. POST /issues/1/comments (봇 footer) | — glg-bot [claude-opus-4-7 / oracle] |
6. GET /repos/.../issues/1 | open, agent:ready, 1 comment |
라이브: https://forge.junghanacs.com/glg-bot/sandbox/issues/1
토큰 백업 이중화
| 자리 | 용도 |
|---|---|
~/.env.local (600) | 쉘/ 에이전트 운영용 — source ~/.env.local 후 $FORGE_TOKEN |
pass api/forge/junghanacs/glg-bot | 백업 + 메타데이터 (scopes/created/note). pass git 자동 commit |
봇멘트의 anonymous→Dev auth 진화 패턴과 같다 — 검증부터 시작해서 진화.
다음 — 담당자 호출 + 자기 문서 수정 루프
다음 단계는 forge-config 담당자 entwurf 호출. 봇멘트 패턴 그대로:
- 봇멘트 스킬을 fork →
forge-config/bin/forge로 변형 list-open / comment / label-add / state4개 명령 minimal- 담당자가 자기
NEXT.md를 자기 손으로 업데이트하는 루프 검증 - 정식 운영 표면 repo의 ownership 결정 (
glg-bot/sandbox는 검증용 임시)
중심은 봇로그가 세워져야 하고, 흔들리지 않게. 모든 진화는 이 노트에서 출발하고 결산한다. forge-config repo는 위성이고 코드 자리. 의미와 history의 SSOT는 이 봇로그.
Comments