Skip to content

fix(examples): make 04-files runnable across cpp/rust/elixir/lua/php/ruby/zig#93

Merged
Nic-dorman merged 2 commits into
mainfrom
feat/v2-299-runnable-04-files-examples
May 19, 2026
Merged

fix(examples): make 04-files runnable across cpp/rust/elixir/lua/php/ruby/zig#93
Nic-dorman merged 2 commits into
mainfrom
feat/v2-299-runnable-04-files-examples

Conversation

@Nic-dorman
Copy link
Copy Markdown
Collaborator

Summary

The `04-files` examples in 7 SDKs were shaped as documentation snippets — they hardcoded `/tmp/example.txt` / `/path/to/myfile.txt` placeholder paths that don't exist on disk. Against a live daemon, the daemon returned `HTTP 400: Bad request: invalid path` from the first `file_cost` call and the example exited 1 (php: 255, same root cause). Same shape as the antd-go fix in #91.

This PR makes the cross-SDK `04-files` examples runnable against a live daemon and surfaces two latent bugs along the way (one fixed here, one filed as a follow-up).

What changes

7 examples rewritten to use real tempfiles + round-trip assertions, mirroring `antd-py/examples/04_files.py` and the post-#91 `antd-go/examples/03-files/main.go`:

  • `antd-cpp/examples/04-files.cpp` — `std::filesystem::temp_directory_path` + `std::ofstream`/`std::ifstream`
  • `antd-rust/examples/04-files.rs` — `std::env::temp_dir` + `std::fs`
  • `antd-elixir/examples/04_files.exs` — `System.tmp_dir!` + `File.write!`/`File.read!`
  • `antd-lua/examples/04-files.lua` — `/tmp/antd-lua-04-files` + `io.open`
  • `antd-php/examples/04-files.php` — `sys_get_temp_dir` + `file_put_contents`/`file_get_contents`
  • `antd-ruby/examples/04_files.rb` — `Dir.mktmpdir` + `File.write`/`File.read`
  • `antd-zig/examples/04-files.zig` — `/tmp/antd-zig-04-files` + `std.fs.createFileAbsolute`

Each example writes a known file, calls `file_cost` → `file_upload_public` → `file_download_public`, asserts byte-equal round-trip, and cleans up the temp dir.

Ruby adapter expanded in `ant-dev/src/ant_dev/cmd_example.py` — the ruby adapter only dispatched `connect`/`data`/`chunks`, so the existing `04_files.rb` and `06_private_data.rb` files on disk were silently skipped. The sweep "passed" ruby in 5s without exercising files. Adapter now dispatches all 5.

Per-SDK signature bugs fixed that were hidden by the daemon-400 error in the original stubs:

  • `antd-elixir`: `Antd.Client.file_cost/4` doesn't exist — only `/3`. Original called `file_cost(client, path, true, false)` (4 args).
  • `antd-ruby`: `file_cost` takes 2 args, not 3. Original called `file_cost(path, true, false)`.
  • `antd-php`: `fileCost()` returns an `UploadCostEstimate` object — original printed it with string interpolation, which is a PHP fatal. Now uses `$cost->cost` / `$cost->chunkCount` properties.

Surfaced (not fixed) — daemon directory-upload bug

The first iteration of these examples exercised `dir_upload_public` too. The daemon returns:

```
HTTP 500 Internal Server Error
Internal error: encryption failed: I/O error: Is a directory (os error 21)
```

The daemon appears to attempt encryption on the directory path itself rather than recursing into files. Verified in cpp/rust/lua/zig logs from the V2-299 sweep — file upload+download works, dir upload doesn't. This is latent because the prior stub examples errored at `file_cost` first, so nobody had exercised the directory route in v2.

To keep this PR scoped to the example fix, the dir-upload step has been removed from these examples. The daemon bug is filed separately for a real fix.

Test plan

  • dev2 sweep with `ant dev example files -l ` for each affected SDK against `ant dev start --enable-evm`:

    SDK Result Time
    cpp ✅ PASS 5s
    rust ✅ PASS 6s
    elixir ✅ PASS 3s
    lua ✅ PASS 4s
    php ✅ PASS 3s
    ruby ✅ PASS 4s
    zig ✅ PASS 12s

    All 7 SDKs: real tempfile, real cost estimate, real upload, real download, real byte-equal assertion, clean exit.

  • Reviewer: re-run the same sweep on a fresh devnet.

