mkdir -p build/bin
GOOS=linux GOARCH=arm64 go build -trimpath -o build/bin/iop-edge ./apps/edge/cmd/edge
NOTE: test-e2e runs auxiliary smoke (Edge-Node + OpenAI) plus Control Plane-Edge wire smoke; completion still requires user-flow verification when changing runtime paths.
./scripts/e2e-smoke.sh
[e2e] NOTE: auxiliary smoke only; completion requires scripts/dev/edge.sh + scripts/dev/node.sh user-flow verification.
[e2e] shellcheck not found, skipping
[e2e] prompt templates: first=ack-short second=ready-short background=bye-short fourth=yes-short base=7
[e2e] preparing honest mock smoke test (using scripted cli adapter)...
[e2e] starting smoke test (profile: mock, port: 33395, persistent: 1, has_status: 0)
[e2e] waiting for node registration (timeout: 60s)
[e2e] > /nodes
[e2e] > /capabilities
[e2e] > /transport
[e2e] > 확인했다는 짧은 응답 테스트입니다. IOP_E2E_ACK_SHORT 만 답하세요.
[e2e] > 준비되었는지 묻는 짧은 테스트입니다. IOP_E2E_READY_SHORT 만 출력하세요.
[e2e] > /session session2
[e2e] > /background on
[e2e] > 짧은 마무리 인사 테스트입니다. 답변은 IOP_E2E_BYE_SHORT 만 쓰세요.
[e2e] > /background off
[e2e] > /sessions
[e2e] > /terminate-session
[e2e] > /exit
=== EDGE OUTPUT ===
[edge] config=/config/workspace/iop-s2/build/e2e-tmp/tmp.0o9xXvfhdE/edge.yaml
IOP Edge console listening on 127.0.0.1:33395
Console target node= adapter=cli target=fake-cli session=default background=false
Start node.sh on another host, then type a message here.
Commands: /nodes, /node <id|alias>, /session <id>, /background on|off, /terminate-session, /status, /capabilities, /sessions, /transport, /exit
edge> [node0-evt] connected reason="registered"
  node0 = test-node (test-node)
edge> [node0-capabilities] adapter=cli target=fake-cli session=default
  adapter = cli
  capacity = 0
  in_flight = 0
  instance_key = 
  max_concurrency = 0
  provider_status = unknown
  queued = 0
  targets = fake-cli
edge> [node0-transport] adapter=cli target=fake-cli session=default
  adapter = cli
  connected = true
  node_id = test-node
  session_id = default
  state = connected
  target = fake-cli
edge> [edge] sent run_id=manual-1785643804259512925 node=node0 adapter=cli target=fake-cli session=default background=false
[node0-evt] start run_id=manual-1785643804259512925
[node0-msg] IOP_E2E_ACK_SHORT
[node0-msg] IOP_E2E_ACK_SHORT_TAIL
[node0-evt] complete run_id=manual-1785643804259512925 detail="idle-timeout"
edge> [edge] sent run_id=manual-1785643805491949718 node=node0 adapter=cli target=fake-cli session=default background=false
[node0-evt] start run_id=manual-1785643805491949718
[node0-msg] IOP_E2E_READY_SHORT
[node0-msg] IOP_E2E_READY_SHORT_TAIL
[node0-evt] complete run_id=manual-1785643805491949718 detail="idle-timeout"
edge> session → session2
edge> background → on
edge> [edge] sent run_id=manual-1785643807292946552 node=node0 adapter=cli target=fake-cli session=session2 background=true
[edge] background run dispatched, events will arrive asynchronously
edge> [node0-evt] start run_id=manual-1785643807292946552 session=session2 background=true
[node0-msg] IOP_E2E_BYE_SHORT
[node0-msg] IOP_E2E_BYE_SHORT_TAIL
[node0-evt] complete run_id=manual-1785643807292946552 detail="idle-timeout"
background → off
edge> [node0-sessions] adapter=cli target=fake-cli session=session2
sessions: 2
  [0] mode=persistent target=fake-cli session=default
  [1] mode=persistent target=fake-cli session=session2
