이 노트에 대하여

이 노트는 힣 하네스의 코드 작업면(forge-config 레이어)을 정의한다. 봇멘트가 정원 댓글이라는 공통면을 만들었다면, 포지 레이어는 이슈·PR·라벨·CI를 공통면으로 만든다. 핵심은 공장식 병렬 코딩이 아니라 공유 컨텍스트의 지속성 — repo 경계가 작업 경계가 아니라는 것.

히스토리

  • [2026-05-27 Wed 11:55] @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 한 줄로 시작.
  • [2026-05-27 Wed 11:37] @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.
  • [2026-05-27 Wed 11:12] @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.
  • [2026-05-27 Wed 10:52] @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 인프라는 힣이 직접 구축 중.
  • [2026-05-27 Wed 10:34] @pi@oracle [claude-opus-4-7] — 매듭: gpt-5.5 담당자 두 사이클 commit/push 박음 (forge-config 1bd7adf). 자기 문서 수정 루프 검증 완료 — 작업면 이관 첫 단계.
  • [2026-05-27 Wed 10:32] @glg-bot@oracle [gpt-5.5] — Turn 2: AGENTS.md 운영 사실 반영(라벨 /forge cmd/footer/secret), bin/forge label-add 검증(sandbox#2 agent:running→done), 자기 문서 수정 루프 두 번째 사이클.
  • [2026-05-27 Wed 10:29] @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 검증.
  • [2026-05-27 Wed 10:20] @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.
  • [2026-05-27 Wed 07:38] 생성 — 이슈 #13 일차 정리 후 시리즈 문서로 박제. 봇멘트의 다음 시리즈.

왜 포지 레이어인가

GitHub는 셀프호스트의 결여로 두 가지 비용이 생긴다.

  1. Latency 비용. webhook → runner spin-up → 첫 step 까지 분 단위. 에이전트 사용자 경험 측면에서 이 지연은 “흐름이 끊긴다”는 뜻이다.
  2. 주체성 비용. 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:failedCI 깨짐

봇멘트도 처음엔 read/reply 두 동작뿐이었다. 운영하면서 부족하면 추가한다.

여러 에이전트가 forge에 코멘트할 때, 각자 Forgejo 사용자를 따로 만들면 토큰/권한 관리가 폭발한다.

v1 권장:

  • 단일 glg-bot Forgejo 사용자 (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:

  1. Spike 1: Forgejo base — 외부 서버 배포, Caddy/HTTPS/PostgreSQL, latency 검증
  2. Spike 2: GitHub mirror — Forgejo origin, GitHub push mirror
  3. Spike 3: Actions runner — Forgejo Runner, ./scripts/ci convention
  4. Spike 4: Label/comment protocol — 라벨, 이슈 템플릿, /agent 명령
  5. Spike 5: Agent bus MVP — webhook 수신, 라벨 전이, dummy 코멘트
  6. Spike 6: Agent worker MVP — clone/worktree, 브랜치/PR/코멘트
  7. 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 코멘트/라벨로 조율하고, 검증을 실행하고, 미래 에이전트가 이해할 수 있는 자취를 남기는 자리가 되는 것이다.

이건 하네스 메모리/워크플로 레이어이지, 코딩 공장이 아니다.

관련 노트

[2026-05-27 Wed] 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)비고
unreadunreadmention/assigned 미응답
listlist-issues / list-prs
read <url>read <#>thread 평탄화로 더 단순
reply <bot> <cid> <url>comment <#>parent thread 없음
comment (독립)issue-create <repo>
(없음)label-add / label-removev1 5개
(없음)pr-createmerge는 human-gated
Dev auth 3-step OAuthAuthorization: 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)

  1. unread — 라벨 agent:ready 또는 mention 있는 미응답
  2. list — 열린 이슈/PR 목록
  3. read <#> — 이슈/PR 본문 + 코멘트 + 라벨 + CI 상태
  4. comment <#> <text> — 코멘트 작성 (footer 서명 자동)
  5. label add <#> <label> / label remove
  6. issue create <repo> <title> <body>

PR 동사(pr create, pr merge)는 v2. merge는 첫 버전 사람 게이트.

  • Forgejo 사용자: glg-bot 하나
  • 토큰: ~/.env.localFORGE_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 — 오전 작업 단계

  1. skills/forge/scripts/forge.sh — botment.sh를 복사하여 endpoint/ 인증 부분만 Forgejo로 교체
  2. SKILL.md — botment SKILL.md 구조 그대로 차용, 동사 표만 갈아끼움
  3. caddy + Forgejo 떠 있으면: 첫 round-trip 실험 — glg-bot으로 “Hello forge” 이슈 생성 + 코멘트 + 라벨 부착
  4. 검증되면 첫 검증 대상 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 (아직 생성 전 — 이름 박아둠)

[2026-05-27 Wed] 첫 round-trip — 인프라부터 봇 코멘트까지

[2026-05-27 Wed 10:20]

인프라 가동 (Oracle)

  • forge.junghanacs.com — Forgejo 15.0.2 LTS
  • DB: postgres:16-alpine 별도 인스턴스 (forge-internal bridge 격리)
  • 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
운영 ownershipforge-config repo (신규 공개)라벨/footer/봇 행동 규약 + bin/forge + agent skill SSOT
agent surfaceagent-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 첫 진입을 위해. 그런데:

  1. wizard 통과 → app.ini 에 INSTALL_LOCK = true 박힘
  2. 다음 부팅 → env가 매번 false 로 덮어씀
  3. 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/reposglg-bot/sandbox
2. POST /repos/.../labels × 5agent:ready/running/done, human:needs-review, ci:failed
3. GET /repos/.../labels5개 확인
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/1open, 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 호출. 봇멘트 패턴 그대로:

  1. 봇멘트 스킬을 fork → forge-config/bin/forge 로 변형
  2. list-open / comment / label-add / state 4개 명령 minimal
  3. 담당자가 자기 NEXT.md 를 자기 손으로 업데이트하는 루프 검증
  4. 정식 운영 표면 repo의 ownership 결정 (glg-bot/sandbox 는 검증용 임시)

중심은 봇로그가 세워져야 하고, 흔들리지 않게. 모든 진화는 이 노트에서 출발하고 결산한다. forge-config repo는 위성이고 코드 자리. 의미와 history의 SSOT는 이 봇로그.