Decision records

ADR-0011: Data-driven application profiles and detection

  • Status: Accepted (2026-10-05)

Context

Handoff §6: pluggable application detection that reports evidence and confidence and proposes a profile; detection never silently changes production. Profiles are data-driven and define recommended cache bypasses, static paths, health checks, security rules and runtime defaults. Initial set: generic PHP, WordPress, WooCommerce, Laravel, generic Node, Next.js, generic Python, Django, Flask, FastAPI.

Decision

  • Profiles are YAML files in profiles/<name>.yaml, embedded into the binary at build time (no runtime lookup of profile files). Each declares detection rules, a runtime template, a health check, static paths, cache-bypass rules, security rules and notes.
  • Detection evaluates rules against an application directory: file/directory presence, bounded substring search in a file, a key in a JSON file (package.json, composer.json), or a bounded search across source files of an extension. Matched rules contribute weights; confidence = sum, capped at 100. A profile is a candidate only if all required rules match and confidence reaches its threshold. Ties are broken by priority (more specific profiles, e.g. WooCommerce over WordPress, rank higher). All reads are bounded (file size, number of files, depth) and confined to the directory (symlinks are not followed).
  • Facts that a template needs (Python module:callable, Django WSGI module, virtualenv, npm start command) are extracted by small named functions in code; the template references them as ${fact}. Substituted values are escaped and the rendered runtime is validated like any configuration.
  • Detection only proposes. scalwsctl detect <dir> prints the evidence, the confidence of every candidate and a proposed application block (or JSON with --json); nothing is written.
  • profile: in configuration is explicit and static: it supplies the runtime when runtime is omitted, but only for templates without detected facts (generic PHP, WordPress, WooCommerce, Laravel); otherwise configuration must contain the runtime (as proposed by scalwsctl detect). It also fills health_path of Node/Python runtimes when unset. Loading a configuration never inspects application files, so a deploy cannot change how an application is run.
  • Cache-bypass and security rules are carried as data now and consumed by the cache and security engine in M5.

Consequences

  • Adding a framework is a YAML file plus, at most, one fact extractor.
  • Profile rules are heuristics; scalwsctl detect shows the evidence so operators can judge.