Stack validation and capability resolution
Propagate owns integrity of the whole deployment stack. propagate validate checks schemas, dependencies, and capability closure before any apply.
Validation modes
Full validation
propagate validateRuns all checks and writes .propagate/resolved.json on success.
Schema only
propagate validate --schema-onlyValidates stack.yaml structure only. Useful before app.manifest.yaml files exist in app repos.
Checks performed
- Stack schema —
stack.yamlconforms to Zod schema (apiVersion, metadata,dns.eventCode, provider, apps) - Catalog membership — each app slug exists in
catalog/apps.yamland is deployable - App manifests —
app.manifest.yamlpresent at each app repo root - Dependency graph —
dependsOnis acyclic; topological sort produces deploy order - Capability closure — every
requirescapability has a matchingprovides(or user/generated source) - User values — all
source: usercapabilities have entries invalues.yaml - Provider credentials — warning if GitHub App not installed, legacy PAT present, org mismatch, or no Vercel token in
credentials.json/VERCEL_TOKENenv
Capability resolution
After validation passes, the resolver:
- Sorts apps by
dependsOn(docs before timelining) - Derives per-app URLs:
https://{appSlug}.{eventCode}.{hostName} - Fills
derivableenv vars (e.g.DOCS_APP_URLfromdocs.web) - Generates shared tokens (e.g.
PRIVATE_API_TOKENfor infra API auth between apps) - Maps
capabilitysources across apps (timeliningDOCS_APP_URL← docs deploy URL)
Example resolution (docs + timelining)
| App | Key env vars | Source |
|---|---|---|
| docs | PRIVATE_API_TOKEN | generated |
| timelining | DOCS_APP_URL | capability ← docs.web |
| timelining | PRIVATE_API_TOKEN | generated (same token as docs) |
| timelining | TELEGRAM_BOT_TOKEN | user (values.yaml) |
| timelining | KV_REST_* | provisioned (override via upstash.kv in values) |
| timelining | NEO4J_* | provisioned (override via neo4j.connection in values) |
resolved.json output
Written to .propagate/resolved.json and consumed by propagate apply:
{
"stack": { "...": "..." },
"deploymentOrder": ["docs", "timelining"],
"apps": [
{
"slug": "docs",
"url": "https://docs.argentina-alj.prisma.events",
"domain": "docs.argentina-alj.prisma.events",
"env": { "PRIVATE_API_TOKEN": "..." },
"provision": [
{ "id": "neo4j.connection", "provisioner": "railway.neo4j", "dockerPath": ".docker" }
]
}
],
"generated": { "docs.infra_token": "..." },
"errors": [],
"warnings": []
}Dependencies
timelining hard-depends on docs APIs (dependsOn: [docs]). Deploy docs first, then timelining. There is no reverse dependency from docs to timelining.
Failure messages
Common validation failures:
| Error | Remedy |
|---|---|
Missing app.manifest.yaml | Run agent task (AGENT_PREPARE_APPS.md) |
Cyclic dependsOn | Fix manifest dependency graph |
| Missing user capability | Re-run propagate create or edit values.yaml |
| No provider for capability | Add provides in another app’s manifest |
| GitHub App not installed | Run propagate auth github |
| GitHub App org mismatch | Re-install on correct target org |
Invalid eventCode | Must be DNS-safe lowercase [a-z0-9-] |
Diff and drift
propagate diffTwo comparisons:
- Manifest — current
stack.yamlvs.propagate/last-applied/stack.yaml - Infrastructure —
pulumi previewagainst live GitHub/Vercel state
Last updated on