Docs/Reference/state.yaml schemaview as markdown
Flightline AppState: YAML Reference
TL;DR
A Flightline state file is a YAML document that declares the desired configuration for one app across every App Store Connect surface Flightline manages. You run flightline fetch <bundleId> once to capture live state, edit the YAML, then use flightline plan and flightline apply to preview and write the diff. The schema is embedded in the binary; your editor autocompletes via the yaml-language-server directive at the top of every fetched file.
Every top-level child of spec is optional. Omitting a section tells Flightline "leave this surface alone." You don't have to manage everything, start with spec.metadata and spec.version, add sections when you need them.
The schema URL is https://flightline.dev/schemas/v1alpha1/state.schema.json. Fields not listed in this document are not part of the v1alpha1 contract; unrecognized keys cause a LoadState error.
This reference covers v1alpha1. The apiVersion constant locks that. If a future release bumps to v1beta1, the diff will be explicit and documented.
See the state-as-code guide for a 5-minute walkthrough.
File anatomy
A minimal state file:
# yaml-language-server: $schema=https://flightline.dev/schemas/v1alpha1/state.schema.json
apiVersion: flightline.dev/v1alpha1
kind: AppState
metadata:
bundleId: com.example.myapp
version: "1.2.0"
spec:
version:
releaseType: AFTER_APPROVAL
The four top-level keys are required: apiVersion, kind, metadata, spec.
| Key | Type | Required | Notes |
|---|---|---|---|
apiVersion |
string const | yes | Must be exactly flightline.dev/v1alpha1 |
kind |
string const | yes | Must be exactly AppState |
metadata |
object | yes | Bundle identity, see metadata |
spec |
object | yes | Desired state, all children optional |
Editor setup. The # yaml-language-server: $schema=... directive at the top of every flightline fetch output activates autocomplete and inline validation in VS Code, Neovim (with yaml-language-server), and any editor that supports the LSP YAML extension. The schema is hosted at https://flightline.dev/schemas/v1alpha1/state.schema.json and also embedded in the flightline binary.
metadata
Identifies the app and the version being described. These three fields are also the keys Flightline uses when resolving the live ASC resource to diff against.
metadata:
bundleId: app.tideterm.ios
version: "1.0.1"
platform: IOS
| Field | Type | Required | Default | Constraint | Gotcha |
|---|---|---|---|---|---|
bundleId |
string | yes | , | ^[a-zA-Z0-9._-]+$, min 3 chars |
Must already exist in ASC. Flightline does not create apps. |
version |
string | yes | , | ^[0-9]+(\.[0-9]+)*$ |
Marketing version (CFBundleShortVersionString). Leading zeros rejected, 01.0 fails schema. Quote it: "1.0". |
platform |
enum | no | IOS |
IOS, MAC_OS, TV_OS, VISION_OS |
Defaults to IOS when absent. Must match the platform your build targets. |
Authoring loop. metadata.bundleId + metadata.version are the identity keys Flightline uses at every step: fetch → lint → plan → apply → preflight.
spec.version
Controls how and when the version releases once approved by Apple Review.
spec:
version:
releaseType: AFTER_APPROVAL
copyright: "© 2026 Tideterm Labs"
| Field | Type | Required | Default | Constraint | Gotcha |
|---|---|---|---|---|---|
releaseType |
enum | no | , | MANUAL, AFTER_APPROVAL, SCHEDULED |
If absent, Flightline leaves the current ASC setting unchanged. SCHEDULED requires earliestReleaseDate. |
earliestReleaseDate |
string | no | , | ISO 8601 datetime | Required when releaseType=SCHEDULED. Apple rejects times in the past. |
copyright |
string | no | , | maxLength 100 | Displayed on the App Store listing. Apple shows this under the app name. |
downloadable |
boolean | no | , | , | Rarely needed; controls whether the build is downloadable from TestFlight or the App Store. |
Release timing. Version release type is set before submission. MANUAL gives you a hold button after approval; AFTER_APPROVAL releases automatically; SCHEDULED releases at the specified time.
spec.build
Attaches a build (already uploaded via Xcode / altool) to this version. Flightline looks up the build by version + number and attaches it by its ASC resource ID.
spec:
build:
number: "42"
| Field | Type | Required | Default | Constraint | Gotcha |
|---|---|---|---|---|---|
number |
string | no | , | , | CFBundleVersion. Quote it: "42" not 42. Bare integers are YAML numbers, not strings, and Flightline's strict loader rejects the type mismatch. |
Builds must reach VALID state in ASC before Flightline can attach them. Upload is done by Xcode, altool, or xcrun notarytool, not by Flightline. If the build isn't processed yet, flightline apply will surface a typed error.
spec.metadata
Per-locale store listing content. Keys under spec.metadata.locales are Apple locale codes (en-US, es-MX, fr-FR, ja, etc.). You can manage any subset of locales, locales not listed here are left alone.
spec:
metadata:
locales:
en-US:
name: ".PassDMV: California"
subtitle: "Pass the test, first try"
description: |
California DMV practice tests with the latest official questions.
keywords: "DMV,driver,test,license,California,permit,practice"
whatsNew: "v1.0.1, bug fixes, faster question loading."
promotionalText: "Free updates as the DMV question bank evolves."
marketingUrl: "https://tideterm.app"
supportUrl: "https://tideterm.app/support"
privacyPolicyUrl: "https://tideterm.app/privacy"
es-MX:
name: ".PassDMV: California"
subtitle: "Aprueba a la primera"
Locale key format
Locale codes follow the pattern ^[a-z]{2}(-[A-Z]{2})?$, two-letter language code, optionally followed by a dash and two-letter region code. Examples: en-US, fr-FR, ja, zh-Hans, pt-BR.
Per-locale fields
| Field | Type | Required | maxLength | Gotcha |
|---|---|---|---|---|
name |
string | baseline | 30 | App name on the store listing. Apple enforces 30 chars; plan/apply will reject strings over this limit before hitting the wire. Applies to appInfoLocalization. |
subtitle |
string | no | 30 | One-line subtitle beneath the app name. Applies to appInfoLocalization. |
description |
string | baseline | 4000 | Long body text on the listing page. Applies to appStoreVersionLocalization. |
keywords |
string | no | 100 | Comma-separated, no spaces around commas. Goes to appStoreVersionLocalization. Not shown to users but affects search indexing. |
whatsNew |
string | no | 4000 | "What's New in this Version" text. Version-scoped: this is for the current metadata.version. |
promotionalText |
string | no | 170 | Promotional text block. Can be updated without resubmission, the only metadata field you can change on a live, approved version. Use it for time-sensitive copy. |
marketingUrl |
URI | no | , | Full URL. |
supportUrl |
URI | baseline | , | Full URL. |
privacyPolicyUrl |
URI | no | , | Full URL. |
Cross-resource routing. name and subtitle live on appInfoLocalization; everything else lives on appStoreVersionLocalization. Flightline handles the routing, you author a single locale map and Flightline dispatches to the correct ASC resource per field.
Per-locale completeness. Every locale you declare must be complete enough for Apple to accept. The localizations.completeness rule checks Flightline's submission baseline (name, description, and supportUrl) even when metadata is the only localized surface, and reports locale coverage gaps across other managed surfaces. Other fields remain lifecycle-dependent; check the Apple documentation for Apple's current requirements.
spec.screenshots
Per-locale, per-device-class screenshot sets. Flightline computes an MD5 of each local file and skips slots whose sourceFileChecksum already matches, making repeated applies no-ops when screenshots haven't changed.
spec:
screenshots:
locales:
en-US:
APP_IPHONE_69:
- path: ./screenshots/iphone69-1.png
- path: ./screenshots/iphone69-2.png
APP_IPHONE_67:
- path: ./screenshots/iphone67-1.png
Device class keys
| Key | Device | Pixel dimensions |
|---|---|---|
APP_IPHONE_69 |
iPhone 16 Pro Max (6.9") | 1320×2868 or 2868×1320 |
APP_IPHONE_67 |
iPhone 14 Plus / 15 Plus (6.7") | 1290×2796 or 2796×1290 |
APP_IPHONE_65 |
iPhone 11 Pro Max / XS Max (6.5") | 1242×2688 or 2688×1242 |
APP_IPHONE_61 |
iPhone 11 / XR (6.1") | 828×1792 or 1792×828 |
APP_IPHONE_55 |
iPhone 8 Plus (5.5") | 1242×2208 or 2208×1242 |
APP_IPAD_PRO_3GEN_129 |
iPad Pro 12.9" (3rd gen+) | 2048×2732 or 2732×2048 |
APP_IPAD_PRO_3GEN_11 |
iPad Pro 11" (1st gen+) | 1668×2388 or 2388×1668 |
APP_APPLE_TV |
Apple TV | 1920×1080 or 3840×2160 |
APP_APPLE_WATCH |
Apple Watch | varies by generation |
APP_APPLE_VISION_PRO |
Apple Vision Pro | 2732×2048 |
screenshotFile fields
| Field | Type | Required | Notes |
|---|---|---|---|
path |
string | yes | Relative paths resolve against the directory containing the state file. PNG or JPG. |
alt |
string | no | Reserved. Apple does not currently surface alt text on the store listing. |
Each device slot accepts 1 to 10 screenshots (minItems: 1, maxItems: 10).
Required devices for new submissions. Apple requires at least one supported large-iPhone screenshot tier for new iOS app submissions. The L3 preflight rule screenshots.requiredDevices catches missing coverage offline.
flightline apply resolves these paths relative to the state file, compares local MD5 checksums with Apple's live checksums, uploads changed files, and deletes live files omitted from the managed device set. Use --resume to continue an interrupted multipart upload. The dedicated flightline screenshots upload command remains available for one-off uploads outside state management.
spec.iap
In-app purchases keyed by productId. This section covers consumable, non-consumable, and non-renewing IAPs. Auto-renewable subscriptions live under spec.testflight (the subscription group surface), they are not represented here.
spec:
iap:
products:
app.tideterm.ios.lifetime:
type: NON_CONSUMABLE
name: "Lifetime Access"
familySharable: false
contentHosting: NON_HOSTED
reviewNote: |
Tap "Unlock Full Access" on the home screen.
reviewScreenshot:
path: ./review-screenshots/lifetime.png
localizations:
en-US:
name: "Lifetime Access"
description: "Unlock all DMV practice tests forever."
iapProduct fields
| Field | Type | Required | Constraint | Gotcha |
|---|---|---|---|---|
type |
enum | yes | CONSUMABLE, NON_CONSUMABLE, NON_RENEWING_SUBSCRIPTION |
AUTO_RENEWABLE_SUBSCRIPTION is not valid here, auto-renewing subs live in ASC's subscription group surface. |
name |
string | no | , | Apple-facing reference name in ASC. Not shown to customers. |
familySharable |
boolean | no | , | Whether Family Sharing is enabled for this IAP. |
contentHosting |
enum | no | HOSTED, NON_HOSTED |
Hosted: Apple hosts the downloadable content. Most IAPs are NON_HOSTED. |
reviewNote |
string | no | maxLength 4000 | Instructions for the App Review team to exercise this IAP. Important for IAPs with non-obvious unlock paths. |
reviewScreenshot |
object | no | , | Path to a screenshot showing the IAP unlock screen. See note below. |
localizations |
map | no | , | Locale-keyed {name, description} pairs. See below. |
reviewScreenshot
reviewScreenshot:
path: ./review-screenshots/lifetime.png
| Field | Type | Required | Gotcha |
|---|---|---|---|
path |
string | yes | Relative to state file directory. The L3 preflight rule iap.reviewScreenshot.exists checks this is populated. Missing review screenshots are a common rejection cause. |
Current limitation. Like app screenshots, the reviewScreenshot binary upload is not driven by flightline apply. Use flightline iap review-screenshot upload <bundleId> --product <productId> --file <path> directly.
iapLocalization fields
| Field | Type | Required | maxLength | Gotcha |
|---|---|---|---|---|
name |
string | no | 30 | Customer-visible IAP name. |
description |
string | no | 45 | Customer-visible IAP description. Note the 45-char ceiling, much shorter than app metadata. |
spec.ageRating
Apple's age-rating questionnaire. Each field answers one content-category question. All fields are optional individually, answering only the categories relevant to your app is fine. However, Apple requires the questionnaire to be complete before you can submit; the L3 preflight rule version.ageRating.answered verifies this.
spec:
ageRating:
cartoonOrFantasyViolence: NONE
realisticViolence: NONE
profanityOrCrudeHumor: NONE
matureSuggestiveThemes: NONE
horrorOrFearThemes: NONE
medicalOrTreatmentInformation: NONE
alcoholTobaccoOrDrugUseOrReferences: NONE
contestsAndGambling: NONE
sexualContentOrNudity: NONE
sexualContentGraphicAndNudity: NONE
gambling: false
unrestrictedWebAccess: false
Frequency fields (enum)
These fields accept NONE, INFREQUENT_OR_MILD, or FREQUENT_OR_INTENSE:
| Field | Flightline name | Apple API field |
|---|---|---|
cartoonOrFantasyViolence |
cartoon or fantasy violence | violenceCartoonOrFantasy |
realisticViolence |
realistic violence | violenceRealistic |
profanityOrCrudeHumor |
profanity or crude humor | profanityOrCrudeHumor |
matureSuggestiveThemes |
mature or suggestive themes | matureOrSuggestiveThemes |
horrorOrFearThemes |
horror or fear themes | horrorOrFearThemes |
medicalOrTreatmentInformation |
medical or treatment information | medicalOrTreatmentInformation |
alcoholTobaccoOrDrugUseOrReferences |
alcohol, tobacco, or drug use | alcoholTobaccoOrDrugUseOrReferences |
contestsAndGambling |
contests and gambling | contests (Apple wire name) |
sexualContentOrNudity |
sexual content or nudity | sexualContentOrNudity |
sexualContentGraphicAndNudity |
graphic sexual content and nudity | sexualContentGraphicAndNudity |
Boolean fields
| Field | Type | Gotcha |
|---|---|---|
prolongedGraphicSadisticRealisticViolence |
boolean | Apple's API uses a frequency enum for this field; Flightline maps any non-NONE value fetched from Apple to true. Set true for apps with prolonged, graphic, sadistic violence. |
gambling |
boolean | Gambling features (not just references, actual gambling mechanics). |
unrestrictedWebAccess |
boolean | App provides unrestricted internet access (e.g., a web browser). |
kidsAgeBand |
enum or null | FIVE_AND_UNDER, SIX_TO_EIGHT, NINE_TO_ELEVEN, or null. Set only for Kids category apps. |
seventeenPlus |
boolean | Read-only. Apple derives the 17+ rating from your answers, you cannot set this field directly. flightline apply returns a typed error if you include this in a change set. You may include it in the file for documentation, but it has no write effect. |
spec.exportCompliance
Per-build encryption declaration. Every build submitted to Apple must have export compliance answered. Most apps that only use system TLS set usesNonExemptEncryption: false and are done.
spec:
exportCompliance:
usesNonExemptEncryption: false
| Field | Type | Required | Gotcha |
|---|---|---|---|
usesNonExemptEncryption |
boolean | no | false for apps that use only standard system TLS/HTTPS. true triggers the ECCN classification block below. |
declaration |
object | no | Required only when usesNonExemptEncryption: true and the app needs full ECCN classification (rare). |
declaration (ECCN classification)
Only populate this if your app implements proprietary cryptographic algorithms beyond TLS.
| Field | Type | Gotcha |
|---|---|---|
containsProprietaryCryptography |
boolean | Your own crypto implementation. |
containsThirdPartyCryptography |
boolean | Third-party crypto beyond the OS. |
availableOnFrenchStore |
boolean | French regulatory requirement. |
usesEncryption |
boolean | Any encryption usage. |
exempt |
boolean | Meets an EAR exemption. |
eccn |
string | ECCN classification string (e.g. 5D002). |
documentName |
string | Name of the encryption documentation. |
documentUrl |
URI | URL to the documentation. |
spec.reviewerDemo
Login credentials for App Review. Apple requires demo credentials for any app that has a sign-in wall. Flightline never logs or echoes passwords.
spec:
reviewerDemo:
username: "demo@tideterm.app"
passwordRef: env:PASSDMV_DEMO_PASSWORD
notes: |
Tap any practice test to start. The IAP unlock screen appears
after the second free question.
contactName: "Joel Nuts"
contactEmail: "devteam@corelift.io"
contactPhone: "+1-555-0100"
| Field | Type | Required | Constraint | Gotcha |
|---|---|---|---|---|
username |
string | no | , | Demo account username. |
passwordRef |
string | no | ^env:[A-Z_][A-Z0-9_]*$ |
Env var reference, e.g. env:DEMO_PASSWORD. Flightline resolves the variable at apply time. Never put the password directly in the YAML file, it will end up in git. |
passwordFile |
string | no | , | Path to a file containing the password. Trailing newline is trimmed. Alternative to passwordRef. |
notes |
string | no | maxLength 4000 | Instructions for the reviewer. Include where to find any non-obvious flows (IAP, restricted features, login-wall bypass). |
contactName |
string | no | , | Your name or the developer's name. |
contactEmail |
string | no | email format | Contact for Review team questions. |
contactPhone |
string | no | , | International format recommended. |
Password field constraint. Exactly one of passwordRef or passwordFile may be present, or neither. Both together is a schema validation error (oneOf). If neither is provided, Flightline applies the other reviewer-demo fields without a password update.
spec.categories
App Store category assignment. Category IDs correspond to Apple's category taxonomy, use flightline categories list to enumerate valid IDs.
spec:
categories:
primary: EDUCATION
secondary: REFERENCE
primarySubcategories:
- EDUCATION_TUTORIALS
| Field | Type | Required | Constraint | Gotcha |
|---|---|---|---|---|
primary |
string | no | , | Primary category ID (e.g. BUSINESS, GAMES, EDUCATION). See /v1/appCategories in the ASC API. |
secondary |
string | no | , | Secondary category ID. |
primarySubcategories |
array of string | no | maxItems 2 | Subcategory IDs under primary. Only valid for categories that have subcategories (notably GAMES). |
secondarySubcategories |
array of string | no | maxItems 2 | Subcategory IDs under secondary. |
spec.pricing
Single base-territory price point. Per-territory overrides are a future L2 extension.
spec:
pricing:
baseTerritory: USA
appPricePointId: "FREE"
| Field | Type | Required | Gotcha |
|---|---|---|---|
baseTerritory |
string | no | ISO 3166-1 alpha-3 territory code (e.g. USA, GBR, JPN, AUS). Not alpha-2. |
appPricePointId |
string | no | Apple's appPricePoint resource ID. Use flightline price-points list to enumerate. "FREE" is the free tier. Quote the value, bare FREE is not a YAML string. |
spec.testflight
TestFlight beta distribution configuration. Only groups listed here are managed by Flightline. Groups absent from this section are left alone.
spec:
testflight:
groups:
friends-and-family:
isInternal: false
publicLink: false
testers:
- email: tester1@example.com
firstName: Test
lastName: One
internal-team:
isInternal: true
Group key format
Group keys match ^[A-Za-z0-9 _-]+$. They are the human-readable names you assign, Flightline resolves the ASC resource ID by matching the name against the live group list.
testflightGroup fields
| Field | Type | Required | Constraint | Gotcha |
|---|---|---|---|---|
isInternal |
boolean | no | , | Internal groups are your App Store Connect team members. External groups are outside testers. |
publicLink |
boolean | no | , | Enables Apple's public invite link for this group. Valid only for external groups. |
publicLinkLimit |
integer | no | 1 to 10000 | Maximum testers via the public link. Requires publicLink: true. |
testers |
array | no | , | Explicit tester list. Each entry requires at least email. |
testflightTester fields
| Field | Type | Required |
|---|---|---|
email |
string (email format) | yes |
firstName |
string | no |
lastName |
string | no |
spec.customProductPages
Custom Product Pages (CPPs), alternate store listings with different screenshots and promotional text, used for ad-driven traffic. Keys are page identifiers (slugs).
spec:
customProductPages:
summer-discovery-2026:
visible: true
localizations:
en-US:
promotionalText: "Pass your DMV test before the road trip."
screenshots:
APP_IPHONE_69:
- path: ./screenshots/cpp-summer-iphone69-1.png
customProductPage fields
| Field | Type | Required | Gotcha |
|---|---|---|---|
visible |
boolean | no | Controls whether the CPP is visible/active. |
localizations |
map | no | Locale-keyed content. |
CPP localization fields
| Field | Type | Required | maxLength | Gotcha |
|---|---|---|---|---|
promotionalText |
string | no | 170 | CPP-specific promotional text, overriding the main listing. |
screenshots |
map | no | , | Device-class screenshot sets, same format as spec.screenshots. |
Device classes for CPPs. CPPs support a subset of device classes: APP_IPHONE_67, APP_IPHONE_69, APP_IPHONE_65, APP_IPHONE_61, APP_IPHONE_55, APP_IPAD_PRO_3GEN_129, APP_IPAD_PRO_3GEN_11. TV, Watch, and Vision Pro device classes are not supported on CPPs.
Current limitation. CPP screenshot binary uploads are not driven by flightline apply, the same as main screenshots. Use the dedicated Custom Product Page screenshot upload command directly.
What Flightline does NOT manage
Privacy nutrition labels
spec.privacyLabels does not exist in the v1alpha1 schema and is not planned for v1. Apple's App Store Connect API v4.3 does not expose the appPrivacyDetails resource, there are no read or write endpoints for privacy nutrition labels in the public API.
Flightline ships a flightline privacy-labels get <bundleId> stub that returns a typed diagnostic explaining the gap. The JSON contract has supported: false and a pointer to the portal.
Manage privacy labels in the App Store Connect web UI.
Asset upload paths (screenshots, IAP review screenshots, CPP screenshots)
The flightline apply orchestrator does not yet drive multipart binary uploads. The sections for spec.screenshots, spec.iap.products[*].reviewScreenshot, and spec.customProductPages.<name>.localizations.<locale>.screenshots are fully supported in flightline fetch and flightline plan (the diff engine compares checksums), but flightline apply returns a typed error for these change paths and directs you to the L1 upload verbs:
flightline screenshots upload <bundleId> --version <v> --locale <l> --device-set <d> <path>
flightline iap review-screenshot upload <bundleId> --product <productId> --file <path>
Asset uploads remain explicit L1 operations and are not dispatched by flightline apply.
Common gotchas
YAML coercion
Flightline uses yaml.v3 with KnownFields(true) and strict type matching. These are the most common coercion traps:
| Problem | Bad | Good |
|---|---|---|
| Boolean string | yes, no, on, off, true, false unquoted in a string field |
Quote strings: "false" |
| Bare boolean in an enum field | releaseType: true |
releaseType: AFTER_APPROVAL |
| Integer version | version: 1.0 (YAML float) |
version: "1.0" (quoted string) |
| Integer build number | number: 42 (YAML int) |
number: "42" (quoted string) |
| Leading zeros | number: "042" fails the pattern ^[0-9]+(.[0-9]+)*$ in version |
Use "42" not "042" |
| Scientific notation | 1e3 parsed as float in some YAML parsers |
Always quote version and build number strings |
The strict loader surfaces these as file:line:col diagnostics before schema validation runs, so you see the specific location.
Per-locale completeness
Every locale you declare in spec.metadata.locales is managed. Fill the baseline name, description, and supportUrl fields for each locale or omit that locale entirely. Flightline's localizations.completeness rule checks those fields and reports locale coverage gaps across the managed localization surfaces.
Cross-resource field routing
name and subtitle live on appInfoLocalization in the ASC API; description, keywords, whatsNew, promotionalText, marketingUrl, supportUrl, and privacyPolicyUrl live on appStoreVersionLocalization. Flightline handles the dispatch, you do not need to know which resource owns which field. But if you see a diff that looks like a no-op update, check whether you have the same locale in two separate ASC resource states.
seventeenPlus is read-only
The seventeenPlus boolean in spec.ageRating reflects Apple's computed rating from your questionnaire answers. You cannot set it directly, Flightline returns a typed error if this field appears in a change set. You may include it in the YAML as documentation of the current state (as written by flightline fetch), but changes to it are ignored with an error, not silently applied.
contestsAndGambling maps to Apple's "contests" field
The schema uses contestsAndGambling for the frequency question about contests and gambling features. On Apple's wire API, this field is called contests. Flightline translates in both directions; you always use contestsAndGambling in your YAML.
Omitted spec sections are not managed
If you omit spec.screenshots entirely, Flightline will not touch your screenshots, not delete them, not diff them, nothing. This is intentional: partial state files let you manage only the surfaces you care about. If you want Flightline to own a surface, fetch the full state first (flightline fetch), then edit.
Version must be in editable state
flightline plan and flightline apply require the version identified by metadata.version to be in an editable state in ASC (e.g., PREPARE_FOR_SUBMISSION, DEVELOPER_REJECTED). If the version is READY_FOR_SALE or under review, writes will fail with a 422 from Apple's API.
See also
- State-as-code guide, 5-minute fetch → edit → plan → apply walkthrough
- Schema source, the JSON Schema 2020-12 contract
- Preflight in CI, run live release checks as a deployment gate
flightline --help,flightline fetch --help,flightline plan --help,flightline apply --help