12factor-fit
Installation
SKILL.md
12factor Fit
Inspect first. Generate nothing until the fit verdict is clear.
Skill Order
- Run this skill first.
- Hand off to
$12factor-rockand$12factor-charmonly after the fit verdict is explicit. - Hand off to
$12factor-juju-terraformlast, after rock, charm, and target environment details are known.
Workflow
- Inspect the repository before asking questions.
- Run
scripts/detect_framework.py <repo>to get a framework verdict with signals. - If the repo clearly separates backend and frontend in a monorepo, stop treating the repo root as the packaging target. Confirm which backend subdirectory should be the 12-factor scope.
- Confirm the detected framework with the user.
- Confirm the application is meant to run as a web service on a Kubernetes-backed Juju model.
- If the repo clearly separates backend and frontend in different subdirectories, default to applying the 12-factor workflow to the backend subdirectory only. Do not default to bundling both into one image.
- If the repo has any frontend or static-asset build question, ask the user explicitly whether that build belongs inside the rock or remains a separate deployment concern. Do not infer
frontend_buildon your own. - Only consider a bundled frontend/backend image when the repo shape already behaves like one runtime application and the user explicitly accepts that exception path.
- Ask the mandatory deployment-context questions from
references/question-bank.md. - Confirm whether the app needs database migrations and whether they should be
handled by framework-native tooling or a root
migrate.sh. - Confirm whether the app needs extra
-workeror-schedulerservices alongside the main web service, and capture the intended commands. - If the user wants any relations declared in the charm beyond those already embedded in the charmcraft extension (ingress, grafana-dashboard, metrics-endpoint, and logging are extension-provided with fixed optionality), ask which of those additional relations should be optional in charm metadata. Do not infer requiredness from repo inspection or app behavior alone.
- If the framework is FastAPI, Go, ExpressJS, or Spring Boot, ask whether the user accepts the current experimental extension path and edge-channel tooling requirement.
- Once the user answers, run
scripts/preflight_targets.pyto verify the chosen Kubernetes context, Juju controller, registry, and required local tools. - Load
references/framework-detection.mdandreferences/known-risks.mdbefore deciding whether to proceed. - Produce a fit verdict with:
- detected framework
- confirmed deployment context
- structured handoff payload
- required follow-up questions
- explicit stop reasons if the project does not fit
Non-Negotiables
- Detect the framework before calling
rockcraft initorcharmcraft init. - Confirm the app is a web application.
- Confirm the deployment target is Kubernetes-backed.
- If the repo clearly separates frontend and backend, target the backend subdirectory for the 12-factor workflow.
- If frontend or static-asset build behavior is ambiguous, stop and ask the user whether that build belongs inside the rock or remains separate.
- Ask which registry, Kubernetes context, and Juju controller to use.
- Verify the chosen targets before build or deploy work.
- Ask before installing or changing tooling on the host.
- Surface experimental extension requirements before promising a supported path.
- If any relation beyond the extension-embedded set (ingress, grafana-dashboard, metrics-endpoint, logging) will be declared in the charm, ask whether it should be optional or required in charm metadata. Do not infer that yourself. Do not ask about extension-embedded relations — their optionality is fixed.
- Treat database migrations as a supported 12-factor path for every supported
framework. Current
paas-charmcode checksmigrate, thenmigrate.sh, thenmigrate.py, thenmanage.py, with the last match winning. For Spring Boot, prefer framework-managed migrations. - Treat extra
-workerand-schedulerservices as supported when they sit alongside a main web service. - Treat unsupported or inconsistent upstream paths as stop conditions, not invitations to improvise.
Mandatory Questions
Ask these unless the answer is already explicit and reliable in the conversation:
- Which framework should I target?
- Is this application meant to run as a web service?
- Which Kubernetes context or cluster should I use?
- Which Juju controller and model should I use?
- Which OCI registry should hold the rock?
- Should the charm stay local, or is Charmhub publication in scope?
- If the repo separates frontend and backend, which subdirectory is the backend application root?
- If the app has a frontend or static-asset build step, should that build stay separate, or do you want it embedded into the backend image for this project?
- If
<framework>is FastAPI, Go, ExpressJS, or Spring Boot: do you accept the current experimental extension path, including edge-channel tooling? - Does the app need a database? If yes, should migrations stay
framework-managed or should the rock expose a root
migrate.sh? - Does the app need extra
-workeror-schedulerservices alongside the main web service? - Does the app need ingress, observability, Redis, RabbitMQ, S3, SMTP, OIDC, SAML, OpenFGA, tracing, or a proxy? (Note: ingress, grafana-dashboard, metrics-endpoint, and logging are already embedded in the charmcraft extension with fixed optionality — do not ask about those.)
- For each additional relation you want declared in the charm beyond the extension-embedded set, should it be optional or required in charm metadata?
- If ingress is needed: the skill will auto-detect the best ingress charm for the cluster and ask you to confirm when multiple options exist.
Use references/question-bank.md for the full grouped list.
Stop Early
Stop and explain why if any of these are true:
- the framework is outside the supported set
- the project is not a web application
- the app cannot reasonably read runtime configuration from env vars
- the deployment target is not Kubernetes-backed Juju
- the user expects a pure Terraform-provider deployment for an unpublished local charm artifact
- the repo clearly separates frontend and backend and the requested scope is to force both into one image anyway
- the repo has a frontend/static build question and the user has not confirmed whether it should be embedded or kept separate
- a relation should be declared in the charm but the user has not confirmed whether it should be optional or required in charm metadata
- the project only works after leaving the extension boundary
Use references/known-risks.md for framework-specific inconsistencies that need explicit acknowledgement.
Handoff Contract
If the project fits, hand off cleanly:
- to
$12factor-rockwith the confirmed framework, repo path, and rock deployment context - to
$12factor-charmwith the confirmed framework, relation list plus explicit optionality, config needs, and minimal-change constraints - to
$12factor-juju-terraformwith the chosen deployment mode and verified target environment
Use this minimum handoff shape so later skills do not guess:
framework: <string>
repo_path: <absolute path>
k8s_context: <string>
kubectl_command: <string>
juju_controller: <string>
juju_model: <string or null>
registry: <string>
charm_publication: local | charmhub
deployment_mode: provider-first | hybrid-local-artifact
relations:
- name: <relation name>
optional: true | false
config_options_needed:
- <option name>
frontend_build: none | embedded-in-backend-image | separate-deployment
migrations:
mode: none | framework-managed | migrate-sh
tool: <string or null>
background_services:
workers:
- <service name or command>
schedulers:
- <service name or command>
experimental_extensions_accepted: true | false
minimal_change_policy: true
ingress:
needed: true | false
external_hostname: <string or null>