usm/cli-multi-lang-scan [built]
Multi-language scanner support, extended with a detector plugin system. usm scan detects services, frameworks, routes, data models, and infrastructure from 12+ languages and 30+ frameworks via built-in detectors. Users and agents extend detection through two declarative surfaces — a detection section in usmconfig.json and auto-discovered .usm/detectors/*.yaml files — with a script escape hatch for convention-based frameworks. The scanner is generalized: service detection is driven by detector manifests, removing the package.json hardcode, so non-JS/TS stacks (Go, Zig, etc.) are first-class. Existing Node.js/TypeScript scanning is unchanged.
Status: built
Why this exists
The .usm format, MCP tools, generators, and validator are all language-agnostic, but the scanner only detects Node.js/TypeScript projects (package.json, Next.js routes, Prisma schemas) plus hardcoded Python/Docker/Prisma/Terraform special-cases. This feature extends the scanner to detect services and routes from common frameworks across all major programming languages AND makes the detection itself extensible without an upstream PR — users drop a .usm/detectors/zig.yaml file or add a detection entry to usmconfig.json, and usm scan picks it up. A script escape hatch handles convention-based frameworks (Next.js file-routing) that regex cannot express. The generalized orchestrator replaces the package.json hardcode so any detector manifest (go.mod, build.zig.zon, etc.) drives service detection.
Design decisions
manifest-based-detection [accepted]
Decision: Detect services by language manifest files
Rationale: Every language ecosystem has a standard manifest file. Detecting these files is reliable, language-specific, and doesn't require parsing source code. The manifest also tells us dependencies (framework detection).
Consequences: Scanner needs a manifest-to-language map. New languages added by adding a manifest entry.
framework-specific-route-detection [accepted]
Decision: Detect routes per-framework, not per-language
Rationale: Route patterns differ by framework, not language. Next.js uses app/page.tsx, FastAPI uses @app.get decorators, Spring uses @GetMapping annotations, Rails uses config/routes.rb, ASP.NET uses [HttpGet] attributes. Each framework needs its own route extractor.
Alternatives considered:
- Generic AST parsing per language — rejected: Too complex, requires language-specific parsers, fragile
- File-pattern matching only (no framework awareness) — rejected: Misses decorator/annotation-based routes (FastAPI, Spring, ASP.NET)
Consequences: Route detection is framework-by-framework. New frameworks need a route extractor.
configurable-in-usmconfig [accepted]
Decision: Language/framework detection rules are configurable in usmconfig.json
Rationale: Users can add custom manifest patterns, route patterns, and data model patterns for frameworks we don't support out of the box. Defaults cover common frameworks; advanced users can extend.
Consequences: usmconfig.json gains a 'detection' section with language/framework rules
all-languages-from-start [accepted]
Decision: Support all major languages from the initial implementation
Rationale: Rather than phasing, include all major languages (Python, Go, Rust, Java, Kotlin, C#, Ruby, PHP, Elixir, Swift, Scala, C/C++) from the start. The manifest detection is simple (file pattern matching) and route detection is regex-based per framework. The complexity is manageable.
Consequences: Larger initial implementation but no phased rollout complexity
detectors-directory [accepted]
Decision: Add .usm/detectors/*.yaml as a second auto-discovered extension surface alongside usmconfig.json detection
Rationale: usmconfig.json describes a single repo's shape and is per-project. Detector files are shareable across repos, versioned with the .usm source of truth, validatable like other USM artifacts, and agent-writable via MCP tools. Two surfaces with the same field shapes give users the cheap path (drop a file) and the config path (inline rules) without forcing one or the other.
Alternatives considered:
- usmconfig.json detection only (original accepted decision) — rejected: Config is per-project and not shareable; cannot express convention-based frameworks that need code
- .usm/detectors/ only, drop the config section — rejected: Reverses an accepted decision and removes the inline config ergonomics for small overrides
Consequences: Two surfaces to keep in sync; precedence rules must be deterministic. Built-in detectors migrate to the same shape as user detectors.
script-escape-hatch [accepted]
Decision: Allow routes.script in a detector to point at a .ts/.js file exporting extractRoutes(sourceDir, framework) for convention-based frameworks
Rationale: Regex cannot capture Next.js app/page.tsx convention routing, Remix, or SvelteKit file-based routes. These need real logic. A script field keeps the declarative detector file as the entry point but delegates extraction to code when needed. The script lives in the user's repo (not auto-loaded from third parties), bounding the trust surface.
Consequences: Detector files can reference local code; USM dynamically imports it. Only opt-in per detector; declarative regex remains the default.
generalized-orchestrator [accepted]
Decision: Generalize structural.ts service detection so any detector manifest drives it, removing the package.json hardcode
Rationale: Today a service directory without package.json is warned and skipped, making non-JS/TS stacks second-class. The whole point of multi-language scan is that a Go app (go.mod) or Zig app (build.zig.zon) is detected as a service without package.json. The orchestrator must ask detectors 'does your manifest match this directory' instead of assuming package.json.
Consequences: The Python pyproject.toml and Docker docker-compose special-case passes become built-in detectors. structural.ts shrinks to an orchestrator that iterates detectors.
infrastructure-as-detector [accepted]
Decision: Treat infrastructure (Terraform today) as a detector kind rather than a hardcoded separate subcommand
Rationale: infrastructure.ts only parses Terraform. Users with CloudFormation, Pulumi, or CDK get nothing. An infrastructure detector kind with the same manifest+pattern shape as other detectors makes IaC extensible without code, consistent with the rest of the plugin system.
Consequences: usm scan infrastructure subcommand continues to work (backed by the built-in Terraform detector); new formats add via detector files or detection.infrastructure in config.
precedence-order [accepted]
Decision: Detector precedence is built-in defaults < .usm/detectors/ files < usmconfig.json detection (last wins)
Rationale: Built-ins provide sane defaults out of the box. Detector files are project-level customizations shared across the team. usmconfig.json is the highest precedence so a user can override a detector file from config without editing the file (useful for one-off overrides). Deterministic order prevents merge ambiguity.
Consequences: Documented precedence; overrides by $id for detectors and by manifest pattern for config arrays.
How it works
Detect services from multiple language manifests (detect-multi-lang-services)
Scanner reads usmconfig.json detection.manifests (or defaults) and checks for manifest files. Each manifest identifies a service and its language. Framework is detected from dependencies in the manifest.
- Read — usmconfig.json detection.manifests (or defaults)
- Scan — directories for all manifest files
- Parse — each manifest for framework dependencies
- Classify — service type (web-app, api, worker) from framework
- Generate — .usm/services/<name>.usm with detected language, runtime, framework
Detect Python routes (FastAPI, Flask, Django) (detect-python-routes)
Scan .py files for decorator-based routes. FastAPI uses @app.get/@router.get, Flask uses @app.route, Django uses urlpatterns in urls.py.
- Scan — .py files in service directory
- Match — FastAPI: @app.(get|post|put|delete|patch), @router.(get|post|...); Flask: @app.route; Django: path('', views)
- Extract — path and HTTP method from decorator/pattern
- Generate — routes[] in feature .usm files
Detect Go routes (chi, gin, echo, net/http) (detect-go-routes)
Scan .go files for router registration patterns. chi/gin/echo use r.GET/r.POST, net/http uses http.HandleFunc, gorilla/mux uses r.HandleFunc.
- Scan — .go files in service directory
- Match — (r|router|mux).(GET|POST|PUT|DELETE|PATCH|HandleFunc|Handle)
- Extract — path and HTTP method
- Generate — routes[] in feature .usm files
Detect Rust routes (Axum, Actix, Rocket) (detect-rust-routes)
Scan .rs files for route patterns. Axum uses .route(), Actix uses #[get(...)] macros, Rocket uses #[get("/path")] attributes.
- Scan — .rs files in service directory
- Match — .route(), .service(), #[get(...)], #[post(...)], #[route(GET, "/path")]
- Extract — path and HTTP method
- Generate — routes[] in feature .usm files
Detect Java/Kotlin routes (Spring Boot, Javalin, Quarkus) (detect-java-routes)
Scan .java/.kt files for annotation-based routes. Spring uses @GetMapping/@PostMapping/@RequestMapping, Javalin uses app.get/post, Quarkus uses @Path + @GET/@POST.
- Scan — .java and .kt files in service directory
- Match — Spring: @GetMapping/@PostMapping/@RequestMapping; Javalin: app.get/post; Quarkus: @GET/@POST + @Path
- Extract — path and HTTP method from annotation
- Generate — routes[] in feature .usm files
Detect C# routes (ASP.NET Core, Minimal APIs) (detect-csharp-routes)
Scan .cs files for attribute-based routes. ASP.NET uses [HttpGet], [HttpPost], [Route], Minimal APIs use app.MapGet/MapPost.
- Scan — .cs files in service directory
- Match — [HttpGet], [HttpPost], [Route("path")], app.MapGet, app.MapPost
- Extract — path and HTTP method from attribute
- Generate — routes[] in feature .usm files
Detect Ruby routes (Rails, Sinatra) (detect-ruby-routes)
Scan config/routes.rb for Rails routes (get/post/resources) and .rb files for Sinatra routes (get '/path' do).
- Scan — config/routes.rb and .rb files in service directory
- Match — Rails: get/post/resources/namespace; Sinatra: get '/path' do, post '/path' do
- Extract — path and HTTP method
- Generate — routes[] in feature .usm files
Detect PHP routes (Laravel, Symfony, Slim) (detect-php-routes)
Scan routes/web.php for Laravel routes (Route::get/post), .php files for Symfony attributes (#[Route]) and Slim ($app->get/post).
- Scan — routes/web.php, routes/api.php, and .php files
- Match — Laravel: Route::get/post; Symfony: #[Route]; Slim: $app->get/post
- Extract — path and HTTP method
- Generate — routes[] in feature .usm files
Detect Elixir routes (Phoenix) (detect-elixir-routes)
Scan router.ex for Phoenix routes (get/post/pipe_through/scope).
- Scan — lib/*_web/router.ex files
- Match — get/post/put/patch/delete within scope blocks
- Extract — path and HTTP method
- Generate — routes[] in feature .usm files
Detect Swift routes (Vapor) (detect-swift-routes)
Scan .swift files for Vapor route registrations (routes.get/post, app.get/post).
- Scan — .swift files in service directory
- Match — routes.get/post/put/delete, app.get/post
- Extract — path and HTTP method
- Generate — routes[] in feature .usm files
Detect Scala routes (Akka HTTP, Play, Tapir) (detect-scala-routes)
Scan .scala files for route patterns. Akka HTTP uses path/endpoints, Play uses routes file, Tapir uses endpoint.get/post.
- Scan — .scala files and conf/routes files
- Match — Akka: path/endpoints; Play: GET /path; Tapir: endpoint.get/post
- Extract — path and HTTP method
- Generate — routes[] in feature .usm files
Detect C++ routes (Crow, Drogon, Pistache) (detect-cpp-routes)
Scan .cpp/.h files for route registration patterns. Crow uses CROW_ROUTE, Drogon uses app.registerHandler, Pistache uses router.get/post.
- Scan — .cpp and .h files in service directory
- Match — Crow: CROW_ROUTE; Drogon: registerHandler; Pistache: router.get/post
- Extract — path and HTTP method
- Generate — routes[] in feature .usm files
Detect data models from multiple ORMs across languages (detect-multi-lang-data-models)
Extend data model detection beyond Prisma to support ORMs across all supported languages.
- Scan — known ORM schema files per language
- Parse — model definitions (class-based, struct-based, macro-based, annotation-based)
- Generate — .usm/data/<name>.usm with models and fields
Discover and load detector files (discover-detector-files)
At scan start, the orchestrator reads .usm/detectors/*.yaml (if present), validates each against detector-v1.json, and merges them with built-in defaults and the usmconfig.json detection section. Precedence: built-in defaults < .usm/detectors/ files < usmconfig.json detection (last wins).
- Read — .usm/detectors/*.yaml if directory exists
- Validate — each detector file against detector-v1.json schema
- Merge — built-in defaults + detector files + usmconfig.json detection (config overrides files)
- Warn — on invalid detector files (skip, do not abort scan)
Detect a service from any language manifest (detect-service-via-detector)
Replaces the hardcoded package.json read. For each directory matched by a services rule, the orchestrator finds the detector whose manifest glob matches a file in that directory and reads that manifest (not always package.json). A Go app with go.mod is detected via the Go detector; a Zig app with build.zig.zon via the Zig detector. No package.json is required.
- Match — directory against usmconfig.json services rules
- Find — detector whose manifest glob matches a file in the directory
- Parse — that manifest file (package.json, go.mod, build.zig.zon, etc.)
- Classify — language, runtime, framework from manifest contents
- Generate — .usm/apps/<name>/service.usm
Extract routes for convention-based frameworks via script (extract-routes-via-script)
Frameworks whose routes come from file conventions (Next.js app/page.tsx, Remix, SvelteKit) rather than declarations cannot be captured by regex. A detector declares routes.script pointing at a .ts file exporting extractRoutes(sourceDir, framework). The orchestrator imports and runs it. Declarative regex patterns remain the default; script is opt-in per detector.
- Read — detector routes.script path
- Import — the script module dynamically
- Call — extractRoutes(sourceDir, framework)
- Generate — routes[] in feature .usm files
Detect infrastructure from IaC files via detectors (detect-infrastructure-via-detector)
Generalizes the Terraform-only infrastructure scan. An infrastructure detector declares a manifest glob (infrastructure/**/.tf, **/cloudformation.yaml, **/.pulumi.ts) and resource extraction patterns. Built-in Terraform detector preserves current behaviour; users add CloudFormation/Pulumi/CDK via detector files or usmconfig.json detection.infrastructure.
- Match — infrastructure detector manifest globs
- Parse — matched IaC files for resources
- Emit — infrastructure YAML block per service
Detect data models from any ORM via detectors (detect-data-schema-via-detector)
Generalizes the Prisma-only data scan. A data-schema detector declares a manifest glob and model extraction patterns. Built-in Prisma detector preserves current behaviour; users add SQLAlchemy, Diesel, GORM, Entity Framework via detector files or usmconfig.json detection.data_models.
- Match — data-schema detector manifest globs
- Parse — model definitions (class, struct, macro, annotation)
- Generate — .usm/packages/<pkg>/<orm>.usm with models[]
Guarantees
manifest-detection-coverage
Scanner detects services from all supported language manifests
Acceptance criteria:
- [ ] package.json → Node.js/TypeScript/JavaScript
- [ ] pyproject.toml or requirements.txt → Python
- [ ] Cargo.toml → Rust
- [ ] go.mod → Go
- [ ] pom.xml or build.gradle → Java/Kotlin
- [ ] .csproj or .sln → C#/.NET
- [ ] Gemfile → Ruby
- [ ] composer.json → PHP
- [ ] mix.exs → Elixir
- [ ] Package.swift → Swift
- [ ] build.sbt → Scala
- [ ] CMakeLists.txt or Makefile → C/C++
- [ ] Framework detected from dependencies in manifest
route-detection-coverage
Scanner detects routes from 30+ frameworks across all languages
Acceptance criteria:
- [ ] Next.js: app/page.tsx, app/route.ts (existing)
- [ ] Express: app.get/post/put/delete in .js/.ts files
- [ ] FastAPI: @app.get/post decorators in .py files
- [ ] Flask: @app.route decorators in .py files
- [ ] Django: path() patterns in urls.py
- [ ] Go chi/gin/echo: r.GET/POST and router.HandleFunc patterns
- [ ] Go net/http: http.HandleFunc patterns
- [ ] Rust Axum: .route() calls
- [ ] Rust Actix: #[get/post] macros
- [ ] Rust Rocket: #[get/post] attributes
- [ ] Spring Boot: @GetMapping/@PostMapping/@RequestMapping annotations
- [ ] Javalin: app.get/post calls
- [ ] Quarkus: @GET/@POST + @Path annotations
- [ ] ASP.NET Core: [HttpGet]/[HttpPost] attributes
- [ ] ASP.NET Minimal: app.MapGet/MapPost
- [ ] Rails: get/post/resources in config/routes.rb
- [ ] Sinatra: get '/path' do in .rb files
- [ ] Laravel: Route::get/post in routes/web.php
- [ ] Symfony: #[Route] attributes
- [ ] Slim: $app->get/post in .php files
- [ ] Phoenix: get/post in router.ex
- [ ] Vapor: routes.get/post in .swift files
- [ ] Akka HTTP: path/endpoints in .scala files
- [ ] Play: GET /path in conf/routes
- [ ] Tapir: endpoint.get/post in .scala files
- [ ] Crow: CROW_ROUTE macro in .cpp files
- [ ] Drogon: registerHandler in .cpp files
- [ ] Pistache: router.get/post in .cpp files
data-model-detection-coverage
Scanner detects data models from ORMs across languages
Acceptance criteria:
- [ ] Prisma: schema.prisma (existing, TypeScript)
- [ ] SQLAlchemy: class definitions in models.py (Python)
- [ ] Django ORM: class definitions in models.py (Python)
- [ ] GORM: struct definitions with gorm tags (Go)
- [ ] Diesel: table! macros in schema.rs (Rust)
- [ ] Hibernate: @Entity annotations in .java (Java)
- [ ] Entity Framework: DbSet properties in DbContext (C#)
- [ ] ActiveRecord: class definitions inheriting ApplicationRecord (Ruby)
- [ ] Eloquent: class definitions extending Model (PHP)
- [ ] Ecto: schema definitions in .ex files (Elixir)
config-extensible
Detection rules are configurable in usmconfig.json
Acceptance criteria:
- [ ] detection.manifests array with {pattern, language, frameworks?} objects
- [ ] detection.routes array with {framework, pattern, method_group, path_group} objects
- [ ] detection.data_models array with {orm, pattern, model_pattern?} objects
- [ ] Defaults cover all supported frameworks; users can add custom patterns
backward-compatible
Existing Node.js/TypeScript scanning unchanged
Acceptance criteria:
- [ ] usm scan on a Node.js project produces same results as before
- [ ] usmconfig.json without detection section uses defaults
detector-file-format
Detector files in .usm/detectors/*.yaml are validated against a detector-v1.json schema before use
Acceptance criteria:
- [ ] Each detector declares $id, kind (service|framework|routes|data-schema|infrastructure), manifest glob, language, runtime
- [ ] Framework detectors declare frameworks[] with name + string-contains detect rule
- [ ] Route detectors declare routes.extensions + routes.patterns[] (regex, methodGroup, pathGroup) OR routes.script (mutually exclusive)
- [ ] Invalid detector files are skipped with a warning, not an abort
dual-extension-surfaces
Users can extend detection through either usmconfig.json or .usm/detectors/ files, with the same expressiveness
Acceptance criteria:
- [ ] usmconfig.json detection.manifests / detection.routes / detection.data_models / detection.infrastructure arrays (existing accepted decision, unchanged)
- [ ] .usm/detectors/*.yaml files as a second, auto-discovered source
- [ ] Both surfaces accept the same field shapes (manifest, frameworks, routes patterns, data model patterns)
- [ ] Detector files support routes.script for convention-based frameworks; usmconfig.json detection.routes supports script too
orchestrator-generalized
Service detection is driven by detector manifests, removing the package.json hardcode in structural.ts
Acceptance criteria:
- [ ] A service directory is detected if ANY detector manifest glob matches a file in it (not only package.json)
- [ ] A Go app with go.mod and no package.json produces a service.usm (no 'No package.json found' warning)
- [ ] A Zig app with build.zig.zon and no package.json produces a service.usm
- [ ] The existing Python pyproject.toml and Docker docker-compose passes become built-in detectors, not special-case code
infrastructure-extensible
Infrastructure detection is extensible beyond Terraform via detectors
Acceptance criteria:
- [ ] Built-in Terraform detector preserves usm scan infrastructure output exactly
- [ ] detection.infrastructure array in usmconfig.json (new, mirrors detection.manifests shape)
- [ ] Infrastructure detector files accepted in .usm/detectors/
- [ ] CloudFormation, Pulumi, CDK detectable via user-supplied detectors without code change
precedence-and-override
Detector sources merge with a deterministic precedence so users can override built-ins
Acceptance criteria:
- [ ] Built-in detectors are the lowest precedence (seeded from current MANIFESTS/ROUTE_PATTERNS/Prisma/Terraform tables)
- [ ] .usm/detectors/ files override built-ins by detector $id
- [ ] usmconfig.json detection section is highest precedence (overrides both built-ins and detector files)
- [ ] A usmconfig.json with no detection section and no .usm/detectors/ dir produces identical results to the current scanner (backward compatible)
backward-compatible-existing-implementations
Existing USM projects with usmconfig.json and .usm files continue to scan identically
Acceptance criteria:
- [ ] usm scan on a Node.js/Next.js project with no detection section and no detectors dir produces the same .usm output as before this feature
- [ ] Existing PRESERVE_FIELDS / UPDATE_FIELDS smart-merge behaviour unchanged
- [ ] Existing CLI flags (--force, --routes, --merge) unchanged in name and default
- [ ] The existing manifest-detection-coverage and route-detection-coverage contracts from the original spec remain satisfied by the migrated built-in detectors
Test specifications
detect-python-service
Given:
- pyproject_toml_with_fastapi: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: python
- assertion: framework: fastapi detected
detect-go-service
Given:
- go_mod_with_gin: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: go
- assertion: framework: gin detected
detect-rust-service
Given:
- cargo_toml_with_axum: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: rust
- assertion: framework: axum detected
detect-java-service
Given:
- pom_xml_with_spring_boot: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: java
- assertion: framework: spring-boot detected
detect-csharp-service
Given:
- csproj_with_aspnet: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: dotnet
- assertion: framework: aspnet-core detected
detect-ruby-service
Given:
- gemfile_with_rails: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: ruby
- assertion: framework: rails detected
detect-php-service
Given:
- composer_json_with_laravel: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: php
- assertion: framework: laravel detected
detect-elixir-service
Given:
- mix_exs_with_phoenix: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: elixir
- assertion: framework: phoenix detected
detect-swift-service
Given:
- package_swift_with_vapor: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: swift
- assertion: framework: vapor detected
detect-scala-service
Given:
- build_sbt_with_akka: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: scala
- assertion: framework: akka-http detected
detect-cpp-service
Given:
- cmake_with_drogon: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: cpp
- assertion: framework: drogon detected
extract-fastapi-routes
Given:
- python_file_with_decorators: true
Then:
- assertion: routes[] extracted with correct paths and methods
extract-spring-routes
Given:
- java_file_with_annotations: true
Then:
- assertion: routes[] extracted from @GetMapping/@PostMapping
extract-aspnet-routes
Given:
- cs_file_with_attributes: true
Then:
- assertion: routes[] extracted from [HttpGet]/[HttpPost]
extract-rails-routes
Given:
- routes_rb_with_get_post: true
Then:
- assertion: routes[] extracted from get/post/resources
extract-laravel-routes
Given:
- web_php_with_route_get: true
Then:
- assertion: routes[] extracted from Route::get/post
extract-phoenix-routes
Given:
- router_ex_with_get_post: true
Then:
- assertion: routes[] extracted from get/post in scope blocks
backward-compatible-node
Given:
- package_json_with_nextjs: true
Then:
- assertion: same results as before this feature
custom-detection-rules
Given:
- usmconfig_with_custom_manifest: true
Then:
- assertion: custom manifest pattern detected
detector-file-zig-detected
Given:
- usm_detectors_zig_yaml: "declares manifest build.zig.zon, framework zap, route regex"
- apps_zig_api_build_zig_zon_with_zap: true
Then:
- assertion: .usm/apps/zig-api/service.usm created with runtime: zig, framework: zap
- assertion: no 'No package.json found' warning
- assertion: routes[] extracted from .zig source files
infrastructure-detector-cloudformation
Given:
- usm_detectors_cloudformation_yaml: "declares manifest **/cloudformation.yaml, resource extraction pattern"
- infrastructure_cloudformation_yaml_with_resources: true
Then:
- assertion: infrastructure YAML block emitted for CloudFormation resources
script-route-extraction-nextjs
Given:
- usm_detectors_nextjs_yaml: "declares routes.script: ./detectors/nextjs.ts"
- apps_web_app_page_tsx: true
Then:
- assertion: routes[] extracted by the script, matching the current Next.js route extraction output
config-overrides-detector-file
Given:
- usm_detectors_go_yaml: "declares framework gin detect 'gin'"
- usmconfig_detection_manifests: "overrides go framework detect to 'echo'"
Then:
- assertion: scan uses the usmconfig.json detection rule (echo), not the detector file (gin)
no-package-json-service-detected
Given:
- apps_go_api_go_mod_with_gin: true
- no_package_json: true
Then:
- assertion: .usm/apps/go-api/service.usm created with runtime: go, framework: gin
- assertion: no warning emitted
backward-compatible-no-detectors-dir
Given:
- package_json_with_nextjs: true
- no_usm_detectors_dir: true
- usmconfig_without_detection_section: true
Then:
- assertion: scan output identical to pre-feature scanner (same services, routes, features)
invalid-detector-file-skipped
Given:
- usm_detectors_bad_yaml: "malformed YAML or missing required fields"
Then:
- assertion: scan continues, warning emitted naming the invalid file
- assertion: other detectors still load and run