Skip to content

fix: fluid bundle wrote a tarball named .yaml and printed nothing on a split contract - #763

Merged
fas89 merged 5 commits into
mainfrom
fix/bundle-default-output
Oct 29, 2026
Merged

fas89 merged 5 commits into
mainfrom
fix/bundle-default-output

Conversation

@fas89

@fas89 fas89 commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

fluid bundle without --out (or with --out -) did not do what its --help says on a contract with a fragments/ directory beside it, the layout fluid split leaves. Found while re-checking drafts 21-29; the fix direction is a founder decision (2026-10-29): follow --help.

What changed

On such a layout fluid bundle wrote contract.bundled.fluid.yaml instead of stdout: fluid bundle contract.fluid.yaml | ... received nothing, --format json put JSON under the .yaml name, and --format tgz put the gzip tarball there, which the next stage read as YAML ('utf-8' codec can't decode byte 0x8b) and --sign / --attest refused (signing_bundle_wrong_format). fluid ship bundles that way on every run. The auto-detect is gone: yaml and json go to stdout and a tgz to <contract id>.fluid.bundle.tgz, the same for every contract (kustomize build and helm template print to stdout the same way). --help names both defaults; the CHANGELOG has a Changed entry and an Upgrade note (a script that read contract.bundled.fluid.yaml passes --out).

From the security review: the default tgz path now gets the validate_cli_path write check an explicit --out gets, so a symlink planted at that name cannot point the write at a forbidden path.

Borrow before build

Bug fix inside existing code; precedent for "no --out means stdout": kustomize build, helm template, and fluid itself (- means stdout in odps.py).

Validation

  • Full suite (-n 6 --dist loadscope --ignore=tests/perf): 30434 passed; the 7 failures and 2 collection errors fail identically on clean main (dlt credential-field drift, the DuckDB sandbox broad-root tests, the live claude CLI test, two CI-only modules); one MCP test that failed under load passes alone on the branch and on main. After the last commit: tests/forge + the bundle, signing, attestation, fragments-bundle, compat-gate and security tests, 1000 passed.
  • ruff, black 24.10.0, import-linter 6/6, mypy --strict hotspots, product guardrail, detect-secrets: green.
  • New tests fail on main (3 of the fragment-layout tests; the symlink test).
  • Live, on a fragment layout: fluid bundle contract.fluid.yaml | head prints the composed contract; --format json pipes into a JSON parser; --format tgz writes commerce.orders_events.fluid.bundle.tgz (gzip), no contract.bundled.fluid.yaml.

Reviews

  • /security-review: PASS. Two LOWs, both pre-existing: the default tgz path skipped the --out write check (fixed here), and the stdout-mode logging swap adds a stderr handler without the redaction filter (follow-up).
  • /world-class-inspection: PASS (codebase, tests, docs, consistency); P3 notes fixed (the JSON test oracle, CHANGELOG precision).
  • /defect-scan: no confirmed defect in touched code. Every caller checked: fluid ship (now leaves <id>.fluid.bundle.tgz, as on flat layouts), engine.bundle(), generated CI (explicit --out), MCP/IDE/templates (no calls).

Follow-ups (pre-existing, not this change)

  • The stdout-mode log swap (bundle.py ~349-357) skips the secret-redaction filter on its new stderr handler and prints some events twice.
  • An id longer than ~238 characters makes the default tgz name too long (also on flat layouts).
  • Stale hints: fluid forge suggests fluid bundle --check (not a flag) and cat contract.bundled.yaml.

Documentation

  • Docs PR linked
  • No docs needed
  • Docs TODO — forge_docs docs/cli/bundle.md ("Default output location" table and the tgz warning), docs/cli/ship.md, docs/cli/split.md, docs/concepts/fragments.md: comes with the forge_docs pages PR of this wave.

fas89 added 3 commits October 29, 2026 08:31
With a fragments/ directory beside the contract, `fluid bundle` without --out
wrote contract.bundled.fluid.yaml instead of stdout: a pipe received nothing,
--format json put JSON under the .yaml name, and --format tgz put the gzip
tarball there too, which the next stage read as YAML ("'utf-8' codec can't
decode byte 0x8b"). `fluid ship` bundles that way on every run. The auto-detect
is gone: yaml and json go to stdout, a tgz to <contract id>.fluid.bundle.tgz,
the same for every contract (kustomize build and helm template print to stdout
the same way). --help now says both defaults.

Founder decision 2026-10-29: follow --help, rather than keep an auto file.
…d the review notes

From the reviews of this branch:
- the default `<contract id>.fluid.bundle.tgz` skipped the `validate_cli_path`
  write check an explicit --out gets, so a symlink planted at that name could
  point the write at a forbidden path (as root in a CI container); it is
  refused now (security review, LOW, pre-existing);
- the stdout test for --format json read the output with yaml.safe_load, which
  would have passed a json format that silently printed YAML; it reads JSON;
- CHANGELOG: `--out -` was redirected too, --sign/--attest failed on such a
  layout, and a leftover contract.bundled.fluid.yaml can be a stale copy.
@github-actions github-actions Bot added cli Changes to the CLI surface or implementation tests Test coverage or test infrastructure changes labels Oct 29, 2026
@fas89
fas89 merged commit 777684a into main Oct 29, 2026
37 checks passed
@fas89
fas89 deleted the fix/bundle-default-output branch October 29, 2026 15:00
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