Skip to content

Implement cross-model replay CLI#86

Merged
4444J99 merged 1 commit into
mainfrom
codex/cross-model-replay-cli
Jun 6, 2026
Merged

Implement cross-model replay CLI#86
4444J99 merged 1 commit into
mainfrom
codex/cross-model-replay-cli

Conversation

@4444J99

@4444J99 4444J99 commented Jun 6, 2026

Copy link
Copy Markdown
Member

Summary

Implements the cross-model replay/diff workflow described in docs/cross-model-replay.md.

Changes

  • Add titan.replay for JSON-backed replay capture, multi-target dispatch, stored run results, and pairwise diff generation.
  • Add titan replay capture, titan replay run, and titan replay diff CLI commands.
  • Add deterministic tests using fake routers so replay behavior is covered without external model calls.
  • Update the replay design doc with implemented command examples and storage behavior.

Related Issues

Fixes #26

Testing

  • uv run --python 3.11 --with '.[dev,dashboard,auth,ratelimit]' ruff check .
  • uv run --python 3.11 --with '.[dev,dashboard,auth,ratelimit]' mypy .
  • uv run --python 3.11 --with '.[dev,dashboard,auth,ratelimit]' pytest tests/test_replay.py tests/test_adapters.py
  • UV_NO_CACHE=1 uv run --python 3.11 --with '.[dev,dashboard,auth,ratelimit]' titan replay --help

Checklist

  • Code follows the project's style guidelines
  • Self-review completed
  • Documentation updated
  • No secrets or credentials included

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

AI Quality Gate Report

Mode: advisory

Category Errors Warnings
Duplication 0 50
Security 0 2
Logic 0 6

