Skip to content

refactor(memory): 공개 챗봇 대화 기록을 클라이언트 전달 방식으로 전환 - #633

Merged
HyungminYoon1 merged 26 commits into
devfrom
feature/mba-318
Jul 29, 2026
Merged

refactor(memory): 공개 챗봇 대화 기록을 클라이언트 전달 방식으로 전환#633
HyungminYoon1 merged 26 commits into
devfrom
feature/mba-318

Conversation

@HyungminYoon1

@HyungminYoon1 HyungminYoon1 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

변경 사항

  • Public Chatbot 대화를 서버 Session/Turn/Transcript에 저장하지 않고 Embed Client가 완료된 최근 대화를 매 요청의 conversation.history로 전달하도록 전환했습니다.
  • Client는 React memory의 완료된 user/assistant pair만 최신 20 turn까지 보내며 welcome/error/pending message와 localStorage/sessionStorage 기반 conversation ID를 제외합니다.
  • Gateway는 exact envelope·role·order·message/envelope 크기를 검증하고 현재 inputs와 history에 4,096-token 상한을 적용합니다. 초과 시 가장 오래된 완료 pair만 제거하며 legacy memory_mode/conversation_id는 거부합니다.
  • Workflow Engine은 client-forged role을 provider role로 신뢰하지 않고 하나의 untrusted data block으로 처리합니다. Public run/node/trace의 input, history, prompt와 completion은 저장하지 않으며 이 비저장 경계를 하위 workflow에도 전파합니다.
  • Public Conversation lifecycle route를 active API에서 제거했습니다. Celery 결과는 소비 직후 제거하고 cleanup 장애는 정상 응답을 실패로 바꾸거나 원문 오류를 로그에 남기지 않습니다.

관련 이슈

Closes #521

  • Linear: MBA-318
  • Authenticated durable Memory follow-up: MBA-387
  • Additional follow-up: MBA-385, MBA-386

변경 유형

  • 버그 수정
  • 새로운 기능
  • 리팩토링
  • 문서 수정
  • 기타

테스트

  • 로컬에서 테스트 완료
  • 기존 테스트 통과 확인

검증 결과:

  • Ruff 0.15.21: 변경 Python 파일 통과
  • Shared/Gateway/API/service/architecture 관련 pytest: 76 passed
  • Workflow LLM/Logger/child lifecycle 관련 pytest: 155 passed
  • Client history Vitest: 3 passed
  • 변경 Client 파일 ESLint: 오류 0건, 기존 no-explicit-any 경고 1건
  • Client 전체 tsc --noEmit --incremental false: 통과
  • ADR registry와 git diff --check: 통과
  • 실제 PostgreSQL, 전체 E2E와 전체 저장소 회귀는 변경 범위 정책에 따라 로컬에서 실행하지 않고 CI에 위임

보호 리소스·외부 실행 경계 (해당 시)

  • 적용 여부: [x] 적용 [ ] 비적용
  • 비적용 사유: 해당 없음. Public 사용자 입력과 외부 LLM 호출을 다루므로 적용
  • 완결성 매트릭스의 계약·구현·검증 증거: docs/features/conversation-memory/{requirements,api_spec,component_spec,test_cases}.md, docs/decisions/ADR-0074-public-chatbot-client-held-history.md
  • 저장·GraphMutation과 secret 비저장
  • 관리 API/UI와 user/team 권한 부여·회수
  • Deployment preflight와 runtime/background 재검증
  • Lifecycle·TOCTOU·stable identity/deduplication·crash replay·capability/lease
  • Audit event 생성·canonical action/status·중복 방지·secret/PII redaction
  • 적용 동작 경계마다 구현 위치와 실행 가능한 테스트 증거 기록
  • 미완료 항목 또는 후속 이슈: Public은 durable resource와 lifecycle을 만들지 않으므로 관리 API/UI, stable identity, lease와 purge는 해당 없음입니다. 인증형 내부 Chatbot의 RBAC·durable lifecycle·provider fence·retention은 MBA-387에서 구현합니다. Public 월 예산 audit은 MBA-385, 별도 execution journal은 MBA-386 범위입니다.

스크린샷 (UI 변경 시)

화면 디자인 변경 없음. Embed Chat의 request payload와 대화 상태 보관 방식만 변경했습니다.

@linear-code

linear-code Bot commented Jul 23, 2026

Copy link
Copy Markdown

MBA-318

@HyungminYoon1
HyungminYoon1 marked this pull request as ready for review July 23, 2026 06:42

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d9e35f093

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +77 to +80
send_workflow_task(
self._celery_app,
"workflow.execute_conversation_turn",
args=[envelope.to_payload()],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Conversation turn task를 Worker에 등록하세요

MEMORY_PUBLIC_RUNTIME_ENABLED=true로 요청을 수락하면 여기서 workflow.execute_conversation_turn을 발행하지만, 해당 이름을 등록하는 Celery task가 커밋 전체에 없고 Workflow Worker 진입점도 기존 tasks.pyknowledge_collection_sync_tasks.py만 import합니다. 따라서 Worker는 메시지를 실행하지 못하고 turn이 pending_dispatch에 머무르므로, 새 실행 use case를 호출하는 실제 task와 composition을 Worker에 연결해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L7-L12

Useful? React with 👍 / 👎.

Comment thread apps/gateway/api/v1/endpoints/run.py Outdated
Comment on lines +156 to +158
"status_path": (
f"/api/v1/run-public/{url_slug}/conversation/turns/{turn_id}"
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 반환한 status_path에서 turn 결과를 조회하게 하세요

정상 접수 응답이 이 URL을 polling 경로로 광고하지만, 실제 get_public_turn_status handler는 유효한 grant까지 검증한 뒤 항상 memory.session_hidden 404를 반환합니다. 따라서 실행이 완료돼도 호출자는 이 응답으로 상태나 assistant 결과를 받을 수 없으며, Accepted ADR-0072의 public turn status/display projection 계약에도 어긋납니다. 상충하는 feature API 문서보다 Accepted ADR이 우선하므로 status query를 구현하거나 동작하는 조회 경로를 반환해야 합니다.

AGENTS.md reference: AGENTS.md:L55-L64

Useful? React with 👍 / 👎.

Comment on lines +137 to +142
if self.dispatch_publisher is not None and result.turn_state not in {
TurnStatus.COMPLETED,
TurnStatus.FAILED,
TurnStatus.CANCELLED,
} and not result.replayed:
self.dispatch_publisher.publish(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Publish 실패 후 exact retry가 dispatch를 재발행하게 하세요

최초 요청에서 turn/dispatch transaction은 이미 commit된 뒤 publisher가 호출되므로, broker 연결 오류로 send_workflow_task가 실패하면 durable turn만 남고 API는 오류를 반환합니다. 같은 idempotency key로 재시도하면 기존 turn이 replayed=True가 되어 이 조건에서 publish를 건너뛰며, 저장소 전체에도 expired dispatch를 다시 발행하는 recovery caller가 없어 session의 active turn이 영구 점유됩니다. Pending/expired dispatch replay 또는 별도 recovery worker가 반드시 재발행을 담당해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

Comment thread apps/memory/application/execution.py Outdated
turn_version=scope.turn.version,
replayed=True,
)
raise StaleTurnVersionError()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 만료 lease의 새 attempt로 running turn을 인계하세요

Worker가 turn을 RUNNING으로 기록한 뒤 종료되고 Workflow admission lease가 만료되면, 재전달은 새 delivery_attempt_id와 lease generation으로 정상 claim됩니다. 그러나 Memory projection에는 이전 latest_attempt_id가 남아 있어 여기서 무조건 StaleTurnVersionError가 발생하므로 새 owner가 input read나 completion으로 진행할 수 없고 active turn도 계속 막힙니다. 만료된 admission lease의 current generation을 검증한 뒤 running turn의 attempt fence를 새 owner로 인계하는 전이가 필요합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

Comment on lines +298 to +302
prompt = data.get(prompt_key)
if prompt is not None and not isinstance(prompt, str):
raise _error("memory.llm_behavior_unsupported")
if prompt:
prompt_variables.update(_JINJA_VARIABLE.findall(prompt))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prompt 변수 위치를 preflight에서 동일하게 검증하세요

Mapped 변수를 system_prompt에만 두고 user_prompt에는 두지 않은 graph도 이 코드는 세 prompt의 변수 합집합만 비교하므로 runtime-ready로 승인됩니다. 이후 Worker의 _messages는 system/assistant 변수를 금지하고 user prompt에 mapped 변수를 요구해, admission과 running 전이 및 context claim까지 마친 뒤 provider 호출 전에 예외를 내고 turn을 점유한 채 남깁니다. Accepted ADR-0072가 요구하는 동일 validator 계약대로 각 prompt의 변수 위치까지 여기서 거부해야 합니다.

AGENTS.md reference: AGENTS.md:L55-L64

Useful? React with 👍 / 👎.

Comment thread apps/gateway/composition/memory.py Outdated
redis_client=None,
) -> PublicConversationRuntimeApplication:
values = environ if environ is not None else os.environ
validate_public_conversation_runtime_configuration(values)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Runtime readiness를 Gateway 시작 시 검증하세요

Runtime 설정 검증이 application을 만드는 요청 경로에서만 호출되고, Gateway startup은 기존 validate_public_conversation_security_configuration만 실행합니다. 따라서 MEMORY_PUBLIC_RUNTIME_ENABLED=true인데 lifecycle flag, worker-ready flag 또는 runtime 전용 keyring이 잘못된 배포도 정상 기동한 뒤 모든 conversation run에서만 503을 반환하며 readiness probe로 구성 오류를 발견할 수 없습니다. 활성화 계약을 process startup/lifespan에 연결해 배포가 fail-closed하도록 해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L17-L17

Useful? React with 👍 / 👎.

self._mark_unknown(usage_attempt, "provider_call_failed")
raise ProviderInvocationOutcomeUnknownError() from exc

text, usage = _response_projection(response)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 잘못된 provider 응답도 usage outcome_unknown으로 기록하세요

Provider 호출이 성공한 뒤 응답에 choices, text 또는 유효한 usage가 없거나 text가 16 KiB를 넘으면 _response_projectionProviderInvocationOutcomeUnknownError를 던지지만, 이 호출은 _mark_unknown을 수행하는 invoke 예외 블록 밖에 있습니다. 상위에서는 Memory와 Workflow admission만 outcome-unknown으로 닫혀 usage operation은 provider_started에 남고, 15분 stale reconciliation 전까지 비용·usage 상태가 서로 어긋납니다. Projection 실패도 즉시 같은 usage attempt를 outcome-unknown으로 전이해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L15-L15

Useful? React with 👍 / 👎.

shared_session=None,
)
)
attribution = lease.finalize_request()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Usage intent 뒤 credential binding을 다시 검증하세요

finalize_request()가 credential permission·revision과 capability를 재검증하지만, 그 뒤 별도 transaction으로 usage intent와 Memory marker를 기록한 다음에는 grant/session만 재검증하고 곧바로 provider를 호출합니다. 따라서 finalize 직후 credential revoke, policy 교체 또는 egress revision 변경이 발생하면 stale client와 이미 복호화된 key로 외부 호출이 진행됩니다. Usage intent commit 뒤 provider-start marker 전에 capability/credential binding을 다시 확인해 revoke가 외부 I/O를 차단하도록 해야 합니다.

AGENTS.md reference: AGENTS.md:L194-L194

Useful? React with 👍 / 👎.

and latency_value >= 0
else 0
)
usage_attempt.record_success(usage=usage, latency_ms=latency_ms)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Assistant checkpoint 전에 usage를 terminalize하지 마세요

유효한 provider 응답을 받은 뒤 여기서 usage success를 먼저 commit하고, 호출자에게 반환된 다음에야 Memory assistant checkpoint가 저장됩니다. 이 두 단계 사이에서 Worker가 종료되면 usage ledger는 terminal success라 provider replay를 금지하지만 raw assistant output은 어느 durable store에도 없어 CompleteTurn을 재개할 수 없으며 turn이 영구 점유됩니다. ADR-0072의 순서대로 assistant output checkpoint를 먼저 durable하게 만든 뒤 usage/completion을 terminalize해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

attempt_id=claimed.attempt_id,
checkpoint=checkpoint,
)
self.admissions.finish(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Memory terminal 상태에서 admission을 재조정하세요

memory.complete와 Workflow admission finish가 서로 다른 transaction이므로, 첫 호출이 commit된 직후 Worker가 종료되면 Memory turn은 completed이고 session은 release됐지만 admission은 계속 leased로 남습니다. 재전달 시 observe_admitted는 completed/failed turn을 허용하지 않아 admission을 terminal result reference로 닫을 경로가 없으며, 동일한 crash window가 failure 처리에도 존재합니다. Memory의 durable terminal/checkpoint를 읽어 admission finish만 idempotent하게 재조정하는 경로가 필요합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

@HyungminYoon1
HyungminYoon1 force-pushed the feature/mba-318 branch 2 times, most recently from 2bb8b86 to 85b3bf7 Compare July 23, 2026 08:44
@HyungminYoon1 HyungminYoon1 reopened this Jul 23, 2026
@HyungminYoon1 HyungminYoon1 changed the title [MBA-318] 대화 메모리 런타임 admission 및 provider fence 구현 feat: 대화 메모리 런타임 admission 및 provider fence 구현 Jul 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee1525c564

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +533 to +536
repository = SqlAlchemyConversationMemoryRepository(session)
return operation(
repository,
SqlAlchemyMemoryUnitOfWork(session),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Context persistence repository를 production 경로에 연결하세요

SqlAlchemyConversationMemoryRuntimeAdapter가 여기서 전달하는 SqlAlchemyConversationMemoryRepository에는 BuildMemoryContextUseCase가 호출하는 list_prior_context_candidates/find_context_plan/add_context_plan과 lease·attempt 관련 메서드가 전혀 구현되어 있지 않습니다(저장소 전체 검색에서도 Protocol과 test fake만 존재합니다). 따라서 정상 turn도 provider 준비 후 첫 build_context에서 AttributeError가 발생해 provider 호출이나 완료에 도달하지 못하므로, context model용 실제 persistence adapter를 구현해 연결해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L7-L10

Useful? React with 👍 / 👎.

Comment thread apps/gateway/api/v1/endpoints/run.py Outdated
# Target Conversation Memory uses its own lifecycle/grant endpoints. Do
# not let a root-level conversation envelope reach the legacy runtime until
# MBA-318 installs the verified vertical execution contract.
if "conversation" in request_body:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Memory-enabled deployment의 legacy fallback을 차단하세요

Versioned Conversation Memory graph에 public caller가 root conversation 필드만 생략하면 이 조건을 통과해 기존 DeploymentService.run_deployment로 실행됩니다. 그 경로는 validate_conversation_memory_runtime을 호출하지 않고 Conversation grant와 새 provider capability fence도 요구하지 않으므로, ADR-0073에서 Memory-OFF 요청에만 보존한 legacy 실행을 Memory-enabled graph에도 열어 둡니다. 요청 필드 존재 여부가 아니라 active deployment contract를 확인해 Memory-enabled graph의 비-envelope 요청을 provider I/O 전에 거부해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

Comment on lines +302 to +309
preparation = self.provider.prepare(
binding=binding,
admission_id=claimed.admission_id,
execution_id=claimed.execution_id,
node_invocation_id=node_invocation_id,
node_data=llm_data,
deployment_config=graph.deployment_config,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Provider 준비 실패에서도 turn을 terminalize하세요

Dispatch 이후 credential이 revoke되거나 capability 발급이 영구 거부되면 provider.prepare()가 예외를 내지만, 이 호출은 아래 provider.generate() 예외 처리 범위 밖에 있습니다. 이미 turn은 RUNNING으로 기록된 상태라 task의 3회 retry가 모두 소진되어도 memory.fail과 admission finish가 실행되지 않고 session.active_turn_id가 계속 점유되어 이후 대화를 막습니다. Provider 미전송 단계의 영구 오류도 안전한 failed 전이로 닫거나 retry 고갈 시 동일한 terminal finalizer를 실행해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L15-L15

Useful? React with 👍 / 👎.

Comment on lines +271 to +276
claimed = self.admissions.claim(
binding,
owner=command.worker_owner,
attempt_id=stable_attempt_id,
lease_deadline=now + self.lease_duration,
now=now,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Provider 호출 동안 execution lease를 유지하세요

Production execution lease는 30초지만 provider client는 일반 OpenAI 요청도 최대 60초, 일부 모델은 180초까지 기다리며 provider.generate() 동안 lease 갱신이 없습니다. 호출이 30초를 넘고 duplicate delivery가 도착하면 새 owner가 generation을 인계해 기존 provider_started usage를 outcome-unknown으로 닫고 turn을 실패시킬 수 있는 반면, 이전 owner는 provider 응답 후 다음 fence 검사 전에 assistant checkpoint까지 쓸 수 있어 성공 응답과 failed 상태가 경합합니다. Provider timeout보다 긴 lease/heartbeat를 적용하고 checkpoint 이전에도 current generation을 검증해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

usage_recorder=recorder,
limits=limits,
),
observer=LoggingConversationObserver(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Conversation 실행을 durable Workflow projection에 기록하세요

Production composition이 선택하는 observer는 세 이벤트를 application log에만 남기며 WorkflowRun/WorkflowNodeRun 또는 durable safe journal을 생성하는 adapter가 저장소 어디에도 없습니다. 따라서 public Conversation turn은 실행·trace 조회에 나타나지 않고 observer/로그 전송 장애도 _observe()에서 그대로 무시되어 reconciliation할 기록이 없으므로, Accepted ADR-0073의 content-free Workflow projection 계약과 audit/trace 데모 흐름을 충족하지 못합니다. Public principal과 workflow/run/node 식별자를 가진 durable observer/outbox를 production composition에 연결해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L10-L14

Useful? React with 👍 / 👎.

except Exception as error:
raise _map_public_error(error) from None
raise _hidden_error()
_set_public_headers(response)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Turn status에 lifecycle ETag를 반환하세요

새 turn status 응답은 _set_public_headers(response)에 lifecycle revision을 전달하지 않아 ETag가 빠집니다. Conversation API 명세는 create/run/transcript/turn/close/reset 모두 현재 session의 "lifecycle-revision-N" ETag를 반환하도록 요구하므로, polling client가 이 query를 통해 현재 lifecycle precondition을 갱신할 수 없고 이후 close/reset/delete에 필요한 If-Match 계약과도 어긋납니다. Status result에 lifecycle revision을 포함해 header를 설정해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L8-L8

Useful? React with 👍 / 👎.

Comment thread apps/gateway/api/v1/endpoints/run.py Outdated
) from None
try:
application = build_public_conversation_runtime_application(db)
result = application.start_turn.execute(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 동기 turn admission을 event loop 밖에서 실행하세요

run_workflow_publicasync def인데 새 Conversation 분기에서 동기 start_turn.execute()를 직접 호출합니다. 이 호출은 PostgreSQL row lock과 commit, Redis admission EVAL, 별도 DB session의 Celery publish까지 모두 완료한 뒤 반환하므로 DB나 broker latency가 발생하면 Gateway event loop가 그 시간 동안 막혀 동일 process의 무관한 요청까지 지연됩니다. 기존 sync lifecycle endpoint처럼 threadpool에서 실행하거나 이 분기만 명시적으로 run_in_threadpool로 오프로딩해야 합니다.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c377afeca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

turn_id=binding.turn_id,
state=ConversationExecutionState.FAILED,
)
context_build = self.memory.build_context(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Provider 이전 오류에서도 running turn을 종결하세요

이전 context의 암호문·reference가 손상되거나 key를 사용할 수 없는 경우처럼 build_context/claim_context/_messages에서 영구 오류가 발생하면, turn은 이미 RUNNING으로 전이됐지만 이 구간은 아래 provider 예외 finalizer 밖에 있습니다. Celery의 bounded retry가 모두 끝난 뒤에도 Turn과 admission이 terminal 상태가 되지 않고 session.active_turn_id가 계속 점유되므로 이후 대화가 막힙니다. Provider 미전송 오류를 safe failure로 종결하거나 retry 고갈 시 같은 terminal reconciliation을 실행해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

Comment thread apps/memory/adapters/security.py Outdated
Comment on lines +497 to +500
digest = hmac.new(
self._keys[self._primary],
payload,
hashlib.sha256,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stored fingerprint key version으로 exact retry를 검증하세요

MEMORY_RUNTIME_ADMISSION_HMAC_KEYS의 primary를 교체하고 이전 key를 keyring에 유지한 환경에서도 fingerprint는 항상 새 primary로만 계산됩니다. 기존 Turn은 request_fingerprint_key_version과 이전 key로 만든 digest를 저장하므로, rotation 이후 동일한 idempotency key와 입력을 재시도하면 ensure_replay_matches가 conflict를 내고 publish 실패 복구 같은 exact retry도 중단됩니다. 기존 Turn의 stored key version으로 fingerprint를 재계산할 수 있게 해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

now=now,
)
self.repository.save_session(session)
self.repository.add_turn(turn)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 저장된 public turn을 transcript에 투영하세요

이 변경은 public Turn과 encrypted entry를 저장하지만, 실제 transcript query인 GetPublicTranscriptUseCase는 여전히 apps/memory/application/public_lifecycle.py:1325-1363에서 turns=()를 고정 반환합니다. 따라서 사용자가 여러 turn을 성공적으로 완료해도 /conversation/transcript는 항상 빈 배열이며, 이번 변경에서 구현 완료로 문서화한 completed bounded display projection과 대화 데모 흐름이 동작하지 않습니다. 승인된 display만 복호화하는 bounded turn query를 transcript composition에 연결해야 합니다.

AGENTS.md reference: AGENTS.md:L198-L198

Useful? React with 👍 / 👎.

turn_id = uuid.uuid4()
user_entry_id = uuid.uuid4()
dispatch_id = uuid.uuid4()
sequence = session.claim_turn(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 101번째 completed turn을 dispatch 전에 차단하세요

공식 Conversation Memory 계약은 public session당 completed turn을 최대 100개로 제한하지만, terminal turn이 release된 뒤 이 경로는 누적 개수를 확인하지 않고 매번 claim_turn과 새 entry/dispatch 생성을 진행합니다. 따라서 101번째 이후 요청도 provider 호출과 영구 row 생성을 계속해 session 단위 비용·저장소 상한을 우회합니다. 현재 completed count를 잠금 범위에서 검사해 Turn/Dispatch write와 broker publish 전에 bounded 오류로 거부해야 합니다.

AGENTS.md reference: AGENTS.md:L150-L150

Useful? React with 👍 / 👎.

Comment on lines +84 to +87
send_workflow_task(
self._celery_app,
"workflow.execute_conversation_turn",
args=[envelope.to_payload()],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Conversation task를 versioned queue로 격리하세요

Shared Celery 설정의 task_routes는 모든 workflow.*를 동일한 workflow queue로 보내고 이 publish 호출도 별도 queue를 지정하지 않습니다. 따라서 rolling deployment에서 아직 workflow.execute_conversation_turn을 등록하지 않은 구버전 Workflow worker가 메시지를 먼저 받을 수 있으며, payload 내부의 minimum_worker_capability guard까지 도달하지 못한 채 Turn은 published 상태로 남아 재발행되지 않습니다. 새 task를 versioned queue/worker pool로 라우팅하고 readiness가 그 consumer를 확인하도록 해야 합니다.

AGENTS.md reference: AGENTS.md:L173-L173

Useful? React with 👍 / 👎.

Comment thread apps/gateway/api/v1/endpoints/run.py Outdated
Comment on lines +125 to +126
result = await asyncio.to_thread(
_start_public_conversation_turn,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Conversation 분기에서도 workflow budget을 강제하세요

Conversation 요청은 여기서 새 StartTurn application을 직접 호출하고 legacy DeploymentService로 진입하지 않는데, monthly Workflow budget 차단은 DeploymentService._execute_deployment_snapshotWorkflowBudgetService.ensure_workflow_budget_allows_execution에만 있습니다. Memory application과 Worker provider 경로에는 동등한 aggregate budget 판단이 없으므로 budget을 이미 소진한 workflow도 public conversation을 계속 dispatch하고 외부 LLM 비용을 발생시킬 수 있습니다. Turn/dispatch 생성 전에 budget을 확인하고 provider I/O 직전에도 필요한 current 정책을 재검증해야 합니다.

AGENTS.md reference: AGENTS.md:L144-L144

Useful? React with 👍 / 👎.

@HyungminYoon1

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa2333521d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/gateway/composition/memory.py Outdated
secrets=secrets,
content_cipher=content_cipher,
fingerprinter=fingerprinter,
budget=WorkflowBudgetDecisionAdapter(db),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 예산 조회를 별도 DB 세션으로 분리하세요

MEMORY_PUBLIC_RUNTIME_ENABLED=true인 새 logical request에서는 preflight commit 후 이 어댑터가 같은 db로 예산을 조회합니다. evaluate_workflow_budget_execution()begin_nested()가 새 outer transaction을 autobegin한 뒤 savepoint만 종료하므로, 예산이 allowed여도 이어지는 _start()가 동일 세션에서 SqlAlchemyMemoryUnitOfWork.begin()을 호출할 때 이미 열린 transaction과 충돌해 memory.adapter_unavailable 503으로 끝납니다. 예산 판정을 disposable session에서 수행하거나 다음 UoW 전에 읽기 transaction을 명시적으로 종료해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L13-L13

Useful? React with 👍 / 👎.

@@ -139,6 +139,12 @@ spec:
key: SECRET_KEY
- name: MEMORY_PUBLIC_CONVERSATION_ENABLED
value: {{ .Values.memoryPublicConversation.enabled | quote }}
- name: MEMORY_PUBLIC_RUNTIME_ENABLED

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge capable worker의 필수 런타임 환경값을 배포하세요

Helm에서 이 값을 true로 바꾸면 Gateway startup validator가 요구하는 MEMORY_CONTENT_*MEMORY_RUNTIME_ADMISSION_HMAC_* 값이 gateway pod에 전혀 렌더링되지 않아 시작부터 실패합니다. 또한 새 queue를 소비하는 worker에도 content key와 MEMORY_RUNTIME_PROVIDER_{INPUT_TOKEN,OUTPUT_TOKEN,COST}_CAP* 값이 전달되지 않아, Gateway를 별도 패치해 시작하더라도 모든 conversation task가 composition 단계에서 재시도 고갈됩니다. Docker Compose도 같은 필수 값들을 gateway/worker environment에 전달하지 않으므로 activation flag와 함께 양쪽 프로세스의 key/cap 설정을 values, Secret 및 environment에 연결해야 합니다.

AGENTS.md reference: AGENTS.md:L173-L173

Useful? React with 👍 / 👎.

before_provider_start(usage_attempt.operation_reference)
except WorkflowBudgetBlockedError:
try:
usage_attempt.record_definitive_failure(reason_code="budget.exceeded")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge budget 차단을 INTENT 상태에서 종결할 수 있게 하세요

Gateway admission 뒤 월 예산이 소진되어 Worker의 provider 직전 재검사가 차단되는 경우, 이 호출 시점의 usage attempt는 아직 mark_provider_started() 전이라 _started=False입니다. 따라서 _LedgerProviderUsageAttempt.record_definitive_failure()가 즉시 provider_usage.outcome_not_allowed를 던지며, ledger domain도 INTENT 상태와 budget.exceeded reason을 허용하지 않습니다. 결국 정상적인 budget 차단이 provider_outcome_unknown으로 오분류되고 durable intent도 미종결 상태로 남으므로, provider 미호출 INTENT -> failed_definitive 전이를 별도로 지원해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L15-L15

Useful? React with 👍 / 👎.

Comment on lines +98 to +101
failure = RecordTurnDispatchPublishFailureUseCase(
repository=repository,
uow=uow,
).execute(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge RECONCILE_REQUIRED dispatch를 자동으로 재처리하세요

Broker 전송 실패로 여기서 dispatch가 RECONCILE_REQUIRED가 되거나 Gateway가 claim commit 뒤 실제 전송 전에 종료되면, repo-wide reference상 이 row를 소비하는 scheduled reconciler는 없고 같은 idempotency key의 후속 POST만 재발행을 시도합니다. 특히 crash 직후 30초 claim deadline 안에 재시도하면 active CLAIMED 상태라 publish 없이도 202를 반환하므로 클라이언트는 성공으로 인식하고, Turn과 session.active_turn_id는 영구 점유되어 이후 모든 새 turn이 충돌합니다. memory_turn_dispatch_jobs를 주기적으로 reclaim/terminalize하는 worker를 연결하거나 미전송 claim을 202로 승인하지 않아야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

Comment thread apps/gateway/composition/memory.py Outdated
Comment on lines +86 to +89
content_cipher = (
FernetMemoryContentCipher.from_environment(values)
if public_conversation_runtime_enabled_from_environment(values)
else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 런타임 비활성화 후에도 기존 transcript를 복호화하세요

한 번 runtime을 활성화해 completed Turn을 저장한 뒤 Worker 장애 대응 등으로 MEMORY_PUBLIC_RUNTIME_ENABLED=false만 전환하면, lifecycle application은 계속 서비스되지만 여기서 content_cipher=None으로 구성됩니다. 그 결과 GetPublicTranscriptUseCase가 기존 approved display를 만날 때마다 MemoryAdapterUnavailableError를 내고, retention 동안 허용되어야 하는 active/closed session transcript가 모두 503으로 바뀝니다. 새 turn admission을 끄는 runtime flag와 기존 content read/decryption readiness를 분리해 rollback 중에도 transcript를 유지해야 합니다.

AGENTS.md reference: AGENTS.md:L140-L144

Useful? React with 👍 / 👎.

Comment on lines +513 to +514
if decision.status == "blocked":
raise WorkflowBudgetBlockedError()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Conversation 예산 차단 audit을 기록하세요

월 예산이 이미 초과된 새 public conversation run은 여기서 WorkflowBudgetBlockedError만 발생시키고, 이 use case와 WorkflowBudgetDecisionAdapter 어디에도 기존 실행 경로의 policy.block audit writer가 없습니다. 따라서 429 응답은 반환되지만 budget-management의 직접 실행 계약인 workflow target, public actor, reason='budget.exceeded', trigger mode를 가진 차단 audit이 영구 누락되며, Worker에서 처음 차단되는 경우도 usage audit만 있고 동일한 policy audit은 남지 않습니다. 실패 응답 transaction과 분리된 durable audit 경계를 Gateway와 Worker budget gate에 연결해야 합니다.

AGENTS.md reference: AGENTS.md:L144-L144

Useful? React with 👍 / 👎.

Comment on lines +1814 to +1819
payload = json.dumps(
{"after": after_sequence, "v": 1},
sort_keys=True,
separators=(",", ":"),
).encode("ascii")
return base64.urlsafe_b64encode(payload).decode("ascii").rstrip("=")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge transcript cursor에 무결성 바인딩을 추가하세요

현재 cursor는 {"after": N, "v": 1}을 URL-safe base64로 인코딩할 뿐 서명이나 session binding이 없습니다. 따라서 유효한 Conversation grant를 가진 호출자는 임의의 sequence로 같은 JSON을 다시 인코딩해 서버가 발급하지 않은 cursor를 통과시키고 transcript 앞부분을 건너뛸 수 있어, MEM-TC-API-040M의 cursor 변조 fail-closed 계약을 만족하지 않습니다. Cursor payload를 organization/session과 함께 HMAC 또는 AEAD로 보호하고 검증 실패를 hidden error로 처리해야 합니다.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e37cf6df9f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

MEMORY_POLICY_VERSION = "memory-policy-v1"
MEMORY_STORAGE_GENERATION = 1
MAX_MEMORY_TURNS = 20
MAX_MEMORY_CONTEXT_TOKENS = 8_192

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Memory context 상한을 4,096 tokens로 낮추세요

maxContextTokens가 4,097~8,192인 graph도 이 상수와 _validate_llm()을 통과하고, 이후 context materializer가 해당 값을 실제 token budget으로 사용합니다. 그러나 docs/features/conversation-memory/requirements.md:202test_cases.md:306은 server upper bound를 4,096으로 고정하고 node config는 이를 낮추기만 허용하므로, provider input cap이 충분히 큰 배포에서는 승인된 상한의 두 배까지 과거 대화를 외부 LLM에 전송해 비용·latency 경계를 위반합니다.

AGENTS.md reference: AGENTS.md:L198-L198

Useful? React with 👍 / 👎.

Comment thread apps/gateway/composition/memory.py Outdated
admission = RedisPublicConversationAdmission(
redis_client if redis_client is not None else _redis_client(values),
hmac_key=_admission_key(values),
policy=public_conversation_admission_policy_from_environment(values),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Public run limiter를 승인된 기본값으로 구성하세요

Helm/Compose처럼 별도 rate-limit 환경값을 주입하지 않는 배포에서 이 새 runtime wiring은 기존 policy 기본값인 deployment 60/min, organization 240/min, grant 30/min을 그대로 사용합니다. 이는 docs/features/conversation-memory/requirements.md:204-205의 승인된 run 기본값 120/600/20과 달라 정상 aggregate traffic은 절반 이하에서 차단하면서 개별 grant에는 허용량보다 50% 많은 호출을 열어 둡니다. Run 전용 기본값을 계약과 맞추고 배포 설정에도 동일하게 노출해야 합니다.

AGENTS.md reference: AGENTS.md:L198-L198

Useful? React with 👍 / 👎.

Comment thread apps/gateway/api/v1/endpoints/run.py Outdated
raise
except Exception as error:
raise _map_public_error(error) from None
response.status_code = status.HTTP_202_ACCEPTED

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Terminal turn 재시도에는 terminal 응답을 반환하세요

같은 idempotency key를 turn 완료 또는 실패 뒤 재시도하면 application은 기존 terminal turn_state를 반환하고 publish를 생략하지만, 이 endpoint는 상태와 무관하게 항상 202status: accepted를 반환합니다. 따라서 최초 응답 유실을 복구하는 호출자는 이미 실패한 요청도 다시 접수된 것으로 판단하며, docs/features/conversation-memory/api_spec.md:81-91의 completed 200 및 failed safe terminal replay 계약도 충족하지 못합니다. result.turn_state에 따라 completed/failed/cancelled replay를 해당 terminal 응답으로 투영해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L8-L8

Useful? React with 👍 / 👎.

Comment thread apps/gateway/api/v1/endpoints/run.py Outdated
# not let a root-level conversation envelope reach the legacy runtime until
# MBA-318 installs the verified vertical execution contract.
if "conversation" in request_body:
mark_public_conversation_transport_boundary(request.scope)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Framework validation에도 Conversation transport 경계를 적용하세요

Conversation boundary 표시는 handler에 진입한 뒤에만 설정되므로, 이 root route에 잘못된 JSON이나 object가 아닌 body를 보내 FastAPI validation이 먼저 실패하면 표시 코드에 도달하지 않습니다. 이 경우 PublicConversationCorsBoundaryMiddleware는 root path만으로 응답을 sanitize하지 않아 전역 credentialed CORS header가 남고 no-store/no-referrer도 누락되며, root OPTIONS 역시 전역 CORS preflight grant를 반환합니다. 이는 docs/features/conversation-memory/requirements.md:187이 body/router 오류까지 포함해 요구하는 transport 계약을 깨므로 framework validation 이전에 해당 요청을 식별할 수 있는 별도 route 또는 outer boundary가 필요합니다.

AGENTS.md reference: apps/AGENTS.md:L8-L8

Useful? React with 👍 / 👎.

Comment on lines +563 to +565
length = len(value.encode("utf-8"))
if not 1 <= length <= MAX_MEMORY_CONTENT_BYTES:
raise ValueError("memory.input_mapping_invalid")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Start input의 graph max_length를 실행 전에 강제하세요

Start variable에 max_length가 설정된 graph도 runtime contract가 그 값을 검증한 뒤 binding에서 버리고, 이 입력 경로는 global 16 KiB 상한만 확인합니다. 예를 들어 owner가 max_length: 100으로 배포해도 101~16,384-byte 입력이 Turn/Entry에 저장되고 provider까지 전달되어 frozen workflow의 입력 제한과 비용 보호가 무시됩니다. Validated max_length를 runtime binding에 보존하고 dispatch 및 provider I/O 전에 mapped input에 적용해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

Comment on lines +191 to +195
sa.ForeignKeyConstraint(
["deployment_id"],
["workflow_deployments.id"],
name="fk_conv_workflow_admission_deployment",
ondelete="RESTRICT",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Terminal admission에 bounded retention을 추가하세요

각 conversation turn은 이 deployment-bound admission row를 하나씩 생성하지만 model에는 retention deadline이 없고 저장소 전체에도 terminal admission을 삭제하는 cleanup 경로가 없습니다. 따라서 runtime을 운영하면 completed/failed row가 영구 누적되며, Memory session과 content를 physical purge한 뒤에도 이 RESTRICT FK가 남아 DeploymentService.delete_deployment()의 commit을 계속 실패시킵니다. docs/features/conversation-memory/requirements.md:174의 bounded operational retention에 맞춰 terminal row 만료·삭제 계약과 deployment lifecycle 정리를 추가해야 합니다.

AGENTS.md reference: AGENTS.md:L144-L144

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fcd10aeaa8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/workflow_engine/tasks.py Outdated
Comment on lines +47 to +50
from apps.workflow_engine.composition.conversation_memory import (
CONVERSATION_EXECUTION_LEASE_SECONDS,
build_conversation_turn_use_case,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Workflow worker 이미지에 Memory 패키지를 포함하세요

이 import는 worker 시작 시 무조건 실행되고, 대상 composition과 runtime adapter가 apps.memory.*를 직접 import하지만 실제 Compose/Helm worker가 사용하는 docker/workflow_engine/Dockerfile은 final image에 apps/sharedapps/workflow_engine만 복사합니다. 따라서 runtime flag가 꺼진 기본 배포에서도 Celery가 task module을 로드하는 즉시 ModuleNotFoundError: apps.memory로 종료되어 기존 workflow queue까지 소비할 수 없습니다. Gateway/log-system 이미지처럼 apps/memory를 worker 이미지에 설치·복사해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 77ddcf4273

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/workflow_engine/tasks.py Outdated
from apps.workflow_engine.workflow.core.workflow_engine import WorkflowEngine

queued_context = dict(execution_context or {})
_enforce_public_request_deadline(queued_context)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Public deadline을 provider 호출까지 유지하세요

기존 broker TTL 지적 이후 추가된 deadline 구현을 확인하면 검사는 task 진입 시 여기서 한 번만 실행되고, 이후 task_deadline은 다시 실행 시작 시점부터 Celery hard limit 600초로 계산됩니다. Gateway polling timeout과 hard limit가 모두 600초이므로 backlog의 599초 시점에 시작한 요청은 이 검사를 통과한 뒤 DB/Knowledge sync를 거쳐 원래 absolute deadline 이후에도 provider를 호출할 수 있어, 이미 504를 받은 요청에 비용과 외부 부수효과가 발생합니다. Engine deadline을 public absolute deadline과 hard limit 중 이른 값으로 설정하고 provider 직전에도 만료를 강제해야 합니다.

AGENTS.md reference: AGENTS.md:L140-L144

Useful? React with 👍 / 👎.

Comment thread apps/workflow_engine/tasks.py Outdated
history_reference = queued_context.pop("public_chat_history_ref", None)
if history_reference is not None:
try:
public_chat_history = consume_public_chat_history(history_reference)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 재시도 전에 history를 소진하지 마세요

Public 요청이 history를 읽은 뒤 DB 연결, preflight 또는 engine 실행에서 retryable 예외를 만나면 아래 공통 예외 경로가 _safe_retry()를 호출하지만, 이 호출은 atomic GET+DELETE라 첫 시도에서 reference가 이미 사라집니다. 따라서 두 번째 delivery는 항상 conversation.history_unavailable로 non-retryable 종료되어 provider 호출 전의 일시적 장애조차 복구하지 못하고, ExternalEffectRetrySignal의 명시적 retry도 같은 방식으로 무효화됩니다. Provider 시작 전에는 task identity에 묶인 lease/ack 방식으로 재시도 가능하게 하거나, 일회 소비 이후에는 성공할 수 없는 Celery retry를 예약하지 않도록 분리해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

contract_version: 'public_chat_conversation.v1',
history_consumer: {
node_id: conversationHistoryConsumerNodeId,
container_path: [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 중첩 LLM의 canonical path를 배포 설정에 보존하세요

loopNode.data.subGraph 안의 LLM을 history consumer로 선택해야 하는 기존 workflow에서는 NodeCanvas가 top-level LLM만 llmNodes로 전달하므로 선택 목록이 비어 배포 버튼이 막힙니다. 중첩 노드를 전달하도록 확장하더라도 여기서 container_path를 항상 []로 저장하면 Gateway가 root node를 찾다가 conversation.consumer_mapping_not_found를 반환합니다. 서버가 지원하는 canonical node location을 UI 선택 항목에 포함하고 실제 loop container path를 직렬화해야 합니다.

AGENTS.md reference: apps/client/AGENTS.md:L10-L11

Useful? React with 👍 / 👎.

Comment on lines +68 to +72
if (pendingUser) {
turns.push([
pendingUser,
{ role: 'assistant', content: message.content },
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 대체 UI 문구를 assistant history에서 제외하세요

Workflow 실행은 성공했지만 output mapping이 없거나 final preview가 비어 있는 경우 페이지는 정상 assistant-* ID로 응답을 처리할 수 없습니다.를 추가합니다. 이 builder는 welcome/error ID만 제외하므로 다음 요청에서 해당 UI 대체 문구를 실제 assistant 응답으로 pair에 넣어 provider prompt와 RAG 검색어로 전송하고, 이후 정상 응답까지 인위적인 실패 문구로 오염시킵니다. 실제 preview가 있는 성공 응답만 history 대상이라고 표시하거나 대체 문구를 error/non-history message로 분류해야 합니다.

AGENTS.md reference: apps/client/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

)
except WorkflowNodeLocationError:
return False
return selected_ref == node_location.safe_reference

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Subworkflow 진입 시 public history binding을 제거하세요

기존 selected-node 지적 이후 추가된 mapping을 더 추적하면 WorkflowNode가 전체 execution context를 child engine에 복사하고 child의 binding_container_path는 다시 ()로 시작하지만, 여기서는 deployment identity 없이 path와 node ID의 digest만 비교합니다. 따라서 선택된 parent root LLM과 호출된 subworkflow의 root LLM이 흔한 동일 ID(예: llm-1)를 가지면 child도 consumer로 오인되어 방문자 history를 자신의 별도 provider와 RAG query에 전송합니다. Workflow-node 경계에서 public history/ref를 제거하거나 consumer identity에 deployment/graph identity를 결박해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b333d714ee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +623 to +624
public_request_deadline = _enforce_public_request_deadline(queued_context)
task_deadline = _workflow_task_deadline(public_request_deadline)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 모든 외부 I/O 전에 Public deadline을 검사하세요

기존 LLM deadline 지적 이후 최신 HEAD를 다시 추적하면, 여기서 합성한 deadline은 LLMNode의 RAG/provider 호출에서만 명시적으로 검사됩니다. Public Chatbot graph가 느린 선행 노드 뒤에 HTTP/Slack/GitHub 노드를 실행하는 경우 ExternalEffectExecutor.execute()는 deadline을 claim 대기에만 사용하고 adapter.invoke_effect() 직전에는 검사하지 않으며, read-only HTTP/GitHub 경로도 guard 후 바로 외부 I/O를 수행합니다. 따라서 600초 경계 직후 Gateway가 이미 504를 반환한 상태에서도 Celery hard kill 전 외부 요청이나 부수효과가 발생할 수 있으므로 공통 external-effect/read-only 경계에서 task_deadline을 fail-closed로 강제해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

Comment on lines +100 to +107
while (
normalized
and _context_token_count(
normalized,
current_text=current_text,
token_counter=token_counter,
)
> max_context_tokens

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 정제 후 history도 token 상한을 다시 적용하세요

허용된 history에 system prompt처럼 탐지되는 짧은 줄을 수백 개 넣으면 이 계산은 정제 전 원문을 4,096 tokens 이하로 승인하지만, Worker는 각 줄을 더 긴 redaction marker로 치환하고 JSON/framing을 추가한 뒤 token 수를 다시 계산하지 않습니다. 그 결과 익명 요청 하나가 계약상 상한보다 훨씬 큰 history projection을 선택된 provider에 전송해 비용·latency 제한을 우회할 수 있으므로, 실제 정제·framing된 projection에도 같은 예산을 적용하거나 정제 과정의 확장을 상한 안에서 제한해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

Comment on lines +341 to +343
require_public_chat_conversation_contract=(
settings.PUBLIC_CHAT_CONVERSATION_ROLLOUT_MODE == "strict"
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Strict 모드에서 legacy Chatbot 재활성화를 차단하세요

strict 검증은 신규 create/preflight에만 전달되고 기존 PATCH /deployments/{id}/toggle 활성화 경로에는 적용되지 않습니다. 따라서 strict 전환 후 mapping이 없는 과거 Chatbot version을 다시 활성화하면 Public info는 legacy_v0를 반환해 Client가 root route를 호출하지만, strict Gateway는 그 요청을 conversation.history_required로 거부하여 공개 챗봇이 즉시 중단됩니다. 재활성화에도 동일한 consumer mapping 검증을 적용하거나 새 version 재배포를 요구해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d881bef82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/workflow_engine/tasks.py Outdated
Comment on lines +670 to +673
except PublicChatHistoryTransientStoreError:
raise NonRetryableWorkflowError(
"conversation.history_unavailable"
) from None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Redis 소비 전 장애는 재시도하세요

client.eval()이 Lua 실행 전에 일시적인 Redis 연결 오류를 내면 history key는 아직 유효하지만, 여기서 모든 transient-store 오류를 NonRetryableWorkflowError로 바꾸므로 Celery의 기존 재시도가 실행되지 않고 공개 채팅이 즉시 500으로 끝납니다. 소비 전 오류는 재시도한다는 ADR-0074의 계약에 맞게 store-unavailable은 _safe_retry() 경로로 보내고, 실제 missing/corrupt 또는 소비가 확인된 경우만 non-retryable로 분리해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

Comment on lines +132 to +136
const publicRequest = buildPublicConversationRequest(
urlSlug,
inputs,
messages,
deploymentInfo?.public_conversation_contract,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 재배포 후 conversation capability를 갱신하세요

같은 slug의 active deployment가 legacy_v0에서 client_history_v1로 교체되어도 이 페이지는 urlSlug가 바뀌지 않는 한 public info를 다시 읽지 않으므로, 열린 탭은 계속 이전 capability로 root route를 호출합니다. Compatibility Gateway에서는 새 배포가 지원하는 history를 보내지 않아 대화가 조용히 무상태로 퇴행하고 strict 전환 뒤에는 conversation.history_required로 실패하며, 반대 방향의 교체에서는 /chat이 consumer mapping 409를 반환합니다. 배포 version/contract 오류 시 public info를 갱신하고 history를 reset하거나 요청을 조회한 deployment version에 결박해야 합니다.

AGENTS.md reference: apps/client/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

Comment thread apps/workflow_engine/tasks.py Outdated
Comment on lines +558 to +560
has_public_history = (
"public_chat_history_ref" in queued_context
or "public_chat_history" in queued_context

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Worker에서 raw history task payload를 거부하세요

기존 Gateway의 opaque-reference publish 수정 이후에도 최신 Worker는 queued context에 public_chat_history 원문만 있어도 public history 실행으로 인정하고, canonical context가 이 필드를 그대로 보존한 채 아래 소비 단계에서는 reference만 pop하므로 원문이 LLM까지 전달됩니다. 구형·오작동·forged producer가 이 필드를 넣은 경우 history가 일회성 Redis GET+DELETE와 TTL을 우회해 broker에 남고, 동일 delivery의 중복 처리에서도 다시 외부 provider로 전송될 수 있으므로 Worker는 raw 필드를 fail-closed로 거부하고 public_chat_history_ref만 허용해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cabe919345

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

or max_projection_tokens > MAX_PUBLIC_CHAT_CONTEXT_TOKENS
):
raise PublicChatHistoryError("conversation.token_count_unavailable")
normalized = list(normalize_public_chat_history(list(value)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 정제된 history에 admission 제한을 다시 적용하지 마세요

기존 정제 후 token-bound 지적을 반영한 최신 HEAD에서도, 이 줄이 sanitize_untrusted_text() 결과를 normalize_public_chat_history()로 다시 검사하여 Gateway admission을 통과한 요청을 Worker에서 실패시킵니다. 예를 들어 JSON content "\u0001"은 Gateway의 non-empty/UTF-8 검사를 통과하지만 정제 후 공백이 되어 conversation.content_invalid가 되고, 32,760자 이내의 system prompt\n 반복은 redaction marker로 약 88KB까지 늘어나 message 길이 제한에 걸립니다. 이 예외는 turn 제거 루프 전에 발생하므로 provider 호출 없이 generic 400으로 끝납니다. 이미 검증된 raw history를 정제한 뒤에는 raw message 제한을 재적용하지 말고, 비게 되거나 확장된 pair를 final projection budget 안에서 제거해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

Comment on lines +129 to +132
client_conversation_history = bound_public_chat_history(
conversation["history"],
current_inputs=user_inputs,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Tokenizer 전에 Public inputs 크기를 제한하세요

확인한 bundled docker/nginx/nginx.conf에서는 일반 /api 요청에 server-wide 100MB 제한이 적용되는데, 이 무인증 경로는 slug/deployment 조회와 budget gate보다 먼저 inputs 전체를 JSON 직렬화하고 tiktoken으로 인코딩합니다. 따라서 공격자는 존재하지 않는 slug에도 빈 history와 수십 MB 문자열을 반복 전송해 provider 비용이나 workflow budget을 소모하지 않으면서 Gateway CPU와 메모리를 고갈시킬 수 있으며, 4,096-token 검사는 전체 O(n) 인코딩이 끝난 뒤에야 거부합니다. Exact tokenization 전에 작은 serialized-input byte 상한을 검사하고 가능하면 이 전용 route의 transport body limit도 별도로 낮춰야 합니다.

Useful? React with 👍 / 👎.

pendingUser = null;
}

return turns.slice(-MAX_PUBLIC_CHAT_TURNS).flat();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Client history를 서버의 message·envelope 한도로 제한하세요

이 builder는 최근 20 turn만 제한하므로 성공한 assistant 출력 하나가 32,768자를 넘거나 여러 유효한 pair의 JSON이 131,072 bytes를 넘으면 그대로 다음 요청에 포함됩니다. Gateway는 token 기반의 오래된 pair 제거 전에 message/envelope 검증에서 422로 거부하며, Client는 오류를 표시한 뒤에도 문제의 완료 pair를 messages에 유지하므로 이후 모든 전송이 refresh 전까지 같은 422를 반복합니다. Server 계약과 같은 UTF-8 message/envelope 한도를 적용해 oversized 응답을 history-ineligible로 표시하고 오래된 완료 pair를 byte budget 단위로 제거해야 합니다.

AGENTS.md reference: apps/client/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

Comment on lines +1383 to +1385
execution_context["execution_actor"] = {"type": "public"}
execution_context["suppress_content_persistence"] = True
execution_context["public_request_deadline_at"] = (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 새 Public task를 구 Worker가 소비하지 못하게 하세요

확인한 Helm 구성은 Gateway와 Worker를 서로 독립된 Deployment로 rolling update하지만, 여기서 추가한 public_chat_history_refsuppress_content_persistence는 기존 workflow.execute task의 선택적 context field일 뿐입니다. 따라서 새 Gateway가 Ready인 동안 구 Worker가 같은 queue에서 delivery를 받으면, 구 코드는 이 필드들을 오류 없이 무시해 history 없는 답변을 생성하고 기존 WorkflowLogger에 현재 입력·prompt·completion content를 저장합니다. Frontend/Gateway capability rollout으로는 이 Worker 혼합 구간을 막을 수 없으므로, 구 Worker가 등록하지 않는 versioned task/queue를 사용하거나 호환 가능한 거부 handler와 Worker readiness를 먼저 배포한 뒤 Gateway publish를 활성화해야 합니다.

AGENTS.md reference: AGENTS.md:L173-L173

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 82aece2557

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +150 to +152
def _optional_public_deployment_version(request_body: dict) -> int | None:
if "deployment_version" not in request_body:
return None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge /chat에서 deployment_version을 필수로 검증하세요

deployment_version을 생략한 /chat 요청은 여기서 None을 반환해 run_deployment()의 active-version 비교를 완전히 건너뜁니다. 따라서 비공식·구형 호출자가 이전 배포의 history를 유지한 상태에서 같은 slug가 재배포되면, version conflict와 history reset 없이 그 원문이 새 graph/provider로 전달되어 배포 버전 결박 계약이 무력화됩니다. 호환성 때문에 root 경로에서는 생략을 허용하더라도 전용 /chat에서는 positive integer를 필수로 요구해야 합니다.

AGENTS.md reference: AGENTS.md:L140-L144

Useful? React with 👍 / 👎.

Comment on lines +1433 to 1434
self._enforce_public_external_io_deadline()
knowledge_result = self._execute_knowledge_search(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 각 RAG embedding 호출 직전에 deadline을 재검증하세요

기존 deadline 리뷰 이후의 최신 수정도 RAG 전체 검색 진입 전에만 검사합니다. 서로 다른 embedding binding을 가진 여러 KB를 조회하면 QueryEmbeddingExecutionService.execute()apps/workflow_engine/application/query_embedding_execution.py:215-223에서 provider를 순차 호출하고 각 adapter의 실제 embed_sync() 직전에는 deadline guard가 없으므로, 첫 호출이나 중간 DB 작업 중 absolute deadline이 지나도 다음 embedding 외부 호출이 시작될 수 있습니다. 이미 Gateway가 504를 반환한 뒤 비용이 발생하지 않도록 deadline을 query-embedding runtime까지 전달해 각 provider invocation 직전에 fail-closed해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

Comment on lines +56 to +60
stored = client.set(
_key(reference),
payload,
ex=ttl_seconds,
nx=True,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Public history Redis 쓰기를 비동기로 제한하세요

Redis가 연결을 즉시 거부하는 대신 응답하지 않는 장애 상황에서는 이 동기 client.set()이 socket timeout 없이 대기합니다. 호출 지점은 async_execute_deployment_snapshot()이고 production entrypoint도 단일 Uvicorn worker를 실행하므로, 익명 /chat 요청 하나가 이벤트 루프를 붙잡아 Public Chatbot뿐 아니라 같은 Gateway의 다른 API까지 장시간 응답하지 못하게 할 수 있습니다. Async Redis client를 사용하거나 thread offload와 명시적인 connect/read timeout을 적용해 bounded 503으로 종료해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

Comment on lines +26 to +28
PUBLIC_CHAT_CONVERSATION_ROLLOUT_MODE: Literal["compatibility", "strict"] = (
"compatibility"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 표준 배포 리소스에 rollout mode를 전달하세요

새 설정은 기본값이 항상 compatibility지만, 확인한 docker/docker-compose.yml은 이 환경 변수를 Gateway에 전달하지 않고 Helm의 ConfigMap과 Gateway Deployment도 해당 key를 렌더링하지 않습니다. 따라서 제공된 Compose/Helm 배포에서는 운영자가 values나 shell 환경으로 strict를 선택할 수 없어 root Public Chatbot 차단과 strict 재활성화 검증이 영구히 비활성화됩니다. Compose environment와 Helm values→ConfigMap→container env 경로에 이 설정을 연결해야 문서화된 rollout을 완료할 수 있습니다.

AGENTS.md reference: AGENTS.md:L168-L173

Useful? React with 👍 / 👎.

Comment thread docker/nginx/nginx.conf
Comment on lines +68 to +72
client_max_body_size 384k;
client_body_timeout 5s;
proxy_request_buffering off;

proxy_pass http://gateway:8000;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Public Chatbot 프록시 타임아웃을 요청 deadline에 맞추세요

확인한 Docker Nginx의 전용 /chat location에는 proxy_read_timeout이 없어 기본 60초가 적용되지만, Gateway는 응답을 스트리밍하지 않은 채 Celery 결과를 최대 600초 기다립니다. 따라서 RAG나 느린 provider 때문에 실행이 60초를 넘으면 Nginx가 먼저 504를 반환하고도 Gateway와 Worker는 최대 약 9분 더 실행하며 provider 비용이나 외부 부수효과를 발생시킬 수 있습니다. 이 location의 upstream timeout을 public absolute deadline과 일치시키거나 전체 request deadline 자체를 프록시 한도에 맞춰 줄여야 합니다.

AGENTS.md reference: AGENTS.md:L168-L173

Useful? React with 👍 / 👎.

Comment thread docker/nginx/nginx.conf

# Anonymous public Chatbot requests carry bounded client-held history.
location ~ ^/api/v1/run-public/[^/]+/chat/?$ {
client_max_body_size 384k;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Nginx의 oversized 응답도 Conversation 계약으로 반환하세요

client_max_body_size 384k는 Gateway의 393,216-byte 상한과 같은 값이므로 이를 초과한 production 요청은 middleware에 도달하지 않고 Nginx 자체 413으로 종료됩니다. 그 응답에는 문서화된 JSON conversation.request_too_large뿐 아니라 Conversation transport가 보장하는 Cache-Control: no-storeReferrer-Policy: no-referrer도 없어, Gateway 단위 테스트와 실제 Docker 배포의 오류 계약이 달라집니다. Nginx 413을 동일한 안전 응답으로 매핑하거나 프록시 상한을 Gateway 상한보다 조금 크게 두어 middleware가 응답을 소유하게 해야 합니다.

AGENTS.md reference: AGENTS.md:L168-L173

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea5cd1fe6a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docker/nginx/nginx.conf
Comment on lines +82 to +83
proxy_send_timeout 610s;
proxy_read_timeout 610s;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Helm Ingress에도 Public timeout을 설정하세요

최신 수정은 Docker Nginx 전용 /chat location에만 610초 timeout을 추가했지만, 확인한 infra/helm/moduly/templates/ingress.yaml/api를 일반 Gateway backend로 전달하고 기본·production values의 Ingress annotations도 비어 있습니다. 따라서 ingress-nginx처럼 upstream timeout이 600초보다 짧은 환경에서는 Helm 배포의 느린 RAG/provider 요청이 프록시에서 먼저 종료된 뒤 Gateway와 Worker가 계속 실행될 수 있습니다. Helm에도 600초 deadline보다 긴 read/send timeout을 렌더링하거나 전용 Public Chat 경로 설정을 제공해야 합니다.

AGENTS.md reference: AGENTS.md:L173-L173

Useful? React with 👍 / 👎.

Comment on lines +341 to +342
if self.execution_context.get("suppress_content_persistence"):
self.logger.suppress_content_persistence()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Public 실행에서 모델 라우팅 학습 저장도 차단하세요

자동 모델 라우팅 learner가 활성화된 Public Chatbot에서는 이 플래그가 WorkflowLogger만 억제하고, LLMNode의 runtime-judge 경로는 계속 현재 방문자 입력으로 learning_feature_text를 만든 뒤 LLMNodeModelRoutingLearningLabel.feature_vector와 feature hash를 DB에 flush/commit합니다. 따라서 WorkflowRun/NodeRun의 원문을 비워도 익명 입력에서 파생된 content-bearing vector가 durable 학습 데이터로 남으므로 Public 비저장 경계가 우회됩니다. suppress_content_persistence 실행에서는 routing label 생성·학습을 건너뛰거나 content-free 통계만 남겨야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 13c316c5de

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

)
try:
client = redis_client or get_redis_client()
payload = client.eval(_CONSUME_SCRIPT, 1, _key(reference))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Redis history 소비에도 timeout을 적용하세요

Redis가 연결은 유지하지만 EVAL 응답을 보내지 않는 장애에서는 get_redis_client()에 socket timeout이 없고 이 동기 호출 자체에도 제한 시간이 없어 예외 처리와 Celery retry가 실행되지 않습니다. 이때 Public Chat task가 요청의 absolute deadline을 지나서도 worker slot을 점유하며, 익명 요청이 반복되면 100개 gevent slot이 고갈되어 정상 workflow 실행까지 지연될 수 있으므로 consume 호출도 남은 public deadline 이하의 명시적 timeout으로 제한해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

selected_model_id = provider_attribution.model_id

# STEP 4. LLM 호출 ----------------------------------------------------
self._enforce_public_external_io_deadline()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Runtime judge 호출 전에도 deadline을 검사하세요

기존 deadline 보완 이후에도 최신 HEAD에서는 이 검사가 main provider 호출 직전에만 실행되며, 앞선 _resolve_model_routing_policy()가 자동 라우팅 정책에서 ModelRoutingRuntimeJudge.assess_requirements()를 호출할 때는 guard가 없습니다. Public Chatbot의 RAG나 DB 처리가 absolute deadline까지 지연된 경우 이미 Gateway가 504를 반환한 뒤에도 judge provider에 방문자 입력 기반 feature를 보내고 비용을 발생시킬 수 있으므로, runtime judge의 실제 provider invocation 직전에도 동일한 fail-closed guard를 적용해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

Comment on lines +590 to +594
for key in (
"execution_actor",
"public_chat_history_consumer_ref",
"public_chat_history_token_budget",
"public_chat_stateless_compatibility",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Public task에서 legacy memory control을 제거하세요

구형·오작동·forged producer가 전용 task context에 memory_mode=trueconversation_id를 넣어도 public contract 검증을 통과하고, 이 canonicalization 목록에서 두 필드는 제거되지 않아 LLMNode._build_memory_summary()까지 전달됩니다. Legacy provider 경로에서는 앱 소유자 기준의 과거 WorkflowRun/WorkflowNodeRun 내용을 조회해 public provider prompt와 응답에 섞을 수 있으므로, public context를 재구성할 때 memory_mode=false, conversation_id=None을 서버에서 강제하거나 해당 queued control을 fail-closed로 거부해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 749ca08a0b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1382 to +1385
if public_transient_mode:
public_request_deadline = datetime.now(timezone.utc) + timedelta(
seconds=PUBLIC_CHAT_REQUEST_TTL_SECONDS
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 요청 수신 시각부터 Public deadline을 계산하세요

기존 proxy-timeout 지적 이후의 최신 tree를 확인하면 Docker Nginx와 기본 Helm Ingress는 610초에 종료되지만, 이 deadline은 요청 수신 시점이 아니라 budget 검사, legacy-secret migration 및 runtime preflight가 모두 끝난 뒤에야 시작되고 Gateway의 600초 polling도 publish 이후에 시작됩니다. 따라서 앞선 DB 작업이 10초를 넘는 장애·지연 상황에서는 proxy가 먼저 504를 반환한 뒤에도 Worker가 이 늦은 deadline까지 provider나 외부 부수효과를 실행할 수 있습니다. endpoint/middleware에서 수신 시각 기준 absolute deadline을 만들고 pre-dispatch 단계와 Worker까지 동일한 값을 사용해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

@HyungminYoon1

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ed7582e82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

description,
type: deploymentType,
config: {},
config: deploymentConfig,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 배포 config와 동일한 graph snapshot을 전송하세요

워크플로우를 편집한 뒤 저장하지 않고 공개 챗봇을 배포하면 consumer 선택은 NodeCanvas의 현재 nodes에서 만들어지지만, 이 preflight 요청과 아래 create 요청에는 graph_snapshot이 없어 Gateway의 _resolve_graph_snapshot()이 마지막 저장 draft를 사용합니다. 따라서 새로 추가했거나 Loop 안으로 이동한 LLM을 선택한 일반적인 배포 흐름이 conversation.consumer_mapping_not_found/invalid로 막힙니다. 현재 graph snapshot을 두 요청에 함께 보내거나, 배포 전에 저장을 강제하고 저장된 snapshot에서 consumer를 선택해야 합니다.

AGENTS.md reference: apps/client/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

payload: Any,
effect_sequence: int = 0,
) -> Any:
self._raise_if_deadline_expired(node_id=context.node_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 모든 공개 외부 I/O에 절대 deadline을 적용하세요

공개 작업이 큐에서 오래 대기했거나 앞선 CPU 노드가 남은 시간을 소진한 경우, 이 검사는 ExternalEffectExecutor를 사용하는 노드만 막습니다. 예를 들어 FileExtractionNoderemote_file_fetcher.fetch_to_temp()를 직접 호출하고 WorkflowEngine의 일반 노드 스케줄러도 task_deadline을 검사하지 않으므로, 600초 절대 deadline과 클라이언트 timeout이 지난 뒤에도 새 원격 다운로드를 시작할 수 있습니다. Celery hard limit은 dequeue 시점부터 다시 계산되므로 이 공백을 닫지 못합니다. 노드 실행 공통 경계에서 deadline을 검사하거나 remote fetcher에도 동일한 guard를 주입해야 합니다.

AGENTS.md reference: AGENTS.md:L144-L144

Useful? React with 👍 / 👎.

Comment on lines +341 to +343
require_public_chat_conversation_contract=(
settings.PUBLIC_CHAT_CONVERSATION_ROLLOUT_MODE == "strict"
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Browser-access 활성화에도 strict consumer 검증을 적용하세요

PUBLIC_CHAT_CONVERSATION_ROLLOUT_MODE=strict에서 기존 legacy Chatbot을 source로 browser-access-revisionsis_active=true로 생성하면, CreateBrowserAccessRevision은 source의 config와 graph를 그대로 복제해 새 버전을 활성화하지만 이 strict 검증을 호출하지 않습니다. 그 결과 consumer mapping 없는 새 active 배포가 만들어지고 public info는 legacy_v0를 광고하는 반면 strict root 실행은 conversation.history_required로 막혀 해당 챗봇이 중단됩니다. 활성 revision을 만들기 전에 source snapshot에 같은 conversation contract 검증을 적용해야 합니다.

AGENTS.md reference: AGENTS.md:L144-L144

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a218f3ff20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 191 to 192
if (!response.ok) {
throw new Error(`API 호출 실패: ${response.status}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 혼합 Gateway에서 legacy root 경로로 폴백하세요

호환 모드에서 구·신 Gateway Pod가 함께 실행되는 롤링 배포 시, /info 요청이 신 Pod에서 client_history_v1을 받은 뒤 /chat POST가 구 Pod로 라우팅되면 구 버전에는 이 경로가 없어 404가 반환됩니다. Helm Gateway Service에는 session affinity가 없는데 이 코드는 409만 복구하고 나머지 오류를 바로 실패 처리하므로, 호환성 rollout 중 공개 챗봇 요청이 높은 확률로 중단됩니다. /chat 미지원 응답에서는 history 없이 legacy root로 재시도하거나 capability와 실행 요청이 같은 Gateway 세대로 라우팅되도록 해야 합니다.

AGENTS.md reference: apps/client/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b120c17b1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +192 to +195
inputs,
[],
'legacy_v0',
deployment.version,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Legacy 폴백에서 owner 범위 memory를 차단하세요

혼합 Gateway 롤아웃 중 /chat의 404 뒤 이 history-free 요청이 구 Gateway로 라우팅되면 방문자 간 대화가 섞일 수 있습니다. 기존 404 가용성 지적을 보완해 구 구현을 대조하면, 구 Gateway는 Chatbot의 memory_mode를 무조건 활성화하고 conversation_id가 없을 때 앱 소유자의 user_id로 과거 성공 run을 조회하므로, 다른 방문자의 입력·응답을 요약해 현재 provider prompt에 넣습니다. 구 Client는 이를 막기 위해 방문자별 conversation_id를 보냈지만 이 폴백은 해당 격리값도 의도적으로 생략하므로, 안전한 Gateway 세대에 대한 affinity/version handshake를 사용하거나 legacy 요청에 충돌하지 않는 일회성 격리 ID를 제공해야 합니다.

AGENTS.md reference: AGENTS.md:L142-L144

Useful? React with 👍 / 👎.

@HyungminYoon1

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 2e52e66274

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@HyungminYoon1

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 2e52e66274

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@HyungminYoon1
HyungminYoon1 merged commit 79da97f into dev Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FE/BE][Chatbot/Memory] Public Chatbot client-held 대화 기록 전달

1 participant