diff --git a/Docs/04-advanced-features/web-servers.md b/Docs/04-advanced-features/web-servers.md
index 231ce23c..41b1b97a 100644
--- a/Docs/04-advanced-features/web-servers.md
+++ b/Docs/04-advanced-features/web-servers.md
@@ -1044,6 +1044,28 @@ otherwise:
end check
```
+## Worked example: Laravel starter app
+
+WFL can express the **public HTTP surface** of the official Laravel
+application skeleton ([laravel/laravel](https://github.com/laravel/laravel)
+13.x) — the starter app, not the framework. That skeleton is thin: `GET /`
+(welcome), `GET /up` (health), `GET /robots.txt`, a 404 fallback, and
+method-not-allowed on the registered paths.
+
+The port lives at [`examples/laravel-app/`](../../examples/laravel-app/README.md):
+
+```bash
+wfl examples/laravel-app/app.wfl
+```
+
+Open `http://127.0.0.1:8000`. The welcome page, `/up`, and `/robots.txt` are
+the same routes Laravel's skeleton exposes. Welcome-page copy and CSS are
+original WFL; they are not a Blade or Tailwind reconstruction.
+
+WFL does **not** port Eloquent, Blade, Artisan, Vite, queues, mail, or
+sessions. Those live in `laravel/framework` and have no equivalent here. The
+example README's capability map lists every mapped route and every gap.
+
## Testing Your Server
### With a Browser
diff --git a/Docs/README.md b/Docs/README.md
index 3c812e2c..41e9f9e7 100644
--- a/Docs/README.md
+++ b/Docs/README.md
@@ -541,6 +541,7 @@ Every code example is tested with the WFL compiler to ensure accuracy!
- **[Installation](02-getting-started/installation.md)** - Get started in 5 minutes
- **[Language Basics](03-language-basics/index.md)** - Core concepts
- **[TestPrograms](../TestPrograms/)** - 90+ example programs
+- **[Laravel starter-app port](../examples/laravel-app/README.md)** - WFL port of laravel/laravel 13.x (the app, not the framework)
- **[Contributing](contributing/contributing-guide.md)** - Workflow + [root CONTRIBUTING.md](../CONTRIBUTING.md)
### Policy Documents
diff --git a/Engineering/designs/2026-09-02-laravel-app-port.md b/Engineering/designs/2026-09-02-laravel-app-port.md
new file mode 100644
index 00000000..d1dc612f
--- /dev/null
+++ b/Engineering/designs/2026-09-02-laravel-app-port.md
@@ -0,0 +1,49 @@
+# Design: port laravel/laravel (the app) to WFL
+
+**Status:** Implemented as `examples/laravel-app/`
+**Source:** [laravel/laravel](https://github.com/laravel/laravel) 13.x
+**Risk class:** R2 (example + HTTP contract; no change to the concurrent
+listen/respond/stream implementation — §11.3 stays on those primitives)
+
+## What “the actual app” is
+
+`laravel/laravel` is the application skeleton users get from `laravel new`.
+It is not the framework. On 13.x the runnable HTTP surface is:
+
+1. `GET|HEAD /` → `resources/views/welcome.blade.php`
+2. `GET|HEAD /up` → framework health registered in `bootstrap/app.php`
+3. `GET|HEAD /robots.txt` → `User-agent: *` / `Disallow:`
+4. Unknown paths → 404
+5. Other methods on those registered paths → 405
+
+Plus unused-but-present scaffolding: empty `Controller`, `User` model,
+`artisan inspire`, Vite/CSS/JS entrypoints, config, migrations.
+
+## Approach
+
+Port the **observable app**, not the framework.
+
+- Keep WFL idioms (`route`, `include from`, `listen`, containers).
+- Extract routing into testable actions (`status_for_path`, `body_for_path`,
+ `content_type_for_path`) so `TestPrograms/` can assert without a socket,
+ and `tests/laravel_app_http_test.rs` can assert the real binary over HTTP.
+- Write original welcome-page copy and CSS. Do not reconstruct Blade,
+ Tailwind, or Laravel’s logo/SVG.
+- Document every gap in `examples/laravel-app/README.md`. A missing
+ framework feature is a documented gap, not a silent pretence.
+
+## Rejected alternatives
+
+1. **Reimplement Laravel in WFL** (service container, Eloquent, Blade,
+ Artisan). WFL does not have those primitives; claiming them would
+ violate the docs-honesty rule.
+2. **Single-file demo that only prints “Hello”.** That is not a port of
+ the starter app’s public routes.
+3. **Copy `welcome.blade.php` verbatim.** Copyrighted; also fights WFL’s
+ no-unlearning / original-example bar.
+
+## Residual gaps (by design)
+
+Eloquent, Blade, Vite, Artisan, queues, cache, mail, sessions,
+broadcasting, service providers, and `.env` config beyond bind address.
+See the capability map in the example README.
diff --git a/Engineering/evidence/2026-09-02-laravel-app-port-green.md b/Engineering/evidence/2026-09-02-laravel-app-port-green.md
new file mode 100644
index 00000000..eef48b24
--- /dev/null
+++ b/Engineering/evidence/2026-09-02-laravel-app-port-green.md
@@ -0,0 +1,19 @@
+# Green evidence — Laravel starter-app port
+
+**Date:** 2026-09-02
+**Risk class:** R2 — new example and HTTP contract. This change does **not**
+modify `main loop concurrently:`, request handling, or streaming statements
+(testing.md §11.3). Those primitives already have R3 coverage in
+`tests/concurrent_*.rs` / `tests/http_*.rs`; this example only calls them.
+**Red ancestor:** `06b3368` (`test: add failing Laravel starter-app port coverage`)
+
+## Layers
+
+| Layer | Command | Result |
+|---|---|---|
+| WFL feature tests | `./target/release/wfl --test TestPrograms/laravel_app/laravel_starter_app.test.wfl` | 16 passed, 0 failed, exit 0 |
+| Rust HTTP e2e | `cargo test --test laravel_app_http_test` | 5 passed, 0 failed |
+| Inspire CLI | `./target/release/wfl examples/laravel-app/inspire.wfl` | printed `Readability is a feature, not a luxury.` |
+| Hygiene | `python3 scripts/check_repo_hygiene.py --mode static` | exit 0 |
+
+HTTP e2e (real `wfl` binary, real sockets): `GET /` 200, `GET /up` 200 + “Application up”, `GET /robots.txt` skeleton body, unknown path 404, `POST /` 405.
diff --git a/Engineering/evidence/2026-09-02-laravel-app-port-red.md b/Engineering/evidence/2026-09-02-laravel-app-port-red.md
new file mode 100644
index 00000000..c6a1af3e
--- /dev/null
+++ b/Engineering/evidence/2026-09-02-laravel-app-port-red.md
@@ -0,0 +1,19 @@
+# Red evidence — Laravel starter-app port
+
+**Date:** 2026-09-02
+**Risk class:** R2 (new user-facing example + HTTP contract; no runtime/concurrency change)
+**Command:** `./target/release/wfl --test TestPrograms/laravel_app/laravel_starter_app.test.wfl`
+**Exit:** 1
+**Result:** 16 tests, 1 passed (`that true is true`), 15 failed for the intended reason.
+
+Stub router returns `418` / `"stub"` / `"application/octet-stream"` so the failures are assertion mismatches, not missing files or parse errors:
+
+- `GET /` / `HEAD /` / `GET /up` / `GET /robots.txt` expected 200, got 418
+- unknown path expected 404, got 418
+- `POST /` expected 405, got 418
+- welcome / health / robots / 404 / 405 bodies did not contain the required markers
+- HTML and `text/plain` content types were `application/octet-stream`
+- `User.display_name()` returned `""` instead of the stored name
+- `inspire_quote` length was 0
+
+This Red run is an ancestor of the Green implementation commit on `cursor/laravel-app-port-b108`.
diff --git a/History/dev-diary/2026/2026-09-02-laravel-app-port.md b/History/dev-diary/2026/2026-09-02-laravel-app-port.md
new file mode 100644
index 00000000..8f3ff77c
--- /dev/null
+++ b/History/dev-diary/2026/2026-09-02-laravel-app-port.md
@@ -0,0 +1,37 @@
+# 2026-09-02 — Port of laravel/laravel (the starter app) to WFL
+
+## What this is
+
+A faithful-as-able port of the official Laravel **application skeleton**
+([laravel/laravel](https://github.com/laravel/laravel) 13.x) — the repo you
+get from `laravel new`, not `laravel/framework`.
+
+## What mapped
+
+The 13.x skeleton's public HTTP surface is small, and WFL can express it:
+
+- `GET /` welcome HTML (original copy and CSS; not a Blade reconstruction)
+- `GET /up` health, the route `bootstrap/app.php` registers
+- `GET /robots.txt` with the skeleton's two-line body
+- unknown paths → 404
+- non-GET/HEAD on registered paths → 405
+- a `User` container for `app/Models/User.php`'s name + email fields
+- `inspire.wfl` as the `artisan inspire` stand-in
+
+Routing is extracted into actions so
+`TestPrograms/laravel_app/laravel_starter_app.test.wfl` can assert without a
+socket, and `tests/laravel_app_http_test.rs` drives the real `wfl` binary
+over HTTP.
+
+## What did not map
+
+Eloquent, Blade, Vite, Artisan, queues, cache, mail, sessions,
+broadcasting, service providers, and most of `.env` / `config/*.php`. Those
+are framework features. The example README's capability map lists them as
+gaps rather than pretending they shipped.
+
+## Tests
+
+Red: stub router returned 418 / `"stub"`; 15 of 16 feature tests failed on
+assertions (`Engineering/evidence/2026-09-02-laravel-app-port-red.md`).
+Green: the same file, plus the HTTP e2e suite.
diff --git a/TestPrograms/laravel_app/laravel_starter_app.test.wfl b/TestPrograms/laravel_app/laravel_starter_app.test.wfl
new file mode 100644
index 00000000..742a7f23
--- /dev/null
+++ b/TestPrograms/laravel_app/laravel_starter_app.test.wfl
@@ -0,0 +1,111 @@
+// Feature + unit coverage for the WFL port of laravel/laravel (13.x starter app).
+// Mirrors Laravel's tests/Feature/ExampleTest.php (GET / is 200) and
+// tests/Unit/ExampleTest.php, and adds the other public routes the skeleton
+// actually exposes: /up (bootstrap health), /robots.txt, 404, and 405.
+
+include from "../../examples/laravel-app/routes.wfl"
+include from "../../examples/laravel-app/user.wfl"
+include from "../../examples/laravel-app/quotes.wfl"
+
+describe "Example":
+ test "that true is true":
+ expect yes to be yes
+ end test
+end describe
+
+describe "Laravel starter app routes":
+ test "GET / returns 200":
+ store reply_status as status_for_path of "/" and "GET"
+ expect reply_status to equal 200
+ end test
+
+ test "HEAD / returns 200":
+ store reply_status as status_for_path of "/" and "HEAD"
+ expect reply_status to equal 200
+ end test
+
+ test "GET /up returns 200":
+ store reply_status as status_for_path of "/up" and "GET"
+ expect reply_status to equal 200
+ end test
+
+ test "GET /robots.txt returns 200":
+ store reply_status as status_for_path of "/robots.txt" and "GET"
+ expect reply_status to equal 200
+ end test
+
+ test "GET /missing returns 404":
+ store reply_status as status_for_path of "/missing" and "GET"
+ expect reply_status to equal 404
+ end test
+
+ test "POST / returns 405":
+ store reply_status as status_for_path of "/" and "POST"
+ expect reply_status to equal 405
+ end test
+end describe
+
+describe "Laravel starter app responses":
+ test "welcome page identifies the WFL starter app":
+ store welcome_html as body_for_path of "/" and "GET"
+ expect welcome_html to contain "Your starter app is ready"
+ expect welcome_html to contain "WFL starter"
+ end test
+
+ test "health page reports the application is up":
+ store health_html as body_for_path of "/up" and "GET"
+ expect health_html to contain "Application up"
+ end test
+
+ test "robots.txt matches the Laravel skeleton":
+ store robots_body as body_for_path of "/robots.txt" and "GET"
+ expect robots_body to contain "User-agent: *"
+ expect robots_body to contain "Disallow:"
+ end test
+
+ test "unknown path returns a Not Found body":
+ store missing_html as body_for_path of "/no-such-route" and "GET"
+ expect missing_html to contain "Not Found"
+ end test
+
+ test "POST / returns a method-not-allowed body":
+ store not_allowed_body as body_for_path of "/" and "POST"
+ expect not_allowed_body to contain "Method Not Allowed"
+ end test
+
+ test "HTML routes advertise an HTML content type":
+ store welcome_type as content_type_for_path of "/"
+ store health_type as content_type_for_path of "/up"
+ store missing_type as content_type_for_path of "/missing"
+ expect welcome_type to equal "text/html"
+ expect health_type to equal "text/html"
+ expect missing_type to equal "text/html"
+ end test
+
+ test "robots.txt is served as text/plain":
+ store robots_type as content_type_for_path of "/robots.txt"
+ expect robots_type to equal "text/plain"
+ end test
+end describe
+
+describe "Laravel starter app User model":
+ test "a User exposes name and email":
+ create new User as demo_user:
+ name is "Test User"
+ email is "test@example.com"
+ end
+ store person_name as demo_user.display_name()
+ store person_email as demo_user.email_address()
+ expect person_name to equal "Test User"
+ expect person_email to equal "test@example.com"
+ end test
+end describe
+
+describe "Laravel starter app inspire command":
+ test "inspire_quote returns a non-empty quote":
+ store quote as inspire_quote
+ expect quote to be of type "Text"
+ store quote_length as length of quote
+ expect quote_length to be greater than 10
+ end test
+end describe
diff --git a/examples/laravel-app/.wflcfg b/examples/laravel-app/.wflcfg
new file mode 100644
index 00000000..22e7cf76
--- /dev/null
+++ b/examples/laravel-app/.wflcfg
@@ -0,0 +1,2 @@
+# Bind the starter app to loopback. Override to 0.0.0.0 to expose it.
+web_server_bind_address = 127.0.0.1
diff --git a/examples/laravel-app/README.md b/examples/laravel-app/README.md
new file mode 100644
index 00000000..740f8b38
--- /dev/null
+++ b/examples/laravel-app/README.md
@@ -0,0 +1,64 @@
+# WFL port of the Laravel application skeleton
+
+This directory is a WFL port of the official
+[laravel/laravel](https://github.com/laravel/laravel) **starter app** (13.x),
+not the Laravel framework.
+
+Laravel's application repo is intentionally thin: a welcome page, a health
+endpoint, a `robots.txt`, an empty controller, a User model, and an `inspire`
+console command. Everything else (Eloquent, Blade, Artisan, Vite, queues,
+mail, sessions) lives in `laravel/framework` and is **not** claimed here.
+
+## Run
+
+```bash
+wfl examples/laravel-app/app.wfl
+```
+
+Then open `http://127.0.0.1:8000`.
+
+```bash
+wfl examples/laravel-app/inspire.wfl # artisan inspire equivalent
+```
+
+`.wflcfg` binds the server to `127.0.0.1`. Change `web_server_bind_address` to
+`0.0.0.0` only when you intend to expose it.
+
+## Capability map
+
+| Laravel 13.x skeleton | WFL port | Status |
+|---|---|---|
+| `GET /` → `view('welcome')` | `welcome_page` HTML | Mapped (original copy/CSS) |
+| `GET /up` health (`bootstrap/app.php`) | `/up` → 200 + “Application up” | Mapped |
+| `public/robots.txt` | same two-line body | Mapped |
+| Unknown path | 404 HTML | Mapped |
+| `Route::get` (GET/HEAD only) | other methods → 405 | Mapped |
+| `app/Models/User.php` | `User` container (`name`, `email`) | Partial — no Eloquent, casts, or auth |
+| `artisan inspire` | `inspire.wfl` / `inspire_quote` | Partial — original quote, not the framework catalog |
+| `app/Http/Controllers/Controller.php` | (empty in Laravel) | Nothing to port |
+| `app/Providers/AppServiceProvider.php` | — | Not ported (no service container) |
+| Blade / Vite / Tailwind | inline HTML in `views.wfl` | Not ported |
+| Eloquent, migrations, factories, seeders | — | Not ported |
+| Artisan, queues, cache, mail, sessions, broadcasting | — | Not ported |
+| `.env` / `config/*.php` | `.wflcfg` bind address only | Partial |
+| PHPUnit Feature/Unit example tests | `TestPrograms/laravel_app/laravel_starter_app.test.wfl` | Mapped + expanded |
+
+## Layout
+
+```text
+app.wfl front controller (public/index.php + listen)
+routes.wfl route table (routes/web.php + /up)
+views.wfl welcome, health, 404, robots bodies
+user.wfl User container
+quotes.wfl inspire_quote
+inspire.wfl console inspire command
+public/robots.txt crawler policy
+.wflcfg loopback bind
+```
+
+## Tests
+
+```bash
+wfl --test TestPrograms/laravel_app/laravel_starter_app.test.wfl
+cargo test --test laravel_app_http_test
+```
diff --git a/examples/laravel-app/app.wfl b/examples/laravel-app/app.wfl
new file mode 100644
index 00000000..f0107e6e
--- /dev/null
+++ b/examples/laravel-app/app.wfl
@@ -0,0 +1,29 @@
+// Front controller for the WFL port of laravel/laravel 13.x.
+// Mirrors public/index.php + bootstrap/app.php: listen, then dispatch routes.
+
+include from "routes.wfl"
+
+store listen_port as 8000
+
+display "WFL starter (laravel/laravel port) on port " with listen_port
+display "Routes: GET / , GET /up , GET /robots.txt"
+
+create map allow_get_head:
+ "Allow" is "GET, HEAD"
+end map
+
+listen on port listen_port as laravel_app
+
+main loop concurrently:
+ wait for request comes in on laravel_app as incoming_request
+ store request_path as path of incoming_request
+ store request_method as method of incoming_request
+ store reply_status as status_for_path of request_path and request_method
+ store reply_body as body_for_path of request_path and request_method
+ store reply_type as content_type_for_path of request_path
+ check if reply_status is equal to 405:
+ respond to incoming_request with reply_body and status reply_status and content_type reply_type and headers allow_get_head
+ otherwise:
+ respond to incoming_request with reply_body and status reply_status and content_type reply_type
+ end check
+end loop
diff --git a/examples/laravel-app/inspire.wfl b/examples/laravel-app/inspire.wfl
new file mode 100644
index 00000000..03cd11b9
--- /dev/null
+++ b/examples/laravel-app/inspire.wfl
@@ -0,0 +1,4 @@
+// Equivalent of `php artisan inspire` from routes/console.php.
+include from "quotes.wfl"
+store quote as inspire_quote
+display quote
diff --git a/examples/laravel-app/public/robots.txt b/examples/laravel-app/public/robots.txt
new file mode 100644
index 00000000..eb053628
--- /dev/null
+++ b/examples/laravel-app/public/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:
diff --git a/examples/laravel-app/quotes.wfl b/examples/laravel-app/quotes.wfl
new file mode 100644
index 00000000..a244c92c
--- /dev/null
+++ b/examples/laravel-app/quotes.wfl
@@ -0,0 +1,6 @@
+// Stand-in for Laravel's `artisan inspire` command. The quote list is original
+// WFL text, not the framework's Inspiring::quote() catalog.
+
+define action called inspire_quote:
+ return "Readability is a feature, not a luxury."
+end action
diff --git a/examples/laravel-app/routes.wfl b/examples/laravel-app/routes.wfl
new file mode 100644
index 00000000..45b01342
--- /dev/null
+++ b/examples/laravel-app/routes.wfl
@@ -0,0 +1,52 @@
+include from "views.wfl"
+
+// Public routes of laravel/laravel 13.x:
+// GET|HEAD / welcome
+// GET|HEAD /up health (bootstrap/app.php)
+// GET|HEAD /robots.txt public/robots.txt
+// other methods on those paths → 405
+// unknown path → 404
+
+define action called status_for_path with parameters request_path and request_method:
+ route request_path:
+ when "/" or "/up" or "/robots.txt":
+ route request_method:
+ when "GET" or "HEAD":
+ return 200
+ otherwise:
+ return 405
+ end route
+ otherwise:
+ return 404
+ end route
+end action
+
+define action called body_for_path with parameters request_path and request_method:
+ route request_path:
+ when "/" or "/up" or "/robots.txt":
+ route request_method:
+ when "GET" or "HEAD":
+ route request_path:
+ when "/":
+ return welcome_page
+ when "/up":
+ return health_page
+ when "/robots.txt":
+ return robots_text
+ end route
+ otherwise:
+ return method_not_allowed_page
+ end route
+ otherwise:
+ return not_found_page
+ end route
+end action
+
+define action called content_type_for_path with parameters request_path:
+ route request_path:
+ when "/robots.txt":
+ return "text/plain"
+ otherwise:
+ return "text/html"
+ end route
+end action
diff --git a/examples/laravel-app/user.wfl b/examples/laravel-app/user.wfl
new file mode 100644
index 00000000..bf3d0bdd
--- /dev/null
+++ b/examples/laravel-app/user.wfl
@@ -0,0 +1,15 @@
+// Stand-in for app/Models/User.php. Eloquent, hashing, and auth are not
+// ported; this container keeps the skeleton's name + email fields.
+
+create container User:
+ property name: Text
+ property email: Text
+
+ action display_name: Text
+ return name
+ end
+
+ action email_address: Text
+ return email
+ end
+end
diff --git a/examples/laravel-app/views.wfl b/examples/laravel-app/views.wfl
new file mode 100644
index 00000000..05c5925b
--- /dev/null
+++ b/examples/laravel-app/views.wfl
@@ -0,0 +1,90 @@
+// Page bodies for the WFL port of the laravel/laravel 13.x starter app.
+// Copy and styling are original; they are not a Blade/Tailwind reconstruction.
+
+define action called welcome_page:
+ return "
+
+
+
+
+ WFL starter
+
+
+
+
+
WFL starter
+
Your starter app is ready
+
This program is a WFL port of the Laravel application skeleton. It serves the same public routes the 13.x starter app exposes, using WFL's built-in web server.