# Architecture
# Architecture [#architecture]
Airlift owns the migration lifecycle: estates, objects, waves, readiness, certificates,
and cutover decisions. It composes execution, conversion, validation, persistence, and
observability through published contracts rather than rebuilding those capabilities.
## Request and evidence flow [#request-and-evidence-flow]
Specialist tools perform the work. Airlift admits their outputs by immutable reference,
checks provenance and policy, and makes the resulting decision traceable. Converter
success alone never proves production readiness.
## Repository map [#repository-map]
| Path | Developer responsibility |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `packages/airlift` | domain module, governed actions, policies, projections, source profiles, certificate contracts |
| `packages/cli` | authenticated engagement onboarding plus local source planning, diagnostics, catalogs, and certificate verification |
| `packages/adapter-lakebridge` | Databricks job submission and immutable Lakebridge result references |
| `packages/store` | durable event and projection persistence |
| `apps/airlift-worker` | assessment and conversion activities, schedules, cutover workflow, effector seam |
| `apps/console` | Databricks Apps identity boundary and operator workbench |
Source integrations extend narrow adapters. They do not add a parallel database or
mutation API. Start with `createSourceMigrationPlan()` to discover the commands, actions,
outputs, and evidence expected by a source profile.
## Governed domain module [#governed-domain-module]
Every externally meaningful state change is an `airlift.*` Platform action. Handlers
return pending domain events; the host appends them only after schema, authorization,
policy, and state-machine checks pass. This produces one mutation and audit path for the
console, workers, agents, and application integrations.
The event ledger folds into organization-scoped views for inventory, the migration
funnel, readiness, waves, conversion attempts, certificates, and audit. Illegal
transitions—such as certifying before conversion or recording a cutover before it is
authorized—are rejected structurally.
Use the CLI to inspect the installed action surface:
```bash
fa actions
fa actions --json > .airlift/action-contract.json
```
## Worker and durable cutover [#worker-and-durable-cutover]
The application owns its deterministic Temporal domain workflow. Harness supplies the
worker and connection plumbing. Workflow code performs no I/O; activities call adapters
and invoke governed actions with stable idempotency keys.
Cutover follows a strict sequence:
1. re-read frozen scope, fresh readiness, certificates, and approvals;
2. create a durable external checkpoint;
3. apply the non-idempotent endpoint change once;
4. verify external state independently;
5. compensate once when verification proves a failed effect was applied;
6. record success/rollback, or leave an uncertain outcome open for reconciliation.
The authenticated `airlift.wave_approve` action is the only approval authority. Temporal
signals can wake a readiness check but cannot carry actor identity, approval, denial, or
waiver authority.
## Extension seams [#extension-seams]
| Seam | Development implementation | Production implementation |
| ---------- | ---------------------------------------- | ------------------------------------------------------------------------------- |
| store | in-memory event store | durable Postgres-compatible store through `@fabricorg/airlift/store` |
| converter | `StubLakebridgeAdapter` | version-pinned Lakebridge workspace jobs |
| repair | disabled unless injected | bounded Harness agent producing one reviewable candidate |
| transfer | typed contract driver | source-specific snapshot/incremental driver with checkpoints and reconciliation |
| validation | fixtures or test runner | admitted provider with immutable evidence and snapshot identities |
| cutover | `StubCutoverEffector` in local mock mode | certified checkpoint/apply-once/verify/compensate implementation |
## Ownership boundaries [#ownership-boundaries]
| Airlift does not implement | Compose instead |
| --------------------------------------- | ------------------------------------------- |
| SQL transpilation | Databricks Lakebridge |
| Temporal connection and worker plumbing | Fabric Harness Temporal package |
| row comparison and test execution | Lakebridge Reconcile or Experiments testkit |
| mutation pipeline and audit storage | Fabric Platform and Platform Host |
| Databricks authentication and clients | Fabric Harness Databricks package |
| general-purpose agent runtime | Fabric Harness |
These boundaries keep source adapters replaceable and prevent a migration project from
creating competing definitions of deployment, monitoring, or task state.
# Deploy Airlift
# Deploy Airlift [#deploy-airlift]
An Airlift installation has four runtime responsibilities:
| Component | Responsibility | Recommended placement |
| -------------- | -------------------------------------------------------------- | ----------------------------------- |
| console | authenticated migration workbench and governed command ingress | Databricks App |
| durable store | Platform invocation, event, idempotency, and projection state | Lakebase or compatible PostgreSQL |
| migration jobs | Lakebridge assessment and conversion adapters | Databricks Jobs |
| worker | schedules, activities, and the durable cutover workflow | separately operated Temporal worker |
The console is not the worker. Keep interactive request handling separate from durable
orchestration so a console deployment cannot interrupt an active migration workflow.
## Validate the Asset Bundle [#validate-the-asset-bundle]
The repository contains a declarative Databricks Asset Bundle:
```bash
pnpm validate:databricks
databricks bundle validate -t
databricks bundle deploy -t
```
Bundle variables supply environment-specific resource names and references. Do not
commit workspace URLs, access tokens, client secrets, warehouse IDs, database passwords,
or signing keys.
## Configure durable storage [#configure-durable-storage]
Production installations use `AIRLIFT_STORE=postgres`. Bind either a Databricks
Lakebase resource or a compatible PostgreSQL connection, then run schema creation as an
explicit controlled-startup step:
```ts
const store = await createAirliftStoreFromEnv(process.env);
await store.ensureSchema();
```
Schema creation must not happen as an import side effect. Run it once during controlled
startup for both the console and worker composition roots.
## Configure identity and authorization [#configure-identity-and-authorization]
The Databricks App authenticates the workspace user before resolving display labels from
forwarded headers. The application derives actor and organization from authenticated
server context; neither value may come from form data, action parameters, workflow
signals, or CLI flags.
Production startup requires:
* a tenant-scoped authorization directory;
* admitted worker and validation-provider principals;
* Databricks App identity verification;
* an immutable evidence registry;
* an Ed25519 signing key stored through a deployment secret reference.
Use [security configuration](/docs/reference/security) and
[`fa doctor`](/docs/cli/doctor) to validate the configuration shape.
## Configure Lakebridge jobs [#configure-lakebridge-jobs]
Provision assessment and conversion jobs in the target workspace. Airlift records job
run IDs, tool versions, output references, and digests; reports and converted artifacts
remain in workspace-controlled artifact storage.
Required adapter settings include the job IDs, an artifact-volume root, the accepted
Lakebridge version, bounded polling, and stable idempotency tokens. Version drift or
malformed output fails the action instead of producing evidence.
## Configure the Temporal worker [#configure-the-temporal-worker]
Create the worker with Temporal mode and an explicitly injected cutover effector:
```ts
await createAirliftWorker({
mode: 'temporal',
runtime,
effector: cutoverEffector,
});
```
The effector identifies its certified profile, implementation version, and certification
digest, then implements `createCheckpoint`, `applyCutover`, `verifyCutover`, and
`compensateCutover`. Apply and compensation are each attempted once. Unknown outcomes
remain unresolved for reconciliation; they are never converted into success by a retry.
## Pre-production verification [#pre-production-verification]
Before admitting migration data, prove:
1. authenticated identity and cross-tenant denial;
2. durable write, projection read-back, and idempotent replay;
3. Lakebridge job connectivity, version enforcement, and artifact digest verification;
4. evidence-signing and offline certificate verification;
5. Temporal bundle determinism, query, cancellation, restart, and replay;
6. frozen-scope staleness, timed rehearsal, certified-effector binding, checkpoint,
apply-once, verification, uncertainty, compensation, and rollback behavior;
7. parallel-run, cutover-verification, hypercare, incident, and source-disposition
evidence paths.
See [production readiness](/docs/status) for the reusable acceptance contract.
# Fabric Airlift developer documentation
# Move your estate to Databricks, with proof [#move-your-estate-to-databricks-with-proof]
Fabric Airlift runs your migration as one governed job instead of a spreadsheet of
hand-offs. A migration team connects a source estate, inventories it, scopes the work,
builds the Databricks target, moves the data, proves parity, and goes live — and Airlift
records every decision, artifact, and piece of evidence on one policy-enforced ledger so
the speed never costs you the audit.
```text
Connect → Inventory → Scope → Build → Move → Prove → Go-live
↘ Fixes (residue / discrepancies) ↗
```
Speed comes from doing each stage once against a shared, typed inventory. Safety comes
from the proof behind it: nothing reaches Go-live without independent validation evidence
and a migration certificate that is **system-minted and Ed25519-signed**, derived from
recorded evidence — never authored by the caller. What the certificate does and does not
claim is spelled out in the [trust appendix](/docs/parity-certificates).
## Who uses which surface [#who-uses-which-surface]
| You are | Your home | What you do there |
| ---------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| a migrator | the Airlift Databricks App | run each stage, work the Fixes queue, watch progress and blockers |
| a migration lead or approver | the Airlift Databricks App | freeze scope, approve waves, accept for the business, decide go-live |
| an auditor or sponsor | the App trust view | read certificates, evidence, and the audit export — read-mostly |
| automation or CI | the `fa` CLI and versioned remote API | record diagnostics and evidence under the admitted automation role — never approve, waive, certify, or cut over |
Every surface invokes the same governed actions; the App, CLI, and API never take
different mutation paths.
## Start in the App [#start-in-the-app]
Follow the [App walkthrough](/docs/getting-started/ui-walkthroughs) — annotated,
public synthetic screenshots of one migration from engagement setup through cutover,
each paired with the exact next developer action.
## What Airlift implements under each stage [#what-airlift-implements-under-each-stage]
| Stage | What your project calls or implements | What Airlift records/enforces |
| ---------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Connect / Inventory | source metadata/export adapter or Lakebridge Profiler/Analyzer | run identity, inventory/dependency digests, tool version |
| Build | Lakebridge, entity mapper, pipeline implementation, or stream bootstrap | method, immutable artifacts, residue disposition |
| Move | source-specific snapshot/incremental driver | watermarks, restart checkpoints, lag, counts, reconciliation |
| Prove | Lakebridge Reconcile, Experiments testkit, or admitted external runner | provider run, immutable evidence, readiness observation |
| Go-live | client-certified checkpoint/apply-once/verify/compensate effector | approvals, separation of duties, freeze digest, rehearsal, operational evidence, durable outcome, rollback, hypercare |
| the certificate itself | no caller implementation | profile-derived, system-minted and Ed25519-signed envelope — see [parity certificates](/docs/parity-certificates) |
## Supported source profiles [#supported-source-profiles]
Airlift ships profiles for warehouses and databases, SAP and SaaS applications, ETL
platforms, mainframe data, federated query engines, and event streams. Every profile
publishes evidence-derived support alongside separate non-evidentiary implementation routing. An
empty tenant registry is always `cataloged`, regardless of shipped implementation paths.
Run
`fa source inspect ` to see the exact boundary, then open
[source systems](/docs/sources) for developer routes.
## The governed programming model [#the-governed-programming-model]
Every meaningful state change is an `airlift.*` Platform action. Console handlers,
workers, and agents invoke the same authenticated runtime. Event handlers return pending
domain events; projections build the inventory, funnel, readiness matrix, wave board,
certificate ledger, and audit trail. Agents may propose or record conversion work but
cannot approve, waive, certify, or cut over.
Read [architecture](/docs/architecture) for package boundaries and
[action catalog](/docs/reference/action-catalog) for the exact mutation contract.
## Automation and CLI [#automation-and-cli]
Install the CLI when you need scripted source plans, diagnostics, or evidence admission:
```bash
npm install --global @fabricorg/airlift-cli
fa sources
fa source inspect dynamics_365
fa source plan dynamics_365 \
--variant dynamics_365_finance_operations \
--json > dynamics-finance-plan.json
```
The generated plan covers inventory, code conversion, data movement, orchestration,
security, validation, performance, consumers, cutover, and post-parity modernization.
It names the applicable specialist adapter boundary, the Airlift actions your integration
must invoke, project outputs, and exit criteria.
Use the same registry in application code:
```ts
import {
AIRLIFT_ACTION_IDS,
createSourceMigrationPlan,
resolveSourceSystemProfile,
} from '@fabricorg/airlift';
const source = resolveSourceSystemProfile('d365');
const plan = createSourceMigrationPlan(source.id, {
variant: 'dynamics_365_finance_operations',
});
console.log(source.workloadSurfaces);
console.log(plan.steps.map((step) => step.airliftActions));
console.log(AIRLIFT_ACTION_IDS.assessmentRecord);
```
Continue with the [source developer workflow](/docs/sources/developer-workflow) for the
complete assessment-to-cutover integration.
## Documentation map [#documentation-map]
* [App walkthrough](/docs/getting-started/ui-walkthroughs) — the primary path through one migration.
* [Quickstart](/docs/getting-started/quickstart) — build, test, run, and inspect a plan.
* [CLI command reference](/docs/cli/command-reference) — every implemented command.
* [Cutover commands](/docs/cli/cutover) — frozen scope through hypercare and source disposition.
* [Source systems](/docs/sources) — executable source-specific routes and known gaps.
* [Migration lifecycle](/docs/migration) — how adapters and actions compose by phase.
* [Parity certificates](/docs/parity-certificates) — the certificate honesty model.
* [Operations](/docs/operations) — identity, evidence, recovery, and scale.
* [Integrations](/docs/integrations) — Platform, Harness, Experiments, Runway, Radar, Tower.
* [Reference](/docs/reference) — actions, events, profiles, configuration, and errors.
Use [production readiness](/docs/status) to verify the installation, source profile,
evidence providers, and cutover effector before operating on production data.
# How Airlift works
# How Airlift works [#how-airlift-works]
Airlift does not replace Lakebridge, a data-transfer engine, or a validation runner. It
connects them through a governed migration model so developers can answer four questions
for every object:
1. What is in scope and what depends on it?
2. Which artifact was produced, by which tool generation?
3. Which independent evidence shows that it is ready?
4. Which approved, reversible wave may move it into production?
## The lifecycle [#the-lifecycle]
| Stage | Tool or integration | What Airlift records and enforces |
| --------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| assess | Lakebridge Profiler and Analyzer | source identity, inventory, dependencies, exclusions, complexity, immutable reports |
| plan | target-design and delivery decisions | object ownership, dependency-aware waves, scope freeze, separation of duties |
| convert | Lakebridge plus optional bounded repair | attempts, tool versions, artifact digests, warnings, and residue disposition |
| transfer | project-specific snapshot and incremental driver | watermarks, manifests, lag, restart checkpoints, counts, and reconciliation |
| validate | Lakebridge Reconcile, Experiments, or another admitted provider | provider run, evidence digest, snapshots, verdicts, and readiness observations |
| certify | Airlift policy evaluation | a system-minted signed envelope derived from the active evidence profile |
| cut over | Temporal workflow plus a target-specific effector | approvals, checkpoint, apply-once result, independent verification, and rollback |
| modernize | Databricks-native release work | a separate backlog and validation profile that preserves baseline parity history |
## Assess the complete estate [#assess-the-complete-estate]
The assessment adapter executes Lakebridge against exported source assets and records
the run and immutable outputs. Inventory SQL together with orchestration, external data,
security mappings, and downstream consumers; otherwise a “converted” warehouse can
still fail when its pipelines or reports move.
Use a source profile to generate the applicable commands and outputs:
```bash
fa source inspect synapse
fa source plan synapse
```
## Plan dependency-aware waves [#plan-dependency-aware-waves]
`airlift.wave_plan` and `airlift.wave_assign` group objects into rehearsable units. The
planner, owner, object assignments, validation expectations, and rollback responsibility
remain explicit. Once approved, a wave cannot silently gain new objects.
## Convert without hiding residue [#convert-without-hiding-residue]
Run deterministic conversion first. If policy admits bounded repair, an agent may return
one typed candidate; it cannot approve, certify, waive, or cut over anything. Route every
unsupported construct into one visible lane:
* deterministic conversion;
* bounded repair candidate;
* human engineering;
* governed exclusion.
Both automated and human conversion use `airlift.conversion_start` and
`airlift.conversion_record`, which keeps provenance and funnel state consistent.
## Validate independently [#validate-independently]
Conversion produces a candidate, not proof. Validation providers compare source and
target behavior and write detailed results to immutable storage. Airlift admits the run
reference, digest, snapshots, tool version, and verdict, then links that observation to
one exact object-profile requirement.
A table, stored procedure, pipeline, and report can require different evidence. A passed
table comparison does not certify a failed orchestration path or downstream consumer.
## Mint a certificate from current evidence [#mint-a-certificate-from-current-evidence]
`airlift.migration_certificate_mint` accepts an object identity and expected digests,
not a caller-authored certificate. The handler derives the signed envelope from the
current governed projection and fails on missing, stale, mismatched, or waived evidence
that policy does not permit.
The useful progress metric is therefore the percentage of in-scope objects deployed and
certified against their assigned profile, with provenance and a rollback path—not the
percentage of files that produced output.
## Cut over with durable verification [#cut-over-with-durable-verification]
Before cutover, Airlift rechecks frozen wave scope, fresh certificates, readiness, and
authenticated approvals. The worker then:
1. creates a durable checkpoint;
2. applies the non-idempotent external effect once;
3. verifies the resulting state independently;
4. records success only after verification;
5. leaves uncertainty unresolved for reconciliation rather than guessing.
Rollback is a governed action with its own reason, evidence, and result.
## Preserve an attestable trail [#preserve-an-attestable-trail]
`airlift.evidence_export` assembles the assessment, conversion, validation,
certification, approval, cutover, and rollback history for an object or wave. The export
is content-digested. Signed certificate envelopes can be verified independently with the
Airlift CLI.
This is Airlift's role: migration tools perform specialist work; Airlift makes their
outputs traceable, policy-bound, independently provable, and safe to use in a production
decision.
# Parity and migration certificates
A migration's credibility is decided by one question: *how do you know the converted
object is safe for its intended release?* Airlift answers with two deliberately distinct
artifacts. Keeping them separate prevents “the rows matched” from becoming “the object is
ready to cut over.”
## Parity facet [#parity-facet]
`airlift.parity_certify` records what a particular comparison actually proved. Required
reproducibility fields include the validator generation, validation run, and source and
target snapshots:
```ts
interface ParityCertificate {
evidenceDepth: 'row_count' | 'aggregates' | 'sampled_rows' | 'full_checksum';
rowCountSource: number;
rowCountTarget: number;
aggregateChecksums?: Record;
sampledRowCount?: number;
checksumMatch?: boolean;
toolVersion: string;
validationRunRef: string;
sourceSnapshotAt: string;
targetSnapshotAt: string;
}
```
`airlift.parity_evidence.v1` enforces:
1. **Minimum depth.** An organization can require evidence stronger than row counts.
2. **No overclaiming.** `sampled_rows` requires a sample count; `full_checksum` requires
its verdict; aggregate depths require their checksums.
3. **Freshness.** Source and target snapshots must remain inside policy's evidence
window.
This facet is inspectable evidence. It does not by itself advance the object to the
migration-certified state.
## Readiness profiles [#readiness-profiles]
Every object receives a versioned profile over nine tracks:
* inventory;
* target design;
* code;
* data movement;
* deployment;
* functional parity;
* non-functional behavior;
* business acceptance;
* cutover readiness.
Each requirement is explicitly `required` or `not_applicable`. Evidence can be pending,
passed, failed, stale, or governed by a time-bounded waiver when policy permits it.
Identity, tenant isolation, separation of duties, functional parity, and uncertain
non-idempotent cutover outcomes are not waivable.
Validation runs enter readiness only when:
* the producer principal is admitted for the organization/provider;
* the provider run completed successfully;
* its immutable evidence reference and SHA-256 digest match the admitted registry;
* the artifact digest, source watermark, target snapshot, tool version, and completion
timestamp are present;
* the functional-parity observation resolves to the active parity run.
## System-minted signed envelope [#system-minted-signed-envelope]
`airlift.migration_certificate_mint` accepts no caller-authored certificate payload. A
system principal supplies only the object ID and the expected profile/readiness digests;
the handler derives the envelope from current governed projections and fails closed on
drift.
The envelope binds:
* organization, estate, object, and optional wave;
* profile ID/version/digest and readiness digest;
* artifact digest, source watermark, and target snapshot;
* parity certificate ID and evidence-manifest digest;
* policy revision and tool generations;
* issuer, issue time, signer key ID, signed digest, and Ed25519 signature.
Private keys come from secret-backed deployment configuration. Offline verification uses
the corresponding key-ID/public-key directory. A hash without a valid signature is never
described as signed evidence.
## Stale and revoked state [#stale-and-revoked-state]
Artifact, watermark, target snapshot, profile, readiness, dependency, policy, or release
drift invalidates the certificate's claim. Airlift preserves the old envelope and marks
it stale or revoked; it never deletes history. Sending an object to rework also removes
its active certification from the wave gate.
## Why this is the moat [#why-this-is-the-moat]
Lakebridge and other engines will keep improving conversion. Airlift's independent value
is the versioned answer to what was validated, by whom, against which identities, under
which profile and policy, and with which signature. Conversion produces a candidate;
independent evidence and governed acceptance produce certification.
# Production readiness
# Production readiness [#production-readiness]
Airlift separates installed capability from project readiness. A successful build or
configured connector does not prove that a source estate is safe to migrate.
## Installation checks [#installation-checks]
The installation must prove:
* authenticated actor and organization derivation;
* fail-closed authorization and cross-tenant denial;
* durable event, projection, idempotency, and recovery behavior;
* secret-backed evidence signing and public-key verification;
* Lakebridge job and artifact-store connectivity;
* Temporal workflow replay, cancellation, timeout, and restart behavior.
Run the structural diagnostic first:
```bash
fa doctor --profile production
```
`doctor` validates configuration shape. Connectivity and behavior require integration
and deployment tests.
## Source-profile checks [#source-profile-checks]
For the selected source, retain evidence for:
* accepted inventory, dependencies, exclusions, and source version;
* representative deterministic conversion and residue classification;
* snapshot and incremental transfer, watermark, restart, lag, and reconciliation;
* object-specific functional, data, security, and performance scenarios;
* business-owner acceptance of the selected validation profile.
Generate the source-specific checklist with:
```bash
fa source plan
```
## Automated cutover checks [#automated-cutover-checks]
Do not enable automated cutover until the target-specific effector proves:
1. a durable pre-effect checkpoint;
2. one non-idempotent apply attempt;
3. independent observation of the resulting external state;
4. explicit handling of `verified`, `not_applied`, `failed_applied`, and `uncertain` outcomes;
5. one compensation attempt after a definitively failed applied effect;
6. a frozen scope, timed runbook, passing rehearsal, and unexpired matching effector certification;
7. passing parallel-run evidence and no open incidents.
Projects that do not provide and certify this effector can use an approved manual
procedure while Airlift records scope, approvals, evidence, outcome, and rollback state.
Automated production cutover remains disabled for that project.
## Claim language [#claim-language]
Use precise terms in project documentation:
* **configured** means required settings are present;
* **connected** means the external boundary completed an authenticated smoke test;
* **validated** means the named scenarios produced immutable admitted evidence;
* **certified** means Airlift minted a signed certificate from the active profile;
* **cut over** means the external effect was independently verified and recorded.
Do not use one term as a substitute for another.
## Modernization and value checks [#modernization-and-value-checks]
Before promoting a modernization release, prove that its baseline migration certificate
is still active, the separate Runway deployment reconciled to the intended artifact, the
Experiments comparison passed every functional guardrail and declared outcome metric,
and the promoter is separate from the recommender and decision-maker.
Before publishing measured value to a client, retain at least three scoped observations
and achieve medium or high computed confidence. A new observation makes an earlier
summary stale. Engagement results are not universal product claims.
# Assessment to frozen plan
# Assessment to frozen plan [#assessment-to-frozen-plan]
This tutorial exercises Airlift's authenticated F1 workflow. It starts with a registered
estate and active engagement, admits a normalized assessment, records dependency edges,
accepts the scope, generates a Databricks target scenario, and freezes the selected plan.
The CLI is a remote client. Set the URL of your deployed Airlift Databricks App and a
Databricks OAuth token first:
```bash
export AIRLIFT_API_URL="https://your-airlift-app.example"
export DATABRICKS_TOKEN=""
npx --yes --package @fabricorg/airlift-cli@0.18.4 fa version
```
The API derives the actor and organization from authenticated Databricks identity. None
of the following requests contains an actor or tenant field.
## 1. Register the estate [#1-register-the-estate]
Create `estate.json`:
```json
{
"name": "Synthetic Synapse warehouse",
"sourceSystem": "synapse",
"owner": "migration-team@example.test",
"environment": "prod",
"priority": "priority",
"connectionRef": "databricks-connection://migration/synapse-metadata"
}
```
```bash
fa estate register --file estate.json --idempotency-key tutorial-estate-1 --json
fa estate list --json
```
Copy the returned `estateId`. Add it to the `estateIds` array in an engagement create
file, then create and activate the engagement. See [Engagements and
connections](/docs/getting-started/engagements) for that request.
## 2. Run and record the assessment [#2-run-and-record-the-assessment]
The Airlift worker composes the source adapter. `assessment_start` records the governed
request; `assessment_record` admits the immutable result produced by that adapter.
```json title="assessment-start.json"
{
"estateId": "est_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"toolVersion": "lakebridge@0.14.x",
"requestRef": "synthetic-assessment-001"
}
```
```bash
fa assessment start \
--file assessment-start.json \
--idempotency-key tutorial-assessment-start-1 \
--json
```
The configured worker normally records the result. Adapter developers can test the
same contract with a synthetic `assessment-record.json`:
```json
{
"assessmentId": "asm_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"estateId": "est_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"jobRunRef": "jobs/run/synthetic-001",
"reportRef": "volume://airlift/tutorial/assessment.json",
"reportDigest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"inventoryDigest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"dependencyDigest": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"toolVersion": "lakebridge@0.14.x",
"objectCounts": {
"tables": 1,
"views": 1,
"storedProcedures": 0,
"etlJobs": 1,
"other": 0
}
}
```
```bash
fa assessment record \
--file assessment-record.json \
--idempotency-key tutorial-assessment-record-1
```
On later runs, Airlift compares the newly admitted inventory and dependency digests with
the preceding assessment for the same estate. **Discover → Assessment studio** displays
per-category object-count deltas and makes digest drift an explicit review state. Missing
digests are treated as drift, not as proof that nothing changed.
Register normalized objects through `airlift.object_register`. Each object records its
type, source identity, complexity, and assessment reference. Supported inventory types
include tables, views, routines, ETL jobs, notebooks, reports, semantic models, ML
assets, security objects, and external dependencies.
```bash
fa inventory register --file object-table.json --idempotency-key tutorial-object-table
fa inventory register --file object-job.json --idempotency-key tutorial-object-job
fa inventory list --estate-id est_01ARZ3NDEKTSV4RRFFQ69G5FAV \
--assessment-id asm_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
```
## 3. Build and accept the dependency graph [#3-build-and-accept-the-dependency-graph]
A dependency graph is created with an expected edge count. Adapters then submit batches
of at most 500 secret-free edges. Airlift validates every object reference, rejects
self-references and duplicates, and refuses acceptance until the declared count is
complete.
The governed action sequence is `dependency_graph_start`, `dependency_batch_record`,
then `dependency_graph_accept`. The CLI commands below submit those exact actions through
the authenticated API.
```json title="graph-start.json"
{
"engagementId": "eng_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"assessmentId": "asm_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"expectedEdgeCount": 1,
"graphRef": {
"system": "lakebridge",
"type": "dependency_graph",
"id": "synthetic-graph-001"
},
"toolVersion": "lakebridge@0.14.x"
}
```
```json title="graph-batch.json"
{
"dependencyGraphId": "dpg_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"edges": [
{
"fromObjectId": "obj_01ARZ3NDEKTSV4RRFFQ69G5FAW",
"toObjectId": "obj_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"kind": "data",
"confidence": 1,
"critical": true
}
]
}
```
```bash
fa inventory graph start --file graph-start.json --idempotency-key tutorial-graph-start
fa inventory graph record --file graph-batch.json --idempotency-key tutorial-graph-batch-1
fa inventory graph show dpg_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
```
Graph acceptance is a human-authority operation:
```json title="graph-accept.json"
{
"dependencyGraphId": "dpg_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"expectedEdgeCount": 1
}
```
```bash
fa inventory graph accept \
--file graph-accept.json \
--idempotency-key tutorial-graph-accept
```
A cycle does not disappear. Airlift records it as an explicit planning blocker that
must be dispositioned before plan freeze.
## 4. Accept the normalized scope [#4-accept-the-normalized-scope]
Assessment acceptance recomputes the normalized inventory digest from projected objects
and binds it to the accepted dependency digest, exclusions, assumptions, engagement,
and authenticated reviewer.
The `assessment_accept` action is a natural-person decision; the CLI submits it as
`fa assessment accept`.
```json title="assessment-accept.json"
{
"assessmentId": "asm_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"engagementId": "eng_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"dependencyGraphId": "dpg_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"expectedObjectCount": 3,
"exclusions": [],
"assumptions": [
"A representative production validation window is available."
]
}
```
```bash
fa assessment accept \
--file assessment-accept.json \
--idempotency-key tutorial-assessment-accept
```
Service accounts and agents may record assessment and graph proposals, but they cannot
accept the graph or scope.
## 5. Generate and compare delivery scenarios [#5-generate-and-compare-delivery-scenarios]
`plan_generate` maps every in-scope object type to a Databricks target pattern, performs
a topological wave grouping, exposes cycles, and calculates effort and value from the
provided assumptions. The calculations remain visible in the returned plan; they are
not a hidden percentage claim.
The governed sequence is `plan_generate`, `plan_select`, then `plan_freeze`. Selection and
freeze require human authority; generation may be performed by admitted automation.
```json title="plan-generate.json"
{
"engagementId": "eng_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"assessmentId": "asm_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"name": "Parity-first delivery plan",
"scenario": "parity_first",
"targetWorkspaceRef": {
"system": "databricks",
"type": "workspace",
"id": "synthetic-target"
},
"maxObjectsPerWave": 100,
"economics": {
"manualHoursPerComplexityPoint": 8,
"assistedHoursPerComplexityPoint": 3,
"blendedHourlyCostUsd": 180,
"sourceMonthlyRunCostUsd": 25000,
"targetMonthlyRunCostUsd": 18000,
"dualRunMonths": 2
},
"assumptions": [
"Unity Catalog prerequisites are complete before Wave 1."
]
}
```
```bash
fa plan generate --file plan-generate.json --idempotency-key tutorial-plan-parity
fa plan compare --engagement-id eng_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
fa plan select pln_01ARZ3NDEKTSV4RRFFQ69G5FAV --idempotency-key tutorial-plan-select
```
Freeze the engagement scope before freezing its selected plan:
```bash
fa engagement freeze eng_01ARZ3NDEKTSV4RRFFQ69G5FAV \
--idempotency-key tutorial-engagement-freeze
fa plan freeze pln_01ARZ3NDEKTSV4RRFFQ69G5FAV \
--idempotency-key tutorial-plan-freeze
```
The frozen digest binds engagement scope, accepted inventory, dependencies, target
workspace reference, target mappings, wave candidates, economics, assumptions, and
risks.
## 6. Export the assessment pack [#6-export-the-assessment-pack]
```json title="assessment-export.json"
{
"estateId": "est_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"reason": "Record the accepted discovery and selected delivery scenario."
}
```
```bash
fa assessment export \
--file assessment-export.json \
--idempotency-key tutorial-assessment-export \
--json
```
The content-digested pack includes the estate, accepted assessment, normalized
inventory, dependency graph and edges, applicable plan scenarios, generation time, and
governance revision.
## Databricks App views [#databricks-app-views]
The same state is available in two authenticated pages:
* **Discover → Assessment studio** shows runs, inventory, graph edges, blockers,
acceptance, and immutable export.
* **Plan → Target blueprint and wave planner** shows target services, mappings, wave
prerequisites, economics, scenario selection, and freeze evidence.
Both pages invoke the same Platform actions used by the CLI. A UI action cannot bypass
schema validation, authorization, tenant isolation, policy, idempotency, or audit.
## Failure and recovery behavior [#failure-and-recovery-behavior]
| Condition | Result | Recovery |
| -------------------------------------------- | --------------------------------- | --------------------------------------------------------------------------- |
| unknown or cross-estate object in an edge | blocked | correct the normalized object mapping and submit a new batch |
| duplicate edge | blocked | remove the duplicate; do not change the idempotency key to force it through |
| incomplete edge count | blocked | record the remaining batches or start a corrected graph |
| inventory count or digest changed | conflict | reload the current assessment projection and review drift |
| dependency cycle | plan generated with blocker | disposition or redesign the cycle, then generate a new scenario |
| agent or service account attempts acceptance | forbidden | an admitted natural-person operator performs the decision |
| plan selected before scope freeze | selection allowed, freeze blocked | freeze the engagement, then retry the same logical freeze safely |
| adapter or API unavailable | exit `6` | retry with the same stable idempotency key after the dependency recovers |
Scope acceptance also requires the operator to choose the engagement explicitly. Airlift
never infers acceptance authority from whichever engagement happens to appear first.
This workflow produces planning evidence. It does not certify conversion, data parity,
deployment, or production cutover; those use their own evidence profiles and gates.
# Choose a developer path
# Choose your path [#choose-your-path]
Airlift moves an estate to Databricks as one governed job:
```text
Connect → Inventory → Scope → Build → Move → Prove → Go-live
↘ Fixes (residue / discrepancies) ↗
```
Start from where you sit in that job rather than reading the documentation front to
back.
## Run or follow a migration [#run-or-follow-a-migration]
Most readers should start in the App and only drop to developer surfaces when a stage
needs an adapter or integration:
| You are | Start here | What you will do |
| -------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| a migrator running the job | [App walkthroughs](/docs/getting-started/ui-walkthroughs) | follow one migration from engagement setup through cutover, stage by stage |
| setting up your first engagement | [Engagements](/docs/getting-started/engagements) | create the engagement, bind a connection, run guided setup |
| a migration lead or approver | [Guided migration journey](/docs/getting-started/guided-migration-journey) | see what each gate requires, who acts next, and what evidence clears it |
| an auditor or sponsor | [Parity certificates](/docs/parity-certificates) | verify what a signed certificate claims and export evidence |
## Implement an integration surface [#implement-an-integration-surface]
When a stage needs code from your project, start with the component you are
implementing:
| You are building | Start here | What you will implement |
| ------------------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| a source migration integration | [Source developer workflow](/docs/sources/developer-workflow) | source plan, Lakebridge route, transfer, validation, and governed action calls |
| an assessment adapter | [Run the assessment](/docs/migration/assessment) | workspace-job submission and immutable output admission |
| a conversion factory | [Conversion factory](/docs/migration/conversion) | deterministic conversion, residue classification, and artifact recording |
| a transfer driver | [Data transfer](/docs/migration/transfer) | checkpointed snapshot/incremental movement and reconciliation |
| a validation provider | [Validation and readiness](/docs/migration/validation) | admitted evidence records and profile-track observations |
| an Airlift deployment | [Deployment](/docs/deployment) | App, durable storage, jobs, Temporal, identity, secrets, and production checks |
| a cutover effector | [Cutover and rollback](/docs/migration/cutover) | checkpoint, apply-once, independent verification, and recovery |
| CI or operator automation | [Airlift CLI](/docs/cli) | source plans, diagnostics, contract snapshots, certificate verification |
## Recommended implementation order [#recommended-implementation-order]
Build one complete, evidence-producing path before adding optional integrations:
1. Generate and review the source profile and migration plan.
2. Wire a live Lakebridge assessment and import the normalized inventory.
3. Convert representative hard objects and retain immutable artifacts and residue.
4. Implement one restartable transfer profile and independent validation runner.
5. Resolve profile tracks, mint certificates, and rehearse a reversible wave.
Runway, Radar, and Tower add release, operations, and work-management evidence. They are
optional integrations rather than prerequisites for assessment, conversion, validation,
or certification. See [family composition](/docs/integrations).
## Trust & certificates [#trust--certificates]
Whichever path you take, two reference pages define what the resulting evidence is
worth:
* [Parity and migration certificates](/docs/parity-certificates) — what a signed
certificate claims, the evidence depth behind it, and how to verify it offline.
* [Security model](/docs/reference/security) — the identity, tenancy, agent, and
approval boundaries the whole migration job runs inside.
# Guided client onboarding
# Guided client onboarding [#guided-client-onboarding]
Use the guided setup flow for a new Azure Synapse-to-Databricks or SQL Server-to-Lakebase
engagement. It turns seven governed requirements into one ordered journey:
1. create the migration workspace;
2. register the source estate;
3. bind a secret-safe connection reference;
4. verify source access with retained evidence;
5. activate the engagement;
6. request an assessment; and
7. review and accept the discovered scope.
The Databricks App and `fa` call the same Fabric Platform actions. The App does not keep a
second checklist: a step becomes complete only when its domain event is present in the
organization-scoped projection.
## Use the guided Databricks App flow [#use-the-guided-databricks-app-flow]
Open **Engagements → New engagement**. Enter a program name, accountable owner, and the
delivery services being requested. **Create draft** creates only the governed container;
it does not connect to a source, run code, or claim readiness.
After creation, Airlift opens **Guided setup** at:
```text
//engagements//setup
```
The page shows all seven requirements but enables only the first unmet action. Each action
panel explains what to provide, why it is required, what evidence it creates, and where to
read the source-specific guide. Completed steps have a green check. Later steps remain
locked so operators cannot accidentally perform onboarding out of order.
### Register the source estate [#register-the-source-estate]
Choose one of the currently supported Azure onboarding routes:
| Client route | Source and variant | Typical target |
| -------------------------- | ----------------------------------------------- | -------------------------------- |
| Synapse dedicated pool | **Azure Synapse · Synapse dedicated SQL pool** | Databricks Lakehouse |
| Synapse serverless pool | **Azure Synapse · Synapse serverless SQL pool** | Databricks Lakehouse |
| Mixed Synapse workspace | **Azure Synapse · Mixed Synapse estate** | Databricks Lakehouse |
| SQL Server | **SQL Server · SQL Server** | Databricks Lakehouse or Lakebase |
| Azure SQL Database | **SQL Server · Azure SQL Database** | Lakebase or Databricks Lakehouse |
| Azure SQL Managed Instance | **SQL Server · Azure SQL Managed Instance** | Lakebase or Databricks Lakehouse |
Name the actual client environment, identify its owner, and select `development`,
`staging`, or `production`. Only estates attached to this engagement appear in its sidebar
and source workspaces.
### Register a secret-safe connection reference [#register-a-secret-safe-connection-reference]
Create the credential in the client-approved secret system first. Then enter only an
opaque reference such as:
```text
databricks-connection://migration/client-synapse-metadata
databricks-secret://migration/sql-server-reader
secret://client-vault/source-metadata
```
A connection reference is never a password. Airlift rejects passwords, tokens, JDBC URLs
containing credentials, query strings, and fragments. The binding declares the source
estate and allowed capabilities such as `inventory_read` and `metadata_read`; the secret
value never enters the Airlift ledger.
### Verify source access [#verify-source-access]
Run the admitted source preflight with the same credential reference. The probe records a
**connectivity diagnostic** covering every declared capability and proves, at minimum:
* authentication using the referenced credential;
* source identity and version;
* metadata visibility for the intended schemas/databases;
* denied access outside the admitted boundary; and
* the adapter/tool generation that produced the result.
The probe is recorded through `airlift.connection_diagnostic_record` by an admitted system
principal; the handler derives the diagnostic digest — a caller can never supply it. Then
choose **Verify binding** in the guided setup panel. The App attaches the recorded
diagnostic automatically; it never asks an operator to paste a digest.
**Verification evidence digest** is the 64-character SHA-256 digest of the recorded
connectivity diagnostic. Airlift records the digest and authenticated verifier; it does
not copy the source credentials or report body. Unattended jobs perform the same step
with `fa connection verify`; its digest recipe lives in
[Authenticated automation](/docs/cli/remote-automation).
The binding changes from `pending` to `verified` only when the named diagnostic is fresh,
matches the current binding revision, and every required probe passed. A login screenshot,
a caller-authored “passed” flag, or a caller-chosen digest is not verification evidence.
### Activate the engagement [#activate-the-engagement]
Review the source estate and verified connection, then choose **Activate engagement**.
Activation accepts the starting configuration and allows governed discovery work. It does
not freeze final scope, deploy artifacts, certify parity, or authorize cutover.
### Run the first assessment [#run-the-first-assessment]
Choose the estate, retain the pinned adapter generation, and supply a unique request
reference. **Start assessment** records an idempotent request for the Airlift worker. The
worker uses the configured source adapter and later records the immutable report,
normalized inventory, dependency data, source version, and exact tool generation.
```json title="assessment-start.json"
{
"estateId": "est_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"toolVersion": "lakebridge@",
"requestRef": "client-discovery-001"
}
```
```bash
fa assessment start \
--file assessment-start.json \
--idempotency-key client-discovery-001
fa assessment status --json
```
The setup flow displays **Assessment is running** until a configured worker records the
result. It never invents inventory to make the step green.
### Accept the discovered scope [#accept-the-discovered-scope]
When assessment recording finishes, choose **Open assessment review**. Review:
* normalized source objects and object types;
* source paths, complexity, and exclusions;
* dependency edges and critical paths;
* tool and report references;
* the expected object and edge counts; and
* assumptions that must remain true for planning.
Accept the dependency graph first, then accept the assessment scope. Airlift computes the
inventory, dependency, and scope digests. Planning consumes these accepted digests rather
than a live source query or mutable spreadsheet.
```bash
fa inventory list --estate-id --assessment-id --json
fa inventory graph show --json
fa assessment accept \
--file assessment-accept.json \
--idempotency-key client-scope-accept-v1
```
After acceptance, **Guided setup** is complete and its primary action changes to **Build
migration plan**.
## Perform the same setup with `fa` [#perform-the-same-setup-with-fa]
Install the private CLI package from the registry configured by your organization:
```bash
npm install --global @fabricorg/airlift-cli
fa --version
fa doctor --profile production
```
The CLI derives organization and actor identity from the authenticated Databricks context.
Do not place an organization ID or user identity in action payloads.
Create the estate, engagement, binding, and assessment with stable idempotency keys.
Verification sits between diagnosis and activation: interactive operators choose **Verify
binding** in the App, while unattended jobs use `fa connection verify` with the recorded
diagnostic digest recipe in [Authenticated automation](/docs/cli/remote-automation) — a
caller-chosen digest is rejected either way.
```bash
fa estate register --file estate.json --idempotency-key client-estate-v1
fa engagement create --file engagement.json --idempotency-key client-engagement-v1
fa connection register --file source-binding.json --idempotency-key client-binding-v1
fa connection diagnose --file probes.json --idempotency-key client-diagnose-v1
# Verify the binding in the App, or with the digest recipe in Authenticated automation.
fa engagement activate --idempotency-key client-activate-v1
fa assessment start --file assessment-start.json --idempotency-key client-assessment-v1
fa engagement status --json
```
Example estate payloads differ only where the source contract differs:
```json title="estate.json — Synapse"
{
"name": "Client production Synapse",
"sourceSystem": "synapse",
"sourceVariant": "synapse_dedicated_sql",
"owner": "client-data-platform",
"environment": "prod",
"priority": "priority"
}
```
```json title="estate.json — SQL Server"
{
"name": "Client operational SQL Server",
"sourceSystem": "sql_server",
"sourceVariant": "sql_server",
"owner": "client-application-team",
"environment": "prod",
"priority": "priority"
}
```
The App immediately renders CLI-created records because both surfaces read the same
governed projections.
## What the client can inspect [#what-the-client-can-inspect]
After onboarding, each engagement exposes five shareable views:
| View | What the client sees |
| -------------------- | ------------------------------------------------------------------------------------------- |
| **Guided setup** | Completed prerequisites, the current requirement, and exact next action |
| **Overview** | Current phase, route, blockers, source scope, and recommended action |
| **Migration status** | Eight evidence gates with terminal counts and blocker reasons |
| **Artifacts** | Immutable report/code/release references, SHA-256 digests, lineage, and producer generation |
| **Run ledger** | Assessment, conversion, movement, deployment, and validation executions |
## Production onboarding boundary [#production-onboarding-boundary]
Before connecting production data, confirm the Databricks App uses workspace identity,
the organization registry admits the client and operators, the durable store and artifact
store are configured, and the source connection follows the client's least-privilege
policy. Automated cutover remains disabled until a source-and-target-specific effector has
passed the production cutover and rollback capability suite.
Do not represent local fixtures or a successful setup walkthrough as client migration
proof. Workspace and client claims require immutable provider run references for the same
source, target, artifact digest, and validation profile.
# Core concepts
# Core concepts [#core-concepts]
## Organization and estate [#organization-and-estate]
An **organization** is the tenant boundary. Membership, events, projections, evidence,
and policy are organization-scoped. An **estate** represents one source system and
environment under migration, such as a production Synapse estate or a staging SQL Server
estate. Credentials never live in an estate record; it carries only an opaque connection
reference.
## Migration object [#migration-object]
A migration object is a table, view, stored procedure, function, ETL job, or report. Its
executive funnel is intentionally simple:
```text
discovered → planned → converting → converted → certified → cutover
↘ rework ↘ rollback/reconciliation
excluded is a governed terminal lane
```
The funnel is a projection, not a substitute for evidence. `converted` means an artifact
was produced. `certified` means the assigned validation profile resolved from admitted,
fresh evidence and Airlift minted a signed certificate.
## Readiness tracks [#readiness-tracks]
Nine tracks explain why an object can or cannot advance: inventory, target design, code,
data movement, deployment, functional parity, non-functional evidence, business
acceptance, and cutover readiness. A profile marks every track `required` or
`not_applicable`; a required track resolves to `pending`, `passed`, `failed`, `stale`, or
`waived`.
## Wave [#wave]
A wave freezes a set of objects into one rehearsable cutover unit. Airlift owns wave
scope, approvals, gate evaluation, cutover decision, outcome, and rollback record. A wave
does not absorb Runway deployment state, Radar observations, or Tower task completion;
it references their stable IDs and digests when those products are present.
## Evidence and certificates [#evidence-and-certificates]
The parity certificate states the depth of a functional or data comparison. The
migration certificate is broader: Airlift derives and signs an envelope from the full
readiness profile. A report digest is not a signature. See
[parity and migration certificates](/docs/parity-certificates).
# Configuration map
# Configuration map [#configuration-map]
Airlift defaults are safe for tests and demonstrations, not production. Use
`fa doctor --profile production` to inspect configuration shape without printing
secret values.
| Concern | Selection | Production expectation |
| ----------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| State store | `AIRLIFT_STORE` | `postgres` with a Lakebase App binding or explicit Postgres binding |
| Authorization directory | `AIRLIFT_AUTHORIZATION_JSON` | Tenant, principal type, role, trusted worker/agent/installer, and validation-principal entries |
| App identity | `AIRLIFT_TRUST_DATABRICKS_APP_HEADERS`, `DATABRICKS_APP_NAME` | Both gates present; OBO remains the authenticated workspace boundary |
| Development self-review | `AIRLIFT_DEPLOYMENT_ENV`, `AIRLIFT_ALLOW_DEVELOPMENT_SELF_REVIEW` | Optional only for a `dev` deployment whose App name ends in `-dev`; recorded as a non-certifying override |
| Evidence admission | `AIRLIFT_EVIDENCE_REGISTRY_JSON` | Immutable provider/run/reference/digest entries from admitted principals; entries bind the run's `artifactDigest`, `verdict`, and `completedAt` but establish no provider provenance, so a governed hazard profile needs a provider-backed `airliftEvidenceVerifier` |
| Certificate signing | `AIRLIFT_EVIDENCE_SIGNING_KEY_ID`, `AIRLIFT_EVIDENCE_SIGNING_PRIVATE_KEY_PEM` | Secret-backed Ed25519 private key and stable key ID |
| Offline verification | `AIRLIFT_EVIDENCE_VERIFY_KEYS_JSON` | Public-key map; private key is never required for verification |
| Converter | `AIRLIFT_CONVERTER` | `lakebridge` plus job IDs, volume root, Databricks App service principal, and a certified version pin |
| Domain workflow | `AIRLIFT_TEMPORAL_MODE`, Temporal connection variables | Temporal for durable cutover; signals wake and never approve |
| Cutover effect | composition-injected effector | Client-certified checkpoint, apply-once, verify, and compensation contract |
Do not place credentials in action parameters, workflow inputs, event payloads, evidence
exports, or CLI flags. See the complete [configuration reference](/docs/reference/configuration).
# Conversion to remediation tutorial
# Conversion to remediation tutorial [#conversion-to-remediation-tutorial]
This tutorial starts after [assessment acceptance and planning](/docs/getting-started/assessment-to-plan).
It uses synthetic identifiers and JSON files so you can exercise the remote API without
placing source code, credentials, or client details in the command payload.
## Prerequisites [#prerequisites]
You need an active engagement, an accepted assessment, and at least one object in
`planned` or `rework`. Configure `AIRLIFT_API_URL` and a short-lived `DATABRICKS_TOKEN`,
then confirm the resources visible to your authenticated organization:
```bash
fa engagement list
fa assessment list
fa inventory list --assessment-id
```
## Run the factory path [#run-the-factory-path]
1. Create a batch with `fa conversion batch create --file batch.json`.
2. Start it with `fa conversion batch start --file batch-start.json`.
3. For each object, call `fa conversion attempt start`, execute your adapter, then call
`fa conversion attempt record`.
4. Register successful output using `fa artifact register`.
5. Create a residue for each failed object.
6. Reconcile with `fa conversion batch complete`.
The batch completes only after Airlift can account for every object. If a converted
attempt's output digest has no matching target artifact, or a failed attempt has no
active residue, reconciliation is blocked with an actionable error.
## Complete the human path [#complete-the-human-path]
```bash
fa residue assign --file assignment.json --idempotency-key example-assignment
fa artifact register --file repaired-artifact.json --idempotency-key example-repair-artifact
fa residue resolve --file resolution.json --idempotency-key example-resolution
fa residue review --file review.json --idempotency-key example-review
```
Assignment works directly from `open`. A delivery lead may record an optional forecast
first with `fa residue estimate`; it is planning metadata, not a developer prerequisite.
Use a different admitted person for `review` than for `resolve`. Use an Experiments run
reference in the resolution file. Review closes the engineering case; it does not mint a
migration certificate. Continue with [validation](/docs/migration/validation).
## Inspect in the App [#inspect-in-the-app]
Open **Conversion** to see batch and attempt lineage. Open **Remediation** to see the
human/agent lanes, effort, skills, assignment, external work reference, artifact, and
review status. Refreshing or replaying the event stream reconstructs the same projections.
## Expected denial checks [#expected-denial-checks]
Verify these before connecting real scope:
* duplicate object IDs are rejected at batch creation;
* an object outside the accepted assessment is rejected;
* starting an attempt with a different converter generation is rejected;
* batch completion blocks on missing attempts or artifacts;
* an agent cannot assign or review remediation;
* a resolver cannot review the same residue;
* a foreign-organization artifact, batch, or residue ID is not readable or mutable.
These are production controls, not optional tutorial assertions.
# Demonstrate an Azure migration
# Demonstrate an Azure migration [#demonstrate-an-azure-migration]
This page is a presentation script layered on the
[guided migration journey](/docs/getting-started/guided-migration-journey/), which is the primary App-first
walkthrough of the Azure Synapse to Databricks Lakehouse path. Walk the screens there;
use this page for what to say, what to show, and which honesty boundaries to hold.
Use this walkthrough when evaluating Airlift with an engineering team. The goal is not to
show a checklist with every box green. The goal is to show how Airlift turns source-system
work into inspectable artifacts, independent evidence, explicit human remediation, and one
safe next action. Lead with the artifacts and the next action; open the run ledger only if
the audience asks about audit.
Start in **Engagements**. Each card is a governed migration workspace. It identifies the
source and Databricks target, current evidence gate, blocker count, owner, completion derived
from terminal evidence, and the next action that can advance the migration.
## Synapse to Databricks Lakehouse [#synapse-to-databricks-lakehouse]
Present the Synapse journey as the complete factory flow, following the screen sequence in
the [guided migration journey](/docs/getting-started/guided-migration-journey/):
1. Airlift admits the source assessment, inventory, dependencies, and accepted scope.
2. Lakebridge or another admitted converter produces target candidates; Airlift records the
exact producer generation and artifact digests.
3. Unsupported transaction, temporary-table, or orchestration behavior becomes visible
residue — normal specialist work, presented as such rather than as an unsafe success.
4. A repaired target artifact is registered and independently validated.
5. Data movement records watermarks, restart checkpoints, counts, and reconciliation.
6. Airlift mints only the certificates supported by the evidence.
7. Runway deployment and production cutover remain independent gates.
Open **Artifacts** from the engagement proof snapshot. A useful technical review should show
the executed source reference, generated or repaired Databricks target, Experiments validation
evidence, SHA-256 digests, and producer versions. Open **Run ledger** only when an engineer
asks what actually ran or how audit works.
## SQL Server to Databricks Lakebase [#sql-server-to-databricks-lakebase]
Use the SQL Server journey to explain target fit and human remediation:
1. `fa lakebase plan` classifies schemas, tables, views, identities, constraints, procedures,
and external operational workloads.
2. PostgreSQL-compatible objects receive Lakebase target mappings and immutable DDL artifacts.
3. A representative workspace certification can exercise a connected SQL Server engine and a
managed Lakebase target with the same plan and dataset digests. `pnpm demo:admit` reports
`workspaceEvidenceCorroborated: false` and preserves the warning until an admitted provider
records a passing validation run that names the registered artifact ID and exact
certification digest; artifact registration never self-attests that proof.
4. SQL Agent, SSIS, reporting, or application behavior that does not belong in Lakebase stays in
a named redesign lane with skills, owner, output artifact, validation, and review requirements.
That last item is a feature, not a failed demo. It shows that Airlift does not hide specialist
work inside an automation percentage. Open the remediation case to show the diagnosis and the
sequence that clears it: assign an owner, register the repaired artifact, attach validation,
submit for review, and record an independent decision.
## Commands to show engineers [#commands-to-show-engineers]
The App and CLI read the same organization-scoped projection:
```bash
fa engagement list --json
fa engagement status --json
fa artifact list --engagement-id --json
fa validation list --engagement-id --json
fa deployment list --engagement-id --json
```
For SQL Server target planning:
```bash
fa lakebase inspect --file sql-server-manifest.json
fa lakebase plan --file sql-server-manifest.json --json \
> .airlift/sql-server-lakebase-plan.json
fa lakebase qualification-check \
--file .airlift/sql-server-lakebase-workspace-qualification.json
```
For Synapse source planning:
```bash
fa source inspect synapse
fa source plan synapse --variant synapse_dedicated_sql --json \
> .airlift/synapse-source-plan.json
fa engagement status --json
```
## What to say about readiness [#what-to-say-about-readiness]
Use the proof label shown by the App and keep its boundary intact:
| Visible state | What it proves | What it does not prove |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| Artifacts recorded | Immutable inputs or outputs and their lineage exist | Behavioral parity |
| Development assurance | Source and target artifacts are linked to a passing governed validation run | Independently observed release or client acceptance |
| Connected workspace evidence | Available only after a sanitized same-digest producer run is admitted; this candidate does not currently claim it | The client estate or production cutover |
| Client proven | Accepted client scope passed the required profile | Production cutover unless its separate gates also pass |
Do not promise a generic time reduction. Use Airlift's measured-value records after a client
baseline exists, and report automation, human effort, elapsed time, defects, and validation
outcomes with their observation windows and reviewers.
# Create a migration engagement
# Create a migration engagement [#create-a-migration-engagement]
An Airlift engagement is the top-level delivery record for one migration or modernization
program. It connects source estates, services, immutable external references, connection
bindings, waves, artifacts, evidence, and operator decisions under one organization.
Use an engagement when your application needs to answer:
* which estates and services are in scope;
* who owns the migration;
* which source, target, and service connections have been verified;
* which exact scope was frozen for delivery or cutover; and
* which governed actions changed that scope.
## Use the Databricks App [#use-the-databricks-app]
Start in **Migration inbox**. It ranks active engagements by blockers and failed evidence,
then gives each row one primary action: resolve the next blocker or continue the earliest
incomplete gate. Use **Status** when you need the complete governed projection rather than
the operating queue.
Open **Engagements** to search or filter the portfolio and see one card per migration workspace. Each card shows the
source-to-target route, owner, current phase, completed gates, active blockers, capability
preflight, and gate-completion percentage.
* Select the card title or non-action area to open the engagement overview.
* Choose **Continue migration** to bypass the overview and open the current actionable
workspace. When conversion residue blocks the engagement, this opens the matching
remediation case rather than another status screen.
* Choose **View migration status** to inspect all eight delivery phases and their evidence.
* Choose **New engagement** to open `/engagements/new`. After draft creation, Airlift opens
`/engagements//setup` and guides source registration, a secret-safe connection
reference, verification evidence, activation, assessment, and scope acceptance in order.
It never redirects to an unrelated existing engagement.
The sidebar starts in **Portfolio view** and deliberately hides source-specific and journey
links until you choose a current engagement. Open **Current engagement**, then select a
migration workspace. Airlift keeps that engagement visible in the desktop context bar and
adds only its attached sources and phase workspaces to navigation. Returning to **Portfolio
view** removes that source-specific navigation without changing migration state.
Choose **Search migrations** or press ⌘K/Ctrl K to jump directly to an
engagement overview, migration status, artifact explorer, run ledger, or source workspace.
Search is navigation only; it never changes scope or runs a governed action.
The sidebar presents the eight evidence gates in delivery order: discover, plan, convert,
move data, prepare the release, validate, certify, and cut over. **Resolve conversion
issues** is a contextual workbench used when the Convert gate produces residue; it is not
a ninth evidence gate. Hover or focus a navigation item to see what that workspace owns.
Compiler, modernization, portfolio, and administration workbenches remain available under
**Advanced tools**; most migration operators do not need them for the everyday guided journey.
The engagement workspace places **Your next step** before the journey and metrics. Its
primary button opens the first incomplete gate or exact blocker, and the current phase card
repeats that action for discoverability. Source-specific entry points, artifact and validation
counts, and **Manage engagement** follow. Management opens automatically when a draft needs
activation or when no source is configured; otherwise it remains collapsed.
Every engagement has five stable, shareable views:
| View | Use it for |
| -------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Overview** | Read the next action, journey, source scope, and governed lifecycle controls. |
| **Guided setup** | Complete client prerequisites in order from source registration through accepted discovery scope. |
| **Migration status** | Explain phase verdicts, blockers, evidence references, and the derived completion percentage. |
| **Artifacts** | Inspect immutable assessment, converted-code, validation, and release references with digests and lineage. |
| **Run ledger** | Trace assessment, conversion, transfer, deployment, and validation executions without copying provider-owned state. |
The lifecycle controls mean:
| Control | What it does | What it does not do |
| ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------ |
| **Create draft** | Creates the governed container for ownership, services, and initial estates. | It does not start assessment or claim readiness. |
| **Activate** | Makes the accepted configuration available for governed migration work. | It does not freeze scope or authorize cutover. |
| **Capability preflight** | Compares requested services with current provider evidence. | It does not certify this customer migration. |
| **Freeze scope** | Computes a stable digest for the accepted estates, services, and references. | It does not deploy, validate, certify, or cut over anything. |
Inline help buttons expose these definitions with mouse, keyboard, or touch. Consequential
prerequisites and blocked reasons remain visible without requiring a tooltip.
## Check the capability boundary before execution [#check-the-capability-boundary-before-execution]
An engagement preflight translates the selected services into an evidence requirement,
then checks every scoped estate and source variant against the tenant capability registry.
Run it before activating a factory pilot, promising a client outcome, or scheduling a
cutover:
```bash
fa engagement preflight eng_
fa engagement preflight eng_ --json
```
Discovery requires hermetic assessment and dependency proof. A factory pilot requires
workspace-proven execution, movement, validation, and target generation. A migration
factory requires representative client proof. Cutover assurance additionally requires a
production-certified cutover and rollback path. Modernization adds its own separately
evidenced requirement.
The command exits with status `1` when blocked and explains the next action for each
missing, proposed, stale, unavailable, or under-proved capability. Human and mixed lanes
are valid delivery paths when proven, but remain visible with their required skills so the
engagement can staff and estimate them explicitly.
## Create and activate the engagement [#create-and-activate-the-engagement]
Applications invoke the same Platform actions used by the Airlift workbench. Derive the
organization and actor from your authenticated server context; never accept them as
authoritative request-body fields.
```ts
import { AIRLIFT_ACTION_IDS, contentDigest } from "@fabricorg/airlift";
const created = await runtime.invokeAction(AIRLIFT_ACTION_IDS.engagementCreate, {
tenantId: session.organizationId,
actorId: session.userId,
actorType: "natural_person",
idempotencyKey: commandId,
params: {
name: "Enterprise warehouse migration",
owner: "data-platform",
services: ["discovery", "migration_factory", "modernization"],
estateIds: [estateId],
externalRefs: [
{ system: "crm", type: "opportunity", id: "OPP-1042" },
],
},
});
if (!created.ok) throw new Error(`${created.stage}: ${created.error}`);
const engagementId = created.data.engagementId as string;
await runtime.invokeAction(AIRLIFT_ACTION_IDS.engagementActivate, {
tenantId: session.organizationId,
actorId: session.userId,
actorType: "natural_person",
idempotencyKey: `${commandId}:activate`,
params: { engagementId },
});
```
Creation produces a `draft`. Activation marks the record ready for execution. Updates are
accepted only while the engagement is `draft` or `active`.
## Register connection bindings [#register-connection-bindings]
A connection binding describes what Airlift may ask an external connection to do. It
stores an opaque credential reference and declared capabilities; it does not store a
password, token, connection string, or secret value.
```ts
const binding = await runtime.invokeAction(
AIRLIFT_ACTION_IDS.connectionBindingRegister,
{
tenantId: session.organizationId,
actorId: session.userId,
actorType: "natural_person",
idempotencyKey: `${commandId}:source-binding`,
params: {
engagementId,
estateId,
name: "Source metadata access",
sourceSystem: "synapse",
direction: "source",
credentialRef: "databricks-connection://synapse-metadata",
capabilities: ["inventory_read", "metadata_read"],
},
},
);
```
Accepted reference schemes are `connection://`, `databricks-connection://`,
`databricks-secret://`, and `secret://`. User information, query strings, fragments, and
unrecognized schemes are rejected by the action schema.
After your connection verifier completes its source-specific checks, record the
connectivity diagnostic as an admitted system principal, then verify the binding with the
recorded diagnostic's derived digest:
```ts
const diagnostic = await runtime.invokeAction(
AIRLIFT_ACTION_IDS.connectionDiagnosticRecord,
{
tenantId: session.organizationId,
actorId: session.servicePrincipalId,
actorType: "system",
idempotencyKey: `${commandId}:diagnose-source-binding`,
params: {
connectionBindingId: binding.data.connectionBindingId,
bindingRevisionDigest: connectionBindingRevisionDigest(bindingRow),
providerId: "acme-source-probe",
providerVersion: "probe@1.0.0",
evidenceRef: "volumes/evidence/diagnostics/binding.json",
evidenceDigest: contentDigest(probeReport),
runRef: "runs/diagnostic/binding",
startedAt,
completedAt,
probes, // every probe required by the declared capabilities, all passing
},
},
);
await runtime.invokeAction(AIRLIFT_ACTION_IDS.connectionBindingVerify, {
tenantId: session.organizationId,
actorId: session.userId,
actorType: "natural_person",
idempotencyKey: `${commandId}:verify-source-binding`,
params: {
connectionBindingId: binding.data.connectionBindingId,
// verificationDigest must be the recorded diagnostic digest; a caller-chosen
// digest is not evidence.
verificationDigest: diagnostic.data.diagnosticDigest,
},
});
```
Registration does not imply connectivity. The binding remains `pending` until admitted
verification evidence is recorded. A retired binding remains visible in the audit and
projection history but cannot return to `verified`.
## Freeze the delivery scope [#freeze-the-delivery-scope]
Freezing is a governed action, not a client-authored flag. Airlift computes the SHA-256
scope digest from the recorded engagement ID, services, estates, and external references.
```ts
const frozen = await runtime.invokeAction(AIRLIFT_ACTION_IDS.engagementFreeze, {
tenantId: session.organizationId,
actorId: session.userId,
actorType: "natural_person",
idempotencyKey: `${commandId}:freeze`,
params: { engagementId },
});
```
A frozen engagement rejects later edits. Use the resulting `scopeDigest` as the stable
input reference for delivery plans, evidence exports, deployment releases, and cutover
prechecks.
## Read the projections [#read-the-projections]
Queries use organization-scoped projections. They do not mutate state.
```ts
const engagement = runtime.db.engagements.get(session.organizationId, engagementId);
const bindings = runtime.db.connectionBindings.listByEngagement(
session.organizationId,
engagementId,
);
```
The Databricks App exposes the same records under **Engagements**. Workbench forms are
thin authenticated transports over these actions; policy or schema denials are shown to
the operator and remain in the governed invocation ledger.
## Action summary [#action-summary]
| Task | Governed action |
| ------------------------------------------ | -------------------------------------- |
| create a draft | `airlift.engagement_create` |
| change editable scope | `airlift.engagement_update` |
| activate execution | `airlift.engagement_activate` |
| compute and freeze scope digest | `airlift.engagement_freeze` |
| register an opaque connection reference | `airlift.connection_binding_register` |
| record an admitted connectivity diagnostic | `airlift.connection_diagnostic_record` |
| verify with a recorded diagnostic digest | `airlift.connection_binding_verify` |
| retire a binding | `airlift.connection_binding_retire` |
Agent principals cannot invoke these actions. A separately admitted service principal
may use the narrow `automation` role through the authenticated remote API. Migration
agents may assess and convert within their admitted bounds; they cannot govern
engagement scope or connection admission.
# Guided migration journey (App walkthrough)
# Follow the guided migration journey [#follow-the-guided-migration-journey]
This is the primary App walkthrough. It follows one synthetic **Azure Synapse to
Databricks Lakehouse** engagement through the App, reading the same governed projection
the CLI and API read. [App walkthroughs](/docs/getting-started/ui-walkthroughs/) is the annotated screenshot
catalog with matching `fa` commands; [Demonstrate an Azure migration](/docs/getting-started/demo-walkthrough/)
adapts this journey into a presentation script for engineering audiences.
Every screenshot is an automated capture from a governed, public synthetic workspace. The
images contain no client data, credentials, real workspace identifiers, or production
claims. If this is your first visit, start with [the in-app guidance](/docs/getting-started/in-app-guidance/)
to learn the product tour, persistent Guide, and contextual help controls.
## Start with the artifact and the next action [#start-with-the-artifact-and-the-next-action]
Airlift does not ask developers to interpret a dashboard and guess what to do. Every
engagement phase page reads the governed migration projection and presents one **What
Airlift needs next** panel before metrics and ledgers, alongside direct links to the
artifacts that phase has already produced.
The panel always answers:
1. **What this phase does.** The outcome this workspace governs and why it exists.
2. **What Airlift observed.** The current phase summary from governed records.
3. **Why progress stopped.** The first missing prerequisite or active blocker.
4. **What is the risk if bypassed?** The concrete migration failure the gate prevents.
5. **Who acts next?** The engineer, architect, provider, or independent approver who owns the action.
6. **Evidence that clears the gate.** The exact artifact, run, digest, approval, or provider observation Airlift must admit.
Choose the primary **Do this next** action. It opens the relevant form or returns you to
the earliest incomplete phase. It never changes readiness merely by navigating.
## The eight phase workspaces [#the-eight-phase-workspaces]
| Phase | Developer work | Evidence Airlift expects |
| --------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Discover | Run the source assessment and review inventory and dependencies | Admitted report plus accepted inventory and dependency digests |
| Plan | Select a target blueprint and dependency-ordered waves | Selected and frozen plan bound to accepted source digests |
| Convert | Run converter batches, inspect attempts, and repair residue | Immutable target artifacts plus governed residue closure |
| Move data | Plan snapshot/incremental movement and operate checkpoints | Verified bindings, watermarks, restart checkpoints, and reconciliation digest |
| Deploy | Declare the required release and use Runway to execute it | Matching release reference, artifact digest, environment, operation, and terminal state |
| Validate | Request independent Experiments-backed checks | Passing object-specific runs and disposition of discrepancies |
| Certify | Review profiles, readiness tracks, and acceptance | System-minted certificates backed by active admitted evidence |
| Cut over | Freeze the wave, rehearse, approve, execute once, and verify | Certified scope, approvals, runbook, operational evidence, and rollback proof |
Later phases remain visible but are locked until the earliest incomplete gate is cleared.
The primary action on a locked page returns to that earlier phase and preserves the
engagement identifier.
## Discover: admit the assessment [#discover-admit-the-assessment]
The discovery workspace turns Lakebridge Profiler and Analyzer output into reviewed
inventory and dependency evidence. The artifact that matters is the immutable assessment
pack; the next action is to accept or dispute its scope.
## Plan: freeze scope against accepted digests [#plan-freeze-scope-against-accepted-digests]
Planning binds the target blueprint, dependency order, and wave scope to the accepted
discovery digests. Freezing is the artifact; conversion starts only against a frozen plan.
## Convert: candidates become immutable artifacts [#convert-candidates-become-immutable-artifacts]
The conversion factory records deterministic attempts and registers every output as an
immutable artifact with its producer generation and content digest. Converter success is
an attempt outcome, not parity evidence.
## Repair residue: normal specialist work [#repair-residue-normal-specialist-work]
Unsupported transaction, temporary-table, or orchestration behavior surfaces as a named
residue case with an owner, a diagnosis, and the evidence needed to close it. Residue is
normal specialist work on any real estate, not a defect in the walkthrough: Airlift makes
it explicit, assignable, measurable, and independently reviewable instead of hiding it in
an automation percentage.
## Move data: restartable, reconciled transfer [#move-data-restartable-reconciled-transfer]
The transfer workspace tracks snapshot and incremental movement through watermarks,
restart checkpoints, counts, rejects, and a reconciliation digest. A completed copy job is
insufficient without that evidence.
## Deploy: a contract between Airlift and Runway [#deploy-a-contract-between-airlift-and-runway]
The deployment page is a contract between Airlift and Runway:
* **Airlift** records what must be deployed and decides whether observed evidence matches.
* **Runway** deploys, promotes, rolls back, reconciles, and owns environment state.
A page showing four zeros means **no release requirement has been declared**. It does not
mean deployment succeeded, failed, or is complete.
| Counter | Meaning | What to do |
| -------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Requirements | Desired release outcomes recorded for this engagement | If zero, select deployable artifacts and create the first requirement |
| Awaiting observation | A requirement exists but no verified matching Runway result has been admitted | Deploy with Runway, then admit or reconcile its release reference |
| Matched | Required and observed digest, environment, operation, and state match | Continue to independent validation |
| Blocked or uncertain | Evidence failed, is unverifiable, or differs from the requirement | Inspect the requirement detail and correct or re-observe the Runway release |
### Create the first deployment requirement [#create-the-first-deployment-requirement]
1. Select the immutable converted code, configuration, and deployment manifests that form
one release.
2. Choose **Deploy a new release** for the first release. Use Promote only for an existing
release and Roll back only for an intentional recovery.
3. Choose the environment where the next validation run will execute.
4. Normally require **Release succeeded**.
5. Create the requirement. Airlift computes the desired digest and waits; it does not
execute Runway.
Runway then deploys the same artifact set. Airlift advances the deployment gate only when
the admitted Runway observation matches every required field.
## Validate: independent evidence against exact digests [#validate-independent-evidence-against-exact-digests]
The validation laboratory requests object-specific Experiments checks against exact
artifact digests, separating validation evidence from the converter that produced the
candidate. Failed required checks become discrepancies with a governed disposition path.
## Certify: readiness is evidence-derived [#certify-readiness-is-evidence-derived]
The assurance center is a read-only projection of which object-specific evidence tracks
have passed, failed, expired, or been waived. Certificates are system-minted from admitted
evidence; nobody authors one.
## Cut over: every gate agrees before execution [#cut-over-every-gate-agrees-before-execution]
The cutover control room stays blocked until frozen scope, evidence, rehearsal, distinct
approvals, operational health, and rollback capability all agree. It reflects the durable
workflow and governed gate; it is not a manually editable checklist.
## If someone asks about audit [#if-someone-asks-about-audit]
The walkthrough leads with artifacts and next actions, not the ledger. When an audience
asks how Airlift proves what ran, open the run ledger: it links provider-owned executions
to the governed engagement without copying their internal state, and a provider success
remains evidence to evaluate rather than an automatic readiness advance.
## Use contextual help [#use-contextual-help]
Select a question-mark control to define a term without leaving the page. Tooltips render
in the browser document layer, reposition on scrolling and resizing, and stay inside the
viewport. They are available by pointer and keyboard; press Escape to close one
and return focus to its help control.
A help card is supplementary. Blockers, prerequisites, and recovery instructions always
remain visible in the phase guidance panel.
## Use the CLI with the same truth [#use-the-cli-with-the-same-truth]
The App and `fa` read the same organization-scoped projections:
```bash
fa engagement status --json
fa artifact list --engagement-id --json
fa deployment list --engagement-id --json
fa validation list --engagement-id --json
```
Use the returned `currentPhase`, `blockers`, and phase `nextAction`; do not compute a
second percentage in automation. A UI click, task completion, or LLM response never
advances the ledger without the owning governed Platform action and its required evidence.
## AI assistance boundary [#ai-assistance-boundary]
An assistant may summarize the same phase guidance, link the relevant developer guide,
or explain a field in more conversational language. Airlift does not let an LLM invent
readiness: the assistant must treat the governed projection as ground truth. It cannot mint
evidence, change the current phase, approve a waiver, certify an object, or authorize
cutover.
# Use the in-app guidance
# Use the in-app guidance [#use-the-in-app-guidance]
Airlift is designed to tell a developer what to do next, why the action is required, and
which evidence will advance the migration. You do not need to infer readiness from a
counter or learn the entire navigation before starting work.
The console has three complementary guidance surfaces:
| Surface | When to use it | What it provides |
| -------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| First-use welcome and tour | Your first visit, or whenever you replay it | A short orientation to engagement context, search, the migration journey, phase workspaces, and the persistent Guide |
| Airlift Guide | From any console page | A plain-language explanation of the current page, the next action, current engagement context, and relevant developer articles |
| Question-mark help | Beside an unfamiliar term or metric | A concise definition plus a link to the deeper developer article when one exists |
## Take the first-use tour [#take-the-first-use-tour]
On your first visit, Airlift explains three product rules:
1. An **engagement** is the governed record for one client migration.
2. Airlift recommends the **earliest action** the ledger can safely accept.
3. **Evidence**, not navigation or ticket completion, advances the migration journey.
Select **Take the 2-minute tour** to highlight the engagement switcher, migration
search, journey navigation, phase workspace, and Guide. Use **Back**, **Next**, or
**Skip tour** at any time. Press Escape to exit.
The tour records only a browser-local display preference. It does not create a Platform
action, change an engagement, or write migration evidence.
## Open the Guide from any page [#open-the-guide-from-any-page]
Select **Guide** in the lower-right corner of the console. The panel answers:
* What is this page for?
* What should I do next?
* Which engagement and phase am I working in?
* How many evidence gates and blockers are recorded?
* Where is the detailed developer article?
Select **Replay the product tour** if you want the orientation again. The Guide is
read-only. It cannot approve, certify, waive, execute cutover, or change any migration
record.
## Use contextual help [#use-contextual-help]
Select a question-mark button beside a field, counter, or workflow term. The help card
stays within the browser viewport and has an opaque, high-contrast surface. It can be
opened by pointer or keyboard.
* Select **Read the developer article** for the complete workflow.
* Press Escape to close the card and return focus to its button.
* Select the close button or click outside the card to dismiss it.
Contextual help defines a term. The phase guidance panel remains the authoritative place
for the observed state, blocker diagnosis, owner, required evidence, and next action.
## Use specialist workbenches without guessing [#use-specialist-workbenches-without-guessing]
The advanced compiler, qualification, enterprise-application, modernization,
Databricks-native, and application-kit pages use the same guidance hierarchy:
1. **Purpose** explains when the workbench belongs in a migration and what it does not
own.
2. **How this workspace works** names the prerequisite and the three-step developer
path.
3. **Summary metrics** show governed records, with question-mark help for terms such as
native generator, registered bundle, Runway matched, or workspace proof.
4. **Current state** distinguishes an empty prerequisite from a completed result.
5. **One primary action** opens the exact upstream workspace needed to continue.
A zero does not automatically mean failure. For example, **Generated candidates: 0**
means the pipeline compiler has not registered an executable artifact set, while
**Workspace proven: 0** means no candidate has yet joined generated bytes, Runway,
Experiments, and workspace identity on one digest. Open the question-mark help to read
the definition, then follow the visible primary action to create the missing upstream
record.
These workbenches are intentionally under **Advanced tools**. Most migration operators
should follow the engagement journey. Open a specialist workbench when the journey sends
you there or when you are responsible for that compiler, release, validation, or
modernization boundary.
## Understand why guidance does not use a free-form agent [#understand-why-guidance-does-not-use-a-free-form-agent]
The console derives guidance from governed engagement projections and deterministic
workflow rules. An LLM explanation must never invent readiness, approve evidence, or
select a cutover decision. Harness-backed agents can assist bounded migration work, but
the Platform action ledger and Airlift policies remain the source of truth.
Continue with [the guided migration journey](/docs/getting-started/guided-migration-journey/) to learn what
each of the eight phase workspaces expects.
# Local lifecycle cookbook
This walkthrough is a single runnable file shipped with the SDK — see the
[full listing](/docs/getting-started/local-lifecycle-listing).
It drives the whole governed lifecycle through `createAirliftRuntime()` composed with
`createAirliftConfiguredAuthorization()` — the composition intended for unit tests,
examples, and local demos — and it is executable documentation: the repository's test
suite runs it on every change, so every snippet below reflects contracts that
actually execute.
The cookbook is versioned against **action manifest generation 11** and asserts the
pin at startup:
```js
import { AIRLIFT_ACTION_MANIFEST_GENERATION } from '@fabricorg/airlift';
if (AIRLIFT_ACTION_MANIFEST_GENERATION !== 11) {
throw new Error('This walkthrough is versioned against action manifest generation 11.');
}
```
To run it, install `@fabricorg/airlift`, save the
[full listing](/docs/getting-started/local-lifecycle-listing) as
`local-lifecycle.mjs`, and run it with Node.js 22+:
```bash
npm install @fabricorg/airlift
node local-lifecycle.mjs ./cookbook-out
```
It also ships inside the npm package itself, at
`node_modules/@fabricorg/airlift/examples/local-lifecycle.mjs`, in releases
published in 0.16.3 and later.
Contributors working from a repository checkout can run it directly against the
built package surface instead:
```bash
pnpm install
pnpm --filter @fabricorg/airlift build
node packages/airlift/examples/local-lifecycle.mjs ./cookbook-out
```
## Configured authorization [#configured-authorization]
The authorization directory is install-time configuration, not request input.
Memberships bind natural persons to tenant roles; system and agent principals are
admitted only through narrow trusted-id lanes.
```js
const authorizationConfig = {
memberships: [
{ organizationId: ORG, principal: 'operator@acme.example', principalType: 'natural_person', role: 'admin' },
{ organizationId: ORG, principal: 'approver@acme.example', principalType: 'natural_person', role: 'approver' },
],
// System principals admitted to the narrow worker transition set.
trustedWorkerIds: ['svc-airlift-worker'],
// Agent principals admitted only to a bounded action set: assessment,
// dependency mapping, plan generation, inventory, conversion, artifacts,
// residue, discrepancy triage, and modernization recommendations. Agents can
// NEVER approve, certify, mint certificates, or execute cutover.
trustedAgentIds: ['agent-convert-01'],
// System principals admitted to the platform registry bootstrap boundary.
platformInstallerIds: ['svc-airlift-installer'],
// Tenant-bound principals permitted to submit independently produced evidence.
admittedValidationPrincipals: [
{ organizationId: ORG, principal: 'svc-airlift-worker', provider: 'experiments' },
],
};
```
The runtime composes that directory with evidence services and an Ed25519 signer:
```js
const runtime = createAirliftRuntime({
authorization: createAirliftConfiguredAuthorization(authorizationConfig),
services: {
// Admits a validation run only when producer, provider, run ref, evidence
// ref, and digest all match an immutable registry entry.
...configuredAirliftEvidenceServices(authorizationConfig, [evidenceRegistryEntry]),
airliftEvidenceSigner: createEd25519EvidenceSigner({ keyId, privateKeyPem }),
},
});
```
## Platform-tenant bootstrap [#platform-tenant-bootstrap]
Organizations are minted from the platform registry tenant, never from an org
tenant. `airlift.org_provision` requires **all three** of:
* `tenantId = AIRLIFT_PLATFORM_TENANT` (`'airlift-platform'`),
* a `system` actor listed in `platformInstallerIds`,
* `{ orgId, name }` params.
```js
await runtime.invokeAction(AIRLIFT_ACTION_IDS.orgProvision, {
tenantId: AIRLIFT_PLATFORM_TENANT,
actorId: 'svc-airlift-installer',
actorType: 'system',
params: { orgId: 'acme-analytics', name: 'Acme Analytics' },
idempotencyKey: 'cookbook:org',
});
```
Invoking it with the org's **own** tenant id fails with
`Module "airlift" is not enabled for tenant …`. That message reads like a defect but
is the installer boundary working as designed: the installer grant is
tenant-bound, so entitlement fails closed outside the platform tenant.
## Happy-path ordering [#happy-path-ordering]
Each stage lists its acting principal; prerequisites are explicit. The runnable
script contains the full parameter shapes for every call.
1. **Estate** — `estate_register` (operator). `environment: 'prod'` engages the full gate policies.
2. **Engagement** — `engagement_create` with `services: ['discovery', 'migration_factory']`, then `engagement_activate` (operator). Discovery and factory actions require an active engagement.
3. **Assessment** — `assessment_record` (operator) with tool version, report refs, digests, and object counts.
4. **Inventory + dependency graph accept** — `object_register` per object (operator); `dependency_graph_start` and `dependency_batch_record` (trusted agent); `dependency_graph_accept` with an explicit `expectedEdgeCount`, then `assessment_accept` with `expectedObjectCount` (operator). Acceptance freezes scope digests.
5. **Plan generate → select → freeze** — `plan_generate` (dependency-aware wave candidates plus economics), `plan_select`, then `engagement_freeze` **before** `plan_freeze` (operator) — freezing a plan requires the engagement scope frozen first.
6. **Wave plan/assign** — `wave_plan`, `wave_assign` per object (operator); objects reach `planned`.
7. **Conversion batch** — `conversion_batch_create` (operator), `conversion_batch_start` (worker), then per object `conversion_start` / `conversion_record` (worker). Every converted object must carry an immutable `target_code` artifact (`artifact_register`) whose digest matches the recorded `outputDigest`.
8. **Residue lane** — for each failed conversion: `residue_create → residue_estimate` (agent) `→ residue_assign` (operator). The batch then completes with `completed_with_residue` — completion requires every failed object to hold an *active* residue case. Remediation continues after completion: `artifact_register` + `residue_resolve` (operator), `residue_review` by a **distinct natural person** (approver), then a fresh governed `conversion_start` / `conversion_record` brings the repaired object from `rework` back to `converted`.
9. **Validation evidence admission** — `validation_profile_assign` (operator), then `validation_run_record` (admitted principal). The run is admitted only because producer, provider, run ref, evidence ref, and digest all match the immutable registry entry — a caller's digest is never trusted merely because it is well formed.
10. **Readiness tracks** — `readiness_record` per required track of the assigned profile, admitting the validation run as evidence.
11. **Parity certify** — `parity_certify` with honest depth fields (see below).
12. **Business acceptance** — `business_accept` is a human decision by the approver.
13. **Migration certificate mint** — `migration_certificate_mint` binds the certificate to the exact `expectedProfileDigest` and `expectedReadinessDigest` the caller observed; the Ed25519 signer signs the envelope digest.
14. **Wave approval** — `wave_approve` by the approver (a natural person distinct from the operator).
15. **Offline verification** — the script verifies the envelope in-process, exports the envelope and public key, and prints the CLI command for you to run, fully offline:
```bash
fa certificate verify ./cookbook-out/migration-certificate.json --keys ./cookbook-out/verify-keys.json
```
## Evidence export [#evidence-export]
`airlift.evidence_export` assembles the replayable evidence pack for one wave
(or one migration object) from the org's event log — assessment, conversions
with tool versions, parity certificates with declared evidence depth,
approvals, and the governance revision in force at export time. It returns the
pack as `reportJson` alongside a sha256 `reportDigest` over that exact string,
and records the export itself as an `AirliftEvidenceExported` event keyed by
that digest — the audit stream proves what was attested without persisting the
pack contents.
The params contract requires a `waveId` or `objectId` plus a `reason`
(1–500 characters):
```js
const result = await runtime.invokeAction(AIRLIFT_ACTION_IDS.evidenceExport, {
tenantId: ORG,
actorId: 'operator@acme.example',
actorType: 'natural_person',
params: { waveId, reason: 'Assemble the replayable evidence pack for the approved wave.' },
idempotencyKey: 'cookbook:evidence-export',
});
// result.data: { waveId, exportKey, reportDigest, reason, reportJson }
```
Authorization requires the `airlift:evidence:export` permission, held by the
admin, operator, approver, and validator roles for natural-person principals
(the cookbook's operator is configured as `admin`).
Evidence export is deliberately **not** a trusted-worker action — unlike the
conversion and certification steps above, a `system` principal is denied even
though it drove most of the lifecycle. The cookbook demonstrates this: the
conversion worker's attempt fails closed with the same generic entitlement
error as the other module-boundary denials, before the operator invokes the
action successfully.
## Expected denials are first-class outcomes [#expected-denials-are-first-class-outcomes]
A suite of passing checks behaves identically with the boundary disabled, so the
cookbook demonstrates each denial and fails if any of them unexpectedly succeeds:
| Attempt | Outcome |
| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `org_provision` from the org's own tenant | Entitlement fails closed: `Module "airlift" is not enabled for tenant …` |
| Agent actor invoking `wave_approve` | Entitlement fails closed with the same generic error — approvals are outside the bounded agent action set, so the agent is not entitled to the module for this action at all |
| Parity certificate claiming `sampled_rows` depth without `aggregateChecksums` | Policy blocks the overclaim: a certificate must carry the evidence for every depth it claims |
| Resolver reviewing their own residue | Policy blocks it: `The resolver cannot independently review the same residue.` |
| System worker invoking `evidence_export` | Entitlement fails closed with the same generic error — evidence export is natural-person-only and deliberately not a trusted-worker action |
## Idempotency keys [#idempotency-keys]
Every mutation passes an `idempotencyKey` following a stable
`:[:]` convention (for example `cookbook:cnv-record:`).
Within the same runtime, re-invoking with the same key collapses onto the original
invocation instead of duplicating the logical command — the cookbook demonstrates
this by registering the estate twice and asserting both calls return the same
estate id. The in-memory store lives only as long as the process, so a rerun of
the script starts from scratch; against the durable Postgres store the same keys
give cross-process replay safety.
## The pre-cutover boundary [#the-pre-cutover-boundary]
The local walkthrough stops at certified objects and an approved wave. It
deliberately does **not** claim cutover execution or rehearsal, data transfer,
workspace job runs, deployment evidence, or production operational evidence. Those
stages require the durable runtime, real effectors, and independently produced
evidence — a local in-memory demo proving them would be an overclaim, which is
exactly what the governance model exists to prevent.
# Local lifecycle cookbook — full listing
Save this listing as `local-lifecycle.mjs` in a directory with `@fabricorg/airlift`
installed (`npm install @fabricorg/airlift`), then run
`node local-lifecycle.mjs ./cookbook-out` with Node.js 22+.
{/* GENERATED:COOKBOOK-LISTING:START */}
```js
// Local lifecycle cookbook for the Fabric Airlift SDK's in-memory runtime.
//
// Runnable, end-to-end walkthrough of the governed migration lifecycle using only
// the published package surface: `createAirliftRuntime()` composed with
// `createAirliftConfiguredAuthorization()` — the composition intended for unit
// tests, examples, and local demos. Every mutation goes through the governed
// action pipeline; nothing here writes to a real workspace.
//
// Usage:
// node examples/local-lifecycle.mjs [output-directory]
//
// The script exits non-zero if any expected-success step fails or any
// expected-denial unexpectedly succeeds. It writes the minted migration
// certificate envelope and the Ed25519 public key to the output directory
// (default: a temporary directory) and prints the offline verification command.
import { generateKeyPairSync } from 'node:crypto';
import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
AIRLIFT_ACTION_IDS,
AIRLIFT_ACTION_MANIFEST_GENERATION,
AIRLIFT_PLATFORM_TENANT,
configuredAirliftEvidenceServices,
createAirliftConfiguredAuthorization,
createAirliftRuntime,
createEd25519EvidenceSigner,
createEd25519EvidenceVerifier,
defaultValidationProfile,
readinessSnapshot,
verifyMigrationCertificateEnvelope,
} from '@fabricorg/airlift';
// ── Generation pin ───────────────────────────────────────────────────────────
// This cookbook is written against action manifest generation 11. A generation
// bump changes governed action contracts; re-verify every step before bumping.
if (AIRLIFT_ACTION_MANIFEST_GENERATION !== 11) {
console.error(
`This cookbook is versioned against action manifest generation 11; the installed @fabricorg/airlift reports generation ${AIRLIFT_ACTION_MANIFEST_GENERATION}. Check the package changelog before trusting these steps.`,
);
process.exit(1);
}
// ── Cast of principals ───────────────────────────────────────────────────────
// Roles are deliberately narrow. Natural persons hold tenant roles; system and
// agent principals are admitted only through the trusted-id lanes below.
const ORG = 'acme-analytics';
const OPERATOR = { actorId: 'operator@acme.example', actorType: 'natural_person' };
const APPROVER = { actorId: 'approver@acme.example', actorType: 'natural_person' }; // distinct person: separation of duties
const WORKER = { actorId: 'svc-airlift-worker', actorType: 'system' };
const AGENT = { actorId: 'agent-convert-01', actorType: 'agent' };
const INSTALLER = { actorId: 'svc-airlift-installer', actorType: 'system' };
// ── Configured authorization ─────────────────────────────────────────────────
// The directory is install-time configuration, not request input:
// - memberships bind natural-person principals to tenant roles,
// - trustedWorkerIds admits system principals to the narrow worker transition set,
// - trustedAgentIds admits agents only to a bounded action set (assessment,
// dependency mapping, plan generation, inventory, conversion, artifacts,
// residue, discrepancy triage, modernization recommendations) — never
// approvals, certification, or cutover,
// - platformInstallerIds admits system principals to the org-registry bootstrap
// boundary (org_provision / org_retire in the platform tenant only),
// - admittedValidationPrincipals lists who may submit independently produced
// validation evidence, per organization and provider.
const VALIDATION_RUN_REF = 'jobs/run/2002';
const VALIDATION_EVIDENCE_REF = 'volumes/evidence/wave-1/fact_sales.json';
const VALIDATION_EVIDENCE_DIGEST = 'a'.repeat(64);
const VALIDATION_ARTIFACT_DIGEST = 'b'.repeat(64);
const authorizationConfig = {
memberships: [
{
// The delivery lead holds `admin` here so the separation-of-duties denial
// below is decided by the governance policy (resolver ≠ reviewer), not
// masked by a missing role permission. Production teams typically split
// the narrower operator / approver / validator roles across people.
organizationId: ORG,
principal: OPERATOR.actorId,
principalType: 'natural_person',
role: 'admin',
},
{
organizationId: ORG,
principal: APPROVER.actorId,
principalType: 'natural_person',
role: 'approver',
},
],
trustedWorkerIds: [WORKER.actorId],
trustedAgentIds: [AGENT.actorId],
platformInstallerIds: [INSTALLER.actorId],
admittedValidationPrincipals: [
{ organizationId: ORG, principal: WORKER.actorId, provider: 'experiments' },
],
};
// ── Runtime services ─────────────────────────────────────────────────────────
// The evidence registry stands in for a provider's authoritative run API: the
// verifier admits a validation run only when every field (producer, provider,
// run ref, evidence ref, digest) matches an immutable registry entry, so a
// caller's digest is never trusted merely because it is well formed.
//
// Binding and attestation are different claims, and this manifest only does the
// first. The entry below BINDS the run's artifact digest and verdict: a run
// submitted with either one different fails verification, so neither can be
// restated after the fact. It does not ATTEST them — a static manifest never sees
// the provider's evidence body, so it cannot derive what the provider reported and
// check the submission against it.
//
// That is sufficient here because this estate is Synapse, outside the governed
// hazard profile set. A governed conversion additionally requires provider-ESTABLISHED
// `verdict` and `completedAt`, and only a verifier that reads the provider's evidence
// body can establish them — the worker's Volume-backed store derives both from the
// digest-checked document. A static manifest cannot, no matter how complete, so a
// governed conversion fails closed until such a verifier is injected. The artifact
// digest is never attested by anyone: provider run documents do not name the artifact
// they exercised, so the conversion-hazard gate corroborates it at certification.
//
// The Ed25519 signer signs migration-certificate envelopes so they verify offline.
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
const SIGNING_KEY_ID = 'cookbook-local-2026';
const publicKeyPem = publicKey.export({ type: 'spki', format: 'pem' }).toString();
const runtime = createAirliftRuntime({
authorization: createAirliftConfiguredAuthorization(authorizationConfig),
services: {
...configuredAirliftEvidenceServices(authorizationConfig, [
{
organizationId: ORG,
producerPrincipal: WORKER.actorId,
provider: 'experiments',
providerRunRef: VALIDATION_RUN_REF,
evidenceRef: VALIDATION_EVIDENCE_REF,
evidenceDigest: VALIDATION_EVIDENCE_DIGEST,
artifactDigest: VALIDATION_ARTIFACT_DIGEST,
verdict: 'passed',
},
]),
airliftEvidenceSigner: createEd25519EvidenceSigner({
keyId: SIGNING_KEY_ID,
privateKeyPem: privateKey.export({ type: 'pkcs8', format: 'pem' }).toString(),
}),
},
});
// ── Invocation helpers ───────────────────────────────────────────────────────
// Idempotency keys make every mutation safely replayable: re-invoking with the
// same key collapses onto the original invocation instead of duplicating it.
// Convention: a stable `:[:]` string per logical mutation.
let failures = 0;
async function ok(actionId, tenantId, actor, params, idempotencyKey) {
const result = await runtime.invokeAction(actionId, {
tenantId,
actorId: actor.actorId,
actorType: actor.actorType,
params,
...(idempotencyKey ? { idempotencyKey } : {}),
});
if (!result.ok) {
failures += 1;
console.error(`✗ ${actionId} failed: [${result.stage}] ${result.error}`);
process.exit(1);
}
console.log(`✓ ${actionId}`);
return result;
}
async function denied(label, expected, actionId, tenantId, actor, params) {
const result = await runtime.invokeAction(actionId, {
tenantId,
actorId: actor.actorId,
actorType: actor.actorType,
params,
});
if (result.ok) {
failures += 1;
console.error(`✗ EXPECTED DENIAL but ${actionId} succeeded: ${label}`);
process.exit(1);
}
// A denial only proves the named boundary if it fired for the expected reason.
if (result.stage !== expected.stage || !result.error.includes(expected.errorIncludes)) {
failures += 1;
console.error(
`✗ ${actionId} was denied for the WRONG reason: expected [${expected.stage}] …${expected.errorIncludes}…, got [${result.stage}] ${result.error}`,
);
process.exit(1);
}
console.log(`✓ denied as expected — ${label}\n [${result.stage}] ${result.error}`);
return result;
}
// ── 0. Platform-tenant bootstrap ─────────────────────────────────────────────
// Organizations are minted from the platform registry tenant, never from an org
// tenant. The installer boundary requires ALL of: tenantId = AIRLIFT_PLATFORM_TENANT
// ('airlift-platform'), a system actor listed in platformInstallerIds, and
// {orgId, name} params. Invoking org_provision with the org's own tenantId is the
// classic trap: the installer grant is tenant-bound, so entitlement fails with
// `Module "airlift" is not enabled for tenant …` — a boundary, not a defect.
console.log('\n— Platform bootstrap —');
await denied(
'org_provision from the org tenant (the installer grant is platform-tenant-bound)',
{ stage: 'handler', errorIncludes: 'is not enabled for tenant' },
AIRLIFT_ACTION_IDS.orgProvision,
ORG,
INSTALLER,
{ orgId: ORG, name: 'Acme Analytics' },
);
await ok(
AIRLIFT_ACTION_IDS.orgProvision,
AIRLIFT_PLATFORM_TENANT,
INSTALLER,
{ orgId: ORG, name: 'Acme Analytics' },
'cookbook:org',
);
// ── 1. Estate ────────────────────────────────────────────────────────────────
// The estate is the source system under migration. `environment: 'prod'` engages
// the full gate policies (dev estates get the approval-skip edge).
console.log('\n— Discovery and planning —');
const estate = await ok(
AIRLIFT_ACTION_IDS.estateRegister,
ORG,
OPERATOR,
{
name: 'Legacy Synapse DW',
sourceSystem: 'synapse',
owner: 'data-platform',
environment: 'prod',
},
'cookbook:estate',
);
const estateId = estate.data.estateId;
// Idempotent replay: within the same runtime, re-invoking with the same key
// collapses onto the original invocation instead of double-applying. (The
// in-memory store lives only as long as this process; the durable Postgres
// store gives the same keys cross-process replay semantics.)
const replay = await ok(
AIRLIFT_ACTION_IDS.estateRegister,
ORG,
OPERATOR,
{
name: 'Legacy Synapse DW',
sourceSystem: 'synapse',
owner: 'data-platform',
environment: 'prod',
},
'cookbook:estate',
);
if (replay.data.estateId !== estateId) {
console.error('✗ idempotent replay minted a second estate instead of collapsing');
process.exit(1);
}
console.log(`✓ replay with the same idempotency key returned the original estate ${estateId}`);
// ── 2. Engagement ────────────────────────────────────────────────────────────
// Engagements scope commercial services over estates; discovery and factory
// actions require an active engagement.
const engagement = await ok(
AIRLIFT_ACTION_IDS.engagementCreate,
ORG,
OPERATOR,
{
name: 'Synapse modernization wave 1',
services: ['discovery', 'migration_factory'],
owner: OPERATOR.actorId,
estateIds: [estateId],
},
'cookbook:engagement',
);
const engagementId = engagement.data.engagementId;
await ok(AIRLIFT_ACTION_IDS.engagementActivate, ORG, OPERATOR, { engagementId }, 'cookbook:engage');
// ── 3. Assessment ────────────────────────────────────────────────────────────
// The assessment records the analyzer run (tool, report refs, digests, counts).
const assessment = await ok(
AIRLIFT_ACTION_IDS.assessmentRecord,
ORG,
OPERATOR,
{
estateId,
jobRunRef: 'jobs/run/assessment-1',
reportRef: 'volumes/discovery/assessment.json',
reportDigest: 'b'.repeat(64),
inventoryDigest: 'c'.repeat(64),
dependencyDigest: 'd'.repeat(64),
toolVersion: 'lakebridge@0.14.2',
objectCounts: { tables: 2, views: 1, storedProcedures: 0, etlJobs: 0, other: 0 },
},
'cookbook:assessment',
);
const assessmentId = assessment.data.assessmentId;
// ── 4. Inventory + dependency graph accept ───────────────────────────────────
// Objects are registered against the assessment; the dependency graph is loaded
// by a trusted agent and accepted by a human with an explicit expected edge
// count, then the assessment itself is accepted with the expected object count.
// Acceptance freezes scope digests — later drift is detectable.
const objectIds = [];
for (const row of [
{ name: 'dbo.fact_sales', objectType: 'table', complexity: 'medium' },
{ name: 'dbo.dim_customer', objectType: 'table', complexity: 'low' },
{ name: 'dbo.vw_sales_by_region', objectType: 'view', complexity: 'medium' },
]) {
const object = await ok(
AIRLIFT_ACTION_IDS.objectRegister,
ORG,
OPERATOR,
{
estateId,
name: row.name,
objectType: row.objectType,
sourcePath: `dw/${row.name}.sql`,
complexity: row.complexity,
assessmentRef: assessmentId,
},
`cookbook:obj:${row.name}`,
);
objectIds.push(object.data.objectId);
}
const graph = await ok(
AIRLIFT_ACTION_IDS.dependencyGraphStart,
ORG,
AGENT,
{
engagementId,
assessmentId,
expectedEdgeCount: 2,
graphRef: { system: 'lakebridge', type: 'dependency_graph', id: 'graph-wave-1' },
toolVersion: 'lakebridge@0.14.2',
},
'cookbook:graph',
);
const dependencyGraphId = graph.data.dependencyGraphId;
await ok(
AIRLIFT_ACTION_IDS.dependencyBatchRecord,
ORG,
AGENT,
{
dependencyGraphId,
edges: [
{
fromObjectId: objectIds[2],
toObjectId: objectIds[0],
kind: 'consumer',
confidence: 1,
critical: true,
},
{
fromObjectId: objectIds[2],
toObjectId: objectIds[1],
kind: 'consumer',
confidence: 1,
critical: false,
},
],
},
'cookbook:edges',
);
await ok(
AIRLIFT_ACTION_IDS.dependencyGraphAccept,
ORG,
OPERATOR,
{ dependencyGraphId, expectedEdgeCount: 2 },
'cookbook:graph-accept',
);
await ok(
AIRLIFT_ACTION_IDS.assessmentAccept,
ORG,
OPERATOR,
{
assessmentId,
engagementId,
dependencyGraphId,
expectedObjectCount: objectIds.length,
assumptions: ['A representative production window is available for validation.'],
exclusions: [],
},
'cookbook:assessment-accept',
);
// ── 5. Migration plan: generate → select → freeze ────────────────────────────
// Plan generation produces dependency-aware wave candidates plus economics; a
// human selects the scenario. Freezing requires the engagement scope frozen
// first — the plan digest then pins the delivery contract.
const generated = await ok(
AIRLIFT_ACTION_IDS.planGenerate,
ORG,
OPERATOR,
{
engagementId,
assessmentId,
name: 'Parity-first delivery plan',
scenario: 'parity_first',
targetWorkspaceRef: { system: 'databricks', type: 'workspace', id: 'acme-target' },
maxObjectsPerWave: 3,
economics: {
manualHoursPerComplexityPoint: 8,
assistedHoursPerComplexityPoint: 3,
blendedHourlyCostUsd: 180,
sourceMonthlyRunCostUsd: 25_000,
targetMonthlyRunCostUsd: 18_000,
dualRunMonths: 2,
},
assumptions: ['Unity Catalog prerequisites are completed before deployment.'],
},
'cookbook:plan',
);
const migrationPlanId = generated.data.migrationPlanId;
await ok(AIRLIFT_ACTION_IDS.planSelect, ORG, OPERATOR, { migrationPlanId }, 'cookbook:plan-select');
await ok(
AIRLIFT_ACTION_IDS.engagementFreeze,
ORG,
OPERATOR,
{ engagementId },
'cookbook:engagement-freeze',
);
await ok(AIRLIFT_ACTION_IDS.planFreeze, ORG, OPERATOR, { migrationPlanId }, 'cookbook:plan-freeze');
// ── 6. Wave plan/assign — objects reach `planned` ────────────────────────────
console.log('\n— Wave and conversion factory —');
const wave = await ok(
AIRLIFT_ACTION_IDS.wavePlan,
ORG,
OPERATOR,
{ estateId, name: 'Wave 1 — sales marts', targetWindow: '2026-09-15' },
'cookbook:wave',
);
const waveId = wave.data.waveId;
for (const objectId of objectIds) {
await ok(
AIRLIFT_ACTION_IDS.waveAssign,
ORG,
OPERATOR,
{ waveId, objectId },
`cookbook:assign:${objectId}`,
);
}
// ── 7. Conversion batch + per-object conversions ─────────────────────────────
// The batch groups conversions under one governed unit; the worker drives each
// object through conversion_start / conversion_record. One object fails on
// purpose to open the residue lane.
const batch = await ok(
AIRLIFT_ACTION_IDS.conversionBatchCreate,
ORG,
OPERATOR,
{
engagementId,
assessmentId,
name: 'Wave 1 stored-code batch',
method: 'lakebridge',
toolVersion: 'lakebridge@0.14.2',
objectIds,
},
'cookbook:batch',
);
const conversionBatchId = batch.data.conversionBatchId;
await ok(
AIRLIFT_ACTION_IDS.conversionBatchStart,
ORG,
WORKER,
{ conversionBatchId },
'cookbook:batch-start',
);
const conversionIds = [];
for (const [index, objectId] of objectIds.entries()) {
const start = await ok(
AIRLIFT_ACTION_IDS.conversionStart,
ORG,
WORKER,
{
objectId,
batchId: conversionBatchId,
method: 'lakebridge',
toolVersion: 'lakebridge@0.14.2',
},
`cookbook:cnv-start:${objectId}`,
);
conversionIds.push(start.data.conversionId);
const isResidue = index === 2; // the view fails conversion and becomes residue
await ok(
AIRLIFT_ACTION_IDS.conversionRecord,
ORG,
WORKER,
isResidue
? {
conversionId: start.data.conversionId,
objectId,
outcome: 'failed',
detail: 'Unsupported dynamic SQL construct.',
}
: {
conversionId: start.data.conversionId,
objectId,
outcome: 'converted',
artifactRef: `volumes/target/${objectId}.sql`,
outputDigest: 'e'.repeat(64),
},
`cookbook:cnv-record:${objectId}`,
);
if (!isResidue) {
// Batch completion requires every converted object to carry an immutable
// target artifact whose digest matches the recorded conversion output.
await ok(
AIRLIFT_ACTION_IDS.artifactRegister,
ORG,
AGENT,
{
engagementId,
objectId,
kind: 'target_code',
name: `Converted SQL for ${objectId}`,
artifactRef: {
system: 'databricks',
type: 'volume_object',
id: `target/${objectId}.sql`,
digest: 'e'.repeat(64),
},
digest: 'e'.repeat(64),
mediaType: 'application/sql',
toolVersion: 'lakebridge@0.14.2',
},
`cookbook:artifact:${objectId}`,
);
}
}
// ── 8. Residue lane: create → estimate → assign → resolve → review ───────────
// Residue is the human-remediation lane for what automation could not convert.
// Resolution requires a registered artifact and validation evidence ref; review
// must come from a different natural person than the resolver (separation of
// duties) — demonstrated below in the denials section.
const residueObjectId = objectIds[2];
const residue = await ok(
AIRLIFT_ACTION_IDS.residueCreate,
ORG,
AGENT,
{
engagementId,
objectId: residueObjectId,
category: 'unsupported_construct',
lane: 'human',
summary: 'Rewrite dynamic SQL for Databricks SQL',
requiredSkills: ['T-SQL', 'Databricks SQL'],
commercialAttribution: 'managed_service',
},
'cookbook:residue',
);
const residueId = residue.data.residueId;
await ok(
AIRLIFT_ACTION_IDS.residueEstimate,
ORG,
AGENT,
{ residueId, estimateMinutes: 240, requiredSkills: ['T-SQL', 'Databricks SQL'] },
'cookbook:residue-estimate',
);
await ok(
AIRLIFT_ACTION_IDS.residueAssign,
ORG,
OPERATOR,
{
residueId,
assignedToRef: { system: 'techfabric', type: 'engineer', id: 'migration-sql-specialist' },
workRef: { system: 'tower', type: 'work_item', id: 'work-42' },
},
'cookbook:residue-assign',
);
// The batch can complete once every failed object has an ACTIVE residue case —
// completion reports `completed_with_residue`; remediation continues after it.
await ok(
AIRLIFT_ACTION_IDS.conversionBatchComplete,
ORG,
WORKER,
{ conversionBatchId },
'cookbook:batch-complete',
);
const repairedArtifact = await ok(
AIRLIFT_ACTION_IDS.artifactRegister,
ORG,
OPERATOR,
{
engagementId,
objectId: residueObjectId,
kind: 'target_code',
name: 'Repaired view SQL',
artifactRef: {
system: 'databricks',
type: 'volume_object',
id: 'target/vw_sales_by_region.sql',
},
digest: 'f'.repeat(64),
mediaType: 'application/sql',
toolVersion: 'human-remediation@1',
},
'cookbook:residue-artifact',
);
await ok(
AIRLIFT_ACTION_IDS.residueResolve,
ORG,
OPERATOR,
{
residueId,
resolvedArtifactId: repairedArtifact.data.artifactId,
resolutionEvidenceRef: {
system: 'experiments',
type: 'validation_run',
id: 'validation-residue-1',
digest: 'a'.repeat(64),
},
actualMinutes: 180,
resolutionNotes: 'Replaced unsupported syntax and passed the scenario suite.',
},
'cookbook:residue-resolve',
);
// ── Expected denial: separation of duties on residue review ──────────────────
console.log('\n— Governed denials (first-class outcomes) —');
await denied(
'separation of duties: the resolver cannot review their own residue',
{ stage: 'handler', errorIncludes: 'The resolver cannot independently review the same residue.' },
AIRLIFT_ACTION_IDS.residueReview,
ORG,
OPERATOR,
{ residueId, decision: 'approved', reviewNotes: 'Self review must fail.' },
);
await ok(
AIRLIFT_ACTION_IDS.residueReview,
ORG,
APPROVER,
{ residueId, decision: 'approved', reviewNotes: 'Artifact lineage and evidence reviewed.' },
'cookbook:residue-review',
);
// The failed conversion sent the object to `rework`; the reviewed repair is
// recorded as a fresh governed conversion (outside the completed batch)
// referencing the repaired artifact.
const reconvert = await ok(
AIRLIFT_ACTION_IDS.conversionStart,
ORG,
WORKER,
{
objectId: residueObjectId,
method: 'manual',
toolVersion: 'human-remediation@1',
},
'cookbook:cnv-start:residue-repair',
);
await ok(
AIRLIFT_ACTION_IDS.conversionRecord,
ORG,
WORKER,
{
conversionId: reconvert.data.conversionId,
objectId: residueObjectId,
outcome: 'converted',
artifactRef: 'target/vw_sales_by_region.sql',
outputDigest: 'f'.repeat(64),
},
'cookbook:cnv-record:residue-repair',
);
// ── Expected denial: agents cannot approve waves ─────────────────────────────
// Authorization is fail-closed at entitlement: wave_approve is outside the
// bounded agent action set, so the agent is not entitled to the module at all
// for this action — the denial is the generic entitlement error, not a
// wave-specific message.
await denied(
'agent actor attempting wave_approve (approvals are outside the bounded agent action set)',
{ stage: 'handler', errorIncludes: 'is not enabled for tenant' },
AIRLIFT_ACTION_IDS.waveApprove,
ORG,
AGENT,
{ waveId },
);
// ── 9. Validation evidence, readiness, certification per object ──────────────
// For each object: validation profile → validation run (admitted principal +
// verifier + immutable run refs) → readiness track records → parity certificate
// (honest depth fields) → business acceptance → migration certificate mint.
console.log('\n— Validation, readiness, and certification —');
function honestParityCertificate() {
const now = new Date().toISOString();
return {
// Depth honesty (ADR-0003): the certificate states its own evidence depth and
// must carry the evidence for every depth it claims — policy blocks overclaims.
evidenceDepth: 'sampled_rows',
rowCountSource: 1_204_331,
rowCountTarget: 1_204_331,
aggregateChecksums: { 'sum(amount)': 'a1b2c3', 'count(*)': '1204331' },
sampledRowCount: 10_000,
toolVersion: 'lakebridge@0.14.2',
validationRunRef: VALIDATION_RUN_REF,
sourceSnapshotAt: now,
targetSnapshotAt: now,
};
}
// ── Expected denial: overclaimed parity depth ────────────────────────────────
const overclaiming = honestParityCertificate();
overclaiming.aggregateChecksums = undefined; // claims sampled_rows depth without aggregate evidence
await denied(
'parity certificate claiming a depth without carrying its evidence',
{ stage: 'policy', errorIncludes: 'missing aggregateChecksums' },
AIRLIFT_ACTION_IDS.parityCertify,
ORG,
WORKER,
{ objectId: objectIds[0], certificate: overclaiming },
);
const certifiedObjects = [];
for (const objectId of objectIds) {
const object = runtime.db.objects.get(ORG, objectId);
const profile = defaultValidationProfile(object.objectType);
await ok(
AIRLIFT_ACTION_IDS.validationProfileAssign,
ORG,
OPERATOR,
{
objectId,
profileId: profile.profileId,
profileVersion: profile.version,
reason: 'Assign the default validation profile for the object type.',
},
`cookbook:profile:${objectId}`,
);
// The run is admitted only because the whole tuple — producer, provider, run ref,
// evidence ref and digest, plus the artifact digest and verdict — matches the
// immutable registry entry configured at startup. That is binding, not attestation:
// the manifest establishes no provider provenance, which is sufficient here because
// this Synapse estate is outside the governed hazard profile set.
const run = await ok(
AIRLIFT_ACTION_IDS.validationRunRecord,
ORG,
WORKER,
{
objectId,
provider: 'experiments',
providerRunRef: VALIDATION_RUN_REF,
evidenceRef: VALIDATION_EVIDENCE_REF,
evidenceDigest: VALIDATION_EVIDENCE_DIGEST,
verdict: 'passed',
toolVersion: 'databricks-testkit@0.9.0',
artifactDigest: VALIDATION_ARTIFACT_DIGEST,
sourceWatermark: 'source:v1',
targetSnapshot: 'target:v1',
completedAt: new Date().toISOString(),
},
`cookbook:validation:${objectId}`,
);
// Readiness tracks: admit the validation run as evidence for every required
// track except business acceptance (a human decision, below) and cutover
// readiness (out of scope for a local demo — see the boundary note).
for (const requirement of profile.requirements) {
if (
requirement.applicability === 'required' &&
requirement.track !== 'business_acceptance' &&
requirement.track !== 'cutover_readiness'
) {
await ok(
AIRLIFT_ACTION_IDS.readinessRecord,
ORG,
WORKER,
{
objectId,
track: requirement.track,
validationRunId: run.data.validationRunId,
reason: 'Validation evidence admitted for the track.',
},
`cookbook:readiness:${objectId}:${requirement.track}`,
);
}
}
await ok(
AIRLIFT_ACTION_IDS.parityCertify,
ORG,
WORKER,
{ objectId, certificate: honestParityCertificate() },
`cookbook:parity:${objectId}`,
);
await ok(
AIRLIFT_ACTION_IDS.businessAccept,
ORG,
APPROVER,
{
objectId,
evidenceRef: `decisions/${objectId}`,
evidenceDigest: 'c'.repeat(64),
decision: 'accepted',
reason: 'Business owner accepted parity and validation evidence.',
},
`cookbook:accept:${objectId}`,
);
// Minting binds the certificate to the exact profile and readiness digests the
// caller observed — stale expectations are rejected.
const assignment = runtime.db.profileAssignments.get(ORG, objectId);
const snapshot = readinessSnapshot(runtime.db, ORG, objectId);
const minted = await ok(
AIRLIFT_ACTION_IDS.migrationCertificateMint,
ORG,
WORKER,
{
objectId,
expectedProfileDigest: assignment.profileDigest,
expectedReadinessDigest: snapshot.digest,
},
`cookbook:mint:${objectId}`,
);
certifiedObjects.push({ objectId, certificateId: minted.data.certificateId });
}
// ── 10. Wave approval by a distinct natural person ───────────────────────────
await ok(AIRLIFT_ACTION_IDS.waveApprove, ORG, APPROVER, { waveId }, 'cookbook:wave-approve');
// ── 11. Offline certificate verification ─────────────────────────────────────
// The minted envelope carries an Ed25519 signature over its content digest, so
// it verifies offline with nothing but the public key. This script verifies
// in-process; the printed `fa certificate verify` command is the offline
// follow-up for you to run against the exported files.
console.log('\n— Offline verification —');
const envelope = runtime.db.migrationCertificates.activeForObject(ORG, objectIds[0]).envelope;
const verifier = createEd25519EvidenceVerifier({ [SIGNING_KEY_ID]: publicKeyPem });
if (!(await verifyMigrationCertificateEnvelope(envelope, verifier))) {
console.error('✗ minted certificate failed offline verification');
process.exit(1);
}
console.log('✓ minted migration certificate verifies offline (in-process)');
const outDir = process.argv[2] ?? mkdtempSync(join(tmpdir(), 'airlift-cookbook-'));
mkdirSync(outDir, { recursive: true });
// ── 12. Governed evidence export ─────────────────────────────────────────────
// `evidence_export` assembles the replayable evidence pack for one wave (or one
// object) from the org's event log — assessment, conversions, parity
// certificates, approvals, everything addressed to or about the subject — plus
// the governance revision in force at export time. Unlike every worker-driven
// step above, this action requires a natural person holding
// `airlift:evidence:export` (admin/operator/approver/validator — the cookbook's
// operator is an admin); it is deliberately
// NOT in the trusted-worker action set, so the conversion worker is denied even
// though it drove the conversions the pack now reports on.
console.log('\n— Evidence export —');
await denied(
'system worker invoking evidence_export (natural-person-only; not a trusted-worker action)',
{ stage: 'handler', errorIncludes: 'is not enabled for tenant' },
AIRLIFT_ACTION_IDS.evidenceExport,
ORG,
WORKER,
{ waveId, reason: 'Automated export attempt from the conversion worker.' },
);
const evidenceExport = await ok(
AIRLIFT_ACTION_IDS.evidenceExport,
ORG,
OPERATOR,
{ waveId, reason: 'Assemble the replayable evidence pack for the approved wave.' },
'cookbook:evidence-export',
);
const evidenceReportFile = join(outDir, 'evidence-export.report.json');
const evidenceMetaFile = join(outDir, 'evidence-export.meta.json');
// `reportJson` is the exact string the action digested — write it byte for byte
// so `reportDigest` remains verifiable against the file on disk.
writeFileSync(evidenceReportFile, evidenceExport.data.reportJson);
writeFileSync(
evidenceMetaFile,
`${JSON.stringify(
{
waveId: evidenceExport.data.waveId,
exportKey: evidenceExport.data.exportKey,
reportDigest: evidenceExport.data.reportDigest,
reason: evidenceExport.data.reason,
},
null,
2,
)}\n`,
);
console.log(`✓ evidence pack exported: ${evidenceReportFile}`);
console.log(` exportKey: ${evidenceExport.data.exportKey}`);
console.log(` reportDigest: ${evidenceExport.data.reportDigest}`);
const certificateFile = join(outDir, 'migration-certificate.json');
const keysFile = join(outDir, 'verify-keys.json');
writeFileSync(certificateFile, `${JSON.stringify(envelope, null, 2)}\n`);
writeFileSync(keysFile, `${JSON.stringify({ [SIGNING_KEY_ID]: publicKeyPem }, null, 2)}\n`);
console.log(`\nCertificate envelope: ${certificateFile}`);
console.log(`Verification keys: ${keysFile}`);
console.log(`Verify offline with:\n fa certificate verify ${certificateFile} --keys ${keysFile}`);
// ── Pre-cutover boundary ─────────────────────────────────────────────────────
console.log(`
— Pre-cutover boundary —
This local demo stops at certified objects and an approved wave. It deliberately
does NOT claim: cutover execution or rehearsal, data transfer, workspace job
runs, deployment evidence, or production operational evidence. Those stages
require the durable runtime, real effectors, and independently produced
evidence — a local in-memory walkthrough proving them would be an overclaim,
which is exactly what the governance model exists to prevent.`);
console.log(
`\nDone: ${certifiedObjects.length} objects certified, wave approved, 1 residue reviewed, evidence exported, 5 denials demonstrated.`,
);
if (failures > 0) process.exit(1);
```
{/* GENERATED:COOKBOOK-LISTING:END */}
# Quickstart
The first path is the Databricks App. Open it, create an engagement, and follow the
guided journey — the App walks each governed gate in order, shows why progress stopped
and who acts next, and never asks you to paste a digest or author evidence by hand.
## Start in the Databricks App [#start-in-the-databricks-app]
1. Open the Airlift Databricks App and choose **Engagements → New engagement**. Enter a
program name, accountable owner, and the delivery services being requested, then
**Create draft**. The draft is only the governed container; it does not connect to a
source, run code, or claim readiness.
2. Follow **Guided setup** for the engagement: register the source estate, bind a
secret-safe connection reference, verify source access with retained evidence,
activate the engagement, request an assessment, and review and accept the discovered
scope. Each step names the evidence it creates and unlocks only when its governed
event is recorded.
3. Continue with the [guided migration journey](/docs/getting-started/guided-migration-journey),
which walks every phase — discover, plan, convert, move data, validate, certify, cut
over — with its gate evidence and next actor.
The App and the CLI call the same Fabric Platform actions and read the same governed
projections, so anything created in one surface appears in the other. See
[guided client onboarding](/docs/getting-started/client-onboarding) for the full setup
walkthrough and [App walkthroughs](/docs/getting-started/ui-walkthroughs) for the
stage-by-stage UI.
## Automation [#automation]
Use the CLI and SDK when you need the same governed operations in CI, scripts, or an
integration service. This appendix is the automation entry point; the complete
authenticated surface is documented under [Airlift CLI](/docs/cli), including
[authenticated automation](/docs/cli/remote-automation) for remote-endpoint identity,
idempotency, and evidence recipes.
### Install the CLI [#install-the-cli]
Prerequisites: Node.js **22+** and npm, pnpm, or another Node.js package manager.
```bash
npm install --global @fabricorg/airlift-cli
fa help
```
Use a pinned, installation-free invocation in CI:
```bash
npx --yes --package @fabricorg/airlift-cli@0.18.4 fa sources --json
```
### Generate a source migration plan [#generate-a-source-migration-plan]
List the source profiles and generate a deterministic project contract before
configuring credentials:
```bash
fa sources
fa source inspect synapse
fa source plan synapse --json > synapse-plan.json
```
The plan covers inventory, conversion, transfer, validation, security, downstream
consumers, cutover, and modernization. Each step names its Airlift actions, expected
outputs, and exit criteria. Planning commands never connect to a source or mutate an
Airlift deployment.
### Connect to a deployed Airlift App [#connect-to-a-deployed-airlift-app]
After an administrator admits your Databricks principal to one Airlift organization,
configure the authenticated remote endpoint:
```bash
export AIRLIFT_API_URL="https://"
export DATABRICKS_TOKEN="$(your-secret-provider read databricks-token)"
```
Create `engagement.json`:
```json
{
"name": "Synapse modernization",
"services": ["discovery", "migration_factory", "modernization"],
"owner": "data-platform"
}
```
Then create and inspect the governed engagement:
```bash
fa engagement create \
--file engagement.json \
--idempotency-key synapse-program-engagement \
--json
fa engagement list
```
The token authenticates the request but is not the organization selector. Airlift derives
the actor and organization at the App boundary and rejects ambiguous memberships.
### Add the SDK to an integration [#add-the-sdk-to-an-integration]
Install the typed source registry, action contracts, schemas, and runtime composition
surface in your Node.js project:
```bash
npm install @fabricorg/airlift
```
```ts
import {
AIRLIFT_ACTION_IDS,
createSourceMigrationPlan,
resolveSourceSystemProfile,
} from '@fabricorg/airlift';
const source = resolveSourceSystemProfile('synapse');
const migrationPlan = createSourceMigrationPlan(source.id);
console.log(source.workloadSurfaces);
console.log(migrationPlan.steps.map((step) => ({
id: step.id,
actions: step.airliftActions,
exitCriteria: step.exitCriteria,
})));
console.log(AIRLIFT_ACTION_IDS.assessmentRecord);
```
Your application invokes governed actions for assessment, inventory, waves, conversion
attempts, validation runs, readiness observations, acceptance, certificate minting,
cutover, rollback, and evidence export. Illegal transitions are rejected structurally;
authorization and policy fail closed with an attributable reason.
### Connect project adapters [#connect-project-adapters]
The SDK is the governance and evidence layer—not a source credential manager. A complete
project supplies narrow adapters for:
1. Lakebridge assessment and conversion jobs in the Databricks workspace.
2. Source-specific snapshot and incremental transfer with restart checkpoints.
3. Independent parity and performance validation.
4. Client-approved cutover checkpoint, apply-once, verification, and rollback effects.
Production cutover composition requires a client-certified `CutoverEffector`; selecting a
runtime mode or setting an environment variable cannot enable an unverified effector.
Use the [source developer workflow](/docs/sources/developer-workflow) to implement those
adapters, then follow the guide for your [source system](/docs/sources). Production
deployment of the Airlift application and workers is delivered as a Databricks project;
it is not installed from the private application repository.
## Next commands [#next-commands]
```bash
fa profiles
fa actions --json
fa doctor --profile production
fa docs sources/synapse
```
Continue with the [complete CLI reference](/docs/cli/command-reference) and the
[Synapse developer guide](/docs/sources/synapse), or select another supported source.
# Transfer and deploy a wave
# Transfer and deploy a wave [#transfer-and-deploy-a-wave]
This tutorial starts after assessment acceptance and conversion. You should already have an engagement, estate, accepted objects, verified connections, and immutable converted artifacts.
## The end-to-end path [#the-end-to-end-path]
Follow these developer guides in order:
1. [Build a resumable data transfer](/docs/migration/transfer) to create a transfer specification, plan scope, run, pause/resume, and reconcile.
2. [Deploy with Fabric Runway](/docs/integrations/runway) to register a deployment manifest, request the desired state, and observe the Runway result.
3. [Validation](/docs/migration/validation) to record independent data and business evidence.
4. [Cutover](/docs/migration/cutover) only after the required readiness tracks pass.
## Read the deployment page before you act [#read-the-deployment-page-before-you-act]
The **Requirements**, **Awaiting observation**, **Matched**, and **Blocked or uncertain**
counters describe a reconciliation workflow; they are not deployment-job counts. Four
zeros mean that no desired release has been declared for the selected engagement.
Choose **Create the first deployment requirement** and select the exact immutable
artifacts that form the release. For a normal first release, choose **Deploy a new
release**, the environment where validation will run, and **Release succeeded**. Airlift
computes the desired digest; it does not deploy the artifacts itself.
The next-action card then gives you the exact `fr deploy` command. Run it from the Runway
artifact directory, copy the returned deployment ID and staged artifact SHA-256, and choose
**Connect Runway deployment**. The connection is only a pointer. Choose **Check Runway result
now** to make admitted automation query Runway and independently verify the release.
After Runway executes the release, Airlift admits the verified release reference and
compares artifact digest, environment, operation, and terminal state. A match unlocks
validation. Missing or different evidence remains awaiting, blocked, or uncertain with a
plain-language next action. If verification is disabled, configure the Runway observer; do
not interpret the disabled button as a successful deployment.
See [Follow the guided migration journey](/docs/getting-started/guided-migration-journey)
for every phase screen and its evidence semantics.
## What to automate in CI [#what-to-automate-in-ci]
```bash
fa transfer status "$TRANSFER_ID" --json > transfer-status.json
fa deployment status "$DEPLOYMENT_REQUIREMENT_ID" --json > deployment-status.json
```
Fail your release when the transfer is not `completed`, the deployment reconciliation outcome is not `matched`, or a required readiness track is stale. Do not infer readiness from a job exit code.
## What to test regularly [#what-to-test-regularly]
* duplicate transfer starts attach to one Temporal execution;
* restart resumes from the latest increasing checkpoint;
* pause is respected without losing an in-flight checkpoint;
* excessive lag and row-count mismatch fail reconciliation;
* a person cannot record runner or Runway evidence;
* deployment digest drift blocks readiness;
* unknown actual state remains uncertain; and
* all governed action replays collapse under the same idempotency key.
# App walkthroughs
# Understand the App before running a migration [#understand-the-app-before-running-a-migration]
For the primary App-first walkthrough, follow the
[guided migration journey](/docs/getting-started/guided-migration-journey/): one synthetic Azure Synapse to
Databricks Lakehouse engagement, artifact by artifact, with the next action per phase.
This page is the annotated screenshot catalog that supports it — each image paired with
the `fa` commands that read the same governed projection.
Every screenshot in this documentation is an automated capture from a governed, public
synthetic Airlift workspace. The images contain no client data, credentials, workspace
hostnames, or internal deployment references. Select an image to open the full-size view.
Each annotated image answers two questions in text as well as visually:
* **What you are seeing** explains which governed projection or workbench produced the UI.
* **What to do next** names the developer action and evidence needed to continue.
The text is intentional. Screen readers, `llms.txt`, `llms-full.txt`, and developers who
cannot inspect the image receive the same workflow explanation.
## 1. Choose the migration [#1-choose-the-migration]
```bash
fa engagement list --json
fa engagement status "$ENGAGEMENT_ID" --json
```
## 2. Read the evidence gates [#2-read-the-evidence-gates]
```bash
fa engagement status "$ENGAGEMENT_ID"
fa residue list --engagement-id "$ENGAGEMENT_ID" --json
```
## 3. Inspect the exact migration output [#3-inspect-the-exact-migration-output]
```bash
fa artifact list --engagement-id "$ENGAGEMENT_ID" --json
fa artifact show "$ARTIFACT_ID" --json
```
## 4. Validate independently [#4-validate-independently]
```bash
fa validation run --file validation-request.json \
--idempotency-key "$ENGAGEMENT_ID-validation-v1"
fa validation list --engagement-id "$ENGAGEMENT_ID" --json
```
## Screenshot transcript [#screenshot-transcript]
The complete documented UI catalog covers these developer-visible states:
| Area | What the image proves | Developer action |
| ---------------- | ------------------------------------------------------------------- | --------------------------------------- |
| Engagements | Scope, owner, phase, blockers, and next action are visible together | Choose or create the governed migration |
| Source workspace | Navigation exposes only sources attached to the engagement | Run and admit the source assessment |
| Discovery | Accepted inventory and dependencies have immutable digests | Review and accept scope |
| Planning | Target design and waves are bound to accepted inventory | Freeze the selected plan |
| Conversion | Attempts, residues, and target artifacts retain lineage | Repair residue and register output |
| Transfer | Watermarks, checkpoints, rejects, and reconciliation are visible | Complete restartable movement |
| Deployment | Airlift requirement is separate from Runway observation | Deploy the same digest with `fr` |
| Validation | Experiments evidence is independent of conversion | Resolve failed discrepancies |
| Assurance | Readiness tracks and certificates are evidence-derived | Complete missing tracks and acceptance |
| Cutover | Frozen scope, rehearsal, approvals, health, and rollback must agree | Execute only after the gate passes |
For a source-specific sequence, open [Azure Synapse](/docs/sources/synapse/) or
[SQL Server](/docs/sources/sql-server/). Other source pages use clearly labeled
representative workflow images until that source has its own admitted workspace evidence.
# What leaders approve
# What leaders approve [#what-leaders-approve]
Most of a migration job runs without ceremony: assessments record, conversions start,
transfers checkpoint, and validation runs execute as ordinary work. A small number of
moments are different. They change what the organization is committed to, and Airlift
requires a named human being — never an agent, never a script — to make each one through
a governed Platform action.
This page covers the common leader decisions. For the canonical action and permission
catalog, full role model, principal
types, and tenant configuration, see
[roles and separation of duties](/docs/operations/roles-and-separation).
## Common human-only decisions [#common-human-only-decisions]
| Decision | Plain-language meaning | When it appears |
| -------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------- |
| **Accept scope** | "These discovered objects and dependencies are the real scope of the job." | After inventory and dependency review, before planning |
| **Freeze the plan** | "Lock this plan; waves are cut against it." | Before conversion at scale |
| **Approve a wave** | "This wave's scope is certified and may proceed toward go-live." | Per wave, once its evidence is green |
| **Request a waiver** | "Ask to carry a known residual risk, with an expiry." | Only when a blocked item cannot be cleared on the default path |
| **Approve a waiver** | "A second, distinct person accepts that residual risk." | After a waiver request; never by the requester |
| **Business accept** | "The business accepts this scope for go-live." | Before the certificate and cutover path |
| **Execute cutover** | "Open the production cutover for this wave." | Only when the cutover gate is green |
One adjacent moment is deliberately **not** a human decision: **certificate minting**.
A migration certificate is system-minted from independently admitted evidence once
policy thresholds are met. No person authors or edits a certificate payload — a leader
cannot mint one by hand, and neither can an agent. The leader's job is the business
acceptance and cutover decisions around the certificate, not the certificate itself.
## How a decision appears in the App [#how-a-decision-appears-in-the-app]
A decision card renders only when policy actually requires a human, and it answers the
same four questions as every other guidance surface: what this decision commits to, why
it is required now, who may make it, and what evidence it is based on. There is one
primary action per card. The App never shows a leader a form asking for a digest, an
idempotency key, or a raw action name — the button on the card invokes the same governed
Platform action the CLI invokes.
## Separation of duties is enforced, not suggested [#separation-of-duties-is-enforced-not-suggested]
Approval authority is checked again at execution time, and it is independent of
submission authority:
* **Distinct approvers.** Waivers and go-live approvals require a second, distinct
natural person. The requester cannot approve their own waiver; the person who
submits a cutover cannot be its only approver.
* **A resolver cannot review the same case.** The person who repaired a residue case
never reviews it; review belongs to a different authorized person.
* **No inherited approval.** An agent, converter, worker, or submitter cannot
manufacture or inherit a natural person's approval from a workflow signal, a form
field, or a CLI flag. Approval exists only as governed Platform state recorded by
that person.
* **Automation is bounded operationally.** The `automation` role may record or execute
explicitly cataloged assessment, inventory, planning, transfer, validation,
discrepancy, observation, and evidence work. It never carries approval, waiver,
certificate, policy, membership, or cutover-execution authority. The exact list is
machine-checked in [organization membership](/docs/reference/organization-membership/).
## Agents never decide [#agents-never-decide]
The agent boundary is absolute and worth stating plainly: a bounded Harness agent may
record assessments and objects, propose dependency graphs and plans, and start and
record conversions. It **cannot approve, certify, waive, execute cutover**, and it
cannot deploy. Agents also never accept assessment or dependency truth, select or freeze
plans, submit readiness evidence, accept on behalf of the business, mint or invalidate
certificates, roll back a cutover, or configure policy — and nothing in the App, the
CLI, or an AI assistant gives an agent an alternative mutation path around these rules.
When an assistant explains a decision card to you, it reads the same deterministic
guidance you do. It may summarize and link; it may never invent a different next action
or make the decision for you.
## Use the CLI with the same truth [#use-the-cli-with-the-same-truth]
Leaders who prefer the terminal exercise the exact same governed actions — there is no
lighter-weight approval path in the CLI:
```bash
fa plan freeze --idempotency-key plan-freeze- --json
fa cutover approve --idempotency-key cutover-approval- --json
fa cutover start \
--window 2030-01-15T02:00:00Z/2030-01-15T04:00:00Z \
--reason "Approved production change window" \
--json
```
Scope acceptance, wave approval, waivers, and business acceptance are exercised from the
decision cards in the App; every one of them records the same governed Platform action
with the same actor and evidence. The App and `fa` read the same projections, so an
approval made in one surface is immediately visible in the other, with the same actor,
evidence, and audit record.
## Where to go deeper [#where-to-go-deeper]
* [Roles and separation of duties](/docs/operations/roles-and-separation) — the five
tenant roles, worker and agent principal lanes, and independent approval checks.
* [Waivers and staleness](/docs/operations/waivers-and-staleness) — waiver expiry,
evidence freshness, and what a waiver can never cover.
* [Cutover control room](/docs/operations/cutover-control-room) — the go-live ceremony,
the cutover gate, and rollback.
* [When something fails](/docs/getting-started/when-something-fails) — the Fix list work that precedes most
waiver requests.
## Trust & certificates [#trust--certificates]
Two reference pages close the loop on what these approvals protect:
* [Parity and migration certificates](/docs/parity-certificates) — what a signed
certificate actually claims, the evidence depth it was minted from, and how to verify
it offline.
* [Security model](/docs/reference/security) — the identity, tenancy, evidence, and
external-effect boundaries every approval on this page is enforced through.
# When something fails (the Fix list)
# When something fails [#when-something-fails]
Something will fail. Unsupported source behavior, a failed independent check, a
deployment observation that does not match the requirement — every real migration
produces work that automation cannot finish alone. Airlift treats that work as a
first-class part of the job, not as an error state:
```text
Connect → Inventory → Scope → Build → Move → Prove → Go-live
↘ Fixes (residue / discrepancies) ↗
```
This page explains the Fix list: how a failure becomes a work item, how the App tells
you **why progress stopped** and **who acts next**, and the recovery action that clears
each item. Follow the [guided migration journey](/docs/getting-started/guided-migration-journey/) first if
you have not seen the eight stage workspaces this page builds on.
## Failure is a work item, not a dead end [#failure-is-a-work-item-not-a-dead-end]
Two kinds of failure land in the Fix list:
* **Residue.** Source behavior the converter could not carry across — an unsupported
transaction pattern, a temporary-table idiom, an orchestration construct with no
target equivalent. Each case is a named item with an owner, a diagnosis, and the
evidence needed to close it.
* **Discrepancies.** A failed required check from an independent validation run against
an exact artifact digest. Each discrepancy has a governed disposition path, not a
retry button.
Residue and discrepancies are **normal specialist work** on any real estate. Airlift
makes them explicit, assignable, measurable, and independently reviewable instead of
hiding them inside an automation percentage. A Fix list with open items is not a broken
migration; it is a migration telling the truth about the work that remains.
## Read one Fix list item [#read-one-fix-list-item]
Every item in the Fix list answers the same questions the stage guidance panel answers:
1. **Why progress stopped.** The blocked reason in plain language — what automation
could not do, or which independent check failed, and the concrete migration risk of
carrying on anyway. Ledger codes and digests stay in the collapsed evidence drawer.
2. **Who acts next.** The named owner in a plain role: you, an assigned teammate, the
system, or a lead.
3. **Recovery action.** One primary action — the exact repair, disposition, or
re-observation that can clear the item.
4. **Evidence that clears it.** The immutable artifact, passing run, review, or provider
observation Airlift must admit. Closing an external ticket never clears the ledger.
## Who acts next [#who-acts-next]
The Fix list names an owner for every item. The same role model applies across stages:
| Owner | Acts when | Typical recovery |
| -------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| **You** (the migrator running the job) | The item needs a repair only a person can design | Implement the fix, register the immutable artifact, request validation |
| **An assigned teammate** | The item needs a specialist — a stored-procedure redesign, a schema decision | Take the assignment; the item stays on their list until evidence is admitted |
| **The system** | A worker or admitted provider can produce the evidence | Starts the job once you trigger it; Airlift records progression |
| **A lead or second approver** | The item needs review, a waiver, or a business decision | Reviews independently; the resolver cannot review their own case |
Separation of duties is enforced, not suggested: a resolver cannot review the same case,
and a bounded Harness agent may triage a discrepancy but can never accept, resolve,
verify, waive, or certify it. Nothing in the Fix list gives an agent an alternative
mutation path.
## Recovery actions in the App [#recovery-actions-in-the-app]
Each failure type has one primary recovery action. The App never asks you to guess it:
| Failure | Recovery action | Evidence that clears it |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Conversion residue | Implement the repair, register the immutable artifact, run validation, request independent review | Repaired artifact digest plus a passing validation run and a distinct reviewer |
| Validation discrepancy | Triage, then accept with a bounded waiver, or resolve and verify | A disposition by the owning role plus a verifying re-run |
| Blocked or unmatched deployment requirement | Correct the requirement or re-observe the Runway release | A verified observation matching digest, environment, operation, and terminal state |
| Stale or expired evidence | Refresh the evidence from its source | A new admitted observation bound to the same scope |
Each action invokes the same governed Platform action the CLI invokes. A UI click never
advances the ledger by itself, and a reopened item keeps its full history.
## The honesty rule: a failed refresh never renders fresh [#the-honesty-rule-a-failed-refresh-never-renders-fresh]
When evidence is refreshed from a provider and the refresh **fails or returns only part
of the picture**, the App keeps showing the last known state as what it is: stale, with
the blocked reason attached. A failed or partial evidence refresh never renders as
"fresh". You always see one of three honest states:
* **Observed** — admitted evidence bound to this exact scope and digest.
* **Stale or blocked** — the last admitted state, plus the reason the refresh could not
complete and who acts next.
* **Not yet observed** — no evidence admitted; a zero means nothing was recorded, not
that something succeeded.
Configuration is never evidence. A connected-looking setup, a green provider console, or
a closed external ticket does not change what the App renders until Airlift admits the
matching observation.
## The first path never asks you to paste a digest [#the-first-path-never-asks-you-to-paste-a-digest]
Humans repair things; they do not transcribe hashes. The App derives every digest,
idempotency key, and scope binding from governed records. The primary recovery path is
always a button on the item itself — register this artifact, request this validation,
assign this owner. Copying a digest out of one system and pasting it into a form is the
advanced fallback, never the first path, and the evidence drawer offers copy-to-clipboard
when an integration genuinely needs the value.
## Use the CLI with the same truth [#use-the-cli-with-the-same-truth]
The App and `fa` read the same governed projections, so the Fix list is identical from
either surface:
```bash
fa residue list --engagement-id --json
fa residue show --json
fa residue resolve --file residue-resolution.json --idempotency-key residue-resolution- --json
fa discrepancy list --engagement-id --json
fa discrepancy triage --file discrepancy-triage.json --idempotency-key discrepancy-triage- --json
```
Use the returned owner, blocked reason, and next action; do not compute a second
readiness model in automation. See [remediation and residue](/docs/migration/residue)
for the full case lifecycle and [validation and readiness](/docs/migration/validation)
for the discrepancy disposition path.
## AI assistance boundary [#ai-assistance-boundary]
An assistant may explain a Fix list item in more conversational language, summarize the
blocked reason, or link the deeper developer article. It reads the same deterministic
guidance card you do and must never invent a different next action, approve a
disposition, waive a check, certify an object, or close an item without the owning
governed Platform action and its required evidence.
# Databricks application-kit commands
# Databricks application-kit commands [#databricks-application-kit-commands]
Use `fa application-kit` when a migration or Databricks-native modernization should
produce an operational application, not only tables, pipelines, and dashboards. Airlift
turns the opportunity into a typed delivery and evidence contract. It does not generate a
success claim from a checklist and it does not deploy the release.
An application kit has two layers:
* a shared foundation: Databricks Apps, Lakebase, Unity Catalog, portable runtime
bindings, an immutable release intent, an isolated preview, and a restore rehearsal;
* one or more domain modules: schemas, grants, synthetic fixtures, BDD scenarios, and
module-specific evidence requirements.
## List available modules [#list-available-modules]
```bash
fa application-kit module list
fa application-kit module list --json
```
| Module | Typical application behavior |
| ------------------------- | --------------------------------------------------------------------------------- |
| `customer_intelligence` | customer profiles, consent, governed features, and service workflows |
| `agent_operations` | agent memory, checkpoints, review queues, tool audit, and evaluation handoff |
| `risk_compliance` | alerts, cases, policy retrieval, separation of duties, and decision evidence |
| `intelligent_operations` | assets, work orders, operational scores, alerts, and maintenance workflows |
| `migration_control_plane` | migration scope, conversion, validation, release references, and cutover evidence |
## Initialize a manifest [#initialize-a-manifest]
```bash
fa application-kit init \
--name "Customer operations" \
--module customer_intelligence \
--cloud azure \
> application-kit.json
```
`--cloud` accepts `aws`, `azure`, or `gcp`. The generated manifest uses logical resource
names and opaque references. It never contains a workspace ID, database connection
string, token, or secret value. Every Databricks App runs with a dedicated service
principal whose grants are resolved per environment.
The generated foundation includes:
```json
{
"foundation": {
"databricksApp": true,
"lakebase": true,
"unityCatalog": true,
"declarativeAutomationBundle": true,
"dedicatedServicePrincipal": true,
"runtimeBindings": [
{
"name": "application-state",
"resourceType": "lakebase",
"permission": "write",
"required": true,
"source": "valueFrom"
},
{
"name": "governed-data",
"resourceType": "unity_catalog_schema",
"permission": "read",
"required": true,
"source": "valueFrom"
}
],
"syncedTables": [],
"preview": {
"isolatedBranch": true,
"restoreRequired": true,
"rehearsalRequired": true
}
}
}
```
Add Synced Table contracts when application serving needs governed lakehouse data in
Lakebase. Each row declares a direction, opaque source and target references, business
keys, and a freshness SLO. Airlift records the requirement; the admitted Databricks
adapter and release own materialization.
## Inspect and plan [#inspect-and-plan]
```bash
fa application-kit validate --file application-kit.json
fa application-kit inspect --file application-kit.json
fa application-kit plan \
--file application-kit.json \
--json > application-plan.json
```
The plan is deterministic. It expands each module into required BDD and assurance
scenarios and produces explicit handoffs:
* Fabric Platform: governed application mutations and audit;
* Fabric Harness: bounded agent execution and Databricks transport;
* Fabric Experiments: BDD, A/B, performance, quality, and agent/model evaluations;
* Fabric Runway: bundle validation, preview, deployment, promotion, and rollback;
* Fabric Radar: SLO and operational evidence;
* Fabric Tower: work references only; and
* Fabric Airlift: application-modernization scope, requirements, and evidence decision.
The plan contains `deploymentCli: "fr"`. There is deliberately no
`fa application-kit deploy` command.
## Qualify evidence [#qualify-evidence]
Without evidence, only the contract can be proven:
```bash
fa application-kit qualify \
--file application-plan.json \
--level contract_only
```
Hermetic qualification requires a passing bundle-validation reference, secret scan,
synthetic-fixture digest, BDD contract, and every required runtime binding:
```bash
fa application-kit qualify \
--file application-plan.json \
--evidence application-evidence.json \
--level hermetic_proven
```
Workspace qualification additionally requires:
* a successful Runway deployment for the qualified artifact digest;
* a passing Experiments execution for that same artifact digest;
* an isolated branch preview and tested restore reference; and
* a healthy Radar observation when the plan declares an operational SLO.
```bash
fa application-kit qualify \
--file application-plan.json \
--evidence application-evidence.json \
--level workspace_proven \
--json > application-qualification.json
```
Digest drift, failed scenarios, missing bindings, or missing required operational
evidence blocks. Airlift stores only provider references and digests; it does not copy
the provider's release, test, or monitoring state.
## Register the immutable plan [#register-the-immutable-plan]
```bash
fa application-kit register \
--file application-plan.json \
--engagement-id \
--estate-id \
--artifact-id \
--idempotency-key
```
Registration invokes governed `airlift.artifact_register` with media type
`application/vnd.fabric.airlift.application-kit+json`. Store the plan body in an
admitted immutable artifact location; Airlift records its reference, digest, producer,
and audit event.
Query only registered application-kit artifacts without filtering the general artifact
ledger yourself:
```bash
fa application-kit list --engagement-id
fa application-kit show --json
```
`module list` is the only module-discovery spelling. Generate the complete current command
surface with `fa commands --json`.
## Execute through the owning products [#execute-through-the-owning-products]
```bash
fr validate --dir generated
fr deploy --dir generated --environment preview
fx apply experiments/
fx report
```
Use Runway for deploy, promote, and rollback. Use Experiments for executable BDD, A/B,
performance, and quality evidence. Application-kit qualification is a read-only Airlift
decision over those foreign results.
See [Operational application modernization](/docs/migration/operational-applications) for
the complete developer lifecycle and proof model.
# Enterprise application-pack commands
# Enterprise application-pack commands [#enterprise-application-pack-commands]
`fa application-pack` is for migrations where the unit of scope is a business object or
data product, not a source table. The executable generation supports SAP Business Data
Cloud and Dynamics 365 / Dataverse.
The compiler carries these requirements with each entity:
* business and alternate keys;
* entity relationships and cardinality;
* snapshot, delta cursor, delete, and restart behavior;
* effective dating and history;
* currency, unit, and hierarchy meaning;
* field-level and row-level authorization;
* privacy classifications;
* business control totals; and
* downstream consumer transitions.
## Create a credential-free manifest [#create-a-credential-free-manifest]
```json
{
"schemaVersion": 1,
"source": {
"system": "dynamics_365_dataverse",
"productVersion": "current",
"snapshotAt": "2030-01-15T12:00:00.000Z",
"connectionRef": "databricks-connection://migration/dataverse"
},
"estate": { "name": "Customer service" },
"entities": [
{
"entityId": "account",
"name": "Account",
"kind": "business_object",
"keys": ["accountid"],
"relationships": [],
"changeBehavior": {
"mode": "managed_connector",
"cursorField": "versionnumber",
"deleteMode": "tombstone",
"effectiveDating": false
},
"semantics": {
"currencyFields": ["revenue"],
"unitFields": [],
"hierarchyFields": ["parentaccountid"],
"controlTotals": ["active_account_count"]
},
"authorization": {
"fieldSecurity": false,
"rowSecurity": false,
"privacyClassifications": ["customer"]
},
"consumers": ["customer-360"],
"sourceFragment": { "logicalName": "account" },
"provenance": {
"sourceRef": "dataverse://metadata/account",
"extractor": "your-dataverse-exporter",
"extractorVersion": "1.0.0",
"observedAt": "2030-01-15T12:00:00.000Z"
},
"unsupportedReasons": []
}
]
}
```
`connectionRef` is an opaque reference. The source adapter or managed connector owns
authentication and ingestion. Airlift does not broker OAuth or store source tokens.
## Inspect and plan [#inspect-and-plan]
```bash
fa application-pack inspect --file application-manifest.json
fa application-pack plan \
--file application-manifest.json \
--json > application-plan.json
```
The plan routes each entity to `deterministic`, `agent_repairable`, or `human_only` and
retains the exact source fragment and provenance digest. It defines the managed-source
checkpoint contract, Experiments validation requirements, Runway deployment handoff,
business-owner acceptance, consumer transitions, and remediation acceptance criteria.
Connector completion is never business-semantic proof. An entity remains blocked when
delete behavior, effective dating, currencies, units, hierarchies, authorization,
control totals, or consumers are unresolved.
## Register [#register]
```bash
fa application-pack register \
--file application-plan.json \
--engagement-id \
--estate-id \
--artifact-id \
--idempotency-key
```
This invokes governed `airlift.artifact_register`. The artifact body remains in your
admitted store; Airlift records the reference, digest, producer generation, and media
type.
SAP S/4HANA, ECC, BW/4HANA, HANA, Datasphere, other Dynamics profiles, Salesforce,
Workday, and ServiceNow remain cataloged profiles. They are not executable application
packs until their source contracts and recurring evidence ship.
# Capability commands
# Capability commands [#capability-commands]
`fa capability` operates the tenant-scoped capability registry. It does not install a
converter, deploy an application, or certify client objects.
## Query [#query]
```bash
fa capability list
fa capability list --source snowflake --variant snowflake_enterprise
fa capability list --source redshift --capability incremental_cdc --json
fa capability list --source synapse --construct merge_statement --artifact-kind source
fa capability show cap_ --json
fa capability matrix --source synapse --variant synapse_dedicated_sql
```
`matrix` overlays active evidence on the installed source profile and prints each
capability's delivery mode, proof level, provider, and provider version. Missing cells are
reported as `unproved`.
Construct filters select registry-v2 cells. Text matrix output lists these cells separately
and states that they do not aggregate into variant proof.
## Mutate through governed actions [#mutate-through-governed-actions]
All lifecycle mutations use a JSON parameter file and a stable idempotency-key prefix:
```bash
fa capability propose --file proposal.json --idempotency-key proposal-v1
fa capability evidence --file evidence.json --idempotency-key evidence-v1
fa capability promote --file promotion.json --idempotency-key promotion-v1
fa capability expire --file expiry.json --idempotency-key expiry-v1
fa capability revoke --file revocation.json --idempotency-key revocation-v1
fa capability reconcile --file reconciliation.json --idempotency-key reconcile-v1
```
The API derives organization and identity from the authenticated principal. Parameter
files cannot select another organization or supply an approving actor.
| Command | Required authority | Result |
| ----------- | --------------------------------- | -------------------------------------------------- |
| `propose` | human operator | proposed source/capability/provider generation |
| `evidence` | admitted system | immutable proof reference and digest |
| `promote` | different human reviewer | active claim at or below proven strength |
| `expire` | admitted system or human operator | inactive time-bounded claim |
| `revoke` | human reviewer | explicitly withdrawn claim |
| `reconcile` | admitted system | matched observation or fail-closed expiry on drift |
See [Capability registry](/docs/sources/capability-registry) for schemas and the proof
ladder.
## Diagnose a source or engagement [#diagnose-a-source-or-engagement]
```bash
fa source doctor snowflake --variant snowflake --level executable
fa source limitations redshift --variant redshift_serverless
fa source certify synapse --variant synapse_dedicated_sql --level certifiable
fa engagement preflight eng_
```
`doctor` and `certify` query active, unexpired tenant evidence and exit with status `1`
when blocked. Their text output includes a reason and next action for each blocker; `--json`
returns the stable diagnosis contract for CI. `source certify` checks whether provider
capabilities are strong enough for the requested outcome; it does not mint a migration certificate. Migration certificates are still
minted only from object-specific admitted validation evidence through `fa certificate
mint`.
An engagement preflight derives its required level from the selected services:
| Engagement service | Capability target |
| -------------------------- | ------------------------------------------- |
| `discovery` | `assessable` |
| `factory_pilot` | `executable` |
| `migration_factory` | `certifiable` |
| `managed_migration_office` | `certifiable` |
| `cutover_assurance` | `cutover_certified` |
| `modernization` | adds an evidenced modernization requirement |
# Catalog commands
# Catalog commands [#catalog-commands]
The CLI exposes two different catalogs:
* `fa sources` describes source-system migration routes and common workload areas;
* `fa profiles` describes object-type evidence requirements used to mint a
certificate.
Do not substitute one for the other. A source profile helps construct the project; a
validation profile is enforced by certificate policy.
## Actions [#actions]
```bash
fa actions
fa actions --json
```
The command reads the installed action definitions and prints action ID, version,
required permissions, policies, and emitted events. Use it to detect documentation or
deployment drift; it does not invoke an action.
## Validation profiles [#validation-profiles]
```bash
fa profiles
fa profiles --json
```
The command prints built-in profile ID/version, object type, canonical SHA-256 digest,
and required readiness tracks. The digest is computed from canonical JSON using the same
function as certificate minting.
Catalog JSON is deterministic and suitable for release manifests or policy review.
See [source planning commands](/docs/cli/sources) for the source catalog.
# Certificate commands
# Certificate commands [#certificate-commands]
## Inspect [#inspect]
```bash
fa certificate inspect migration-certificate.json
```
Inspection validates the envelope schema and reports certificate, organization, estate,
object, wave, profile, issuer, key ID, and computed unsigned-envelope digest. It shows
whether the digest matches the signed digest but does not claim signature validity.
## Verify [#verify]
Create a JSON file that maps key IDs to Ed25519 public PEM values:
```json
{
"airlift-migration-cert-v1": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
}
```
Then verify:
```bash
fa certificate verify migration-certificate.json --keys public-keys.json
```
Without `--keys`, verification reads `AIRLIFT_EVIDENCE_VERIFY_KEYS_JSON`. The command
recomputes canonical content, requires the signed digest to match, selects the declared
key ID, and verifies the Ed25519 signature. Malformed input, tampering, unknown keys, and
invalid signatures return non-zero.
# Command reference
# Command reference [#command-reference]
The examples assume `fa` resolves to the installed CLI. Run `fa help` to print the
same command inventory from the installed generation.
## Global automation options [#global-automation-options]
`--format text|table|json|yaml|jsonl` may appear on any command that produces structured
data. `--json` remains an alias for `--format json`. JSONL emits one item per line when the
result is an array. Human-readable text or tables remain the default.
Use `--file -` to read a JSON manifest or mutation request from stdin. Use
`--correlation-id ` to propagate a trace identifier into a governed mutation and
`--timeout ` to bound remote calls. Neither option can override identity,
organization, policy, or approvals.
Run `fa help ` for focused help, `fa commands --json` for the machine-readable
command contract, and `fa completion bash|zsh|fish` for shell completion. The
[generated command index](/docs/cli/generated-command-index) is built from that same
manifest.
Remote commands require `AIRLIFT_API_URL` and `DATABRICKS_TOKEN`. Mutation commands also
require a stable, non-secret `--idempotency-key` prefix. See [authenticated
automation](/docs/cli/remote-automation) for identity and retry semantics.
Modernization and measurement commands are documented together in
[Modernization and value commands](/docs/cli/modernization-and-value).
Source capability commands and their evidence-authority split are documented in
[Capability commands](/docs/cli/capabilities).
The executable Synapse manifest compiler and governed registration flow are documented
in [Synapse golden-path commands](/docs/cli/synapse).
Executable SQL Server, Snowflake, Redshift, Oracle, Teradata, and Hadoop manifest
compilation, governed registration, and certification are documented in
[migration-pack commands](/docs/cli/migration-packs).
Native ADF import and file generation, plus normalized-manifest routing for SSIS,
PowerCenter, SAS, DataStage, Talend, ODI, dbt, and Airflow, are documented in
[pipeline import and generation](/docs/cli/migration-ir).
Business-semantic SAP BDC and Dataverse compilation is documented in [enterprise
application-pack commands](/docs/cli/application-packs). Existing Databricks estate
modernization is documented in [Databricks-native commands](/docs/cli/databricks-native).
Portable Databricks Apps and Lakebase foundation/module planning, same-digest evidence
qualification, and the `fa` versus `fr` boundary are documented in [application-kit
commands](/docs/cli/application-kits).
Qualification, reusable delivery kits, connector policies, external value claims,
activation evidence, and public-content checks are documented in [delivery and claim
commands](/docs/cli/delivery).
Deployment-access inspection and admitted preflight recording (`fa access`) are documented
in [Databricks access and integrations](/docs/integrations/databricks-access).
Evaluation-candidate freeze, rehearsal, and window inspection (`fa evaluator`) are
documented in [Evaluator readiness](/docs/integrations/evaluator-readiness).
Unity Catalog evidence recording (`fa uc-evidence`) is documented in
[Unity Catalog evidence](/docs/integrations/uc-evidence).
Organization provisioning and retirement are platform-installer-only actions in the
`airlift-platform` tenant; no tenant CLI command exists. See the
[local lifecycle cookbook](/docs/getting-started/local-lifecycle-cookbook).
Parity certification is recorded by the admitted worker through `airlift.parity_certify`;
no caller-authored parity command exists. See [Parity certificates](/docs/parity-certificates).
Waiver requests and approvals are App decision-card surfaces; the CLI deliberately exposes
no waiver subcommand. See [Waivers and staleness](/docs/operations/waivers-and-staleness).
## `fa organization membership set` [#fa-organization-membership-set]
```bash
fa organization membership set \
--file membership-changes.json \
--idempotency-key membership-grant-2026-08
```
Apply governed organization membership grants and revocations through
`airlift.organization_membership_set`. The file holds one tenant-free `changes` array;
the authenticated API derives the organization from the caller's membership. Requires
the `admin` role (`airlift:membership:manage`) and refuses to leave an organization
without an active admin. See
[Organization membership](/docs/reference/organization-membership).
## `fa engagement list|show` [#fa-engagement-listshow]
```bash
fa engagement list
fa engagement show eng_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
```
Query engagements in the organization derived from the authenticated Databricks
principal. There is no organization selection flag.
## `fa engagement preflight` [#fa-engagement-preflight]
```bash
fa engagement preflight eng_01ARZ3NDEKTSV4RRFFQ69G5FAV
fa engagement preflight eng_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
```
Evaluate every scoped source variant against the evidence level implied by the engagement
services. Exit status `1` means the engagement is blocked. The result includes missing or
stale claims, next actions, governed human lanes, and the requested support level.
## `fa engagement create|update` [#fa-engagement-createupdate]
```bash
fa engagement create \
--file engagement.json \
--idempotency-key project-42-create
fa engagement update \
--file engagement-update.json \
--idempotency-key project-42-scope-v2
```
The create file accepts `name`, one or more `services`, `owner`, and optional target
dates, estate IDs, and structured external references. The update file includes
`engagementId` plus at least one changed field. The API validates referenced estates and
rejects edits after scope is frozen.
## `fa engagement activate|freeze` [#fa-engagement-activatefreeze]
```bash
fa engagement activate eng_01ARZ3NDEKTSV4RRFFQ69G5FAV \
--idempotency-key project-42-activate
fa engagement freeze eng_01ARZ3NDEKTSV4RRFFQ69G5FAV \
--idempotency-key project-42-freeze
```
Activation moves a draft scope to active. Freeze computes and records the canonical
scope digest. A frozen engagement rejects later edits.
## `fa connection list|register` [#fa-connection-listregister]
```bash
fa connection list --engagement-id eng_01ARZ3NDEKTSV4RRFFQ69G5FAV
fa connection register \
--file source-binding.json \
--idempotency-key project-42-synapse-binding
```
The registration file contains the engagement ID, display name, direction, capabilities,
and an opaque `credentialRef` such as
`databricks-connection://migration/synapse-metadata`. It never contains a password,
token, private key, or connection string.
## `fa connection verify|retire` [#fa-connection-verifyretire]
```bash
fa connection verify bnd_01ARZ3NDEKTSV4RRFFQ69G5FAV \
--digest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
--idempotency-key project-42-synapse-verify
fa connection retire bnd_01ARZ3NDEKTSV4RRFFQ69G5FAV \
--reason "Credential rotated" \
--idempotency-key project-42-synapse-retire
```
Verification records an admitted SHA-256 evidence digest and authenticated verifier.
Retirement is terminal for that binding; register a new binding for a rotated reference.
## `fa estate list|show|register` [#fa-estate-listshowregister]
```bash
fa estate list --json
fa estate show est_01ARZ3NDEKTSV4RRFFQ69G5FAV
fa estate register --file estate.json --idempotency-key estate-synapse-1
```
Register a source estate or query the tenant-scoped estate projection. Registration
accepts a source profile ID, owner, environment, priority, and optional opaque connection
reference.
## `fa assessment list|status|start|record|accept|export` [#fa-assessment-liststatusstartrecordacceptexport]
```bash
fa assessment list --estate-id est_01ARZ3NDEKTSV4RRFFQ69G5FAV
fa assessment status asm_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
fa assessment start --file assessment-start.json --idempotency-key assess-start-1
fa assessment record --file assessment-record.json --idempotency-key assess-record-1
fa assessment accept --file assessment-accept.json --idempotency-key assess-accept-1
fa assessment export --file assessment-export.json --idempotency-key assess-export-1
```
Start and record compose the configured worker/source-adapter lifecycle. Acceptance is a
human decision that binds normalized inventory and accepted dependency digests. Export
returns a content-digested pack containing the accepted assessment, inventory,
dependencies, and applicable plan scenarios.
## `fa inventory list|register` [#fa-inventory-listregister]
```bash
fa inventory list --estate-id est_01ARZ3NDEKTSV4RRFFQ69G5FAV \
--assessment-id asm_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
fa inventory register --file object.json --idempotency-key object-sales-1
```
Inventory registration normalizes one source object into the governed migration ledger.
Use repeated idempotent calls for objects emitted by an assessment adapter. Filter by
`--assessment-id` whenever an estate has multiple snapshots so results cannot be mixed.
## `fa inventory graph-*` [#fa-inventory-graph-]
```bash
fa inventory graph list --assessment-id asm_01ARZ3NDEKTSV4RRFFQ69G5FAV
fa inventory graph show dpg_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
fa inventory graph start --file graph-start.json --idempotency-key graph-start-1
fa inventory graph record --file graph-batch.json --idempotency-key graph-batch-1
fa inventory graph accept --file graph-accept.json --idempotency-key graph-accept-1
```
One graph declares its expected edge count. Each record call accepts at most 500 edges.
A human operator accepts the complete graph and Airlift computes its digest and cycle
count.
## `fa plan list|show|compare|generate|select|freeze` [#fa-plan-listshowcomparegenerateselectfreeze]
```bash
fa plan list --engagement-id eng_01ARZ3NDEKTSV4RRFFQ69G5FAV
fa plan compare --engagement-id eng_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
fa plan show pln_01ARZ3NDEKTSV4RRFFQ69G5FAV
fa plan generate --file plan.json --idempotency-key plan-parity-1
fa plan select pln_01ARZ3NDEKTSV4RRFFQ69G5FAV --idempotency-key plan-select-1
fa plan freeze pln_01ARZ3NDEKTSV4RRFFQ69G5FAV --idempotency-key plan-freeze-1
```
Generation maps accepted objects to Databricks target patterns, topologically groups
candidate waves, detects cycles, and calculates transparent effort/value estimates.
Freeze requires a selected scenario, frozen engagement scope, and no blocking issues.
## `fa wave list` [#fa-wave-list]
```bash
fa wave list
fa wave list --estate-id est_01ARZ3NDEKTSV4RRFFQ69G5FAV --json
```
Query materialized execution waves. Plan candidates remain part of the frozen plan until
the operator materializes execution waves through governed wave actions.
## `fa cutover` and `fa hypercare` [#fa-cutover-and-fa-hypercare]
```bash
fa cutover list
fa cutover status wav_01J00000000000000000000000 --json
fa cutover freeze --file wave-freeze.json --idempotency-key wave-freeze-v1
fa cutover runbook --file runbook.json --idempotency-key runbook-v1
fa cutover rehearse --file rehearsal.json --idempotency-key rehearsal-v1
fa cutover observe --file observation.json --idempotency-key parallel-run-v1
fa cutover certify-effector --file effector.json --idempotency-key effector-v1
fa cutover approve wav_01J00000000000000000000000 --idempotency-key approval-1
fa cutover start wav_01J00000000000000000000000 --window 2030-09-14T02:00Z --reason "Approved production window"
fa cutover workflow-status airlift-v2-wav_
fa hypercare start --file start.json --idempotency-key hypercare-v1
fa hypercare observe --file observation.json --idempotency-key hypercare-observe-1
fa hypercare complete --file decision.json --idempotency-key hypercare-decision-1
fa hypercare decommission --file disposition.json --idempotency-key disposition-1
```
These commands operate Airlift-owned migration cutover policy and its durable workflow.
They do not deploy or promote release artifacts and do not create monitoring state. See
the complete [cutover command reference](/docs/cli/cutover).
## `fa artifact list|show|register` [#fa-artifact-listshowregister]
```bash
fa artifact list --engagement-id eng_01J00000000000000000000000
fa artifact list --object-id obj_01J00000000000000000000000 --json
fa artifact show art_01J00000000000000000000000
fa artifact register --file artifact.json --idempotency-key artifact-v1
```
Query or register immutable artifact references. Registration requires the artifact
store reference, SHA-256 digest, media type, producer generation, and engagement/object
lineage. Artifact bodies and credentials are never uploaded to Airlift.
## `fa conversion list|show|diff` [#fa-conversion-listshowdiff]
```bash
fa conversion list --object-id obj_01J00000000000000000000000
fa conversion show cnv_01J00000000000000000000000 --json
fa conversion diff obj_01J00000000000000000000000 --json
```
Read the conversion-attempt ledger. `diff` returns the same object's ordered attempt
records for comparing method, tool/model/prompt generation, content digests, diagnostic,
and validation reference. Fetch code bodies from the artifact store.
## `fa conversion batch-*` [#fa-conversion-batch-]
```bash
fa conversion batch list --engagement-id eng_01J00000000000000000000000
fa conversion batch show cbh_01J00000000000000000000000 --json
fa conversion batch create --file batch.json --idempotency-key batch-1
fa conversion batch start --file batch-start.json --idempotency-key batch-1-start
fa conversion batch complete --file batch-start.json --idempotency-key batch-1-complete
```
Create an accepted-scope batch, admit worker attempts, and reconcile terminal results.
Completion derives counts and blocks on missing attempts, missing artifact lineage, or
failed objects without residue.
## `fa conversion attempt-*|retry` [#fa-conversion-attempt-retry]
```bash
fa conversion attempt start --file attempt-start.json --idempotency-key object-1-start
fa conversion attempt record --file attempt-result.json --idempotency-key object-1-result
fa conversion retry --file retry-start.json --idempotency-key object-1-retry-2
```
`retry` is an explicit alias for starting another conversion attempt. Use a new stable
key and an eligible `rework` object. Airlift preserves prior attempts.
## `fa residue list|show|create|estimate|assign|resolve|review|cancel` [#fa-residue-listshowcreateestimateassignresolvereviewcancel]
```bash
fa residue list --engagement-id eng_01J00000000000000000000000
fa residue show res_01J00000000000000000000000 --json
fa residue create --file residue.json --idempotency-key residue-1
fa residue estimate --file estimate.json --idempotency-key residue-1-estimate
fa residue assign --file assignment.json --idempotency-key residue-1-assign
fa residue resolve --file resolution.json --idempotency-key residue-1-resolve
fa residue review --file review.json --idempotency-key residue-1-review
fa residue cancel --file cancellation.json --idempotency-key residue-1-cancel
```
Operate the remediation lifecycle. Assignment works directly from an `open` case and,
like review, requires natural-person authority. `estimate` is optional delivery-planning
metadata; it is not required before assignment. Resolution requires an artifact from the
same object plus a validation evidence reference. The resolver cannot review the same
case. In the App, cancellation is disclosed separately and requires a reason plus explicit
confirmation.
## `fa transfer list|status|plan|run|checkpoint|pause|resume|reconcile|reconcile-record|fail|cancel` [#fa-transfer-liststatusplanruncheckpointpauseresumereconcilereconcile-recordfailcancel]
```bash
fa transfer list --engagement-id eng_01J00000000000000000000000
fa transfer status xfr_01J00000000000000000000000 --json
fa transfer plan --file transfer-plan.json --idempotency-key transfer-plan-v1
fa transfer run xfr_01J00000000000000000000000 --idempotency-key transfer-run-v1
fa transfer pause xfr_01J00000000000000000000000 --reason "Source maintenance" --idempotency-key transfer-pause-1
fa transfer resume xfr_01J00000000000000000000000 --idempotency-key transfer-resume-1
fa transfer reconcile xfr_01J00000000000000000000000 --idempotency-key transfer-reconcile-1
```
`plan`, `run`, `pause`, `resume`, `reconcile`, and `cancel` are operator controls. `checkpoint`, `reconcile-record`, and `fail` submit runner evidence and require admitted automation; a natural-person invocation is rejected. See [data transfer and reconciliation](/docs/migration/transfer) for input schemas and the Temporal execution boundary.
## `fa deployment list|status|require` [#fa-deployment-liststatusrequire]
```bash
fa deployment list --engagement-id eng_01J00000000000000000000000
fa deployment status dpr_01J00000000000000000000000 --json
fa deployment require --file deployment-requirement.json --idempotency-key wave-3-release
```
`require` declares the Runway operation, environment, immutable artifacts, and terminal
state needed by the migration. Airlift computes the desired digest. The admitted Runway
integration records observations and reconciliation; caller-authored `observe`, `sync`,
and `reconcile` commands do not exist. Use `fr` for deployment execution. See
[Deploy with Fabric Runway](/docs/integrations/runway).
## `fa migration-ir qualify` [#fa-migration-ir-qualify]
```bash
fa migration-ir qualify \
--file generated/artifact-set.json \
--root generated \
--bindings release-bindings.json \
--resolutions residue-resolutions.json \
--proof hermetic_proven \
--output qualification.json
```
The command verifies generated bytes, invokes Databricks bundle validation, checks runtime
binding and residue evidence, and optionally evaluates matching Runway and Experiments
workspace evidence. It is read-only and never writes a provider verdict to the Airlift
ledger. See [release qualification](/docs/migration/release-qualification).
## `fa validation list|status|runs|readiness|run|cancel` [#fa-validation-liststatusrunsreadinessruncancel]
```bash
fa validation run --file validation-request.json --idempotency-key wave-2-validation-v3
fa validation list --engagement-id "$ENGAGEMENT_ID"
fa validation status "$VALIDATION_EXECUTION_ID" --json
fa validation runs --object-id "$OBJECT_ID" --json
fa validation readiness --object-id "$OBJECT_ID" --json
fa validation cancel "$VALIDATION_EXECUTION_ID" --reason "Superseded suite" --idempotency-key validation-cancel-v1
```
`run` creates Airlift validation scope; the durable worker delegates the generated suite
to Experiments. `runs` shows admitted provider runs. `readiness` shows derived track
observations. Cancellation is governed and leaves external provider state subject to
reconciliation.
## `fa discrepancy list|show|create|triage|accept|resolve|verify` [#fa-discrepancy-listshowcreatetriageacceptresolveverify]
```bash
fa discrepancy list --object-id "$OBJECT_ID"
fa discrepancy show "$DISCREPANCY_ID" --json
fa discrepancy create --file discrepancy.json --idempotency-key discrepancy-42-create
fa discrepancy triage --file triage.json --idempotency-key discrepancy-42-triage
fa discrepancy accept --file triage-acceptance.json --idempotency-key discrepancy-42-accept
fa discrepancy resolve --file resolution.json --idempotency-key discrepancy-42-resolution
fa discrepancy verify --file verification.json --idempotency-key discrepancy-42-verify
```
Human operators can triage, accept eligible low/medium differences, and submit
remediation. `create` and `verify` are admitted automation operations. Verification takes
a `validationRunId` and succeeds only for a passing Experiments run belonging to the
same object and matching the resolution evidence identity.
## `fa certificate list|show|mint|invalidate` [#fa-certificate-listshowmintinvalidate]
```bash
fa certificate list --object-id "$OBJECT_ID"
fa certificate show "$CERTIFICATE_ID" --json
fa certificate mint --file certificate-mint.json --idempotency-key object-42-mint-v1
fa certificate invalidate --file certificate-invalidate.json --idempotency-key object-42-stale-v1
```
List and show are authenticated reads. Mint and invalidate call the system-owned
certificate actions and normally require the admitted worker principal. They do not
replace offline `certificate inspect` and `certificate verify`.
## `fa evidence export` [#fa-evidence-export]
```bash
fa evidence export --file evidence-export.json --idempotency-key wave-2-evidence-v1
```
Creates a content-digested governed evidence pack for an object or wave. The export
contains references and policy evidence, not artifact bodies or credentials.
## `fa help` [#fa-help]
```bash
fa help
fa --help
fa -h
```
Print every command and the CLI mutation boundary. Exit `0`.
## `fa version` [#fa-version]
```bash
fa version
fa --version
fa -V
```
Print the installed CLI version. Exit `0`.
## `fa docs [topic]` [#fa-docs-topic]
```bash
fa docs
fa docs sources/sql-server
fa docs migration/validation --json
```
Print `https://airlift.fabric.pro/docs`, optionally with the URL-encoded topic appended.
This command prints a URL; it does not launch a browser.
## `fa sources` [#fa-sources]
```bash
fa sources
fa sources --json
```
List the installed source profiles with archetype, evidence-derived level, implementation
routing (non-evidence),
variants, applicable
Lakebridge capabilities, program track, and migration-area metadata.
## `fa sources export --plans` [#fa-sources-export---plans]
```bash
fa sources export --plans --format jsonl
fa sources export --plans --json
```
Export one canonical plan record per source × variant in the installed registry. Each
record wraps a compiled plan with `sourceSystem`, `sourceVariant`, `implementationRoutingLevel`,
`externalGates`, a `docsUrl`, and a `contentDigest` computed over the plan alone, so
unchanged plans keep the same digest across CLI releases for incremental re-indexing. The
default text output emits the same JSONL form as `--format jsonl`; `--json` emits a single
canonical array instead. `--plans` is required; omitting it returns exit `2`.
## `fa source inspect ` [#fa-source-inspect-source]
```bash
fa source inspect sql_server
fa source inspect mssql --json
fa source inspect teradata
```
Resolve a profile ID or alias and print its variants, archetype, implementation routing
(non-evidence), adapter
contract, workload surfaces, applicable Lakebridge routing, transfer strategy,
validation checks, residue, and modernization targets. Unknown sources fail with exit
`1`. Run `fa sources --json` for canonical IDs and variants.
## `fa source plan ` [#fa-source-plan-source]
```bash
fa source plan synapse
fa source plan dynamics_365 --variant dynamics_365_finance_operations --json
```
Generate the deterministic archetype-aware plan. Each phase contains specialist
adapter commands or boundaries, exact governed Airlift action IDs, outputs, and exit
criteria. `--variant` selects a registered specialization. There is no legacy schema
selector. Generating a plan does not create governed state.
## `fa source certification-check ` [#fa-source-certification-check-file]
```bash
fa source certification-check source-certification.json
fa source certification-check source-certification.json --json
```
Validate an immutable live-run manifest and report evidence missing for its requested
support level. Eligible returns `0`; missing required evidence returns `1`. The command
does not promote the registry or mint a certificate.
## `fa doctor` [#fa-doctor]
```bash
fa doctor
fa doctor --profile local
fa doctor --profile production --json
```
Check Node, persistence, authorization, evidence registry, signing and verification,
Databricks forwarded identity, Lakebridge job binding, Temporal mode, and the cutover
effector boundary. Production profile failures return exit `1`; warnings do not.
`doctor` validates configuration shape. It cannot prove connectivity, validate client
data, or certify a cutover effector.
## `fa actions` [#fa-actions]
```bash
fa actions
fa actions --json
```
Print every governed action with its version, required permissions, policies, and emitted
events. The command reads the installed contract; it never invokes an action.
## `fa profiles` [#fa-profiles]
```bash
fa profiles
fa profiles --json
```
Print built-in object validation profiles, their canonical SHA-256 digests, and required
readiness tracks. These are certificate profiles, not source-system profiles.
## `fa certificate inspect ` [#fa-certificate-inspect-file]
```bash
fa certificate inspect migration-certificate.json
fa certificate inspect migration-certificate.json --json
```
Parse the certificate schema and show its identity, profile, issuer, key, signed digest,
computed digest, and digest match. Inspection does not verify the signature.
## `fa certificate verify ` [#fa-certificate-verify-file]
```bash
fa certificate verify migration-certificate.json --keys public-keys.json
AIRLIFT_EVIDENCE_PUBLIC_KEYS_JSON='{"airlift-prod":"...PEM..."}' \
fa certificate verify migration-certificate.json
```
Verify both envelope digest and Ed25519 signature. `--keys` accepts a JSON object mapping
key IDs to public PEM strings. Without it, the command reads Airlift's verifier
configuration from the environment. Valid returns `0`; invalid returns `1`.
## Usage failures [#usage-failures]
Unknown commands, missing required arguments, or unsupported subcommands print a concise
message plus `Run 'fa help' for usage.` and return exit `2`. Remote authentication
returns `3`, not found returns `4`, blocked/conflict returns `5`, and dependency outage
returns `6`. Malformed local files, unknown source profiles, and failed diagnostics
return `1`.
# Cutover and hypercare commands
# Cutover and hypercare commands [#cutover-and-hypercare-commands]
`fa cutover` manages migration cutover truth and starts Airlift's durable workflow.
`fa deployment` does not deploy releases; use the Runway CLI for release execution and
then admit the resulting immutable reference through an Airlift deployment requirement.
Set the authenticated API boundary once:
```bash
export AIRLIFT_API_URL=https://
export DATABRICKS_TOKEN=
```
The API derives organization and identity from authentication. No cutover command
accepts an actor or organization override.
## Inspect control state [#inspect-control-state]
```bash
fa cutover list --estate-id est_01J00000000000000000000000
fa cutover status wav_01J00000000000000000000000 --json
```
The response includes the frozen digest, runbook, rehearsal, operational evidence,
effector certification, incidents, hypercare, source disposition, and any stale reason.
## Freeze exact scope [#freeze-exact-scope]
```json title="wave-freeze.json"
{
"engagementId": "eng_01J00000000000000000000000",
"waveId": "wav_01J00000000000000000000000",
"transfers": {
"applicability": "required",
"ids": ["xfr_01J00000000000000000000000"]
},
"deployments": {
"applicability": "required",
"ids": ["dpr_01J00000000000000000000000"]
},
"releaseEvidenceRefs": [
{
"system": "runway",
"type": "promoted_release",
"id": "release-v42",
"digest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
],
"reason": "Freeze the certified production scope for the approved window."
}
```
```bash
fa cutover freeze --file wave-freeze.json --idempotency-key wave-freeze-v1
```
For a genuinely inapplicable transfer or deployment track, use
`{"applicability":"not_applicable","reason":"..."}`. Airlift requires a reason; an
empty list cannot silently bypass the track.
## Configure and rehearse the runbook [#configure-and-rehearse-the-runbook]
```json title="runbook.json"
{
"waveId": "wav_01J00000000000000000000000",
"version": "2026.09.14-1",
"reason": "Pin the approved timed procedure and restore path.",
"steps": [
{
"stepId": "checkpoint",
"name": "Create routing checkpoint",
"ownerRole": "cutover_operator",
"offsetMinutes": -15,
"expectedDurationMinutes": 5,
"actionKind": "governed_action",
"actionRef": "effector:checkpoint"
},
{
"stepId": "switch",
"name": "Apply consumer routing once",
"ownerRole": "cutover_operator",
"offsetMinutes": 0,
"expectedDurationMinutes": 10,
"actionKind": "governed_action",
"actionRef": "effector:apply",
"rollbackStepId": "restore"
},
{
"stepId": "restore",
"name": "Restore the checkpoint",
"ownerRole": "cutover_operator",
"offsetMinutes": 15,
"expectedDurationMinutes": 10,
"actionKind": "governed_action",
"actionRef": "effector:compensate"
}
]
}
```
```bash
fa cutover runbook --file runbook.json --idempotency-key runbook-v1
fa cutover rehearse --file rehearsal-result.json --idempotency-key rehearsal-v1
```
`rehearse` requires an admitted automation principal and immutable evidence reference.
A human cannot author a passing rehearsal verdict.
## Admit operational evidence [#admit-operational-evidence]
```json title="parallel-run.json"
{
"waveId": "wav_01J00000000000000000000000",
"phase": "parallel_run",
"providerRef": {
"system": "radar",
"type": "observation_window",
"id": "window-42",
"digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"evidenceDigest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"sloProfileId": "airlift.cutover.production_slo.v1",
"observedFrom": "2030-09-13T00:00:00.000Z",
"observedUntil": "2030-09-14T00:00:00.000Z",
"verdict": "passed"
}
```
```bash
fa cutover observe --file parallel-run.json --idempotency-key parallel-run-v1
```
Only Radar or an admitted external monitor may provide operational evidence. Airlift
retains the foreign reference, digest, window, SLO profile, and verdict; the monitor
definition and raw observations remain with the provider.
## Certify the effector and approve the wave [#certify-the-effector-and-approve-the-wave]
```bash
fa cutover certify-effector \
--file effector-certification.json \
--idempotency-key effector-cert-v1
fa cutover approve wav_01J00000000000000000000000 \
--note "Change authority approval" \
--idempotency-key wave-approval-1
```
Certification requires a passing rehearsal and a natural-person certifier. The file
identifies the implementation and proves `checkpoint`, `apply_once`, `verify`, and
`compensate`. Approval is a separate governed action and follows separation of duties.
## Start the durable workflow [#start-the-durable-workflow]
```bash
fa cutover start wav_01J00000000000000000000000 \
--window 2030-09-14T02:00Z \
--reason "Approved customer change window"
fa cutover workflow-status airlift-v2-wav_
fa cutover wake airlift-v2-wav_
```
`start` calls the dedicated workflow endpoint. It does not invoke
`airlift.cutover_execute` from the CLI process. The worker re-reads governed readiness,
waits durably for approvals, performs checkpoint/apply-once/verify, and records the
outcome through Platform actions.
## Incidents and hypercare [#incidents-and-hypercare]
```bash
fa cutover incident-open --file incident.json --idempotency-key incident-1
fa cutover incident-resolve --file incident-resolution.json --idempotency-key incident-1-resolve
fa hypercare start --file hypercare-start.json --idempotency-key hypercare-v1
fa hypercare observe --file hypercare-observation.json --idempotency-key hypercare-observe-1
fa hypercare complete --file hypercare-decision.json --idempotency-key hypercare-accept-1
fa hypercare decommission --file source-disposition.json --idempotency-key source-disposition-1
```
An open incident blocks readiness or hypercare acceptance. Resolution requires immutable
evidence. Hypercare completion and source disposition are human decisions with
separation-of-duties checks; agents and monitors cannot accept or decommission.
## Exit behavior [#exit-behavior]
| Code | Meaning |
| ---- | ----------------------------------------------------- |
| `0` | request completed or attached to an existing workflow |
| `2` | invalid command arguments |
| `4` | authentication failed |
| `5` | authorization failed |
| `6` | dependency unavailable or invalid API response |
| `7` | governed readiness conflict |
# Databricks-native commands
# Databricks-native commands [#databricks-native-commands]
Use `fa databricks-native` when the customer already runs Databricks. This is not a
pretend migration from an external system. The source estate is `databricks`, and the
program starts from an immutable current-state baseline.
## Register the source profile [#register-the-source-profile]
```bash
fa source inspect databricks
fa source plan databricks --variant databricks_unity_catalog
```
Available variants are `databricks_unity_catalog`,
`databricks_workspace_consolidation`, and `databricks_cost_performance`.
## Create the native manifest [#create-the-native-manifest]
```json
{
"schemaVersion": 1,
"estate": {
"name": "Analytics platform",
"variant": "databricks_unity_catalog",
"workspaceRefs": ["workspace://analytics-prod"],
"snapshotAt": "2030-01-15T12:00:00.000Z"
},
"reason": "unity_catalog_upgrade",
"baselineEvidenceRef": "evidence://experiments/native-baseline-v1",
"assets": [
{
"assetId": "sales-database",
"name": "Sales Hive database",
"kind": "hive_metastore_object",
"dependencies": [],
"owner": "sales-data",
"currentStateDigest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"issues": ["Uses workspace-local storage paths."],
"metrics": { "tables": 42 }
}
]
}
```
Inventory may cover workspaces, metastores, Hive Metastore objects, Unity Catalog
assets, jobs, pipelines, notebooks, warehouses, clusters, policies, permissions,
dashboards, models, endpoints, shares, lineage, cost observations, and operational
dependencies. Store identities, digests, aggregate metrics, and issues—not credentials
or raw client data.
Typed reasons include Unity Catalog upgrades, workspace consolidation, permission and
path modernization, serverless or Lakeflow adoption, Delta and liquid clustering,
runtime upgrades, dashboard transitions, and cost/performance optimization.
## Inspect and plan [#inspect-and-plan]
```bash
fa databricks-native inspect --file native-manifest.json
fa databricks-native plan \
--file native-manifest.json \
--json > native-plan.json
```
Every asset becomes a deterministic, advisory, or human recommendation. The plan
requires baseline behavior, allow-and-deny permission tests, performance and cost
guardrails, immutable release and rollback evidence, an operational observation window,
and owner acceptance.
## Register [#register]
```bash
fa databricks-native register \
--file native-plan.json \
--engagement-id \
--estate-id \
--artifact-id \
--idempotency-key
```
The plan records the family execution contract:
* Airlift owns the baseline, modernization intent, recommendations, evidence references,
promotion policy, and value record.
* Harness may produce bounded advice or repair candidates.
* Experiments owns A/B, BDD, parity, performance, and cost evaluations.
* Runway owns preview, deployment, promotion, reconciliation, and rollback.
* Radar owns operational observations.
* Tower owns work references.
* Platform owns governed mutations and audit.
Use `fa` to inspect Airlift migration/modernization status and request governed changes.
Use `fr` to execute releases. Airlift may block on Runway or Radar evidence; it never
copies their state or provides a second deploy command.
# Delivery and claim commands
# Delivery and claim commands [#delivery-and-claim-commands]
`fa delivery` makes the migration method repeatable without turning internal delivery
state into public documentation or unsupported claims. These commands are local,
deterministic checks; governed engagement mutations still use the normal authenticated
commands.
## Qualify a source engagement [#qualify-a-source-engagement]
Create a credential-reference-only qualification file with source and target authority,
representative-data approval, object count, required/excluded surfaces, network shape,
RPO/RTO, and cutover/rollback owners.
```bash
fa delivery preflight --file qualification.json
```
The command returns assessment and cutover-design readiness separately. Missing source
authority, target authority, or representative-data approval blocks assessment. Missing
cutover or rollback owners blocks cutover design. Private-link and offline shapes add a
connectivity rehearsal instead of pretending online dependencies are available.
## Generate the delivery kit [#generate-the-delivery-kit]
```bash
fa delivery kit \
--file qualification.json \
--json > delivery-kit.json
```
The generated kit contains contracts for:
* source qualification;
* assessment report;
* target blueprint;
* wave plan;
* human-residue estimate;
* migration certificate;
* cutover and rollback runbook; and
* measured value report.
It also lists factory setup, client adapter, human remediation, cutover assurance,
modernization, and hypercare playbooks plus backup/restore, signer rotation, connector
rotation, retention/export, disaster recovery, and upgrade/rollback operations.
## Validate a connector operating envelope [#validate-a-connector-operating-envelope]
```bash
fa delivery connector-policy-check --file connector-policy.json
```
A promoted connector policy must bound concurrency, rate, quota, cost, timeout, retry
classes, and circuit breaking. Checkpoint and reconciliation are mandatory. The schema
fixes non-idempotent effects at one attempt.
## Gate an external value claim [#gate-an-external-value-claim]
```bash
fa delivery claim-check --file external-claim.json
```
Eligibility requires at least three engagements, at least ten comparable observations,
medium or high confidence, observed methods beyond expert estimates alone, a versioned
cohort and release generation, exclusions, limitations, a reviewed methodology, and an
immutable evidence digest. A rejected packet exits nonzero and emits exact blockers.
This is stricter than an engagement-scoped client value report. It exists for aggregate
external language. Never hard-code “60% faster” into product material.
## Check activation evidence [#check-activation-evidence]
```bash
fa delivery activation-check --file activation-evidence.json
```
Requirements are data, not hard-coded program assumptions. Record each credential
category's current required and observed counts, approved outcome references,
thought-leadership references, repeatable demo evidence, reviewer, and evidence digest.
Repository tests do not count as customer outcomes.
## Scan public developer content [#scan-public-developer-content]
```bash
fa delivery public-check --file public-document.md
```
The scanner blocks known private path, deployment, evidence-ledger, credential, and
client-identity patterns. It complements review and CI; it does not declassify a document
or replace client approval.
# fa doctor
# `fa doctor` [#fa-doctor]
```bash
fa doctor --profile local
fa doctor --profile production --json
```
Doctor checks Node generation, store selection and binding shape, authorization
directory, evidence registry, Ed25519 signer/verifier configuration, Databricks Apps
identity gates, Lakebridge job configuration, Temporal selection, and the client-effector
boundary. It also checks the observation-only Runway API binding and the Experiments
case-module/evidence-store binding used by release qualification.
Production profile fails when durable store, authorization, identity, signing, live
Lakebridge, Runway observation, or Experiments validation structure is missing. Temporal
absence and the composition-injected cutover
effector remain explicit warnings because connectivity and client certification cannot
be proven from environment shape.
Doctor prints variable names, key IDs, counts, and dispositions. It does not print
connection strings, private keys, tokens, authorization-directory contents, or other
secret values. A passing shape check is not deployment or client certification evidence.
# Live evidence commands
# Live evidence commands [#live-evidence-commands]
Airlift admits immutable artifact references into the migration ledger. It does not turn a
JSON file into a validation verdict, deployment result, or cutover authorization. Fabric
Experiments, Runway, Radar, and client-approved adapters retain ownership of their results;
Airlift stores their identifiers, content digests, producer generations, and lineage.
## 1. Inspect before admission [#1-inspect-before-admission]
```bash
fa evidence inspect live-evidence.json
fa evidence inspect live-evidence.json --json > .airlift/evidence-inspection.json
```
Inspection is local. It validates the secret-free schema, timestamp order, source identity,
run references, artifact digests, and disallowed credential-like keys. A historical failed
run remains valid evidence—it is reported as failed rather than erased.
## 2. Admit artifact references [#2-admit-artifact-references]
```bash
fa evidence admit \
--file live-evidence.json \
--engagement-id \
--estate-id \
--idempotency-key source-workspace-run-v1 \
--json > .airlift/evidence-admission.json
```
The command invokes the governed `airlift.artifact_register` action once for the manifest
and once for each referenced artifact. It records no file bodies and no credentials. The
JSON result includes `providerVerdictsAdmitted: false`; provider evidence must still enter
through its admitted principal and policy path before readiness can pass. Artifact
admission cannot manufacture an Experiments verdict.
## 3. Inspect the admitted ledger [#3-inspect-the-admitted-ledger]
```bash
fa evidence list --engagement-id
fa evidence show --json
fa artifact list --engagement-id
```
In the Databricks App, open **Engagements → active engagement → Artifacts** for immutable
references and **Runs** for assessment, conversion, transfer, deployment, and validation
execution references.
## 4. Export governed evidence [#4-export-governed-evidence]
```bash
fa evidence export \
--file evidence-export.json \
--idempotency-key wave-evidence-export-v1
```
An evidence export is a content-digested view of the governed event trail and current
projection. The digest proves byte identity; only a valid signing envelope proves signer
identity. Retain the export with the application build, source snapshot, provider runs,
certificates, approvals, and observation windows that supported the decision.
## Automation outcomes [#automation-outcomes]
| Exit | Meaning |
| ---- | ---------------------------------------------------------------- |
| `0` | schema inspection, admission, query, or export request succeeded |
| `1` | the governed action or policy rejected the request |
| `2` | command usage or the local manifest is invalid |
| `4` | the requested remote resource was not found |
| `6` | authentication, transport, or service availability failed |
# Generated command index
# Generated command index [#generated-command-index]
This page is generated from Fabric Airlift CLI 0.18.4. The same manifest
drives `fa help`, group help, shell completion, tests, and this reference. It describes
developer commands only; it does not include credentials, tenant data, deployment state, or
internal delivery notes.
Use resource-first paths: `fa `. For a nested resource, continue the
path: `fa inventory graph list`, `fa conversion batch create`, and
`fa application-kit module list`. There are no deprecated spellings or hidden aliases.
Global automation options:
* `--format text|table|json|yaml|jsonl`; `--json` is the concise JSON form;
* `--file -` reads a JSON request or manifest from stdin;
* `--timeout ` bounds remote requests and explicit waits; and
* `--correlation-id ` carries a caller trace ID into governed mutations.
Generate completion with `fa completion bash`, `fa completion zsh`, or
`fa completion fish`. Emit this command model for tooling with `fa commands --json`.
## `fa organization` [#fa-organization]
Govern the projected organization membership registry.
| Command | Behavior |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `fa organization membership set --file --idempotency-key ` | Apply governed membership grants and revocations (admin role; last-admin protected). |
## `fa engagement` [#fa-engagement]
Create and govern migration engagements.
| Command | Behavior |
| --------------------------------------------------------------------------- | ----------------------------------------------------------- |
| `fa engagement list` | List engagements visible to the authenticated principal. |
| `fa engagement show ` | Show one engagement. |
| `fa engagement status ` | Show the derived eight-phase migration status and blockers. |
| `fa engagement create\|update --file --idempotency-key ` | Create or update an engagement. |
| `fa engagement activate\|freeze --idempotency-key ` | Advance the engagement lifecycle. |
| `fa engagement preflight ` | Evaluate source-capability and human-lane blockers. |
## `fa connection` [#fa-connection]
Register source connection references without storing credentials.
| Command | Behavior |
| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `fa connection list [--engagement-id ]` | List connection bindings. |
| `fa connection register --file --idempotency-key ` | Register a secret-backed connection reference. |
| `fa connection verify --digest --idempotency-key ` | Verify a binding with the digest of a recorded connectivity diagnostic. |
| `fa connection retire --reason --idempotency-key ` | Retire a connection binding. |
| `fa connection test [--json]` | Report observed connectivity state for a binding; exit 0 only when observed. |
| `fa connection diagnose --file --idempotency-key ` | Record an admitted connectivity diagnostic (system principal). |
## `fa access` [#fa-access]
Inspect derived Databricks deployment access and record admitted access preflights.
| Command | Behavior |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| `fa access show [--json]` | Report the derived access-preflight state and five target-integration states; exit 0 only when access is observed. |
| `fa access record --file --idempotency-key ` | Record an admitted access preflight (system principal). |
## `fa evaluator` [#fa-evaluator]
Inspect derived evaluation readiness and record admitted candidate freezes and rehearsals.
| Command | Behavior |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `fa evaluator window [--json]` | Report derived evaluation readiness and the evaluation-window manifest; exit 0 only when evaluation\_ready is true. |
| `fa evaluator freeze --file --idempotency-key ` | Freeze an evaluation candidate (admitted system principal; in-process path). |
| `fa evaluator rehearsal --file --idempotency-key ` | Record an evaluator rehearsal against the frozen candidate (admitted system principal; in-process path). |
## `fa uc-evidence` [#fa-uc-evidence]
Inspect the derived Databricks-native evidence state and record admitted projection runs and reconciliations.
| Command | Behavior |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `fa uc-evidence show [--engagement-id ] [--json]` | Report the derived UC evidence state; exit 0 only when the evidence is current. |
| `fa uc-evidence record --kind projection-run\|reconciliation --file --idempotency-key ` | Record an admitted UC projection run or target reconciliation (admitted system principal; in-process path). |
## `fa estate` [#fa-estate]
Register and inspect source estates.
| Command | Behavior |
| ----------------------------------------------------------------- | ------------------- |
| `fa estate list` | List estates. |
| `fa estate show ` | Show an estate. |
| `fa estate register --file --idempotency-key ` | Register an estate. |
## `fa assessment` [#fa-assessment]
Run and accept governed source assessments.
| Command | Behavior |
| ------------------------------------------------------------------------------------------ | -------------------------------- |
| `fa assessment list [--estate-id ]` | List assessments. |
| `fa assessment status ` | Show assessment status. |
| `fa assessment start\|record\|accept\|export --file --idempotency-key ` | Mutate the assessment lifecycle. |
## `fa inventory` [#fa-inventory]
Register inventory and build dependency graphs.
| Command | Behavior |
| --------------------------------------------------------------------------------------- | ------------------------------------ |
| `fa inventory list [--estate-id ] [--assessment-id ]` | List migration objects. |
| `fa inventory register --file --idempotency-key ` | Register inventory. |
| `fa inventory graph list [--assessment-id ]` | List dependency graphs. |
| `fa inventory graph show ` | Show graph edges. |
| `fa inventory graph start\|record\|accept --file --idempotency-key ` | Build and accept a dependency graph. |
## `fa plan` [#fa-plan]
Generate, compare, select, and freeze migration plans.
| Command | Behavior |
| --------------------------------------------------------------- | ------------------------ |
| `fa plan list\|compare [--engagement-id ]` | List or compare plans. |
| `fa plan show ` | Show a migration plan. |
| `fa plan generate --file --idempotency-key ` | Generate a plan. |
| `fa plan select\|freeze --idempotency-key ` | Select or freeze a plan. |
## `fa wave` [#fa-wave]
Inspect dependency-aware migration waves.
| Command | Behavior |
| --------------------------------- | ------------------- |
| `fa wave list [--estate-id ]` | List cutover waves. |
## `fa conversion` [#fa-conversion]
Run conversion batches and govern remediation residue.
| Command | Behavior |
| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `fa conversion list [--object-id ]` | List conversion attempts with their hazard-scan state (clean, hazardous, or unassessed). |
| `fa conversion show ` | Show a conversion attempt; hazardous attempts block certification until their conversion\_hazard residue is reviewed. |
| `fa conversion diff ` | Compare attempts for an object. |
| `fa conversion batch list [--engagement-id ]` | List conversion batches. |
| `fa conversion batch show ` | Show a conversion batch. |
| `fa conversion batch create\|start\|complete --file --idempotency-key ` | Mutate a conversion batch. |
| `fa conversion attempt start\|record --file --idempotency-key ` | Start or record a conversion attempt. |
| `fa conversion retry --file --idempotency-key ` | Retry through the governed conversion action. |
## `fa residue` [#fa-residue]
Estimate, assign, and resolve human or agent remediation.
| Command | Behavior |
| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `fa residue list [--engagement-id ] [--object-id ]` | List remediation residue. |
| `fa residue show ` | Show one residue item; conversion\_hazard cases name their exact origin conversion and assessment digest. |
| `fa residue create\|estimate\|assign\|resolve\|review\|cancel --file --idempotency-key ` | Mutate the residue lifecycle. After an approved review, retry certification with a FRESH idempotency key — a denied key stays denied. |
## `fa transfer` [#fa-transfer]
Plan, operate, and reconcile data movement.
| Command | Behavior |
| --------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `fa transfer list [--engagement-id ] [--estate-id ]` | List transfer plans. |
| `fa transfer status ` | Show transfer status. |
| `fa transfer plan\|checkpoint\|reconcile-record\|fail --file --idempotency-key ` | Record transfer plans and evidence. |
| `fa transfer run\|resume\|reconcile --idempotency-key ` | Operate a transfer workflow. |
| `fa transfer pause\|cancel --reason --idempotency-key ` | Pause or cancel a transfer. |
## `fa validation` [#fa-validation]
Run Experiments-backed parity validation and inspect readiness.
| Command | Behavior |
| ------------------------------------------------------------------- | ---------------------------------------- |
| `fa validation list [--engagement-id ] [--estate-id ]` | List validation executions. |
| `fa validation status ` | Show validation status. |
| `fa validation runs\|readiness [--object-id ]` | Inspect admitted evidence and readiness. |
| `fa validation run --file --idempotency-key ` | Request validation. |
| `fa validation cancel --reason --idempotency-key ` | Cancel validation. |
## `fa certificate` [#fa-certificate]
Inspect, verify, mint, and invalidate migration certificates.
| Command | Behavior |
| ------------------------------------------------------------------------------ | --------------------------------------- |
| `fa certificate list [--object-id ]` | List certificates. |
| `fa certificate show ` | Show a governed certificate record. |
| `fa certificate inspect ` | Inspect a certificate envelope locally. |
| `fa certificate verify [--keys ]` | Verify digest and signature locally. |
| `fa certificate mint\|invalidate --file --idempotency-key ` | Mutate governed certificate state. |
## `fa cutover` [#fa-cutover]
Freeze, approve, execute, and observe governed cutover.
| Command | Behavior |
| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `fa cutover list [--estate-id ]` | List cutover controls. |
| `fa cutover status ` | Show cutover readiness. |
| `fa cutover freeze\|runbook\|rehearse\|observe\|certify-effector --file --idempotency-key ` | Record cutover control evidence. |
| `fa cutover incident-open\|incident-resolve --file --idempotency-key ` | Govern a cutover incident. |
| `fa cutover approve [--note ] --idempotency-key ` | Approve through the authenticated mutation boundary. |
| `fa cutover start --window --reason [--wait] [--timeout ]` | Start or attach to the durable cutover workflow. |
| `fa cutover workflow-status\|wake ` | Query or wake a durable cutover workflow. |
## `fa hypercare` [#fa-hypercare]
Record post-cutover observation and decommission evidence.
| Command | Behavior |
| -------------------------------------------------------------------------------------------------- | ------------------------------- |
| `fa hypercare start\|observe\|complete\|decommission --file --idempotency-key ` | Mutate the hypercare lifecycle. |
## `fa artifact` [#fa-artifact]
Register and inspect content-digested migration artifacts.
| Command | Behavior |
| ------------------------------------------------------------------- | ----------------------------------------- |
| `fa artifact list [--engagement-id ] [--object-id ]` | List artifacts. |
| `fa artifact show ` | Show one artifact. |
| `fa artifact register --file --idempotency-key ` | Register an immutable artifact reference. |
## `fa deployment` [#fa-deployment]
Declare and inspect deployment requirements; Runway executes releases.
| Command | Behavior |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `fa deployment list [--engagement-id ] [--estate-id ]` | List deployment requirements. |
| `fa deployment status ` | Show a deployment requirement. |
| `fa deployment require --file --idempotency-key ` | Declare a required Runway outcome. |
| `fa deployment connect --file --idempotency-key ` | Connect an existing requirement to a Runway deployment reference. |
## `fa modernization` [#fa-modernization]
Separate baseline migration from measurable Databricks modernization.
| Command | Behavior |
| ------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| `fa modernization list [--engagement-id ] [--object-id ]` | List modernization items. |
| `fa modernization show ` | Show one modernization item. |
| `fa modernization recommend\|decide\|plan\|start\|evidence\|promote\|rework --file --idempotency-key ` | Mutate modernization state. |
## `fa value` [#fa-value]
Record baselines and publish measurable outcome reports.
| Command | Behavior |
| ---------------------------------------------------------------------------------- | ----------------------- |
| `fa value observations\|summaries\|reports [--engagement-id ]` | Inspect value evidence. |
| `fa value record\|summarize\|publish --file --idempotency-key ` | Mutate value evidence. |
## `fa capability` [#fa-capability]
Inspect and govern source-capability evidence.
| Command | Behavior |
| -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `fa capability list [--source ] [--variant ] [--capability ] [--construct ] [--artifact-kind ]` | List capability entries. |
| `fa capability show ` | Show one capability entry. |
| `fa capability matrix --source [--variant ]` | Render the capability matrix. |
| `fa capability propose\|evidence\|promote\|expire\|revoke\|reconcile --file --idempotency-key ` | Mutate capability evidence. |
## `fa source` [#fa-source]
Inspect, plan, diagnose, and qualify supported source profiles.
| Command | Behavior |
| ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `fa source inspect ` | Inspect one source profile. |
| `fa source plan [--variant ]` | Create a source migration plan. |
| `fa source recipe [--variant ]` | Render the complete developer command and artifact sequence for one source. |
| `fa source constructs [--variant ] [--construct ] [--artifact-kind ]` | List repository-owned construct routing without implying provider proof. |
| `fa source doctor\|certify [--variant ] [--level ]` | Diagnose or certify capability evidence. |
| `fa source limitations [--variant ]` | List explicit source limitations. |
| `fa source certification-check ` | Evaluate source certification evidence. |
## `fa migration-pack` [#fa-migration-pack]
Compile and qualify source-specific migration packs.
| Command | Behavior |
| ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| `fa migration-pack inspect\|plan --file ` | Inspect or compile a migration pack. |
| `fa migration-pack register --file --engagement-id --estate-id --artifact-id --idempotency-key ` | Register a compiled pack. |
| `fa migration-pack certification-check --file ` | Evaluate pack certification evidence. |
## `fa migration-ir` [#fa-migration-ir]
Import, compile, generate, and qualify ETL migration IR.
| Command | Behavior |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `fa migration-ir import --source --file --estate-name --snapshot-at [--product-version ] [--credential-map