Why now

The dev2 e2e sweep is the validation gate for v1.0. Pre-this-PR, 6 of 9 graph-cleaned SDKs (post-#92) failed on `files` due to placeholder paths. After this PR, the file-example slice is green. The remaining v1.0 gaps are: the directory-upload daemon bug (separate ticket), the external-signer marquee example, and the SDKs that still have no examples/ dir at all (csharp/dart/swift/java/kotlin).

🤖 Generated with Claude Code

Nic-dorman and others added 2 commits May 18, 2026 11:28
…ruby/zig

The 04-files examples in 7 SDKs were shaped as documentation snippets
with hardcoded /tmp/example.txt (or /path/to/myfile.txt) placeholder
paths. Against a live daemon, the daemon returns HTTP 400 "Bad request:
invalid path" and the example exits 1 (php exits 255 — same root cause,
PHP-style uncaught exception). Observed in the dev2 sweep on 2026-05-18.

Rewrite each example using the antd-py 04_files.py / antd-go 03-files
post-PR-#91 pattern:

* Create a real temp directory (language-idiomatic — sys_get_temp_dir
  in PHP, std::env::temp_dir in Rust, System.tmp_dir in Elixir,
  std::filesystem::temp_directory_path in C++, Dir.mktmpdir in Ruby,
  /tmp/antd-... fallback in Lua/Zig).
* Write a known file (hello.txt) and a subdir with a known file
  (mydir/file_in_dir.txt).
* Call file_cost on the real path.
* Upload + download + assert byte-equal round-trip on hello.txt.
* Upload + download + assert byte-equal round-trip on the directory's
  inner file.
* Clean up the temp directory on exit (defer/finally/block-scope).
* Exit non-zero with a clear message on assertion failure.

Plus: expand the ruby adapter in ant-dev/cmd_example.py to dispatch
the existing 04_files.rb and 06_private_data.rb examples — they were
on disk but never wired into the runner, so the sweep silently
skipped them and "passed" ruby in 5s without running them.

Verified end-to-end on dev2 with `ant dev example files -l <lang>`
for each of the 7 affected SDKs against `ant dev start --enable-evm`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dev2 sweep on the prior commit surfaced two distinct issues:

1. Directory upload is broken in the daemon. `POST /v1/dirs/upload/public`
   returns `HTTP 500 Internal error: encryption failed: I/O error: Is a
   directory (os error 21)` — the daemon attempts encryption on the
   directory path itself instead of recursing into files. Reproduces in
   cpp/rust/lua/zig (the SDKs whose new examples exercised this path).
   File upload/download works correctly on the same temp directory.
   Daemon fix is its own ticket; the example sidesteps by removing
   the dir_upload_public / dir_download_public step.

2. Three SDKs had wrong argument counts on `file_cost` in the original
   stub examples, hidden because the daemon errored at 400 first:
   * antd-elixir: was `file_cost(client, path, true, false)` (4 args).
     Function signature is `/3`. Drop the trailing `false`.
   * antd-ruby: was `file_cost(path, true, false)` (3 args). Signature
     is `/2`. Drop the trailing `false`.
   * antd-php: `fileCost()` returns an `UploadCostEstimate` object;
     printed it with string interpolation, which is a PHP fatal. Use
     `$cost->cost` and `$cost->chunkCount` properties.

What remains in each example: temp dir creation → write hello.txt with
known content → file_cost → file_upload_public → file_download_public
→ read back → assert byte-equal round-trip → cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Nic-dorman Nic-dorman merged commit a167db0 into main May 19, 2026
3 checks passed
Nic-dorman added a commit that referenced this pull request May 19, 2026
… + 15 SDKs + docs)

Resolved against origin/main (which now contains #91/#92/#93/#94/#96) using
dev2 integration HEAD aa94940 as the canonical post-merge state. Net of the
example-file overlap with #93/#94, V2-300 reduces to 99 files / 44+ / 1694-.
Nic-dorman added a commit that referenced this pull request May 19, 2026
… + 15 SDKs + docs) (#95)

Resolved against origin/main (which now contains #91/#92/#93/#94/#96) using
dev2 integration HEAD aa94940 as the canonical post-merge state. Net of the
example-file overlap with #93/#94, V2-300 reduces to 99 files / 44+ / 1694-.
Nic-dorman added a commit that referenced this pull request May 21, 2026
Cuts v0.8.0 atop v0.7.1. Substantial breaking-change roll-up of the
put/get rename, the private-file PUT/GET gap close, and several minor
surface cleanups -- bundled here so the v1.0 cut can ship stable on top.

## Breaking (antd daemon)

- feat(antd)!: bind to 127.0.0.1 by default on REST and gRPC (#107).
  Previously bound 0.0.0.0; use --bind-rest / --bind-grpc to override.
- chore: remove dead graph_entry surface from antd proto + 5 SDKs (#92).
  GraphService and its 4 RPCs are gone; REST mounts dropped.
- chore: remove dir_upload_public / dir_download_public surface (#95).
  Use file_put_public on a directory path instead; the daemon recurses.
- feat(antd)!: normalize put/get convention + close private-file PUT and
  GET gaps (#115). Method renames across proto + REST + SDKs:
    data_put_private    -> data_put
    data_get_private    -> data_get
    file_upload_public  -> file_put_public
    file_download_public -> file_get_public
  New: file_put / file_get for the private file path (previously only
  the public variant existed). New typed results: DataPutResult,
  DataPutPublicResult, FilePutResult, FilePutPublicResult; PutResult
  is now annotated as chunk_put only.

## Additive

- feat(antd): honor payment_mode on gRPC put/cost paths and REST cost
  endpoints (#114). Optional kwarg threaded through every put/cost
  signature; empty/omitted maps to "auto" so older clients keep working.
- feat: external-signer public uploads + single-chunk prepare/finalize
  across 15 SDKs (#90).
- docs+spec: openapi.yaml refreshed for the v1.0 surface, including
  POST /v1/chunks/prepare and /v1/chunks/finalize for single-chunk
  external-signer publish (#126).

## SDK fan-out (PaymentMode + put/get convention, all 15)

#116 antd-go, #117 antd-py/ruby/elixir, #118 antd-rust, #119 antd-csharp,
#120 antd-java, #121 antd-swift, #122 antd-dart, #123 antd-kotlin,
#124 antd-cpp, #125 antd-js/php/zig/lua, #127 antd-mcp.

## SDK example + build fixes

- fix(antd-go): make 03-files example self-contained and runnable (#91)
- fix(examples): make 04-files runnable across cpp/rust/elixir/lua/php/ruby/zig (#93)
- fix(examples): runnable dart 04_files + java Example03Files; add java Example03Chunks (#94)
- feat: gRPC transport example for antd-py and antd-rust (#113)
- feat(antd-py): 07_external_signer example + ant-dev dispatcher entry (#98)
- feat(antd-js): 07-external-signer example + antd-py empty-payments fix (#99)
- feat(rust/go): 07-external-signer examples (#100)
- feat(antd-csharp): 07_external_signer example (#101)
- feat(antd-java): 07_external_signer example (#102)
- feat(antd-kotlin): 07_external_signer example (#103)
- feat(antd-dart): 07_external_signer example (#104)
- feat(antd-ruby): 07_external_signer example (#105)
- feat(antd-php): 07_external_signer example (#106)
- chore(antd-kotlin): drop stale GraphDescendant from local proto copy (#108)

## Docs / infra

- docs: external-signer flow reference + ABI + python smoke test (#97)
- docs: add SECURITY.md with threat model and disclosure policy (#109)
- docs!: refresh per-SDK READMEs + llms-full.txt + openapi.yaml for v1.0 surface (#126)
- ci: add Go lint + test + vuln scanning for antd-go (#112)
- ci: extend antd-rust to sibling-repo parity (fmt + clippy + audit + doc) (#111)
- ci: skip antd/openapi.yaml and llms-full.txt from triggering CI (#128)
- chore(scripts): add full-stack + integration sweep helpers (#96)
- fix(antd-rust): regenerate Cargo.lock to unbreak --locked CI (#110)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant