elanous 는 어떻게 도나
· 2026-09-22 · ⛔ 이 문서는 「수」를 박지 않는다. 각 칸에 «재는 명령»이 붙어 있다. 🔗
RFC 공진화·PRFAQ
한 문장
말 한 줄에 눈·손·기억이 한꺼번에 도는 자기치유 코딩 하니스 — 에이전트가 에이전트를 개발한다.
❶ 전체 — 한 문장이 병합된 변경이 되기까지
사람 / 에이전트 세션
│ "이 파일의 테스트를 붙여라" ⬅ 입구: harness say · harness ask · /harness · TUI 고정구
▼
┌──────────────────────────────────────────────────────────────────┐
│ ① 골 저작 한 줄 → 골 «문서» │
│ 마커: 대상 경로 · GoalType · 불변식 · 경계 · │
│ 판정 신호(조건 / 관측 / 기대) │
│ 📏 bun scripts/ask-marker-check.ts <ask> │
└──────────────────────────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────┐
│ ② 하니스 실행 격리 워크트리 ⊕ 자식 에이전트 ⊕ 게이트 ⊕ 무인 리뷰 │
│ ⬇ 노드 어휘는 ❷ 를 보라 — «되돌아가는 간선»이 있다 │
└──────────────────────────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────┐
│ ③ 원장 · 관측 런마다 무엇을 남기나 │
│ 골 문서 `## 실행 기록` · 워크트리/브랜치 · │
│ run ledger · 구조화 로그 │
│ 📏 elanous logs --category <c> · elanous self run-ledger │
└──────────────────────────────────────────────────────────────────┘
그 셋을 떠받치는 셋 — 눈 · 손 · 기억
👁️ 눈 elanous pty snapshot <ref> 남의 화면을 «소유권 없이» 읽는다 (--ansi 면 SGR 까지)
firecrawl · omni-crawl · aside · CDP/Chrome ⚠️ 상당 부분이 «빌린 것»이다 (❹)
✋ 손 툴 루프 Read/Grep/Glob/ListDir/Edit/Write + Bash
elanous pty text|key <ref> 밖에서 실 TUI·자식에 «입력»한다 (크로스-프로세스)
🧠 기억 elanous memory (user|feedback|project|reference) — 매 턴 주입
elanous self recall 자기 구현 이력
🔑 차별점은 셋이 «같이» 도는 것이다. 대부분의 에이전트는 셋 중 하나만 잘한다. 🔲 안 쟀다: 한 요청 «안»에서 셋이 몇 번 맞물리는지. 지금 이 주장은 «구성 요소»로만 참이다.
❷ 그래프 — 「다이나믹 ⊕ 리지드」는 이미 선언으로 있다 (정정 2026-09-22 · 이 축에서 내 세 번째)
⛔ 이 문서는 이 절을 두 번 틀리게 썼다. 둘 다 적어 둔다 — 다음 창이 같은 자리를 밟지 않도록.
1차 «하니스와 wf 사이에 이음새가 없다» → 틀렸다. orchestrator.ts:5060 이 이미 wf 를 돌린다2차 «위상이 코드에 박혀 골이 못 바꾼다» → 틀렸다. 위상은 YAML 선언이고 골 타입이 고른다🔑 두 번 다 ***코드를 안 누르고 문면으로 판정***했다. 세 번째에야 실물에 물었다.
ⓐ 실측 — 위상은 «선언»이고 운영이 그것을 쓴다
📏 재는 명령:
bun -e "import {GRAPH_TEMPLATES, GRAPH_TEMPLATES_SOURCE} from './src/self-implement/graph-templates.ts'; \
console.log(GRAPH_TEMPLATES_SOURCE, Object.keys(GRAPH_TEMPLATES))"
source yaml (⊕ builtin-fallback 갈래가 있으나 지금은 yaml 이 이긴다)
디렉토리 graphs/
선언된 그래프 7 — self-implement · research-loop · document-loop · operate-loop
ad-loop · default-loop · plan-loop
self-implement author → plan → decompose → implement → gate → review → rework
→ main-sync → regate → open-pr → merge (11노드)
간선 gate → [rework | review | main-sync | open-pr] ← «분기»가 선언에 있다
review → [rework | main-sync | open-pr] · rework → implement (되돌아간다)
노드 계약 inputs · tools · outputs 가 노드마다 선언된다
🔎 반증 명령이 코드에 이미 있다: goalTypesWithoutTemplate() → []
⇒ 골 타입 넷(implement·research·document·operate)이 전부 템플릿을 갖는다.
ⓑ 그런데 선언이 실행을 «몰지» 않는다 — 여기가 진짜 남은 칸
checkPipelineTraversal(observedNodes, terminalNode) ← «관측된 걸음»을 선언과 대조한다
이벤트 이름 pipeline-traversal-shadow
↑ 이름이 스스로 «그림자»라고 말한다
🔑 선언은 비추고 있다. 몰지는 않는다.
실행 제어는 여전히 오케스트레이터(TS)에 있고, YAML 은 모양을 선언하고 어긋남을 «보고»한다. ⇒ 남은 칸의 이름은 **「이음새 만들기」도 「넓히기」도 아니고 ⟪선언 → 실행 권한⟫**이다. ⚠️ 그림자로 둔 것은 의도일 수 있다 — 뒤집는 것은 위험한 변경이라 그렇게 시작하는 것이 옳다.
ⓒ 선언 7 · 실제로 걷는 것 2
📏 elanous logs --event pipeline-node-entry --limit 40 --all --include-test --json --json-data
self-implement 34행 실제로 돈다
default-loop 6행 실제로 돈다 (graph-authority 의 기본값)
ad-loop ad-pipeline/run.ts 가 고른다
research/document/operate-loop 골 타입 매핑이 고른다
plan-loop 🔲 «선언만» 있다 — 골 타입 매핑에 없고, recipe(planning·plan_review·
plan_judge·implementation)를 실행하는 구현이 저장소에 없다
⊕ 로더가 스스로 흠을 낸다: 그 4노드에 «계약이 없다»(GRAPH_TEMPLATES_ISSUES 4건)
⛔ 대표 이 준 예시 파일이라 계약을 ***지어내지 않았다***
ⓓ wf 와의 관계 — 이음새는 «한 칸»에서 쓰인다
src/self-implement/orchestrator.ts:5060 runWorkflowToCompletion({ workflow: reworkBudgetWorkflow, … })
src/self-implement/rework-budget-judgment.ts · classify-shadow.ts executeClassifyNode
하니스의 판정 계약은 지금 rework-budget@v1 하나뿐이고, 그 하나가 wf 로 돈다.
⇒ 「wf 를 살린다」의 첫 칸은 판정을 하나 더 «선언»으로 옮기는 것이다(재발명 아님 · 본보기가 있다).
❸ 3축 — intake · workflow · task manager (대표 "3축도 원활하게 돌아갔으면")
「말만 하면 이뤄진다」의 실체는 wf 하나가 아니라 문장을 받아 쪼개고 전체를 도는 세 축이다.
intake src/intent-gate/gate.ts 문장 → 트리아지 → 도메인 → «자동 페이즈 분해» → 미션
실제 배선: TaskStore · createMission · triageGoal · resolveDomain
autoDecomposeMission · shouldPhaseDecompose · saveMissionOrigin
아크·페이즈 src/autopilot/mission-arc-*.ts ***21 파일*** — classify · budget · drift · preflight
reshape · revise · surgery · verify · edit-lifecycle · phase-cap-governor
workflow src/workflow-runtime/ YAML DAG · 스키마가 받는 변종 21종
task src/task-orchestrator/ store · runtimes · surfaces · task-to-workflow
🚦 인테이크의 «문»은 셋이고, 선언된 채널 여섯 중 셋은 생산자가 없다 (재측 2026-09-22)
🔴 정정 — 이 절은 처음에 "문이 «하나»(telegram)" 라고 썼고 틀렸다. 그때 쓴 자가
rg "from '…intent-gate/'"라 정적 import 만 봤는데, 세 문 중 둘이 동적await import(…)였다. 그 절은 마지막 줄에 «이 자는 동적 경로를 못 본다»고 스스로 적어 두고도 머리말과 결론에서 그것을 무시했다. ⛔ 더 나쁜 것: 그 파일 머리말이 "호출자 셋(telegram·tui·api)" 이라 적고 있었는데 내가 그것을 「46일 늙었다」고 선언했다. 머리말이 맞았다.
📏 재는 명령 — ⛔ 정적·동적을 둘 다 봐야 한다:
rg -n --no-ignore -g '!*.test.ts' 'intent-gate/gate' src/ scripts/ bin/
intent-gate/gate.ts submitIntent
마커 판독 → triageGoal → resolveDomain → autoDecomposeMission/shouldPhaseDecompose
→ createMission → saveMissionOrigin → captureTaste → spawnMissionPrepare
살아 있는 문 «셋»
src/telegram.ts:2160 정적 import channel='telegram'
src/dashboard/slash-runtime/…/dashboard-handlers.ts:2719 동적 import channel='tui'
src/autopilot/system-repair-spawn.ts:106 동적 import channel='api'
📏 IntentChannel 이 여섯을 선언한다 — 그중 값을 «내는» 쪽:
telegram ✅ pwa ⛔ 생산자 0
tui ✅ voice ⛔ 생산자 0
api ✅ cli ⛔ 생산자 0
🔑 그래서 남은 진짜 결손은 CLI 다 — 터미널에서
harness say로 쏘는 길은하니스로 바로 가고 인테이크(트리아지·도메인·페이즈 분해)를 거치지 않는다.
pwa·voice도 선언만 있고 생산자가 없다.
⛔ 함정: 「submitIntent」가 이 저장소에서 «둘»을 가리킨다.
src/intent-gate/gate.ts:68 submitIntent(input) ← 인테이크
src/domains/trade-blackboard.ts:68 submitIntent(db, source, input) ← 매매 블랙보드
둘 다 SubmitIntentInput 이라는 타입을 각자 선언한다. ⇒ 이름만으로 호출부를 세면
scripts/*-cycle.ts 셋이 인테이크 호출자로 «잘못» 잡힌다.
실측 — 서로를 import 하는가 (방향별 파일 수 · 2026-09-22)
📏 rg -l --no-ignore "from '[^']*<to>/" src/<from>/ | wc -l
| from \ to | harness | wf | task | intake | autopilot | self-impl |
|---|---|---|---|---|---|---|
| harness | · | 0 | 2 | 0 | 16 | 18 |
| wf | 0 | · | 2 | 0 | 2 | 1 |
| task | 0 | 2 | · | 0 | 0 | 1 |
| intake | 0 | 0 | 2 | · | 2 | 0 |
| autopilot | 1 | 0 | 81 | 0 | · | 8 |
| self-impl | 14 | 4 | 1 | 0 | 21 | · |
읽는 법
✅ 두껍다 autopilot ↔ task (81) 아크·페이즈 기계와 태스크 저장소는 «한 몸»에 가깝다
✅ 이어짐 intake → task (2) · intake → autopilot (2)
✅ 이어짐 self-impl → wf (4) ⬅ ❷ⓒ 의 그 이음새다
⚠️ 얇다 task ↔ wf (2 / 2) task-to-workflow.ts 가 변환기로 «있다»
🔴 0 harness ↔ wf (0 / 0) 하니스 «코어»는 wf 를 모른다
🔴 0 intake ↔ wf (0 / 0) ***문장이 wf 로 못 간다*** ← 3축이 안 도는 자리
🔑 wf 는 고립이 아니라 «가장 얇게 붙은» 축이다. 끊긴 변은 딱 둘 — 하니스↔wf · intake↔wf.
⇒ 「wf 를 살린다」는 새로 짓는 일이 아니라 저 두 변을 잇는 일이다.
❹ 하니스 통합 — 여덟 자리 중 «둘»이 46일째 그대로다
좌표는 이미 있다(REPORT-nl-to-harness-control-points-2026-08-07 §10). 그 여덟을 오늘 다시 눌렀다:
✅ ① src/core-turn/types.ts CoreTurnDispatchTool 의 ctx 에 userText «있다»
🔴 ③ src/session/chat.ts:431 dispatchTool: async (name, args) => dispatchTool(name, args)
🔴 ④ src/session/chat.ts:456 dispatchTool: async (name, args /*, ctx */) => {
⛔ ***주석까지 08-07 그대로다***
✅ ⑦ src/session-runtime/index.ts dispatchAutonomousTool 이 ctx 를 나른다 — 완비된 유일한 경로
✅ ⑧⑨ daemon-tools/self-implement.ts originalUserText → goalSourceText → authorGoal
③④ 때문에
session/chat.ts경로에서ctx.userText는 구조적으로 undefined 다.
⇒ 말 한 줄이 골이 되는 배관이 «한 경로에서만» 완비돼 있다. 출시 전에 닫을 칸이다.
❺ 셀프힐링 — 하니스가 스스로 트리아지하고 정리한다
분류 src/self-implement/abandoned-classification.ts 중단 런의 «이유»를 어휘로 낸다
artifact-deficit · budget-exhausted · contract-conflict · credential-failure
already-satisfied · completed-without-changes · cited-evidence-exists-without-changes …
⊕ environment-*-outranks-run-stage-evidence ← 「환경 탓」과 「자식 탓」을 «우선순위»로 가른다
트리아지 src/autopilot/triage.ts 골을 도메인·긴급도로 가른다
판정 src/self-implement/rework-budget-judgment.ts wf classify 노드로 rework 예산을 판정
src/self-implement/classify-shadow.ts 그림자 분류(실행 않고 비교)
정리 elanous harness clean 워크트리·브랜치 회수 (수명 사다리 ⊕ 소유)
수리신호 elanous self repair-signals · elanous self parked
🔲 안 쟀다: 이 트리아지가 «실제로 얼마나 자주 옳은가». 어휘는 풍부하나 정확도를 잰 적이 없다.
❻ 네 가지 우주 — 자식을 띄우면 갈린다
prod · prod→격리 · test · test→격리
부모/owner/checker 가 각자 우주를 정해 갈린다. 우주는 effectiveInstanceRoot() 로 한 번 해석하고
그 값으로 자식을 못 박는다. ⛔ 작업 디렉토리는 «별개 축» — 우주만 격리하면 사람 트리가 자식의 쓰기 구역이 된다.
📏 elanous where · elanous harness worktrees
❼ 세계를 보는 눈은 «빌린 것»이다
📏 재는 명령: rg -c '^\s*- id:' catalog/resources.yaml
자원 맵 catalog/resources.yaml — 항목마다 「없으면 무엇이 안 되나」(free_fallback)를 적는다
✅ 있다 firecrawl · tavily · jina · 각 LLM 프로바이더 …
❌ 없다 omni-crawl · aside · CDP/Chrome
↑ 「깔려 있어야 하는 바이너리·브라우저」라는 «의존의 한 종류»가 통째로 빠졌다
⚠️ 자격이 필요한 항목의 다수가 free_fallback 칸이 «비어 있다»
🔑 무료 폴백이 «적혀 있는» 능력만 「코어」로 내보낼 수 있다. 이 칸이 곧 오픈코어 경계선이다.
❽ 표면 — 같은 실행체에 여러 입구
CLI elanous <명령> 최상위 69 · 잎 약 576
📏 bun scripts/gen-docs-cli-reference.ts
NEXUS / PWA elanous nexus 데몬 lifecycle ⊕ 웹 대시보드
TUI 3-pane ⚠️ 여러 표면 중 «하나» — 더 이상 이 프로젝트의 축이 아니다
MCP elanous mcp elanous 를 MCP 서버로
채널 telegram · discord · matrix …
⚠️ 실물 편차가 크다 — 이름만 있는 것이 섞여 있다
ACP elanous acp · attach 외부 에이전트를 붙인다
⛔ 이 문서가 «일부러» 안 그린 것
① 끊긴 변을 이어진 것처럼 — harness↔wf · intake↔wf 는 «0»이다. 그대로 뒀다
⑤ 트리아지 «정확도» — 어휘는 셌고 「맞는가」는 안 쟀다
② 「눈·손·기억이 N 번 맞물린다」의 N — 안 쟀다
③ 채널별 완성도 — 파일 수로는 세었으나 «동작»은 안 눌렀다
④ 수를 본문에 박기 — 전부 「재는 명령」으로 뒀다