Skip to content

feat(validate,plan): warn about an id the build phase refuses - #813

Merged
fas89 merged 4 commits into
mainfrom
claude/adoring-satoshi-8a5b12
Nov 4, 2026
Merged

fas89 merged 4 commits into
mainfrom
claude/adoring-satoshi-8a5b12

Conversation

@fas89

@fas89 fas89 commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

fluid validate and fluid plan now warn when a contract with builds has a contract.id or a builds[].id that the build phase refuses: a leading digit (1orders) or more than 128 characters. The schema's $defs.identifier admits such ids. Since #803, fluid apply in a build mode refuses them before it applies anything, but validate and plan said nothing. On 2026-11-03 the founder decided to keep the two grammars as they are and to warn early instead.

$ fluid validate contract.fluid.yaml
✅ Valid FLUID contract (schema v0.7.5)
⚠️  1 warning(s)

Validation Warnings:
====================
 1. contract.id '1orders' cannot be used by the build phase, which puts it in directory names and generated code, so it must start with a letter or underscore, use only letters, digits, '_', '.' and '-', and be at most 128 characters. fluid apply in a build mode (amend-and-build, replace-and-build) refuses this id before it applies anything. Rename it to fit.

Behaviour

  • fluid validate: a normal validation warning. Plain validate exits 0, --strict exits 1 (founder's call), and --format json lists it under warnings. It runs with the other 0.7.x contract rules, so a contract file and a bundle's resolved contract both get it. Workspace mode runs only the schema check, as it already documents.
  • fluid plan: prints the warning and still writes plan.json with exit 0, whatever --mode says (founder's call). It runs after _gate_contract_for_plan_or_apply, not inside it, because apply shares that gate and keeps its typed ERR_APPLY_BUILD_IDENTIFIER_INVALID.
  • As in apply, a contract without builds is not checked, an empty id is skipped, and only the first failing id is named.
  • Apply's refusal is unchanged. Its hint said validate and plan "accept it"; it now says they "only warn about it".

How

  • build_runners/_ids.py: build_id_warning(contract) wraps validate_build_ids, the rule apply and the build runner already use, and returns the text. IDENT_RULE puts the grammar in words for the warning and for apply's hint.
  • cli/validate.py::_build_id_check, next to _databricks_binding_check. If the check itself crashes, that is reported as a warning, never swallowed.
  • cli/plan.py::_warn_unusable_build_ids prints with cprint_literal, because the id is contract data, not markup. There is no warn() log event: the console prints WARNING events as JSON lines, so the text would appear twice.

Gates

  • Borrow: in-repo reuse. validate_build_ids is the rule. The validate side follows continuous_trigger_warnings and the helper shape of _databricks_binding_check. There is nothing external to borrow: this is message text over an existing validator.

  • Defect scan (diff-scoped; a finding counts only if it is reproduced): one low finding, reproduced and fixed in 950b9e3. Validate runs contract rules on a contract the schema refused too, so on id: 123 the warning said "The contract schema allows more". The text no longer makes that claim, and a test pins it. The hunter also confirmed --env overlays (an overlay that sets or fixes the id), plan followed by apply plan.json --dry-run, both build modes, the .tgz bundle, --quiet, --verbose, --format json, and ids with [/] or [red]. Mutation checks showed the tests catch a warn() double print and cprint instead of cprint_literal.

  • Security review: no findings. _IDENT_RE, validate_identifier and validate_build_ids are unchanged, and apply and the build runner still enforce them. Crafted ids (ESC/OSC sequences, \n log forging, Rich [link=...] markup, U+202E, token=...) print escaped by repr, with markup off on every path and redaction still applied. Cost is linear in the id length. One informational note, adopted in 950b9e3: plan now prints with highlight=False, soft_wrap=True, as validate does.

  • Live test, real fluid CLI on the merged tree from a scratch directory (plan and an id: 123 contract run again after 950b9e3):

    Command Exit Warning
    validate with id: 1orders 0 yes
    validate --strict with id: 1orders 1 yes
    validate --strict with a 129-character build id 1 yes
    validate --strict, clean contract 0 no
    validate --strict, id: 1orders and no builds 0 no
    validate --format json 0 listed under warnings
    plan --mode amend-and-build 0 printed once, plan.json written
    validate --strict on the .tgz bundle 1 yes
    plan on the .tgz bundle 0 yes
    apply --mode amend-and-build 1 ERR_APPLY_BUILD_IDENTIFIER_INVALID with the new hint
  • Tests: the new tests/cli/test_validate_plan_build_ids_warning.py has 35 tests, and every one that expects a warning failed before the change. 31 touched suites: 731 passed, 31 skipped. After 950b9e3 the affected suites ran again: 284 passed. Full suite, run the way CI runs it: 32,987 passed. The 8 failures and 2 errors (dlt dotenv allow-list, DuckDB sandbox temp roots, the startup-budget compat gate, the live Claude CLI test, two CI-script imports) fail the same way on a clean export of the base commit 61ccba5, so they come from this local environment.

  • Lint & Format battery: whole-tree ruff, the dead-code gate, black 24.10.0, license headers and the writer test, lint-imports (both configs), product guardrail, and detect-secrets-hook on the changed files.

  • Startup budget: tests/perf/test_startup_budget.py gives the same numbers as a clean main export (1030 / 1932 modules; the same three local-venv failures on both).

  • No contract shipped in the repo triggers the warning (76 contracts scanned, 37 with builds).

Documentation

It fixes the apply.md "Build ids" sentence from Agenticstiger/forge_docs#186, which said validate and plan accept such ids, and the hint text shown there. It also adds a validate.md section, "An id the build phase refuses", a plan.md note and an upgrading.md entry.

fas89 added 3 commits November 4, 2026 00:55
The schema's $defs.identifier admits ids the build phase refuses: a
leading digit (1orders) or more than 128 characters. fluid apply in a
build mode refuses such an id before it applies anything (#803), but
fluid validate and fluid plan said nothing, so the first sign was a
failed apply. The two grammars stay different by decision.

For a contract with builds, both now warn and name the id, using the
rule apply uses (build_runners._ids.validate_build_ids):

- validate adds a ValidationResult warning, so plain validate passes
  and --strict fails (founder's call). It runs with the other 0.7.x
  contract rules, on a contract file and on a bundle's resolved
  contract. If the check itself crashes, that is reported as a warning.
- plan prints the warning and still writes plan.json, whatever its
  --mode (founder's call). It runs after the gate plan shares with
  apply, not inside it, so apply keeps its typed CLIError.

IDENT_RULE states the grammar in words, and both the new warning and
apply's hint use it. The hint said validate and plan "accept" such an
id; it now says they only warn about it.
…d id

Review follow-ups to the build-id warning:

- validate runs its contract rules on a contract the schema refused too,
  so the warning could say "The contract schema allows more" about an id
  the schema had just refused (id: 123). The text now only says what the
  build phase needs and that fluid apply in a build mode refuses the id.
- plan prints the warning the way validate prints its warnings: literal,
  soft_wrap (the terminal wraps, so Rich never breaks a line inside the
  id) and no highlighter.
@github-actions github-actions Bot added cli Changes to the CLI surface or implementation tests Test coverage or test infrastructure changes needs-docs Pull request needs a linked docs update or justification labels Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

📄 Documentation Reminder

This PR appears to be missing a documentation reference. Our docs live in a separate repo.

Please update the PR description with one of:

  • Link a docs PR — check the "Docs PR linked" box and paste the URL
  • Mark as no docs needed — check "No docs needed" with a justification
  • Acknowledge docs TODO — check "Docs TODO" and create the docs PR before merge

See the Contributing Guide for details.

@github-actions github-actions Bot removed the needs-docs Pull request needs a linked docs update or justification label Nov 4, 2026
@fas89
fas89 merged commit 8c8116e into main Nov 4, 2026
38 checks passed
fas89 added a commit that referenced this pull request Nov 4, 2026
…and #820 (#821)

Real merges of main (through 85174c0) and of #817 and #819 into the managed Flink feature branch, plus the schema_validator fix #811's guard required. Landed as a merge commit so main stays an ancestor.
@fas89 fas89 mentioned this pull request Nov 4, 2026
2 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Changes to the CLI surface or implementation tests Test coverage or test infrastructure changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant