Skip to content

feat(file): wire the file agent's read/write/write-csv verbs (#268)#269

Merged
pawellisowski merged 1 commit into
mainfrom
feat/file-agent-write-verbs
Jun 25, 2026
Merged

feat(file): wire the file agent's read/write/write-csv verbs (#268)#269
pawellisowski merged 1 commit into
mainfrom
feat/file-agent-write-verbs

Conversation

@pawellisowski

Copy link
Copy Markdown
Contributor

Closes #268.

What

The _core/file agent published a contract (read/write/write-csv/watch) but had no runtime handler — the builtin dispatch in cli/src/runtime/invoker.rs had arms for html-report/ui/vision/viewer-3d/ifc but none for file, so every file/* node fell through to "no handler" and apps referencing it were rejected at validate/compile (status: planned). This wires the three single-shot IO verbs so an app can export a node's output to disk.

How

cli/src/render/file.rs (new, mirroring render/ifc.rs) + 3 dispatch arms:

  • write — string→UTF-8; encoding: base64→raw bytes (land a pre-generated binary artifact like a .xlsx/image/zip); non-string JSON→compact JSON. mode: write — it can overwrite an arbitrary path, so it mutates external state like http.post; a consuming app declares a safety: block (which a frozen node skips).
  • write-csv — RFC-4180 with explicit columns order; object or positional-array rows; a row wider than columns is rejected (no silent truncation).
  • read — UTF-8 text or base64; encoding validated up front; dry-run returns an empty stub (never requires the file to exist).

Disk write gated to a real run (skipped under --dry-run/--simulate), like ifc.write. Outputs report an absolute path. Manifest: agent status planned→available; added encoding input; columns marked required; watch kept per-command status: planned (the #240 streaming watcher is separate). Sidecar docs updated; registry-catalog.json regenerated.

Verification

  • cargo build · clippy -D warnings · fmt --check · full cargo test (0 failures) · agent reindex --check fresh.
  • 10 new file unit tests (base64 round-trip, JSON serialize, dry-run writes nothing, RFC-4180 quoting across object/array rows, over-wide-row rejection, absolute-path output, bad-encoding rejection on both dry-run and live).
  • Real end-to-end aware app run: a .flo with file/write-csv + file/write encoding:base64 (each with a safety: block) wrote a correct RFC-4180 CSV and a PK-magic binary file to disk; a no-safety variant is correctly refused at the run pre-flight.

Review

Dual-reviewed (Codex + pr-review-toolkit). Codex's blocker — read-mode bypasses the safety contract for an arbitrary-path writer — was upheld and fixed by restoring mode: write (consistent with http.post; ifc.write is the narrow domain-export exception). All should-fixes (absolute-path output, encoding-before-dry-run, over-wide-row rejection) and pr-review's doc/folder-alias/columns-required items are addressed.

Why it matters

Unblocks floless.app's steel-takeoff BOM export as freezable run-stage nodes (file/write-csv for CSV; file/write + base64 for a host-baked .xlsx) — and gives the substrate a generic "land tabular/binary output on disk" primitive any app can compose.

The `_core/file` agent published a contract (read/write/write-csv/watch) but had
no runtime handler — the builtin dispatch in invoker.rs had arms for
html-report/ui/vision/viewer-3d/ifc but none for file, so every file/* node
fell through to "no handler" and apps referencing it were rejected at
validate/compile (status: planned).

Wire the three single-shot IO verbs (render/file.rs, mirroring ifc.rs):
- write   — string→UTF-8, encoding:base64→raw bytes (land a pre-generated binary
            artifact like a .xlsx), non-string JSON→compact JSON. mode: write
            (mutates external state like http.post; a consuming app declares a
            `safety:` block, which a frozen node skips).
- write-csv — RFC-4180 with explicit column order; object or positional-array
            rows; a row wider than the columns is rejected (no silent truncation).
- read    — UTF-8 text or base64; dry-run returns an empty stub (never requires
            the file to exist). encoding validated up front.

The disk write is gated to a real run (skipped under --dry-run/--simulate),
exactly like ifc.write. Outputs report an absolute path. The streaming `watch`
event source (#240) stays per-command status: planned until its watcher lands.

Manifest flipped agent status planned→available; added the `encoding` input;
`columns` marked required; sidecar docs updated; registry-catalog regenerated.

Verified: cargo build/clippy -D warnings/fmt, full test suite, 10 new file unit
tests, `agent reindex --check` fresh, and a real `aware app run` writing a
correct CSV + a base64 binary file to disk. Dual-reviewed (Codex + pr-review).
@pawellisowski pawellisowski merged commit 51714f4 into main Jun 25, 2026
1 check passed
pawellisowski added a commit that referenced this pull request Jun 26, 2026
…#269 file manifest fix) (#271)

#269 changed `20-agents/_core/file/manifest.yaml` (status: planned → available) INSIDE the
rolling `main.tar.gz`, but did not touch `registry-index.json`. The agent-install tarball cache
is keyed on `Index::snapshot_fingerprint()` (a hash of registry-index.json), so a manifest change
that leaves the index unchanged never rotates the key — users who cached `main.tar.gz` between
#257 (2026-06-18) and #269 (2026-06-25) keep getting the STALE `file` manifest (status: planned).
`aware app run` then rejects an actually-runnable `file/write` node with E_APP_AGENT_UNAVAILABLE.

Bumping `updated-at` is the documented "manual refresh lever" (registry/index.rs): it rotates the
fingerprint so every stale tarball cache busts on the next install (index is fetched live from main
with a 1h TTL), and `file` installs as status: available. Data-only; `aware agent reindex --check`
stays green (the catalog carries its own timestamp). Systemic fix tracked separately.
pawellisowski added a commit that referenced this pull request Jun 26, 2026
…backs (#270)

Ships the residual tarball-cache staleness fixes merged since v0.81.0:
- #272 (#270): 1h TTL on the agent-install tarball cache so an in-archive manifest
  change that leaves registry-index.json byte-identical self-heals within an hour,
  with graceful fallback to a prior index-consistent cache on offline/corrupt/raced
  refreshes.
- #271: bump registry-index.json updated-at to bust warm caches for the #269 file agent.
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.

file/write and file/write-csv fall through to "no handler" — the file agent's export verbs are unrunnable (and write has no binary mode)

1 participant