Duplication

  • WARNING adapters/base.py:287 - 8-line normalized block duplicates adapters/base.py:185 (async def complete()
  • WARNING adapters/base.py:338 - 8-line normalized block duplicates adapters/base.py:210 (async def stream()
  • WARNING adapters/base.py:349 - 8-line normalized block duplicates adapters/base.py:296 (import httpx)
  • WARNING adapters/base.py:351 - 8-line normalized block duplicates adapters/base.py:299 (ollama_messages = [])
  • WARNING adapters/base.py:352 - 8-line normalized block duplicates adapters/base.py:300 (if system:)
  • WARNING adapters/base.py:353 - 8-line normalized block duplicates adapters/base.py:301 (ollama_messages.append({"role": "system", "content": system}))
  • WARNING adapters/base.py:354 - 8-line normalized block duplicates adapters/base.py:302 (for msg in messages:)
  • WARNING adapters/base.py:559 - 8-line normalized block duplicates adapters/base.py:185 (async def complete()
  • WARNING adapters/base.py:643 - 8-line normalized block duplicates adapters/base.py:336 ())
  • WARNING adapters/base.py:645 - 8-line normalized block duplicates adapters/base.py:210 (async def stream()
  • WARNING adapters/base.py:695 - 8-line normalized block duplicates adapters/base.py:185 (async def complete()
  • WARNING adapters/base.py:765 - 8-line normalized block duplicates adapters/base.py:336 ())
  • WARNING adapters/base.py:767 - 8-line normalized block duplicates adapters/base.py:210 (async def stream()
  • WARNING adapters/base.py:823 - 8-line normalized block duplicates adapters/base.py:185 (async def complete()
  • WARNING adapters/base.py:824 - 8-line normalized block duplicates adapters/base.py:288 (self,)
  • WARNING adapters/base.py:865 - 8-line normalized block duplicates adapters/base.py:335 (raw_response=data,)
  • WARNING adapters/base.py:866 - 8-line normalized block duplicates adapters/base.py:336 ())
  • WARNING adapters/base.py:868 - 8-line normalized block duplicates adapters/base.py:210 (async def stream()
  • WARNING adapters/base.py:869 - 8-line normalized block duplicates adapters/base.py:339 (self,)
  • WARNING adapters/base.py:870 - 8-line normalized block duplicates adapters/base.py:340 (messages: list[LLMMessage],)
  • WARNING adapters/base.py:890 - 8-line normalized block duplicates adapters/base.py:842 ("https://api.groq.com/openai/v1/chat/completions",)
  • WARNING adapters/base.py:891 - 8-line normalized block duplicates adapters/base.py:843 (headers={)
  • WARNING adapters/base.py:892 - 8-line normalized block duplicates adapters/base.py:844 ("Authorization": f"Bearer {self.config.api_key}",)
  • WARNING adapters/router.py:554 - 8-line normalized block duplicates adapters/base.py:336 ())
  • WARNING adapters/router.py:557 - 8-line normalized block duplicates adapters/router.py:383 (self,)
  • WARNING adapters/router.py:558 - 8-line normalized block duplicates adapters/router.py:384 (messages: list[LLMMessage],)
  • WARNING adapters/router.py:571 - 8-line normalized block duplicates adapters/router.py:397 (Args:)
  • WARNING adapters/router.py:572 - 8-line normalized block duplicates adapters/router.py:398 (messages: Conversation messages)
  • WARNING adapters/router.py:583 - 8-line normalized block duplicates adapters/router.py:409 (""")
  • WARNING adapters/router.py:584 - 8-line normalized block duplicates adapters/router.py:410 (await self._ensure_initialized())
  • WARNING adapters/router.py:587 - 8-line normalized block duplicates adapters/router.py:413 (if provider and classification:)
  • WARNING adapters/router.py:588 - 8-line normalized block duplicates adapters/router.py:414 (allowed = CLASSIFICATION_ALLOWED_PROVIDERS.get(classification, set()))
  • WARNING adapters/router.py:589 - 8-line normalized block duplicates adapters/router.py:415 (if provider not in allowed:)
  • WARNING adapters/router.py:590 - 8-line normalized block duplicates adapters/router.py:416 (raise LLMAdapterError()
  • WARNING adapters/router.py:591 - 8-line normalized block duplicates adapters/router.py:417 (f"Provider {provider.value} not allowed for ")
  • WARNING adapters/router.py:592 - 8-line normalized block duplicates adapters/router.py:418 (f"classification={classification.value}")
  • WARNING adapters/router.py:593 - 8-line normalized block duplicates adapters/router.py:419 ())
  • WARNING adapters/router.py:595 - 8-line normalized block duplicates adapters/router.py:422 (supports_native_tools = True)
  • WARNING adapters/router.py:596 - 8-line normalized block duplicates adapters/router.py:423 (if provider:)
  • WARNING adapters/router.py:597 - 8-line normalized block duplicates adapters/router.py:424 (if not self._providers[provider].available:)
  • WARNING agents/archetypes/assemblage.py:11 - 8-line normalized block duplicates agents/archetypes/actor_network.py:11 (""")
  • WARNING agents/archetypes/assemblage.py:408 - 8-line normalized block duplicates agents/archetypes/assemblage.py:370 ({)
  • WARNING agents/archetypes/bureaucracy.py:12 - 8-line normalized block duplicates agents/archetypes/actor_network.py:11 (""")
  • WARNING agents/archetypes/dao.py:12 - 8-line normalized block duplicates agents/archetypes/actor_network.py:11 (""")
  • WARNING agents/archetypes/orchestrator.py:318 - 8-line normalized block duplicates agents/archetypes/orchestrator.py:291 (if current:)
  • WARNING agents/archetypes/orchestrator.py:319 - 8-line normalized block duplicates agents/archetypes/orchestrator.py:292 (subtasks.append()
  • WARNING agents/archetypes/security_analyst.py:10 - 8-line normalized block duplicates agents/archetypes/devops.py:10 (""")
  • WARNING agents/archetypes/security_analyst.py:12 - 8-line normalized block duplicates agents/archetypes/devops.py:12 (from future import annotations)
  • WARNING agents/archetypes/security_analyst.py:14 - 8-line normalized block duplicates agents/archetypes/devops.py:14 (import logging)
  • WARNING agents/learning/entity_extractor.py:217 - 8-line normalized block duplicates agents/learning/entity_extractor.py:110 ("react",)

Security

  • WARNING titan/persistence/checkpoint.py:218 - pickle-load: review generated code for unsafe default
  • WARNING titan/persistence/checkpoint.py:221 - pickle-load: review generated code for unsafe default

Logic

  • WARNING dashboard/app.py:584 - while True block has no obvious break, return, or raise
  • WARNING hive/topology.py:1348 - if/else branches are identical for Compare(left=Subscript(value=Attribute(value=Name(id='source', ctx=Load()), attr
  • WARNING titan/api/inquiry_ws.py:173 - while True block has no obvious break, return, or raise
  • WARNING titan/cli.py:1148 - while True block has no obvious break, return, or raise
  • WARNING titan/workflows/narrative_synthesizer.py:404 - if/else branches are identical for Compare(left=Name(id='style', ctx=Load()), ops=[Eq()], comparators=[Attribute(va
  • WARNING titan_mcp/server.py:214 - if/else branches are identical for Compare(left=Attribute(value=Name(id='session', ctx=Load()), attr='agent_type',

Advisory mode does not block merge. Review findings before strict mode is enabled.

@4444J99 4444J99 merged commit e92e472 into main Jun 6, 2026
8 checks passed
@4444J99 4444J99 deleted the codex/cross-model-replay-cli branch June 6, 2026 15:14

@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: af3afab447

ℹ️ 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 titan/replay.py
Comment on lines +236 to +238
provider=target.provider,
model=target.model,
)

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 Isolate adapters for each requested model

When a replay includes two targets from the same provider with different models (for example --targets openai:gpt-4o,openai:gpt-4o-mini), both calls share the same LLMRouter; LLMRouter._get_adapter caches adapters only by provider (adapters/router.py:293-326), so whichever model creates the provider adapter first is reused for the other target and the diff silently compares two calls to the same model. This undermines the main cross-model replay use case; use a per-target router/adapter cache key or otherwise force the requested model per call.

Useful? React with 👍 / 👎.

Comment thread titan/replay.py
target = raw_target.strip()
if not target:
continue
separator = ":" if ":" in target else "/" if "/" in target else ""

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 Prefer slash parsing before Ollama model tags

For common Ollama targets that include a tag, such as ollama/llama3.1:70b, this separator choice sees the : in the model tag first and splits the provider as ollama/llama3.1, causing parse_model_targets to reject the target as an unknown provider. Since the parser documents slash-form targets specifically for provider/model input, check for / before falling back to : so tagged Ollama models can be replayed.

Useful? React with 👍 / 👎.

Comment thread titan/replay.py

from adapters.base import LLMMessage, LLMProvider

DEFAULT_REPLAY_ROOT = Path(".titan") / "replays"

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 Keep replay artifacts out of the repository

With this default, titan replay capture writes prompt and context JSON under the current repo, but the repo's .gitignore does not exclude .titan/; in normal workflows those captured prompts or context files become untracked files that can be added and committed accidentally. Since replay inputs may include security-review prompts, source snippets, or secrets, use an ignored location (for example under the user's home directory) or add the replay path to .gitignore.

Useful? React with 👍 / 👎.

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.

feat: implement cross-model interaction replay and diff

1 participant