Skip to content

Add binary file I/O and MIME type support for web assets - #574

Merged
logbie merged 2 commits into
mainfrom
claude/wfl-binary-file-support-idfziz
Jul 4, 2026
Merged

Add binary file I/O and MIME type support for web assets#574
logbie merged 2 commits into
mainfrom
claude/wfl-binary-file-support-idfziz

Conversation

@logbie

@logbie logbie commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds comprehensive binary file I/O and MIME type detection to WFL, enabling lossless serving of non-text assets (fonts, images, PDFs, etc.) over the web server. Fixes issue #573 by preserving binary content through the entire request/response pipeline.

Key Changes

Binary File I/O

  • read binary / write binary operations in file I/O that preserve bytes exactly, unlike text reads which require valid UTF-8
  • Binary values stored as Value::Binary(Arc<[u8]>) throughout the interpreter
  • Support for reading/writing lists of byte values (0–255) or binary content directly
  • length of <binary> returns byte count for binary values

Web Server Binary Support

  • Request bodies now stored as raw bytes (Vec<u8>) internally, exposed to WFL as:
    • body — lossy UTF-8 text view (backward compatible)
    • body_bytes — lossless binary view for binary uploads
  • Response content stored as raw bytes; text responses encode to UTF-8, binary responses preserve verbatim
  • Binary responses default to application/octet-stream content type (not text/plain)
  • Accurate Content-Length headers calculated from exact byte counts

MIME Type Helper

  • New mime_type of <filename> function maps file extensions to HTTP content types
  • Supports fonts (.ttf, .woff2, etc.), images (.png, .jpg, .svg, .ico), text (.html, .css, .js, .json), and documents (.pdf, .wasm, .zip)
  • Case-insensitive matching; unknown extensions fall back to application/octet-stream
  • Enables static-file routes to serve assets with correct types automatically

Documentation & Testing

  • Added comprehensive binary file I/O guide in Docs/04-advanced-features/file-io.md
  • Added binary serving and MIME type sections in Docs/04-advanced-features/web-servers.md
  • New WFL test program TestPrograms/binary_file_and_mime_test.wfl demonstrating round-trip binary I/O and MIME detection
  • Four Rust integration tests in tests/web_server_binary_test.rs verifying:
    • Byte-identical serving of binary files with custom content types
    • Default application/octet-stream for binary without explicit type
    • Inbound binary request body preservation (echo test)
    • Text responses remain unchanged by the bytes migration

Type System & Analyzer Updates

  • Type checker recognizes mime_type as a built-in returning Type::Text
  • Analyzer updated to include body_bytes: Type::Binary in request object properties
  • length function extended to handle Value::Binary

Implementation Details

  • Binary content flows through the interpreter as Vec<u8> / Arc<[u8]> to avoid lossy UTF-8 conversions
  • HTTP response building uses raw bytes directly; Content-Length is the exact byte count
  • MIME type detection uses only the final extension and basename (e.g., archive.tar.gz.gzapplication/gzip)
  • 50 MB safety limit on binary read/write operations (matching text file limits)
  • Backward compatibility maintained: existing text-based code works unchanged; body still provides lossy UTF-8 text view

https://claude.ai/code/session_019kn1nQnJKZmhXDYZ3DKSv6

The web server was text-only: WflHttpResponse.content and
WflHttpRequest.body were String, so binary values passed to `respond to`
were rendered via `format!("{:?}", ...)` (emitting "[Binary: N bytes]")
and inbound bodies were flattened with from_utf8_lossy. Fonts, images,
and other non-UTF-8 assets could not be served or received losslessly.

Keep bytes end-to-end (additive, backward-compatible):

- Response: WflHttpResponse.content is now Vec<u8>. `respond to req with`
  carries Value::Binary through as raw bytes; text/number/bool keep their
  UTF-8 rendering. Binary responses default to application/octet-stream
  when no content_type is given. The warp reply already emitted bytes.
- Request: WflHttpRequest.body is now Vec<u8>. `body` stays a lossy-UTF-8
  text variable (unchanged behavior); a new `body_bytes` binding exposes
  the raw bytes for binary uploads (write binary / echo).
