# Cross-repo API contracts Gortex detects API contracts across repos and matches providers to consumers: ```bash # After indexing, contracts are auto-detected gortex track . # Via MCP tools contracts # list all detected contracts (default action) contracts {action: "check"} # find mismatches and orphans ``` | Contract type | Detection | Provider | Consumer | |--------------|-----------|----------|----------| | **HTTP routes** | Framework annotations (gin, Express, FastAPI, Spring, etc.) | Route handler | HTTP client calls (fetch, http.Get) | | **gRPC** | Proto service definitions | Service RPC | Client stub calls | | **GraphQL** | Schema type/field definitions | Schema | Query/mutation strings | | **Message topics** | Pub/sub patterns across Kafka, RabbitMQ, NATS, and Redis (`KindTopic` nodes, `produces_topic` / `consumes_topic` edges); dynamic topic names suppressed | Publish calls | Subscribe calls | | **WebSocket** | Event emit/listen patterns | `emit()` | `on()` | | **Env vars** | `os.Getenv`, `process.env`, `.env` files | `Setenv` / `.env` | `Getenv` / `process.env` | | **OpenAPI** | Swagger/OpenAPI spec files | Spec paths | (linked to HTTP routes) | | **Temporal workflows** | Go SDK `worker.RegisterActivity(WithOptions)` / `RegisterActivities` / Java `@ActivityInterface` / `@WorkflowInterface` annotations | Activity / workflow function (carries `temporal_role` Meta) | `workflow.ExecuteActivity` / `ExecuteChildWorkflow` / `client.ExecuteWorkflow` / handler & signal/query calls | Contracts are normalized to canonical IDs (e.g., `http::GET::/api/users/{id}`) and matched across repos to detect orphan providers/consumers and mismatches. ## Temporal edge taxonomy The Go and Java extractors tag Temporal call sites with a `via` Meta value on the `EdgeCalls` edge (plus `temporal_kind` and `temporal_name`); `ResolveTemporalCalls` rewrites the resolvable ones (`temporal.stub` / `temporal.start`) to the registered handler / workflow node. Because they are ordinary `EdgeCalls`, `find_usages` / `get_callers` / `explain_change_impact` traverse them with no temporal-specific code. | `via` | Direction | Emitted from | `temporal_kind` | Resolved? | |-------|-----------|--------------|-----------------|-----------| | `temporal.register` | provider tag | `worker.RegisterActivity(WithOptions)` / `RegisterWorkflow(WithOptions)` / `RegisterActivities` | `activity` / `workflow` | indexed, not rewritten | | `temporal.stub` | workflow → activity / child-workflow | `workflow.ExecuteActivity` / `ExecuteLocalActivity` / `ExecuteChildWorkflow` | `activity` / `workflow` | yes → registered handler | | `temporal.start` | service → workflow | `client.ExecuteWorkflow` / `SignalWithStartWorkflow` | `workflow` | yes → registered workflow | | `temporal.handler` | workflow exposes | `workflow.SetQueryHandler` / `GetSignalChannel` / `SetUpdateHandler` (+`WithOptions`) | `query` / `signal` / `update` | provider edge | | `temporal.signal-send` | sender → running workflow | `workflow.SignalExternalWorkflow` / `client.SignalWorkflow` | `signal` | consumer edge | | `temporal.query-call` | caller → running workflow | `client.QueryWorkflow` | `query` | consumer edge | Extra Meta on these edges: `temporal_registered_name` (the `RegisterOptions{Name}` override that is the actual dispatch key), `temporal_register_plural` (a `RegisterActivities(&Struct{})` registration whose exported methods are each promoted), and `temporal_name_origin=env_default` (a dispatch name resolved from an env-var-with-literal-default, landed at the speculative tier). Node roles are stamped as `temporal_role` (`activity` / `workflow` / `activity_interface` / `workflow_interface` / `signal` / `query` / `update`). Aliased `import wf "go.temporal.io/sdk/workflow"` receivers are canonicalised before detection.