edge> terminated session session2 node=node0
edge> bye
=== NODE OUTPUT ===
[node] config=/config/workspace/iop-s2/build/e2e-tmp/tmp.0o9xXvfhdE/node.yaml
[node] waiting for edge at 127.0.0.1:33395 timeout=30s
[node] edge is reachable
[Fx] PROVIDE	fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE	fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE	fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] PROVIDE	*config.NodeConfig <= iop/apps/node/internal/bootstrap.Module.func2()
[Fx] PROVIDE	*zap.Logger <= iop/apps/node/internal/bootstrap.Module.func3()
[Fx] INVOKE		iop/apps/node/internal/bootstrap.Module.func4()
[Fx] RUN	provide: go.uber.org/fx.New.func1()
[Fx] RUN	provide: iop/apps/node/internal/bootstrap.Module.func2()
[Fx] RUN	provide: iop/apps/node/internal/bootstrap.Module.func3()
[Fx] RUN	provide: go.uber.org/fx.(*App).shutdowner-fm()
[Fx] HOOK OnStart		iop/apps/node/internal/bootstrap.Module.func4.1() executing (caller: iop/apps/node/internal/bootstrap.Module.func4)
[Fx] HOOK OnStart		iop/apps/node/internal/bootstrap.Module.func4.1() called by iop/apps/node/internal/bootstrap.Module.func4 ran successfully in 14.625µs
[Fx] RUNNING
{"level":"info","ts":1785643803.0935593,"caller":"bootstrap/runtime_supervisor.go:116","msg":"connecting to edge","initial":true,"attempt":1,"max_attempts":0,"unlimited":true,"interval_sec":1}
{"level":"info","ts":1785643803.2011025,"caller":"transport/client.go:209","msg":"registered with edge","node_id":"test-node","alias":"test-node"}
{"level":"info","ts":1785643803.2071965,"caller":"store/store.go:62","msg":"store ready","dsn":"file:iop.db?cache=shared&mode=rwc"}
{"level":"info","ts":1785643803.2086594,"caller":"cli/cli.go:239","msg":"cli adapter: persistent session started","target":"fake-cli"}
{"level":"info","ts":1785643803.2096426,"caller":"bootstrap/module.go:163","msg":"connected to edge","node_id":"test-node","alias":"test-node"}
{"level":"info","ts":1785643803.8126714,"caller":"node/command_handler.go:20","msg":"command request","request_id":"caps-1785643803812017175","type":"NODE_COMMAND_TYPE_CAPABILITIES","adapter":"cli","target":"fake-cli"}
{"level":"info","ts":1785643804.024796,"caller":"node/command_handler.go:20","msg":"command request","request_id":"transport-1785643804024536759","type":"NODE_COMMAND_TYPE_TRANSPORT_STATUS","adapter":"cli","target":"fake-cli"}
{"level":"info","ts":1785643804.260219,"caller":"node/run_handler.go:19","msg":"run request received","run_id":"manual-1785643804259512925","adapter":"cli","target":"fake-cli"}
[edge-message] 확인했다는 짧은 응답 테스트입니다. IOP_E2E_ACK_SHORT 만 답하세요.
[node-event] start run_id=manual-1785643804259512925
[node-message] IOP_E2E_ACK_SHORT
IOP_E2E_ACK_SHORT_TAIL
[node-event] complete run_id=manual-1785643804259512925 detail="idle-timeout"
{"level":"info","ts":1785643805.492475,"caller":"node/run_handler.go:19","msg":"run request received","run_id":"manual-1785643805491949718","adapter":"cli","target":"fake-cli"}
[edge-message] 준비되었는지 묻는 짧은 테스트입니다. IOP_E2E_READY_SHORT 만 출력하세요.
[node-event] start run_id=manual-1785643805491949718
[node-message] IOP_E2E_READY_SHORT
IOP_E2E_READY_SHORT_TAIL
[node-event] complete run_id=manual-1785643805491949718 detail="idle-timeout"
{"level":"info","ts":1785643807.2933862,"caller":"node/run_handler.go:19","msg":"run request received","run_id":"manual-1785643807292946552","adapter":"cli","target":"fake-cli"}
[edge-message] 짧은 마무리 인사 테스트입니다. 답변은 IOP_E2E_BYE_SHORT 만 쓰세요.
[node-event] start run_id=manual-1785643807292946552
[node-message] IOP_E2E_BYE_SHORT
IOP_E2E_BYE_SHORT_TAIL
[node-event] complete run_id=manual-1785643807292946552 detail="idle-timeout"
{"level":"info","ts":1785643808.7559805,"caller":"node/command_handler.go:20","msg":"command request","request_id":"sessions-1785643808755735094","type":"NODE_COMMAND_TYPE_SESSION_LIST","adapter":"cli","target":"fake-cli"}
{"level":"info","ts":1785643808.965377,"caller":"node/cancel_handler.go:16","msg":"cancel request","run_id":"","action":"CANCEL_ACTION_TERMINATE_SESSION"}
{"level":"info","ts":1785643809.1772017,"caller":"transport/session.go:137","msg":"disconnected from edge","transport_close_reason":"remote_closed","transport_close_error":"EOF"}
[edge-event] disconnected reason="transport_closed" transport_close_reason="remote_closed" transport_close_error="EOF"
[Fx] TERMINATED
[Fx] HOOK OnStop		iop/apps/node/internal/bootstrap.Module.func4.2() executing (caller: iop/apps/node/internal/bootstrap.Module.func4)
[Fx] HOOK OnStop		iop/apps/node/internal/bootstrap.Module.func4.2() called by iop/apps/node/internal/bootstrap.Module.func4 ran successfully in 667.958µs
===================
[e2e] Auxiliary smoke test PASSED.
[e2e] Completion still requires scripts/dev/edge.sh + scripts/dev/node.sh user-flow verification.
./scripts/e2e-openai-ollama.sh
[openai-ollama] OpenAI-compatible Ollama serving test PASSED.
./scripts/e2e-control-plane-edge-wire.sh
[cp-edge-wire] NOTE: auxiliary smoke only - verifies Control Plane-Edge hello and disconnect via real processes.
[cp-edge-wire] shellcheck not found, skipping
[cp-edge-wire] ports: cp_http=29182 cp_ws=30402 cp_edge_wire=31233 edge_node=32336 edge_bootstrap=33701 edge_metrics=35000
[cp-edge-wire] building temp binaries...
[cp-edge-wire] starting Control Plane...
[cp-edge-wire] waiting for Control Plane edge wire port 31233 (timeout: 20s)...
[cp-edge-wire] Control Plane edge wire port ready
[cp-edge-wire] starting Edge...
[cp-edge-wire] waiting for hello accepted (timeout: 30s)...
[cp-edge-wire] CP: hello accepted
[cp-edge-wire] Edge: connected to control plane
[cp-edge-wire] stopping Edge process to trigger disconnect...
[cp-edge-wire] waiting for disconnect marker on CP (timeout: 20s)...
[cp-edge-wire] CP: edge disconnected
=== CONTROL PLANE OUTPUT ===
{"level":"info","ts":1785643820.2151985,"caller":"control-plane/server.go:30","msg":"control-plane client wire endpoint reserved","protocol":"protobuf-socket","transport":"proto-socket-ws","listen":"127.0.0.1:30402"}
{"level":"info","ts":1785643820.2157617,"caller":"control-plane/server.go:35","msg":"control-plane edge wire endpoint reserved","protocol":"protobuf-socket","transport":"proto-socket-tcp","listen":"127.0.0.1:31233"}
{"level":"info","ts":1785643820.2159538,"caller":"wire/client.go:92","msg":"starting client wire WS server","host":"127.0.0.1","port":30402,"path":"/client"}
{"level":"info","ts":1785643820.2177405,"caller":"wire/edge_server.go:231","msg":"starting edge wire TCP server","host":"127.0.0.1","port":31233,"transport":"proto-socket-tcp"}
{"level":"info","ts":1785643820.2181334,"caller":"control-plane/server.go:227","msg":"control-plane http endpoint listening","listen":"127.0.0.1:29182"}
{"level":"info","ts":1785643820.730078,"caller":"wire/edge_server.go:176","msg":"edge hello accepted","edge_id":"smoke-edge-wire","edge_name":"Smoke Edge Wire","version":"0.1.0"}
{"level":"info","ts":1785643821.2078662,"caller":"wire/edge_server.go:212","msg":"edge disconnected","edge_id":"smoke-edge-wire","reason":"remote_closed"}
=== EDGE PROCESS OUTPUT ===
[Fx] PROVIDE	fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE	fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE	fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] PROVIDE	*config.EdgeConfig <= iop/apps/edge/internal/bootstrap.Module.func1()
[Fx] PROVIDE	*bootstrap.Runtime <= iop/apps/edge/internal/bootstrap.NewRuntime()
[Fx] INVOKE		iop/apps/edge/internal/bootstrap.Module.func2()
[Fx] RUN	provide: go.uber.org/fx.New.func1()
[Fx] RUN	provide: iop/apps/edge/internal/bootstrap.Module.func1()
[Fx] RUN	provide: iop/apps/edge/internal/bootstrap.NewRuntime()
[Fx] HOOK OnStart		iop/apps/edge/internal/bootstrap.Module.func2.1() executing (caller: iop/apps/edge/internal/bootstrap.Module.func2)
[Fx] HOOK OnStart		iop/apps/edge/internal/bootstrap.Module.func2.1() called by iop/apps/edge/internal/bootstrap.Module.func2 ran successfully in 2.318959ms
[Fx] RUNNING
[Fx] TERMINATED
[Fx] HOOK OnStop		iop/apps/edge/internal/bootstrap.Module.func2.2() executing (caller: iop/apps/edge/internal/bootstrap.Module.func2)
[Fx] HOOK OnStop		iop/apps/edge/internal/bootstrap.Module.func2.2() called by iop/apps/edge/internal/bootstrap.Module.func2 ran successfully in 688.417µs
=== EDGE LOG ===
{"level":"info","ts":1785643820.7275248,"caller":"transport/server.go:154","msg":"edge listening for nodes","addr":"127.0.0.1:32336"}
{"level":"warn","ts":1785643820.7281463,"logger":"bootstrap","caller":"bootstrap/artifact_server.go:35","msg":"bootstrap artifact directory does not exist","dir":"/config/workspace/iop-s2/build/e2e-tmp/tmp.oj7yo39dV3/artifacts"}
{"level":"info","ts":1785643820.7283704,"logger":"bootstrap","caller":"bootstrap/artifact_server.go:59","msg":"bootstrap artifact server listening","addr":"127.0.0.1:33701","dir":"/config/workspace/iop-s2/build/e2e-tmp/tmp.oj7yo39dV3/artifacts"}
{"level":"info","ts":1785643820.7307236,"logger":"controlplane","caller":"controlplane/connector.go:358","msg":"connected to control plane","wire_addr":"127.0.0.1:31233","protocol":"protobuf-socket"}
===========================
[cp-edge-wire] Control Plane-Edge wire smoke PASSED.