- New `mime_type of <name>` stdlib helper maps a file name/path to a
  content type by extension (fonts, images, common web types), falling
  back to application/octet-stream.
- `length of <binary>` now returns the byte count.
- Analyzer/typechecker updated: register body_bytes and mime_type,
  allow Binary as response content.

Verified end-to-end: a WFL server reading a real Alegreya .ttf with
`read binary` and responding serves it byte-identical (matching SHA-256,
content-type font/ttf). Adds tests/web_server_binary_test.rs (lossless
serve, octet-stream default, inbound roundtrip, text unchanged),
mime_type unit tests, and TestPrograms/binary_file_and_mime_test.wfl.

Docs: binary file I/O section and binary-serving + body_bytes + mime_type
web-server docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019kn1nQnJKZmhXDYZ3DKSv6
Copilot AI review requested due to automatic review settings July 4, 2026 14:16
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@logbie, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f6ac3427-cb77-4918-8180-2e57ab90a30e

📥 Commits

Reviewing files that changed from the base of the PR and between 8fde366 and 649d0e9.

📒 Files selected for processing (10)
  • Docs/04-advanced-features/file-io.md
  • Docs/04-advanced-features/web-servers.md
  • TestPrograms/binary_file_and_mime_test.wfl
  • src/analyzer/mod.rs
  • src/builtins.rs
  • src/interpreter/mod.rs
  • src/stdlib/list.rs
  • src/stdlib/web.rs
  • src/typechecker/mod.rs
  • tests/web_server_binary_test.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/wfl-binary-file-support-idfziz

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end binary (byte-preserving) content handling to WFL’s file I/O and web server pipeline, plus a mime_type helper for selecting appropriate HTTP Content-Type values when serving static assets (issue #573).

Changes:

  • Introduces binary request/response body plumbing (body_bytes, raw byte responses, accurate Content-Length, octet-stream default for binary).
  • Adds mime_type of <name> builtin (stdlib + builtins registry + typechecker) with extension-based mappings and unit tests.
  • Extends length to support binary values; adds docs + integration/WFL test programs for the new functionality.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/web_server_binary_test.rs New integration tests covering lossless binary serving, inbound body byte preservation, and unchanged text behavior.
TestPrograms/binary_file_and_mime_test.wfl New WFL program demonstrating binary round-trip file I/O and mime_type mappings.
src/typechecker/mod.rs Allows respond to content to be binary and registers mime_type return type.
src/stdlib/web.rs Implements and registers mime_type plus unit tests for extension mapping behavior.
src/stdlib/list.rs Extends length to return byte count for binary values and updates error text.
src/interpreter/mod.rs Switches request/response bodies to raw bytes, adds body_bytes exposure, preserves binary responses, and computes Content-Length from bytes.
src/builtins.rs Registers mime_type as a builtin and defines its arity.
src/analyzer/mod.rs Adds body_bytes: Binary to request object properties for analysis/type hints.
Docs/04-advanced-features/web-servers.md Documents body_bytes, binary responses, default content type, and mime_type usage for static assets.
Docs/04-advanced-features/file-io.md Adds a binary file I/O guide section with examples and safety limit note.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/typechecker/mod.rs Outdated
…esponses

Address PR review: `respond to ... with ...` now accepts text or binary
content, but the typechecker still passed Type::Text as the `expected`
type to type_error, making the diagnostic claim only text was allowed.
Pass None so the message ("Response content must be text or binary")
stands on its own without a misleading "Expected Text" clause.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019kn1nQnJKZmhXDYZ3DKSv6
@logbie
logbie merged commit 1e5847c into main Jul 4, 2026
17 checks passed
@logbie
logbie deleted the claude/wfl-binary-file-support-idfziz branch July 4, 2026 14:57
logbie pushed a commit that referenced this pull request Jul 13, 2026
…ace)

Blocker 1 — resolve the "every vs representative" correctness-gate contradiction:
- Reword the completion claim to per-issue coverage with an explicit,
  representative (not exhaustive) #578 sample; scope the "reproduces every open
  defect" line to defects encoded in this file.
