From c578442a6cc0e78149e9575950c558875deb64ad Mon Sep 17 00:00:00 2001 From: toki Date: Thu, 13 Aug 2026 09:16:34 +0900 Subject: [PATCH] =?UTF-8?q?docs(handoff):=20=EC=8A=A4=ED=8A=B8=EB=A6=AC?= =?UTF-8?q?=EB=B0=8D=20=EC=84=B8=EC=85=98=20=EC=A0=95=EC=A7=80=20=EC=A7=84?= =?UTF-8?q?=EB=8B=A8=EC=9D=84=20=EC=9D=B8=EA=B3=84=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HANDOFF-ORNITH-SESSION-STALL.md | 214 ++++++++++++++++++++++++++++++++ HANDOFF.md | 197 ----------------------------- 2 files changed, 214 insertions(+), 197 deletions(-) create mode 100644 HANDOFF-ORNITH-SESSION-STALL.md delete mode 100644 HANDOFF.md diff --git a/HANDOFF-ORNITH-SESSION-STALL.md b/HANDOFF-ORNITH-SESSION-STALL.md new file mode 100644 index 00000000..012acea6 --- /dev/null +++ b/HANDOFF-ORNITH-SESSION-STALL.md @@ -0,0 +1,214 @@ +--- +handoff_version: 1 +handoff_status: implementation-not-started +created_at: 2026-08-13 +target: dev OpenAI-compatible ornith:35b streaming session stall +local_source_revision: b205433a323e6fd47ceb55998d72a624d432887e +observed_dev_revision: fd32abb4b6b15037c24be01821b430a960afd967 +--- + +# Handoff: `ornith:35b` streaming session stall과 terminal 지연 + +## 현재 타겟 + +- 증상: Chronos `agent-task`에서 Pi `iop/ornith:35b` worker가 `failed:session-stall:143`으로 세 번 연속 종료된다. +- 범위: IOP dev OpenAI-compatible Chat Completions provider tunnel, Stream Evidence Gate, Node/provider liveness timeout 정합성. +- 상태: 진단만 수행했다. 코드, 설정, 배포는 변경하지 않았다. +- 다음 세션 첫 진입점: + 1. `apps/edge/internal/openai/stream_gate_runtime.go` + 2. `apps/edge/internal/openai/stream_gate_tunnel_codec.go` + 3. `apps/node/internal/adapters/openai_compat/provider_tunnel.go` + 4. `packages/go/execution/liveness.go` + +## 환경 정정 + +초기 조사에서 `dev-corp`를 열어 DGX Spark 설정을 본 것은 잘못된 환경 선택이었다. Spark 관련 해석은 이 장애의 근거로 사용하지 않는다. + +실제 Pi가 호출한 dev Edge 런타임을 기준으로 다시 확인했다. + +- dev에는 `gx10-vllm-node`, `onexplayer-lemonade-node`, `rtx5090-lemonade-node`가 등록돼 있다. +- 현재 배포 설정에서 `gx10-vllm`은 `laguna-s:2.1`에 연결돼 있다. +- 현재 `ornith:35b`는 `onexplayer-lemonade`와 `rtx5090-lemonade`에 연결돼 있다. +- 이번 세 실패의 마지막 요청은 모두 실제 provider `onexplayer-lemonade`로 dispatch됐다. +- 제품 의도가 `ornith:35b`도 GX10에서 실행되는 것이라면, 현재 배포 route/catalog가 그 의도와 다르다. 구현 전에 운영 의도와 현재 binding을 먼저 확정해야 한다. + +## 확정된 관측 + +Chronos dispatcher의 세 attempt는 모두 native session이 `active`이고 pending tool call이 없는 상태에서 약 180초 동안 새 출력이 없어 dispatcher가 `SIGTERM`을 보냈다. Unix exit `143`은 이 `SIGTERM`의 결과이며 provider가 직접 반환한 오류 코드가 아니다. + +| Attempt | 마지막 IOP 요청 | StreamGate 관측 | Chronos 종료 | +|---|---|---|---| +| 21 | 입력 추정 `40,665` tokens, `onexplayer-lemonade` | 네 번 release 후 terminal 없음 | silence `180.048s`, dispatcher `SIGTERM` | +| 22 | 입력 추정 `40,830` tokens, `onexplayer-lemonade` | 두 번 release 후 terminal 없음 | silence `180.063s`, dispatcher `SIGTERM` | +| 23 | `40,995`-token turn은 약 `224.7s` 뒤 정상 terminal; 다음 `41,080`-token turn도 같은 provider | 다음 turn은 `response_start`만 staging되고 delta/terminal 없음 | silence `180.070s`, dispatcher `SIGTERM` | + +Chronos 측 마지막 출력 시각과 IOP의 마지막 `release_committed` 시각이 밀리초 수준으로 일치한다. IOP 이후 Pi/dispatcher 구간에서 출력이 별도로 유실된 정황은 없다. + +관련 Chronos evidence: + +- attempt 21: `/config/workspace/chronos-s0/.git/agent-task-dispatcher/runs/20260813T074422+0900__m-chronos-product-delivery-install-update-lifecycle__01_release_metadata__p2__worker__a21/` +- attempt 22: `/config/workspace/chronos-s0/.git/agent-task-dispatcher/runs/20260813T075124+0900__m-chronos-product-delivery-install-update-lifecycle__01_release_metadata__p2__worker__a22/` +- attempt 23: `/config/workspace/chronos-s0/.git/agent-task-dispatcher/runs/20260813T075511+0900__m-chronos-product-delivery-install-update-lifecycle__01_release_metadata__p2__worker__a23/` + +각 디렉터리의 `locator.json`과 `stream.log`만 보면 dispatcher 종료 사유와 마지막 native output을 재확인할 수 있다. 이 경로들은 Chronos worktree-local evidence이므로 정리 전에 필요한 요약을 별도 보존해야 한다. + +## Stream Evidence Gate 판정 + +dev Edge에서 `stream_evidence_gate.enabled=true`이며 다음 정책이 `ornith:35b`에만 selector로 적용된다. + +- filter: `repeat_guard` +- enforcement: `blocking` +- hold: `500` Unicode runes +- filter timeout: `5000ms` + +그러나 실패 시간대의 raw-free filter observation은 모두 다음과 같다. + +- `openai.repeat_guard`: `pass`, `repeat_rolling_clear` +- attempt 23의 정상 완료 turn: `openai.repeat_guard.action`도 `pass`, `repeat_action_clear` +- `violation`, `fatal`, `recover`, continuation dispatch는 없음 + +따라서 반복 검증 판정이 요청을 차단하거나 recovery loop에 넣은 것이 직접 원인은 아니다. + +다만 500-rune rolling hold는 증상을 확대한다. 이 값은 항상 마지막 500 runes를 보류한다는 뜻이 아니다. pending이 500 runes 미만이면 평가와 release를 기다리고, 500 이상이 되면 filter를 평가해 pass 시 그 epoch의 pending 전체를 즉시 release한다. terminal trigger가 오면 500 미만이어도 평가한다. 따라서 provider가 다음 delta나 terminal trigger를 보내지 않으면 현재 sub-threshold pending tail만 caller에게 release되지 않는다. caller가 180초 후 종료되면 그 tail도 정상 terminal 없이 폐기된다. + +### 종료 신호 경계와 현재 관측 공백 + +OpenAI-compatible stream에는 서로 다른 계층의 종료 신호가 있다. + +1. `choices[].finish_reason`: 모델 생성이 끝났다는 논리적 완료 신호 +2. SSE `data: [DONE]`: OpenAI stream framing의 완료 신호 +3. upstream response EOF와 Node tunnel `END`: 실제 transport 완료 신호 +4. StreamGate Core terminal: codec/runtime이 위 신호를 normalized terminal로 변환한 결과 + +현재 codec은 `finish_reason` chunk를 terminal trigger로 만들지 않고 wire-only frame으로 staging한다. `[DONE]` 또는 tunnel `END`가 와야 Core terminal 경로가 실행된다. 그러므로 `finish_reason`은 도착했지만 `[DONE]`/EOF/`END`가 지연되거나 누락되면, 500-rune 미만 pending tail과 staged finish frame이 함께 보류될 수 있다. + +기존 raw-free observation으로 확인된 것은 실패 요청에서 Core terminal 및 tunnel `END`가 관측되지 않았다는 사실까지다. 실패 요청에 `finish_reason`이 먼저 도착했는지, `[DONE]`이 codec에 도착했지만 변환 중 유실됐는지는 현재 로그만으로 확정할 수 없다. 따라서 이 경계는 확정 원인이 아니라 코드상 유력한 버그 후보로 취급한다. + +## 현재 원인 판정 + +### 확인됨 + +1. 요청은 Edge admission을 통과했고 provider tunnel의 response start와 일부 body를 받았다. +2. 실패한 turn에서 `repeat_guard`는 모두 pass했다. +3. 마지막 release 뒤 Node tunnel의 `END`, provider error terminal 또는 Core terminal이 오지 않았다. +4. attempt 23의 다음 turn은 response start 뒤 body 자체가 오지 않았다. +5. 세 요청 모두 약 40.7k~41.1k input-token 장문맥이고 실제 provider는 `onexplayer-lemonade`였다. + +### 유력하지만 추가 분리가 필요함 + +`onexplayer-lemonade` backend가 장문맥 생성 중 진행을 멈췄거나, Node의 upstream HTTP reader/tunnel relay가 body 또는 EOF/END를 전달하지 못했을 가능성이 높다. 현재 Edge observation만으로는 provider backend와 Node relay 중 정확한 소유자를 단정할 수 없다. + +### timeout 정합성 결함 + +- Chronos dispatcher session silence timeout: `180s` +- IOP Node `response_stall_timeout_ms`: 배포 설정에서 생략됨 +- 생략 시 IOP 기본 response stall timeout: `300000ms` +- dev Edge OpenAI request timeout: `3600s` + +따라서 Chronos가 180초에 Pi를 종료해 버리고, IOP Node의 300초 liveness watchdog은 typed `response_stalled` terminal을 만들 기회를 얻지 못한다. 이 순서 때문에 IOP의 terminal/error/recovery 경계가 동작하기 전에 외부에서 `143`으로 끝난다. + +부분 출력이 이미 caller에게 commit된 attempt 21/22는 stall을 감지하더라도 exact replay 대상이 아니다. 그래도 IOP가 dispatcher보다 먼저 명시적 error terminal을 보내야 truncated open stream 대신 원인을 관측할 수 있다. attempt 23의 두 번째 turn처럼 caller-visible commit 전 stall은 alternate provider exact replay 후보가 될 수 있다. + +## Pi TUI가 정상인 이유 + +Pi TUI가 Stream Evidence Gate를 우회하는 것은 아니다. 같은 OpenAI-compatible 경로를 사용한다. + +- TUI에는 Chronos dispatcher의 180초 silence watchdog이 없다. +- 보통 fresh 또는 더 짧은 context에서 호출된다. +- 이번 stall은 결정적이지 않다. attempt 23의 첫 40,995-token turn도 같은 provider에서 정상 terminal까지 완료했다. + +따라서 짧은 TUI 호출의 정상 완료는 filter 우회 증거가 아니며, 같은 약 41k context와 provider binding으로 재현해야 비교가 성립한다. + +## 관련 구현 경계 + +- `apps/node/internal/adapters/openai_compat/provider_tunnel.go` + - upstream response body를 읽는 동안 `BODY` frame을 보내고 EOF 뒤 `END` frame을 보낸다. +- `apps/edge/internal/openai/stream_gate_runtime.go` + - tunnel frame을 normalized event로 변환하고 `END`에서 transport finish를 만든다. + - event source wait timeout은 OpenAI request timeout 기반이므로 현재 dev에서는 dispatcher보다 훨씬 늦다. +- `apps/edge/internal/openai/stream_gate_tunnel_codec.go` + - semantic delta에 provider wire를 연결한다. + - `finish_reason` chunk는 protocol state/wire-only frame으로 staging할 뿐 terminal trigger로 만들지 않는다. + - `[DONE]`은 terminal 경로를 실행하며, transport `END`도 runtime을 통해 terminal을 만든다. +- `apps/edge/internal/openai/stream_gate_release_sink.go` + - release payload를 flush하고 성공 terminal에서 staged terminal payload를 쓴다. +- `packages/go/execution/liveness.go` + - provider별 값이 없을 때 response stall timeout을 `300000ms`로 정규화한다. + +## 다음 작업 순서 + +1. **route 의도 확정** + - dev에서 `ornith:35b`의 의도된 실제 장비/provider가 GX10인지 확인한다. + - GX10이 맞다면 현재 model catalog/provider binding drift를 먼저 수정 대상으로 잡는다. + +2. **provider와 Node relay 분리 재현** + - 동일한 약 41k-token Chat Completions 요청을 `stream=true`로 준비한다. + - provider 직접 호출과 IOP Node tunnel 호출을 각각 수행한다. + - raw prompt/output은 저장하지 않고 아래 이벤트를 request correlation과 monotonic timestamp로 각각 기록한다. + - `response_start`, body/delta progress, 마지막 release + - `finish_reason_observed`와 모든 active choice가 finished됐는지 여부 + - `done_marker_observed` + - `upstream_eof_observed` + - `tunnel_end_emitted` + - `core_terminal_emitted`와 terminal commit + - 각 종료 신호 시점의 `pending_runes`, staged finish/usage frame 수 + - provider 직접 응답, Node ingress, tunnel emit, Edge codec, Core terminal의 시각을 대조해 어느 경계에서 신호가 멈췄는지 분리한다. + +3. **timeout ownership 정렬** + - provider의 `response_stall_timeout_ms`를 Chronos dispatcher보다 짧게 설정하는 방안을 검토한다. 예: IOP `120s`, dispatcher는 IOP terminal/recovery가 끝날 margin을 포함해 더 길게 둔다. + - 수치는 장문맥에서 정상적으로 180초 이상 무출력 후 완료되는 사례가 있는지 측정한 뒤 확정한다. + - post-commit stall은 명시적 terminal error, pre-commit stall은 현재 ExactReplay gate에 따라 alternate provider recovery가 가능한지 각각 검증한다. + +4. **회귀 테스트 추가** + - active `repeat_guard`에서 partial `BODY` 뒤 `END`가 오지 않는 fixture + - response start만 온 pre-commit stall과 alternate-provider recovery + - stream-open stall의 단일 SSE error terminal + - pending 500-rune tail이 있는 상태의 stall/cancel 정리 + - 정상 finish chunk와 `[DONE]`/transport `END`의 exact-wire 단일 terminal + - `finish_reason`은 왔지만 `[DONE]`/EOF/`END`가 오지 않는 fixture에서 pending release와 종료 grace 동작 + - `finish_reason` 뒤 usage chunk와 정상 `[DONE]`이 오는 fixture에서 wire 순서 보존 및 terminal exactly-once + +5. **종료 처리 수정 방향 검토** + - 모든 active choice에 유효한 `finish_reason`이 확인되는 순간을 filter의 safe release trigger로 사용할지 검토한다. + - filter가 pass하면 sub-threshold pending content와 staged finish chunk를 release한다. + - trailing usage와 정상 `[DONE]`을 받을 수 있도록 짧고 bounded된 terminal grace를 둔다. + - grace 안에 `[DONE]`/`END`가 오지 않으면 terminal을 exactly-once로 finalize하고, 뒤늦은 중복 종료 신호는 무시한다. + - exact-wire passthrough 및 trailing usage 호환성 때문에 filter release trigger와 transport terminal commit을 같은 상태로 단순 합치지 않는다. + +6. **dev live smoke** + - Pi TUI와 headless dispatcher를 같은 model, context 크기, provider binding으로 비교한다. + - terminal 도착 시간, 마지막 progress부터 terminal까지의 idle, 선택 provider만 기록한다. + - secret, request 원문, response 원문은 evidence에 남기지 않는다. + +## 이번 세션에서 수행한 검증 + +환경: + +```text +/config/.local/bin/go -> /config/opt/go/bin/go +go version go1.26.2 linux/arm64 +GOROOT=/config/opt/go +``` + +실행 결과: + +```text +go test -count=1 ./apps/edge/internal/openai \ + -run 'Test(StreamGateChatConfiguredOutputFiltersCleanStreamSingleTerminal|OpenAITunnelCodecTerminalWire|StreamGateTunnelBuildRuntimeNoopPassRelaysRawBytes|DevRepeatGuardRequestDeadline)$' +PASS + +go test -count=1 ./packages/go/streamgate \ + -run 'Test(StreamReleaserPassThroughAndTerminal|StreamReleaserTerminalAfterFullReleaseSkipsSecondaryRelease|CommitBoundaryCommitsEmptySuccessAndTerminalOnce)$' +PASS +``` + +이 테스트들은 정상 terminal/release 경로가 현재 소스에서 동작함을 확인한다. 실제 장문맥 provider stall을 재현하거나 timeout 정합성을 검증한 것은 아니다. + +## 작업 시 주의사항 + +- `dev-corp` Spark evidence를 이 장애에 재사용하지 않는다. +- `onexplayer-lemonade` 같은 runtime alias만 보고 물리 장비를 단정하지 않는다. +- filter가 원인이라고 전제하지 않는다. 현재 증거상 filter 판정은 전부 pass다. +- 반대로 filter의 500-rune hold가 보이는 truncation을 확대한다는 사실도 누락하지 않는다. +- 단순히 Chronos dispatcher timeout만 늘리면 open stream을 오래 유지할 뿐이다. IOP liveness terminal과의 순서를 함께 설계한다. +- live 설정 변경, provider 재시작, route 변경, 배포는 이번 handoff 범위에서 수행하지 않았다. diff --git a/HANDOFF.md b/HANDOFF.md deleted file mode 100644 index a6e08ad0..00000000 --- a/HANDOFF.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -handoff_version: 1 -handoff_status: final -source_revision: 3155be0e275437a8eedc1aa93497955a7d30465b -rollback_revision: 3155be0e275437a8eedc1aa93497955a7d30465b -task13_complete_log_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/complete.log -task13_complete_log_sha256: 30d86f5364f12dd9f2bd77c6d7b44c9689d06454e43860321228a5cbd5783821 -pre_deletion_receipt_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/pre-deletion-transfer-receipt-v1.md -pre_deletion_receipt_sha256: bc1bf9eec498ebd8544eaf847f9f0a721436a41c9cb83c65d1aa6a5acfa460ba -pre_deletion_audit_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/pre-deletion-audit.log -pre_deletion_audit_sha256: 01be31c584e4ac16a8c92b8aae6bd9af74386b5b5596fade51d39bfb2cc39a85 -task13_verifier_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/verify-pre-deletion-receipt-v1.py -task13_verifier_sha256: 0efd71c57dedd61d4c4ab59bb36b6ab66f955b6a1af866914a617aac8891dd25 -original_manifest_artifact_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/iop-agent-chronos-ownership-manifest.tsv -original_manifest_sha256: d8598134dee99d96fc05a045091dd3d8932d4708d2c980c2a78c8b11825d2ccf -original_manifest_row_count: 300 -original_manifest_file_count: 290 -original_manifest_state_count: 10 -original_manifest_retain_generic_count: 135 -task03_historical_row_count: 303 -task03_historical_file_count: 293 -task03_historical_state_count: 10 -task03_universe_residuals: 0 -task03_duplicate_rows: 0 -boundary_delta_artifact_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/boundary-disposition-delta-v1.tsv -boundary_delta_sha256: c807d2aa87c0ffb54c3c43bb244be91926206b6a7a40ae8abf9d4d39b9fa667f -boundary_delta_row_count: 137 -boundary_addendum_artifact_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/boundary-surface-addendum-v1.tsv -boundary_addendum_sha256: c678d415daf01a3f68260a3912ffc0b4596f6b03d4f20a38cf796deb9623b670 -boundary_addendum_row_count: 137 -effective_matrix_artifact_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/effective-disposition-matrix-v1.tsv -effective_matrix_sha256: 34a85a470020329c79b1ffb61810ecd1aca5b795abdf1957d1c12de7fcf3975a -effective_matrix_row_count: 437 -state_schema_artifact_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/iop-agent-chronos-transfer-state-v1.schema.json -state_schema_sha256: e509b541b26b54401a33a3a1ae0ea8b8581933d64d9f1465bb714969c7eb63d4 -state_fixture_artifact_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/iop-agent-chronos-transfer-state-v1.json -state_fixture_sha256: 8a71dcc22cee5ed9990cb3a204ca8bc3bc8ffb949618a8a8bb6664a500016148 -bundle_receipt_artifact_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/13+09,10_receipt_lock_audit/iop-agent-chronos-transfer-bundle-receipt-v1.json -bundle_receipt_sha256: dab36aac1e3876b7b6a80d0b26a8e89ed59acd06d578db825e0cd79c290625de -state_original_manifest_logical_id_count: 10 -state_addendum_logical_id_count: 2 -state_logical_id_count: 12 -state_logical_ids_sha: 9bce499e286514baeb7ad9bc7c59119a3b5725f69763706dfeba06569d7404b9 -state_fixture_record_count: 12 -state_fixture_category_count: 8 -state_fixture_quarantine_count: 1 -real_state_export_location: bundle-member:acceptance-v1/state/state-export-v1.json -real_state_export_sha256: 93e4e19d67a6c29fa0cad8517cf2ca68cd94742bbc1ed5d3eedd3ae079fab74f -real_state_export_record_count: 12 -real_state_export_category_count: 8 -real_state_export_quarantine_count: 1 -bundle_location: withheld-owner-local -bundle_sha256: dd4bea1cd1d39e679f17bcd701d12274eee8298f4065b4f2442b615755439d94 -task09_complete_log_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/09+08_remove_agent_ui_close/complete.log -task09_complete_log_sha256: 580fb464b6c0a805a3a9c836ad29328fdc76a9b79cfa1e38660d8f63b26612a6 -task10_complete_log_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/10+09_neutral_execution/complete.log -task10_complete_log_sha256: 2f9a2583181ec2fd8d972bfa6a2d1cc33e25e8c8e92f32862f553a07b04e5fbe -import_graph_audit_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/10+09_neutral_execution/node-boundary-audit.log -import_graph_audit_sha256: 462bc1566b37327470d1faa23b1adf7311a5ede12d43364cd0f379bf229db23e -import_graph_residuals: 0 -task14_complete_log_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/14+13_remove_migration_surface/complete.log -task14_complete_log_sha256: 88f01218275429a1465553ba9bfdf3c55fdee21b589f3cb486537d4804c39efc -task14_removal_audit_path: agent-task/archive/2026/08/m-iop-agent-chronos-extraction-decoupling/14+13_remove_migration_surface/migration-removal-audit.log -task14_removal_audit_sha256: dad20631157fd35c461e7c72992462a396040aefc19c894b3736eef3a5b48659 -task14_removal_status: pass -removed_target_count: 12 -pre_delete_digest_count: 12 -post_delete_absence_count: 12 -final_transfer_remove_residuals: 0 -final_retained_path_mismatches: 0 -final_renamed_source_residuals: 0 -final_unclassified_paths: 0 -final_duplicate_paths: 0 -provider_node_regression: pass -full_go_regression: pass -readability_audit: pass -forbidden_surface_scan: pass -iop_node_owner: model-provider-device -chronos_connection_surfaces: 0 -cli_agent_terminal_workspace_surfaces: 0 -chronos_repository_mutations: 0 -external_mutations: 0 -canonical_promotion: applied -staging_promotion_commit: 7b90b7e5af9035fae2b5349c65eb322096d21b1b -canonical_base_commit: c3a24ec5febab9fc978fd62392efcb6c96e12ec9 -canonical_promotion_commit: c8e98d4e10b30114de7bafe426a4045abd6c1205 -canonical_promotion_verified_at: 2026-08-02T11:42:32Z -staging_boundary_correction_commit: 7cc9f2d142fac863eff173515f2a81e0a5c9e0f4 -canonical_boundary_correction_commit: 81243284cb89206911ec45e99f80701b591c88ae -canonical_boundary_correction_verified_at: 2026-08-02T12:13:14Z -canonical_origin_dev_commit: c3a24ec5febab9fc978fd62392efcb6c96e12ec9 -canonical_promotion_push_status: not-pushed -canonical_user_roadmap_restore_status: pass -canonical_user_roadmap_path_count: 3 -canonical_recovery_patch_initial_path: /tmp/iop-canonical-promotion.QOCOar/canonical-roadmap.patch -canonical_recovery_patch_initial_sha256: 1f1afe533e910b6311390071e07799bf6cc24ca742853bb45a3eceb3da1d4575 -canonical_recovery_stash_initial_commit: a91e4af2bc23c7fab2ef0ea026e2b453e9393848 -canonical_recovery_stash_initial_name: iop-canonical-promotion-roadmap-20260802T102956Z -canonical_recovery_patch_prepromotion_path: /tmp/iop-canonical-promotion.QOCOar/canonical-roadmap-prepromotion-20260802T112929Z.patch -canonical_recovery_patch_prepromotion_sha256: 314132fd4943d5a49e47a0fd499c39761bf4ff65f725f129c3a949f7e2927db6 -canonical_recovery_stash_prepromotion_commit: ff81ecb5e8b16fc81ecce4273afe24ff06236287 -canonical_recovery_stash_prepromotion_name: iop-canonical-promotion-roadmap-20260802T112929Z -integration_conflict_inventory_path: /tmp/iop-canonical-promotion.QOCOar/integration-unmerged.txt -integration_conflict_inventory_sha256: 15e564744e83accd74981a5976432d14d8638815cab0fefc1d1eaabe97a4aca4 -integration_conflict_count: 34 -integration_unexpected_conflicts_path: /tmp/iop-canonical-promotion.QOCOar/unexpected-conflicts.txt -integration_unexpected_conflicts_sha256: 15e564744e83accd74981a5976432d14d8638815cab0fefc1d1eaabe97a4aca4 -integration_qualification_worktree: /config/workspace/iop-canonical-integration -integration_evidence_directory: /tmp/iop-canonical-promotion.QOCOar/evidence -canonical_full_go_regression: pass -canonical_flutter_test_count: 44 -canonical_flutter_regression: pass -canonical_web_build: pass -canonical_control_plane_edge_wire: pass -canonical_credential_slot_smoke: pass -canonical_readability_audit: pass -canonical_reconnect_diagnostic: pass -canonical_generation_idempotency: pass -canonical_provider_only_boundary_audit: pass -agent_ui_reconciliation: pass -agent_ui_sync_state_sha256: abce070a46563ebd9b45599882c91814ac1236966c4d3c6a53ce3dbb840f1788 -downstream_lock: chronos:chronos-architecture-ownership-boundary -downstream_lock_status: enable -milestone_archive_status: completed -milestone_archived_at: 2026-08-02T12:34:51Z -staging_lock_identity: iop-s1:agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-chronos-extraction-decoupling.md -staging_lock_result: none -canonical_lock_identity: iop:agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-chronos-extraction-decoupling.md -canonical_lock_result: rely-on:chronos:chronos-architecture-ownership-boundary ---- -# Handoff: Chronos Extraction Decoupling Ownership Boundary - -> D04 전환 경계 정정: Chronos Server와 독립 Chronos Node가 loop, agent, workspace/tool, terminal/PTY, remote host control을 소유한다. Chronos는 필요할 때 IOP external inference API의 일반 client로 작동할 수 있지만, IOP에는 Chronos bridge, control hook, API, proto, config, target registry가 존재하지 않는다. - -- 작성일: 2026-07-31 (2026-08-02 D04 정리) -- 현재 타겟: IOP standalone surface 제거 및 D04 경계 수립 -- Status: canonical promotion verified; milestone closure and archive completed. -- 기록 위치: IOP 선행 분리 원본은 IOP Roadmap/SDD에, 완료 뒤 제품 개발 원본은 Chronos Roadmap에 둔다. - -## Canonical Promotion Receipt - -- The reviewed staging commit `7b90b7e5af9035fae2b5349c65eb322096d21b1b` was rebuilt as the single canonical promotion commit `c8e98d4e10b30114de7bafe426a4045abd6c1205` on base `c3a24ec5febab9fc978fd62392efcb6c96e12ec9`. -- A post-promotion boundary correction removed the residual standalone-Agent and terminal-session ownership wording from `packages/go/execution/doc.go`, `packages/go/execution/types.go`, and `apps/node/cmd/node/main.go`, and added the `TestRootCmdUsesProviderOnlyDescription` Node CLI regression. It landed as the staging child commit `7cc9f2d142fac863eff173515f2a81e0a5c9e0f4` on parent `7b90b7e5af9035fae2b5349c65eb322096d21b1b` and the canonical child commit `81243284cb89206911ec45e99f80701b591c88ae` on parent `c8e98d4e10b30114de7bafe426a4045abd6c1205`. Both children carry the identical four-path change set and the same stable patch id `2e89dc0ed8a45542531af2e16efaf397dd3b2602`; neither rewrites the reviewed promotion commit, and canonical-only credential support in `types.go` remains untouched. -- The exact 34-path conflict inventory is stored at `/tmp/iop-canonical-promotion.QOCOar/integration-unmerged.txt` with SHA-256 `15e564744e83accd74981a5976432d14d8638815cab0fefc1d1eaabe97a4aca4`. Every conflict was resolved in favor of the current managed projection, mTLS, credential-recipient, lease, and provider-runtime boundaries while removing standalone Agent, workspace, terminal, and session ownership. -- The canonical `dev` branch was fast-forwarded locally. `origin/dev` remains at the base commit and no push was performed. -- All three pre-existing roadmap edits were restored as unstaged user changes. Both patch generations and both retained stash commits above remain available for recovery. -- The final integration and canonical verification matrix passed full Go tests, 44 Flutter tests, the Flutter web build, Control Plane/Edge wire checks, deterministic credential-slot smoke profiles, readability audit, reconnect diagnostics, generated-source idempotency, and provider-only forbidden-surface scans. -- `console-shell` is reconciled as implemented with no Agent enum, slot, or rail action. The milestone reconciliation record is stored in `agent-ui/.sync-state.json`. -- The Milestone and SDD were archived after the canonical verification gates passed, and the Chronos dependency was changed to `enable`. The canonical branch remains local and was not pushed. - -## 사용자 확정 사항 - -1. `/config/workspace/iop-s0`에서 진행했던 `IOP Agent CLI Runtime` Milestone은 기존 범위대로 완료됐다. -2. Chronos Server 및 독립 Chronos Node가 loop/agent/workspace/tool/terminal/PTY/remote host control 소유권을 전적으로 관리한다. -3. Chronos는 IOP external inference API의 일반 client로 작동할 수 있으나, IOP Node는 Chronos bridge나 control hook을 갖지 않는다. -4. Task 10은 `agent-contract/inner/iop-agent-cli-runtime.md`를 물리 삭제한다. -5. evidence 순서는 다음과 같이 관리하며 이 단계에서 최종 완료를 선포하지 않는다: - - task 10: contract 원문 및 transitional runtime/CLI provider 물리 삭제 - - task 13: effective matrix와 pre-deletion receipt 생성 - - task 14: 잔여 migration surface 최종 삭제 - - task 15: `HANDOFF.md` final composite receipt 작성 - -## 프로젝트 이름과 상징 - -프로젝트명은 **Chronos**로 확정한다. - -- 영문 문구: `Chronos — Take your time back.` -- 한국어 문구: `일은 맡기고, 시간은 되찾다.` -- 저장소 기본명: `chronos` -- CLI 기본명: `chronos` -- daemon 기본명: `chronosd` -- runtime package/product family: `chronos-runtime` - -## 최종 방향 - -Chronos Server 및 독립 Chronos Node가 agent control과 workspace execution을 독립 수행한다. -IOP는 external inference API와 model/provider/device execution, cancel, status, usage lifecycle만 제공한다. - -```text -Chronos Server / Chronos Node -> loop, agent, workspace/tool, terminal/PTY, remote control -Chronos -> optional ordinary client of IOP external inference API -IOP -> no Chronos bridge/API/proto/config/target/registry/control hook -``` - -책임은 다음과 같이 고정한다. - -| 소유자 | 책임 | -|---|---| -| `Chronos` | standalone runtime core, CLI/daemon, local control, workflow adapter, durable state/replay, scoped execution | -| IOP | external inference API, model/provider execution, cancel, status, usage lifecycle | - -## Next Steps and Evidence Sequence - -1. Task 10은 `agent-contract/inner/iop-agent-cli-runtime.md`를 물리 삭제한다. -2. Task 13: effective matrix와 pre-deletion receipt 기록. -3. Task 14: 남은 migration surface 최종 삭제. -4. Task 15: `HANDOFF.md` final composite receipt 작성.