Repository navigation
feat: engine: flink (0.7.6 preview) runs outside forge-cli; Flink SQL stays off the inline-SQL paths - #731
Merged
Conversation
_determine_source_table took the output table of the contract's last build. When that build produced none in the local planner (a dbt model, which it does not run), the fallback picked the first table it had loaded, an input, and the expose landed it raw: columns the expose does not declare, PII among them, included. It now takes the output of the last build that has one.
A build can now declare Apache Flink as its engine. forge-cli validates and plans it but ships no runner: the job runs on the Flink Kubernetes Operator, AWS Managed Service for Apache Flink, or whatever platform runs it. 0.7.5 is unchanged. Deployment settings go under properties.parameters, because embeddedLogicPattern is closed; the schema description says so and names the operator's job spec keys. Before, the only option was engine: custom or engine: sql with properties.language: flink_sql, and either took the inline-SQL path: is_embedded_sql_build never looked at the language, so `fluid apply --mode amend-and-build` ran the Flink SQL on DuckDB when no platform was declared, refused kubernetes as an unknown embedded-SQL platform, and the local, AWS, GCP and Snowflake planners turned it into DuckDB, Athena, BigQuery or Snowflake statements. util/contract.is_externally_run_build is the one test: engine flink, or properties.language flink_sql, the way a 0.7.5 contract declares the same job. Every consumer that ran or planned a build's SQL checks it: the build runner, the local planner and provider, and the AWS, GCP and Snowflake planners (Snowflake's replace mode still ensures a Flink output table, since no CTAS recreates it). The runner reports such a build on its own summary line, not as a skip, so a contract whose only builds run elsewhere is not "every build was skipped"; the run report records it as skipped with reason runs_outside_forge. The local planner lands nothing for an expose such a build writes, instead of a loaded input in its place; when builds[].outputs does not say whether a Flink build or a local one writes an expose, it lands neither and warns rather than guess. Scheduler synthesis and the Airflow fluid-apply DAGs give it no task, and a contract with no other build and no hand-declared orchestration tasks has nothing to schedule (`fluid generate schedule` exits 0; an unknown --scheduler is still an error); `fluid generate transformation` says there is no code to generate for it. Other properties.language values keep the behaviour they had.
…i, the local source table An Added entry for the 0.7.6 engine, an Upgrade note for builds with properties.language: flink_sql (no longer run or planned as SQL), and a Fixed entry for the local planner landing the raw input.
📄 Documentation ReminderThis PR appears to be missing a documentation reference. Our docs live in a separate repo. Please update the PR description with one of:
See the Contributing Guide for details. |
5 of 9 tasks
…e for base The runner test imported fluid_build.build_runners.base twice, as a module and by name (code-quality review on #731).
This was referenced Oct 19, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
A team running long-lived Flink SQL jobs (Kafka in, Kafka out) on the Flink Kubernetes Operator and on AWS Managed Service for Apache Flink asked for a way to declare Flink as a build's engine. Their only option was
engine: custom, which loses the engine's identity. Worse, it sent the job down the inline-SQL path:is_embedded_sql_buildnever looked atproperties.language;fluid apply --mode amend-and-buildran the Flink SQL on DuckDB when no platform was declared, and refusedplatform: kubernetesas an unknown embedded-SQL platform;This PR:
flinkto the build engine enum. 0.7.5 is unchanged. Deployment settings go underproperties.parameters, becauseembeddedLogicPatternis closed. The schema description says so and names the Flink Kubernetes Operator's job spec keys (parallelism,upgradeMode) andflinkConfiguration.util/contract.is_externally_run_build. It matchesengine: flink, and alsoproperties.language: flink_sql, which is how a 0.7.5 contract declares the same job.properties.languagevalues keep the behaviour they had.fluid apply --mode amend-and-buildreports such a build as run outside forge-cli and counts it on its own summary line, not as a skip. So a contract whose only builds run elsewhere is not "every build was skipped". The run report records it asskippedwithreason: runs_outside_forge.fluid generate scheduleon a contract with no other build, and no hand-declared tasks, exits 0 with "nothing to schedule". An unknown--scheduleris still an error.fluid generate transformationsays there is no code to generate.builds[].outputsdoes not say which writes which expose, it lands neither and warns rather than guess. The local plan of all 37 repo contracts is unchanged frommain.flinkbuild; the platform that runs the job executes it.fix(local)(its own commit in this PR). The local planner took the output table of the contract's last build. When that build produced no table (a dbt model), it landed the first loaded input raw in the expose, columns the expose does not declare included. It now takes the last build that produced a table.The requester asked about
triggerfor a continuous job:triggeris already optional in the schema, so a continuous job omits it. Acontinuoustrigger type is left until a scheduler needs to tell always-on from manual.Borrowed, not built
parallelismandupgradeMode(savepoint/last-state/stateless), and FlinkDeploymentSpecflinkConfiguration. These are the recommendedproperties.parametersnames; there is no new typed block, which RFC-streaming-extension §12 also rejected.JobTypeJobFacet. IntegrationFLINK,processingTypeSTREAMING; it confirms "flink" as the engine identity.Validation
tests/test_flink_engine.py(55 tests) andtests/providers/test_local_planner_source_table.py.main.mainwith the same venv: duckdb 1.5.6 sandbox tests, dbt not installed, the live coding-agent test.check_license_headers.pyandlint-imports.fluidCLI, on the requester's contract (parallelism moved underproperties.parameters) and on its 0.7.5engine: sql+language: flink_sqltwin:validatepasses on 0.7.6 and rejectsflinkon 0.7.5.planpasses.apply --mode amend-and-build, both dry run and real, prints "engine 'flink' runs on kubernetes, outside forge-cli", "Run outside forge-cli: 1", exit 0. Onmainthe dry run printed "Would execute SQL: INSERT INTO …"; on 0.7.5mainexited 1 withembedded_sql_platform_unsupported.generate transformationandgenerate scheduleexit 0 with their notes.ripwire --quality-delta, measured against a no-op control onmain. Accepted rows:run_builds_from_argsgrows 96 → 99, the minimum one dispatch branch plus a summary line costs inside an already 96-complexity function;plan_actionsgrows by a skip;ApplyRunReport.record_build, which is called through a dynamically typed report;Review verdicts
flinkConfigurationnaming;Documentation
Follow-ups (not in this PR)
fluid apply(simple mode) picks a build'sruntime.platform(kubernetes) as the provider:provider_unknown, the same root cause asprovider_unknown kafka.flinkyet (KNOWN_BUILD_ENGINESfeeds its prompt, and it writes 0.7.5 contracts).policy.privacy.maskingon an expose a Flink or Python build writes gets no advisory.properties.languagevalues (pyspark,python,scala,r) withproperties.sqlstill run on DuckDB, and forge's Python templates report a false success.outputsnaming another expose).fluid plan's abstractscheduleTaskstill carries the Flink SQL (nothing executes it).