Subchapter 23.31
references/phases/discover/discover.mdMarkdown10 KBView on GitHub
Only runs for build_deploy / migrate when the user provided a code path. Stays independent (does NOT require a separate migration plugin).
In the provided path, look for:
Framework (imports / requirements.txt / package.json): strands, langgraph /
langchain, crewai / autogen, openai (Agents SDK), else custom / none.
Model call inventory: for every model-bearing unit, record exact provider, raw model-ID
strings, SDK/package, API surface, and source paths. Provider vocabulary:
anthropic | openai | azure_openai | google_genai | bedrock | none | unknown. API surfaces
include Anthropic messages, OpenAI chat_completions / responses, and Bedrock
converse / invoke_model. Do not collapse exact IDs such as
claude-3-7-sonnet-latest into a coarse claude family.
base_url (or OPENAI_BASE_URL env
var) containing openrouter.ai, or an OPENROUTER_API_KEY / OR_API_KEY present, means
the calls are routed through OpenRouter, not the provider’s direct API. This is a transport
difference, not a different model — classify provider from the underlying model, not as
unknown. OpenRouter model IDs are provider-prefixed slugs (openai/gpt-5.4,
anthropic/claude-sonnet-4-6, google/gemini-2.5-pro); the prefix names the real
provider. Record the raw slug unchanged in model_ids[] (do not strip the prefix — Model
Recommend’s family detection tolerates it) and set sdk to "openai (via OpenRouter)" so
the transport is visible downstream. The same rule applies to a detected LiteLLM router
(litellm import, LITELLM_PROXY_BASE_URL), with sdk: "openai (via LiteLLM)". Without
this rule, a real OpenAI model routed through a gateway falls through to provider: "unknown" and is scored as if it were an unclassified/Bedrock-native source.Anthropic migration-sensitive features: budget_tokens; temperature / top_p /
top_k; assistant-prefill messages; refusal handling; explicit or framework-default
max_tokens; structured output; prompt caching; server tools; Files API / URL sources;
Message Batches; fallbacks; conversation-state IDs; Skills/MCP connector/managed-agent
dependencies. Record observed feature codes and source paths for Model Recommend.
Session/timeout hints: timeout configs, long-running loops, queue/HITL patterns.
Multi-tenant hints: per-user/tenant scoping, separate contexts.
Compute hints: GPU instance types, heavy compute (compilation, ML inference).
Data store hints: Redis/DynamoDB/vector store connections.
Temporal orchestration: temporalio (Python) / go.temporal.io (Go) /
temporal-sdk (Java) / @temporalio/* (TypeScript) imports or dependencies.
Unit inventory: enumerate the system’s workload units. Signals:
agent_sessionbatchlight_ioserviceGrouping rule (first, before writing units): agents that interact IN-PROCESS —
nodes of one framework graph, members of one swarm, direct in-process calls — are
ONE unit; they deploy and scale together, and that unit’s multi_agent scoring
answer is yes. Workloads that interact across processes (queue, api, a2a)
or not at all (none) are separate units. in_process therefore never appears in
a written unit’s coupling.mode — it is the merge criterion, not a link type.
Closed vocabularies: workload_class ∈ {agent_session, batch, light_io,
service, temporal_worker_poll}; coupling.mode ∈ {queue, api, a2a, none}.
Temporal detection (when the signals above are found): create one
temporal_worker_poll unit per worker fleet (task-queue group), plus one unit per
Activity execution class. Activity classification uses the vocabulary in
references/decision-refs/temporal.md (Tier 2): agent-session Activities →
agent_session; batch Activities → batch; light-IO Activities → light_io. Worker
registration patterns (Python: Worker.run() or client.execute_workflow(),
Go: worker.RegisterWorkflow, Java: @WorkflowMethod / @ActivityMethod,
TypeScript: Worker.create({workflowsPath})) and task-queue names in config files or
Worker constructors identify worker fleets. Each temporal_worker_poll unit gains
"queues": ["<task queue names this unit polls>"] (from the detected task_queues,
grouped per unit). Each Activity-class unit (agent_session / batch / light_io created
from an Activity) gains "task_queue": "<the task queue this Activity runs on>" — the
queue that ties it to the worker fleet that executes it (from the Activity’s
in / / Worker registration). This is the join key
between a poll unit's and its Activity units: a diagram or plan connects ONLY
the fleet whose contains an Activity’s , never a cartesian product
across all fleets. With a single fleet on a single queue, every Activity carries that one
queue. On Temporal detection, record the following
detection signals: , , , or
package imports; Worker registration calls (, ,
); task-queue names in source or config; Workflow decorators /
annotations (, ); Activity decorators / annotations
(, ); call sites;
(TypeScript); server connection config ( or
self-hosted address). Write a top-level context object in
: .
Write $RUN_DIR/context-signals.json mapping detected signals onto scoring keys, e.g.:
{
"framework": "langgraph",
"multi_agent": "yes",
"session_state": "hitl",
"model_provider": "openai",
"_detected": [
"framework from imports",
"multi_agent from graph with 2+ nodes",
"model_provider from SDK imports"
],
"units": [
{
"id": "chat-agent",
"workload_class": "agent_session",
"trigger": "request",
"source": "detected",
"framework": "langgraph",
"model_source": {
"provider": "anthropic",
"model_ids": ["claude-3-7-sonnet-latest"],
"sdk": "anthropic",
"api_surface": "messages",
"source_paths": ["src/agent.py"],
"detected_features": ["budget_tokens", "assistant_prefill"]
},
"coupling": {
"interacts_with": ["summarizer"],
"mode": "queue"
},
"evidence": "LangGraph StateGraph with 3 nodes; pushes summaries to Redis queue"
},
{
"id": "summarizer",
"workload_class": "batch",
"trigger": "schedule",
"source": "detected",
"coupling": {
"interacts_with": [],
"mode": "none"
},
"evidence": "Celery worker consuming summary queue; runs nightly"
}
]
}Only include keys you can detect with reasonable confidence. Everything else stays for Clarify.
Keep the top-level model_provider compatibility field, but derive it from the primary unit’s
model_source.provider. model_provider is not a scoring key. The structured per-unit
model_source object is authoritative for Model Recommend and the downstream handoff.
List the detected signals so the user can correct them in Clarify. These pre-fills let Clarify skip questions (Clarify asks fewer for build_deploy/migrate).
Determinism boundary (important): these detections are a best-effort LLM interpretation of code, NOT deterministic facts. They become inputs to the deterministic scoring engine, so a wrong detection silently biases scoring. Mitigation: (1) only write a signal you can detect with high confidence — when unsure, omit it and let Clarify ask; (2) always present detected signals to the user as “detected: X (correct me if wrong)” so they have a correction opportunity before scoring runs. This is the one point where LLM interpretation enters the otherwise deterministic pipeline.
Set phases.discover = completed.
task_queue=execute_activityproxyActivitiesqueues[]queues[]task_queuetemporaliogo.temporal.io@temporalio/*temporal-sdkWorker.run()worker.RegisterWorkflowWorker.create()@workflow.defn@WorkflowMethod@activity.defn@ActivityMethodworkflow.ExecuteActivityproxyActivities<T>()*.tmprl.cloudtemporalcontext-signals.json{ "detected": true, "server": "cloud|self_hosted|unknown", "sdks": [], "task_queues": [] }Trigger detection: each unit gains a trigger field capturing how it is invoked.
Closed vocabulary: trigger ∈ {request, event, schedule, temporal, unknown}.
Detect from handler type (REST/WebSocket handlers → request), cron entries or
scheduler configs → schedule, event-source wiring (queue consumers, S3 listeners,
SNS/webhook handlers) → event, Temporal Activity registration → temporal.