- Add a CLI-level end-to-end #590 guard (complements the in-process test the
  review noted), and a `with`-form-concat #578 reproducer (still reproduces).
- Correct #573 to FIXED: binary read/write + MIME shipped in #574 with byte
  round-trip tests; the issue's own latest verification recommends closing.
- Re-verified with the release binary: #578's `add`-to-List<Any> test-mode drop
  and `double of 5 minus 1` inference items no longer reproduce (fixed), so they
  are not encoded — documented as such.

Blocker 2 — fuzz_module_loading did not fuzz module loading:
- Rename it to `fuzz_frontend` (it fuzzes the static frontend: checked lex →
  parse → analyze → type-check). Mark the module-loading fuzz surface as an
  explicitly OPEN Phase 1 item (safe async loader harness is non-trivial —
  executing fuzzer WFL would also spawn subprocesses/network/web/file writes).
  Diary/exit-gate now say three-of-four surfaces covered, not four.

Blocker 3 — CI could not provide the "authoritative full-suite aggregate":
- ci.yml "Run Tests" now runs `cargo test --workspace` (was root-package-only,
  which skipped wflpkg's 204 tests). Record the observed scope-labeled head-SHA
  numbers (root 1206/0/24 across 76 suites, wfl-lsp 69, wflpkg 204) and note the
  authoritative combined aggregate now comes from the --workspace CI run.

Verified: suite 3 passed / 9 ignored; all 9 ignored reproduce under --ignored;
fmt + clippy -D warnings clean; fuzz crate type-checks after the rename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw
logbie pushed a commit that referenced this pull request Jul 13, 2026
…seline (#614)

Round-3 maintainer blocker — reconcile the audit trail:
- Containment §4: replace the derived/pending estimate with the MEASURED
  full-workspace run — CI run 29240959575 (`cargo test --workspace`): 1480
  passed / 0 failed / 25 ignored across 95 result suites, with the workflow link.
- Containment §2 + test-suite header: state plainly that "convert every known
  correctness defect" is PARTIAL (only #578's reproducible confirmed bugs are
  encoded; exhaustive #578 classification is open) — not a redefinition of
  "every defect" as "every issue".
- Exit-gate: baseline now measured (not pending); the --workspace-aggregate
  follow-up is closed; module-loading fuzz + exhaustive #578 remain the open
  Phase 1 items.
- Inventory diary: correct the #573 row — it was recorded open in error; #574
  shipped binary serving + MIME before the inventory, so it is effectively
  fixed (5 tracked issues genuinely remain open, not 6).

PR description and #610 checkbox reconciliation handled separately via the API.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw
logbie added a commit that referenced this pull request Jul 13, 2026
* test: Phase 1 baseline & containment for #610

Executes the remaining Phase 1 (baseline & containment) tasks of the
production-readiness tracker (#610).

Production-readiness area: Reliability, Testing, Correctness, Maintenance
Tracked issue/risk: #610 Phase 1; defects #569 #571 #592 #578
Gate improved: regression corpus; fuzz targets; supported-platform docs

- Regression suite (tests/phase1_correctness_regression_test.rs): the single
  auditable index of every inventoried correctness defect. Passing guards for
  fixed defects (#569 action-return-type inference; #571 precedence/division/
  modulo/between) and #[ignore]d reproducers for the open ones (#592 bare
  zero-arg included action; five confirmed #578 bugs) that flip green when
  Phase 2 fixes land. Also documents that #578's `ends with` misparse no longer
  reproduces on 26.7.36.

- Fuzz targets (fuzz/): standalone cargo-fuzz workspace with fuzz_lexer,
  fuzz_parser, fuzz_pattern, and fuzz_module_loading, tracked seeds, and a
  README. Kept out of the stable root build via its own [workspace] and root
  `exclude = ["fuzz"]`. Type-checks cleanly against the API on stable; the
  sustained run + corpus retention is Phase 3.

- Supported platforms (Docs/reference/supported-platforms.md): three-tier model
  grounded in the CI matrix (Tier 1 Linux/Windows x86_64; Tier 2 macOS/musl;
  32-bit unsupported), toolchain (stable, MSRV 1.88, edition 2024), runtime
  requirements, and support boundaries. Linked from Docs/README.md and
  SECURITY.md; refreshed the stale SECURITY.md version-support row to 26.7.x.

- Baseline metrics + ExecutionBudget verification + issue inventory/scorecard
  evidence recorded in three Dev diary entries.

Evidence: new regression tests (2 pass, 6 ignored repros verified failing under
--ignored); `cargo check --manifest-path fuzz/Cargo.toml` green; `cargo metadata`
validates the workspace.
Regression protection: every known correctness defect now has an end-to-end test
(passing guard if fixed, ignored reproducer if open).
Compatibility impact: none (tests, docs, and an excluded fuzz workspace only).
Resource impact: none on the shipped runtime.
Remaining work: Phase 2 fixes flip the ignored tests green; sustained fuzz run,
docs-in-CI, coverage instrumentation, and the consistency suite are tracked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* fix: address PR #614 review — honesty corrections, wider coverage, rebase fixups

Re-applies the `time::format_description::parse` → `parse_borrowed::<2>`
deprecation fix (dropped during the rebase onto main) and addresses the
maintainer's request-changes review on #614:

Correctness / tests (tests/phase1_correctness_regression_test.rs):
- #592 now parameterized across top-level AND action-body (both verified fatal),
  so a half-fix can't turn it green.
- #578: added a verified `format_date` friendly-pattern reproducer; tightened
  the pattern (exit-status) and Number+Text (non-zero-exit) assertions so they
  can't false-pass; harness gains a 30s watchdog + drained pipes so a looping
  regression can't consume the job timeout.
- #571 now tests both `divided by` and the `/` symbol as division.
- Honestly reframed #578 as an umbrella issue (representative, not exhaustive);
  noted its nested-`for each` crash did not reproduce and `ends with` is fixed.
- Corrected bookkeeping: execution_budget_test has 32 tests (not 33); coverage
  map header clarified (16 tracked issues + the #610 tracker).

Fuzz:
- fuzz_module_loading broadened to the real static loading path: checked lex →
  parse → include/load-module detection → analyze → type-check, with honest
  scope (no async/FS resolution).
- fuzz_pattern now fuzzes pattern/haystack pairs (ReDoS needs both sides).
- Committed the standalone fuzz/Cargo.lock; fixed README seed/`-timeout`
  commands; added a `fuzz-check` CI job so API drift can't silently break the
  excluded fuzz crate.

Docs honesty:
- supported-platforms.md: added a per-platform PR-CI coverage table; corrected
  that the full test suite is Linux-only, the installer test is nightly/
  post-merge, MSRV 1.88 is declared-not-tested, docs-in-CI is unmet, and PR CI
  publishes no artifacts.
- SECURITY.md: footer → 26.7.37; corrected "no cryptographic functions" and the
  `max_nesting_depth`-as-recursion-defense claims; noted ExecutionBudget.
- containment diary: replaced the mislabeled "CI-measured on fc21f2f" baseline
  with a real local `cargo test --all` methodology (DB suites skip when env
  absent); corrected the run_web_tests coverage claim (no workflow invokes it).

Rebased onto current main; kept #613's canonical inventory/scorecard diaries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* chore: apply PR #614 automated-review nits (Copilot + CodeRabbit)

- supported-platforms.md: rename the "integration-test scripts" lane to
  "Rust integration tests (cargo test --test '*')" — CI runs Rust integration
  tests, not external scripts (Copilot).
- ci.yml fuzz-check: add `needs: fmt` (consistency with every other job) and
  `persist-credentials: false` on checkout (zizmor artipacked; the job only
  runs `cargo check` and needs no write creds) (Copilot + CodeRabbit).
- fuzz/Cargo.toml: edition 2021 → 2024 to match the workspace and surface
  edition-specific breakages in the fuzz crate (Copilot). Verified it still
  type-checks under 2024.
- fuzz/README.md: add a `text` language tag to the layout fenced block
  (markdownlint MD040) (CodeRabbit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* fix: address PR #614 round-2 blockers (honesty, fuzz scope, CI workspace)

Blocker 1 — resolve the "every vs representative" correctness-gate contradiction:
- Reword the completion claim to per-issue coverage with an explicit,
  representative (not exhaustive) #578 sample; scope the "reproduces every open
  defect" line to defects encoded in this file.
- Add a CLI-level end-to-end #590 guard (complements the in-process test the
  review noted), and a `with`-form-concat #578 reproducer (still reproduces).
- Correct #573 to FIXED: binary read/write + MIME shipped in #574 with byte
  round-trip tests; the issue's own latest verification recommends closing.
- Re-verified with the release binary: #578's `add`-to-List<Any> test-mode drop
  and `double of 5 minus 1` inference items no longer reproduce (fixed), so they
  are not encoded — documented as such.

Blocker 2 — fuzz_module_loading did not fuzz module loading:
- Rename it to `fuzz_frontend` (it fuzzes the static frontend: checked lex →
  parse → analyze → type-check). Mark the module-loading fuzz surface as an
  explicitly OPEN Phase 1 item (safe async loader harness is non-trivial —
  executing fuzzer WFL would also spawn subprocesses/network/web/file writes).
  Diary/exit-gate now say three-of-four surfaces covered, not four.

Blocker 3 — CI could not provide the "authoritative full-suite aggregate":
- ci.yml "Run Tests" now runs `cargo test --workspace` (was root-package-only,
  which skipped wflpkg's 204 tests). Record the observed scope-labeled head-SHA
  numbers (root 1206/0/24 across 76 suites, wfl-lsp 69, wflpkg 204) and note the
  authoritative combined aggregate now comes from the --workspace CI run.

Verified: suite 3 passed / 9 ignored; all 9 ignored reproduce under --ignored;
fmt + clippy -D warnings clean; fuzz crate type-checks after the rename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* chore: apply PR #614 Copilot review nits (test markers, docs)

- #590 CLI guard: assert the exact labeled marker `VAL=0` (was `contains('0')`,
  which could match unrelated output) — program now prints `display "VAL=" with …`.
- #592 action-body reproducer: invoke run_it with an explicit `call` (was a bare
  `display run_it`) so the test stays focused on included-action name resolution
  and doesn't depend on top-level bare-call semantics. Still reproduces (exit 3).
- fuzz/README: add a naming note that the frontend target was renamed from
  `fuzz_module_loading` → `fuzz_frontend` (for readers cross-referencing older PR
  text).

Declined (incorrect): Copilot flagged `cargo test --test '*'` in the platform doc
as invalid — it is a valid cargo glob and the exact command `ci.yml` runs
(line 165), so the doc accurately mirrors CI; left unchanged.

Verified: suite 3 passed / 9 ignored; all 9 ignored reproduce; fmt + clippy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* chore: apply PR #614 Copilot nits — doc link + drop redundant LSP test step

- supported-platforms.md: make the macOS row's `installation.md` reference a
  proper relative link (`../02-getting-started/installation.md`).
- ci.yml: remove the now-redundant `Run LSP Tests` step — `cargo test
  --workspace` (added earlier in this PR) already runs wfl-lsp's tests, so the
  separate `cargo test -p wfl-lsp` only duplicated them. Kept the explicit
  `Build LSP` step as a focused Send/Sync build gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* chore: apply PR #614 Copilot nits (fuzz perf, exact clippy cmd, robust count)

- fuzz_frontend: drop the redundant `Analyzer::analyze` call — `check_types`
  already runs the analyzer internally (analyzer_already_run == false), so the
  separate call double-analyzed each input and halved fuzz throughput. One
  `check_types` call still exercises both analysis and type checking.
- containment diary: record the exact CI clippy gate `cargo clippy --all-targets
  -- -D warnings` (dropped the stray `--all-features` that CI does not pass).
- #578 repeat test: count lines equal to `hi` instead of substring
  `matches("hi")`, so a diagnostic containing "this"/"which" can't inflate it.

Verified: fuzz crate type-checks; suite 3 passed / 9 ignored; the repeat
reproducer still fails under --ignored with the robust count; fmt + clippy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* docs: correct wfl_exe() comment re test profile (PR #614 Copilot nit)

CI's test job runs `cargo test --workspace` (debug), not `--release`, so the
prior comment claiming the integration suite runs under `--release` was
misleading about which profile `CARGO_BIN_EXE_wfl` points to. Reword to state
it matches the test's own compile profile (debug by default / under
--workspace; release only under `cargo test --release`).

(The #571 slash-division coverage the codex bot flagged is already present —
`store c as 10 / 4` with a `C=2.5` assertion — added in an earlier commit.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* chore: enforce committed fuzz lockfile with --locked; clarify fuzz run cwd

PR #614 Copilot nits:
- ci.yml fuzz-check + fuzz/README type-check: use `cargo check --locked
  --manifest-path fuzz/Cargo.toml` so dependency-resolution drift fails the job
  instead of silently regenerating the committed fuzz/Cargo.lock. Verified
  `--locked` passes against the current lockfile.
- fuzz/README: state that the fuzz commands are run from the repository root
  (`cargo fuzz` locates `fuzz/` automatically; the seeding commands use
  repo-root-relative `fuzz/…` paths), resolving the "which manifest?" ambiguity.

Note: the codex bot's "#571 slash division" thread keeps re-surfacing because its
line moved, but the `/` case is already covered (`store c as 10 / 4` → `C=2.5`).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* docs: align Phase 1 record with honest partial state + measured CI baseline (#614)

Round-3 maintainer blocker — reconcile the audit trail:
- Containment §4: replace the derived/pending estimate with the MEASURED
  full-workspace run — CI run 29240959575 (`cargo test --workspace`): 1480
  passed / 0 failed / 25 ignored across 95 result suites, with the workflow link.
- Containment §2 + test-suite header: state plainly that "convert every known
  correctness defect" is PARTIAL (only #578's reproducible confirmed bugs are
  encoded; exhaustive #578 classification is open) — not a redefinition of
  "every defect" as "every issue".
- Exit-gate: baseline now measured (not pending); the --workspace-aggregate
  follow-up is closed; module-loading fuzz + exhaustive #578 remain the open
  Phase 1 items.
- Inventory diary: correct the #573 row — it was recorded open in error; #574
  shipped binary serving + MIME before the inventory, so it is effectively
  fixed (5 tracked issues genuinely remain open, not 6).

PR description and #610 checkbox reconciliation handled separately via the API.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* docs: make Phase 1 open-item count + #578 phase-ownership consistent (#614)

Round-4 maintainer blocker — the sources disagreed on what's left in Phase 1.
Reconciled to a single answer across tracker/PR/diary/test-header:

- THREE open Phase 1 items (matching the three unchecked #610 boxes):
  (1) module-loading fuzz target; (2) exhaustive per-item #578 classification;
  (3) line-coverage baseline (not instrumented). The containment exit-gate now
  lists all three and no longer moves coverage to the Phase 2/3 hand-offs.
- #578 phase ownership made consistent: classification / regression coverage of
  #578 is **Phase 1** work (part of "convert every known correctness defect");
  *fixing* #578 is Phase 2. Fixed the test header line that called classification
  "Phase 2 scoping work".
- §4 line-coverage row reframed as the open Phase 1 baseline-coverage item.

PR description updated separately to say three items; #610 already shows three
unchecked Phase 1 boxes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* docs: reconcile inventory open-count with the #573 correction (#614)

The summary total still read "6 tracked" while the #573 reclassification note +
table say 5. Update line 28 to "~~6~~ 5 tracked" so the inventory is internally
consistent (CodeRabbit nit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* docs: clarify run_files output ordering (PR #614 Copilot nit)

The helper concatenates stdout then stderr (`format!("{stdout}{stderr}")`); the
doc comment said "merged stdout+stderr", which reads as time-interleaved. Clarify
that both streams are captured in full but not interleaved by time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* test: harden #578 exit-code assertions against timeout false-pass (PR #614)

`code != Some(0)` also accepts a timeout kill (`code == None`), so a future
interpreter hang would let these acceptance tests pass green. Require an explicit
non-zero exit via `matches!(code, Some(c) if c != 0)` in the Number+Text and
`with`-form #578 reproducers, so exit 0 AND a hang/timeout both fail (Copilot).

Verified: suite 3 passed / 9 ignored; both hardened reproducers still fail under
--ignored; fmt + clippy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* fix: close #578 timeout false-pass + reconcile inventory arithmetic (PR #614)

Two maintainer blockers on PR #614:

- tests/phase1_correctness_regression_test.rs: the `with`-form #578
  reproducer's success branch (`out.contains("42")`) ignored the exit
  code, so "prints 42 then hangs" (code == None) still went green. Pin
  the success branch to `code == Some(0)` and match an exact output line
  (`line.trim() == "42"`) so a timeout kill or a stray `42` in
  diagnostics can no longer pass. The failure branch already required a
  concrete non-zero exit.

- Dev diary inventory: the totals could not reconcile (10 closed + 5
  remaining + #610 = 16, not 17) and conflated two senses of "open".
  Replace the summary with an explicit reconciliation table: 10
  verified-fixed-and-closed + 1 verified-fixed-but-open-pending-closure
  (#573) + 5 genuinely-unresolved = 16 tracked, + #610 = 17. "Open on
  GitHub" (6) vs "genuinely unresolved" (5) are now distinguished.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* test: drain harness output as bytes + from_utf8_lossy (PR #614)

`run_files` drained child stdout/stderr with `read_to_string`, which
returns an error and stops capturing if the program emits any non-UTF-8
byte — silently truncating the very output these regression assertions
check. Drain as raw bytes via `read_to_end` and decode with
`String::from_utf8_lossy` so non-UTF-8 becomes U+FFFD instead of
dropping capture, matching the other integration tests' behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* test: narrow #592 guard to the fatal diagnostic form (PR #614)

The #592 reproducer asserted `!out.contains("is not defined")`, which
would also false-fail on a benign non-fatal note (e.g. "This action is
not defined in this file …") once the fix lands. Match only the fatal
`Variable 'greet' is not defined` form the issue actually emits, so the
guard flags the real defect and nothing else.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* docs: reclassify #600 as an open High security/release risk (PR #614)

#600 was classified "post-production, not a release-gate blocker", but
its native-TLS refactor is the vehicle for clearing open high-severity
Dependabot alert #49 (rustls-webpki DoS via panic). The vulnerable chain
is still live in Cargo.lock (warp 0.3.7 -> tokio-rustls 0.25.0 -> rustls
0.22.4 -> rustls-webpki 0.102.8) and warp pins it, so no in-line bump
exists. That contradicts the mandatory no-open-high-severity-security
release gate.

Reclassify #600 as High (security) across all in-repo evidence and make
the exit reads honest:
- inventory diary: #600 row, severity legend (High now covers an open
  high-severity security advisory), and Phase 1 exit-gate read (the
  release gate stays open; this PR classifies, does not fix).
- regression-index header (tests/...): #600 row.
- containment diary exit-gate read: #600 added as a third open High.

This PR classifies the risk; it does not implement the TLS rewrite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* ci: gate bump-version on fuzz-check; test: policy-agnostic #578 reject (PR #614)

- ci.yml: add `fuzz-check` to `bump-version.needs`. It was the only
  compile gate for the excluded fuzz workspace, but the write-capable
  bump-version job (documented "only after ALL checks pass") omitted it,
  so a push to main could tag a version while fuzz-check was red — and
  the bump commit carries [skip ci], so no corrective rerun follows.
  (Maintainer blocker.)

- phase1 regression test: the #578 Number+Text reproducer required a
  non-zero exit to count as "rejected", but WFL type errors are
  non-fatal (a "Type checking warnings:" diagnostic, exit 0) — only
  ExecutionBudget breaches are fatal (src/main.rs). That baked in an
  exit-code policy the runtime doesn't use for type errors, so the
  reproducer could never flip green on a natural fix. Accept a non-zero
  exit OR an explicit type-checker diagnostic on a completed run; a
  timeout (code == None) still fails both branches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* test: make the #610 regression harness hermetic against global config (PR #614)

`run_files` spawned the wfl binary inheriting the ambient environment, so
the child could read a machine-global `/etc/wfl/wfl.cfg` (or the legacy
`/etc/wfl/.wflcfg` fallback) and silently change timeouts/limits/behavior,
making the suite non-hermetic and flaky outside CI.

Pin `WFL_GLOBAL_CONFIG_PATH` to an empty `NamedTempFile` for the child.
The file must exist and be empty: per src/config.rs the loader falls back
to the legacy `/etc/wfl/.wflcfg` when the configured path is missing, so a
nonexistent path would not isolate it. The temp file lives outside the
working dir (directory-listing reproducers can't see it) and stays in
scope until after the child exits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* ci: keep fuzz/Cargo.lock in sync during the auto version bump (PR #614)

`fuzz/` is a separate workspace that path-depends on root `wfl`, so
`fuzz/Cargo.lock` pins the root version too. `scripts/bump_version.py`'s
`update_cargo_lock()` only refreshed the root lock and never staged
`fuzz/Cargo.lock`, so every post-merge `--update-all` bump left the fuzz
lock stale at the old version. Because the bump commit carries
`[skip ci]`, the breakage surfaced only on the *next* PR, whose
`cargo check --locked --manifest-path fuzz/Cargo.toml` (`fuzz-check`)
would fail. Adding `fuzz-check` to `bump-version.needs` gates the
pre-bump state but not the mutation the bump itself makes.

Add `update_fuzz_cargo_lock()`: after the root lock is updated it runs
`cargo update -p wfl --manifest-path fuzz/Cargo.toml`, verifies the fuzz
lock now records the new version, runs the same `cargo check --locked`
gate to prove the mutation is consistent before anything is
committed/tagged, and stages `fuzz/Cargo.lock`. It's called from the
`--update-all` path, so retry bumps (which re-run the whole script) are
covered. Verified locally: the sync command re-pins a deliberately
stale fuzz lock back to the root version.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* docs: correct #600 — vulnerable dep present but not reachable (PR #614)

A source-level reachability re-review shows the earlier "#600 = High
(security)" reclassification overreached: it treated the *presence* of
`rustls-webpki` (Dependabot alert #49 / GHSA-82j2-j2ch-gfr8) in the
dependency graph as WFL exploitability. The advisory's panic requires
opt-in `RevocationOptions` AND attacker-controlled CRL bytes; default
rustls configs are unaffected. WFL's only TLS setup is
`warp::serve(routes).tls().cert_path().key_path()`
(src/interpreter/mod.rs:6441) with client auth off and NO CRL /
`RevocationOptions` anywhere (verified by grep), so the vulnerable path
is not reachable.

Re-disposition across the audit evidence: alert #49 = "vulnerable code
not used"; #600 is the separate SNI / multi-cert enhancement
(post-production), NOT a reachable High WFL defect, and its TLS rewrite
is not established as required remediation. The literal
no-open-high-severity-security policy gate may stay administratively open
until #49 is formally triaged. Updated the inventory #600 row + severity
legend (High now requires a *reachable* advisory, not mere presence) +
exit-gate read, the containment exit-gate read, and the regression-index
header row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* test: separate stdout/stderr with a newline in the #610 harness (PR #614)

`run_files` joined the captured stdout and stderr with no delimiter, so a
`contains(...)` / exact-line assertion could false-match a substring that
straddled the boundary (end of stdout + start of stderr). Insert a
newline separator between the two captures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

* refactor: share the wfl-lock version regex in bump_version.py (PR #614)

Extract `_extract_wfl_lock_version(lock_path)` and use it from both
`update_cargo_lock` (root) and `update_fuzz_cargo_lock` (fuzz workspace),
removing the duplicated `[[package]] name = "wfl"` parse so the two
copies can't drift if the Cargo.lock format ever changes. Behaviour is
unchanged: same regex, same "not found" hard-fail. (CodeRabbit nit.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016egRFdqLHCiAttAGQMoFZw

---------

Co-authored-by: Claude <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.

3 participants