diff --git a/dist/uce_0.1.1.deb b/dist/uce_0.1.1.deb
deleted file mode 100644
index 09f08a4..0000000
Binary files a/dist/uce_0.1.1.deb and /dev/null differ
diff --git a/dist/uce_0.1.2-1_amd64.deb b/dist/uce_0.1.2-1_amd64.deb
deleted file mode 100644
index df54d7d..0000000
Binary files a/dist/uce_0.1.2-1_amd64.deb and /dev/null differ
diff --git a/dist/uce_0.1.3-1_amd64.deb b/dist/uce_0.1.3-1_amd64.deb
deleted file mode 100644
index c02e12e..0000000
Binary files a/dist/uce_0.1.3-1_amd64.deb and /dev/null differ
diff --git a/dist/uce_0.1.4-1_amd64.deb b/dist/uce_0.1.4-1_amd64.deb
deleted file mode 100644
index 6886b42..0000000
Binary files a/dist/uce_0.1.4-1_amd64.deb and /dev/null differ
diff --git a/docs/react-developer-affordances-todo.md b/docs/react-developer-affordances-todo.md
deleted file mode 100644
index 81d8b04..0000000
--- a/docs/react-developer-affordances-todo.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# React Developer Affordances Todo
-
-## Objective
-
-Add practical value for developers coming from React frameworks while preserving UCE's server-first C++ model. Defer component syntax/children work, avoid global head/assets/islands in the runtime, and focus on function-library data helpers, diagnostics, docs, examples, demos, and a starter-local router with starter-local asset/island components.
-
-## Success Criteria
-
-- [x] Function library has useful collection/data-shaping helpers with docs and tests.
-- [x] Compile/runtime diagnostics are more helpful, especially for generated-code and common preprocessor mistakes.
-- [x] Docs include a concise React/Next/Remix orientation guide.
-- [x] Starter example uses a centralized hierarchical/file-based router in `index.uce` efficiently.
-- [x] Starter-local asset/island affordances live as component handlers in the starter, not global runtime APIs.
-- [x] Network tests and relevant build checks pass on `k-uce`.
-
-## Current State
-
-- Status: complete
-- Last updated: 2026-05-28
-- Source of truth: `/root/mount_ssh/k-uce-root-htdocs-uce`
-- Runtime/live target: `k-uce:/Code/uce.openfu.com/uce`; rebuilt and restarted `uce.service` on `k-uce`.
-
-## Goal Tree
-
-Legend: `[ ]` not started, `[~]` in progress, `[x]` done, `[!]` blocked, `[-]` superseded
-
-- [x] G1: Add collection/data helpers to function library
- - Why: React-framework developers routinely shape arrays/objects near render code.
- - Done when: helpers are declared, implemented, documented, and covered by tests.
- - Verify: build plus focused site/network tests.
- - [x] G1.1: Identify current `StringList`/`DValue` idioms and choose helper surface.
- - [x] G1.2: Implement minimal high-value helpers without broad template complexity.
- - [x] G1.3: Add docs and examples for helpers.
- - [x] G1.4: Add/extend tests.
-- [x] G2: Improve developer diagnostics
- - Why: React frameworks win by making failures easy to act on.
- - Done when: compile/runtime error output includes actionable context and docs mention debugging flow.
- - Verify: intentional broken page surfaces improved message.
- - [x] G2.1: Inspect current compiler/runtime error rendering path.
- - [x] G2.2: Add source excerpt / generated path / common-hint text where appropriate.
- - [x] G2.3: Document diagnostics.
-- [x] G3: Add React/Next/Remix orientation docs
- - Why: mapping familiar concepts reduces onboarding cost without adding syntax.
- - Done when: docs page exists and is linked from docs/README/demo surfaces.
- - Verify: docs page renders live.
-- [x] G4: Starter-local router and starter affordances
- - Why: User specifically wants hierarchical/file-based routing beautifully in starter `index.uce`.
- - Done when: starter routes go through a central router in `index.uce`, and starter-local asset/island component handlers exist and are used where sensible.
- - Verify: key starter routes render 200.
- - [x] G4.1: Inspect current starter routing.
- - [x] G4.2: Refactor to clear route table / hierarchical file resolution in `index.uce`.
- - [x] G4.3: Add starter-local `COMPONENT:asset` / `COMPONENT:island` style handlers in one unit.
- - [x] G4.4: Use them efficiently in starter pages/layout.
-- [x] G5: Demos and examples
- - Why: Affordances must be visible to developers, not hidden in APIs.
- - Done when: docs/demo/tests expose examples.
- - Verify: demo URLs return 200 and tests pass.
-- [x] G6: Verification and project docs
- - Done when: build/test commands are run on `k-uce`, project notes updated, and adversarial review completed.
-
-## Execution Queue
-
-Complete.
-
-## Decisions
-
-- 2026-05-28: Defer component children/slots and JSX-like preprocessor syntax.
-- 2026-05-28: Do not add global runtime head/assets/islands APIs; implement asset/island as starter-local components.
-- 2026-05-28: Do not add a generic runtime file-based-routing system; demonstrate hierarchical/file routing inside starter `index.uce`.
-- 2026-05-28: Keep collection helpers explicit (`list_*`, `dv_*`) instead of overloading generic names such as `map`/`sort`.
-
-## Assumptions
-
-- Current source-of-truth mount is live-editable; runtime validation requires SSH to `k-uce`.
-- Existing site tests are the right place for function-library coverage.
-
-## Blockers and Risks
-
-- No current blockers.
-- Future risk: if UCE grows a full parser or component-tag syntax, keep this pass's explicit component/router APIs as a stable lower-level fallback.
-
-## Evidence and Verification Log
-
-- 2026-05-28: Created plan after reviewing README, preprocessor docs, and function library headers.
-- 2026-05-28: `ssh k-uce 'cd /Code/uce.openfu.com/uce && bash scripts/build_linux.sh'` succeeded.
-- 2026-05-28: Restarted `uce.service` on `k-uce`.
-- 2026-05-28: `tests/run_network_tests.py --match core` passed.
-- 2026-05-28: Manual checks returned `200` for `/examples/uce-starter/index.uce`, `?dashboard`, `?workspace/projects`, `?themes`, `/demo/collections.uce`, `/doc/index.uce?p=coming_from_react`, `/doc/index.uce?p=list_map`, and `/doc/index.uce?p=dv_group_by`.
-- 2026-05-28: Full internal network suite passed, 25/25.
-- 2026-05-28: Temporary broken `/tests/diagnostic-probe.uce` returned `500` with formatted `UCE compile error` diagnostics; source and cache artifacts were removed afterward.
-
-## Change Log
-
-- 2026-05-28: Created initial goal tree.
-- 2026-05-28: Implemented helpers, diagnostics, docs, demo, starter router, starter-local web affordance components, tests, and validation.
-
-## Follow-up Cleanup 2026-05-29
-
-- Removed duplicate route cleanup from `starter_router_candidates()` because `app_make_route()` is the single normalization point for `l_path`.
-- Weeded out nearby duplicate/obsolete starter code:
- - `starter_router_add_candidate(...)` now owns repeated candidate tree construction.
- - Removed unused `app_resolve_view()` / `starter_resolve_view()` after moving routing into starter `index.uce`.
- - Removed unused legacy registered asset rendering functions from `lib/app.uce`; registered assets now render through `components/theme/web_affordances.uce`.
- - `app_init()` now reuses `app_base_url(context)` instead of repeating base URL derivation.
- - `web_affordances.uce` now uses one `starter_render_asset_group(...)` loop for CSS and JS.
-- Verification: rebuilt on `k-uce`, restarted `uce.service`, checked key starter routes, and ran `tests/run_network_tests.py --match core` successfully.
-
-## Follow-up Routed Views 2026-05-29
-
-- Changed starter route dispatch from `unit_render(...)` to `component(...)`.
-- Converted all routed `site/examples/uce-starter/views/*.uce` files to `COMPONENT(Request& context)` rather than `RENDER(Request& context)` because they are central-router-only views.
-- Updated the starter README and verified key starter routes. No service restart was required because only `.uce`/docs changed.
-
-## Follow-up Canonical Starter URLs 2026-05-29
-
-- Canonicalized starter self-links from `/examples/uce-starter/index.uce?...` to `/examples/uce-starter/?...` with `app_canonical_script_url(...)`.
-- Updated the starter README to show canonical directory URLs.
-- Touched the starter front controller to force the `#load`ed helper change into the cached generated unit.
-- Verified canonical/direct starter routes and checked generated self-links. No service restart was required.
-
-## Follow-up Not Found Component 2026-05-29
-
-- Moved `starter_router_render_not_found` markup into `components/basic/notfound.uce`.
-- Router now delegates 404 body rendering through `component("components/basic/notfound", props, context)`.
-- Verified missing routes return `404` and normal dashboard route returns `200`. No service restart required.
-
-## Follow-up Page Shell Component 2026-05-29
-
-- Moved app page rendering into `themes/page.uce` as a component.
-- Removed `app_render_page`, `app_theme_page_component`, and `starter_render_page` from `lib/app.uce`.
-- Page template resolution now follows `context.call["app"]["page_type"]`: current theme first, common fallback second.
-- Verified representative HTML routes and the JSON page-type fallback. No service restart required.
-
-## Follow-up Deep Starter Context Cleanup 2026-05-29
-
-- Removed repeated `starter_boot(context)` calls; root `index.uce` is the boot point.
-- Removed `context.call["starter"]` duplicated state and JSON side-channel state.
-- JSON routes now use only `context.call["app"]["page_type"]` plus normal captured output.
-- Simplified `themes/page.uce` and `themes/common/page.json.uce` accordingly.
-- Replaced `starter_*` alias helper usage with direct `app_*` helpers and removed alias wrappers except `StarterUser`.
-- Verified key routes and core tests. No service restart required.
-
-## Follow-up Route Context Flattening 2026-05-29
-
-- Flattened `context.call["app"]["route"]` to `context.call["route"]`.
-- Moved former `context.call["app"]["router"]` metadata into `context.call["route"]`.
-- Verified representative starter HTML, 404, and JSON routes. No service restart required.
diff --git a/docs/wasm-baselines/native-baseline-2026-06-12.json b/docs/wasm-baselines/native-baseline-2026-06-12.json
deleted file mode 100644
index 28ad94c..0000000
--- a/docs/wasm-baselines/native-baseline-2026-06-12.json
+++ /dev/null
@@ -1,104 +0,0 @@
-[
- {
- "backend": "native",
- "target": "template-heavy-doc",
- "url": "/doc/singlepage.uce",
- "ok": true,
- "status": 200,
- "samples_ms": [
- 318.72209906578064,
- 340.9293442964554,
- 319.705568253994,
- 324.0259513258934,
- 350.93583166599274,
- 313.20811808109283,
- 317.0944079756737,
- 310.312956571579,
- 307.89367109537125,
- 308.1864267587662,
- 311.681292951107,
- 313.39504569768906,
- 314.3857270479202,
- 312.5988021492958,
- 313.25943768024445,
- 312.9996135830879,
- 334.4864323735237,
- 314.2518773674965,
- 309.7490146756172,
- 317.4726217985153
- ],
- "median_ms": 313.8234615325928,
- "mean_ms": 318.2647120207548,
- "min_ms": 307.89367109537125,
- "max_ms": 350.93583166599274,
- "note": ""
- },
- {
- "backend": "native",
- "target": "sqlite-page",
- "url": "/demo/sqlite.uce",
- "ok": true,
- "status": 200,
- "samples_ms": [
- 3.4144148230552673,
- 6.088584661483765,
- 3.463640809059143,
- 3.498159348964691,
- 3.5023540258407593,
- 6.214611232280731,
- 3.563329577445984,
- 3.3720433712005615,
- 3.330707550048828,
- 3.3808723092079163,
- 3.322914242744446,
- 3.3655911684036255,
- 3.374151885509491,
- 3.358304500579834,
- 3.3307820558547974,
- 3.268897533416748,
- 3.360658884048462,
- 3.3061057329177856,
- 6.233863532543182,
- 3.432638943195343
- ],
- "median_ms": 3.3775120973587036,
- "mean_ms": 3.809131309390068,
- "min_ms": 3.268897533416748,
- "max_ms": 6.233863532543182,
- "note": ""
- },
- {
- "backend": "native",
- "target": "component-heavy-starter",
- "url": "/examples/uce-starter/?dashboard",
- "ok": true,
- "status": 200,
- "samples_ms": [
- 74.01428371667862,
- 40.9795418381691,
- 40.995217859745026,
- 41.04568809270859,
- 40.724173188209534,
- 41.10313206911087,
- 40.59913754463196,
- 41.28593951463699,
- 41.10439121723175,
- 40.649913251399994,
- 42.06441342830658,
- 42.074643075466156,
- 43.475523591041565,
- 41.8255478143692,
- 41.068583726882935,
- 41.116394102573395,
- 41.09777510166168,
- 42.58237034082413,
- 41.312023997306824,
- 70.98117470741272
- ],
- "median_ms": 41.11039265990257,
- "mean_ms": 44.50499340891838,
- "min_ms": 40.59913754463196,
- "max_ms": 74.01428371667862,
- "note": ""
- }
-]
diff --git a/docs/wasm-baselines/native-baseline-2026-06-12.md b/docs/wasm-baselines/native-baseline-2026-06-12.md
deleted file mode 100644
index 413facc..0000000
--- a/docs/wasm-baselines/native-baseline-2026-06-12.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Phase 5 native baseline — 2026-06-12
-
-Host: `k-uce` / `uce.openfu.com` via localhost with Host header.
-
-This is a durable informational snapshot. The Phase 5 gate recomputes native medians during paired native/WASM runs; these numbers are not hard-coded budgets.
-
-- Warmup suite: 82/82 passed; excludes `site tests tasks` to avoid perturbing task lifecycle state.
-- Measured full network suite: 83/83 passed.
-- Measured starter subset: 14/14 passed.
-- Static audit default scan: 50 code findings, documentation prose excluded by default.
-
-| target | median ms | mean ms | samples |
-|---|---:|---:|---:|
-| template-heavy-doc | 313.8 | 318.3 | 20 |
-| sqlite-page | 3.4 | 3.8 | 20 |
-| component-heavy-starter | 41.1 | 44.5 | 20 |
-
-Raw benchmark JSON: `native-baseline-2026-06-12.json`.
-Static audit snapshot: `site-static-audit-2026-06-12.md`.
diff --git a/docs/wasm-baselines/site-static-audit-2026-06-12.md b/docs/wasm-baselines/site-static-audit-2026-06-12.md
deleted file mode 100644
index a199d6f..0000000
--- a/docs/wasm-baselines/site-static-audit-2026-06-12.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Phase 5 site static-state audit
-
-Findings: 50 code, 0 documentation prose.
-
-| file | line | severity | kind | code | note |
-|---|---:|---|---|---|---|
-| site/demo/index.uce | 76 | code | background task | ` if(allow_server_demos) { render_card("task_repeat.uce", "Task Repeat", "Recurring task scheduling"); } ?>` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/once-init.uce | 1 | code | static local/global | `static s64 demo_worker_init_count = 0;` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/demo/once-init.uce | 2 | code | static local/global | `static s64 demo_component_hits = 0;` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/demo/once-init.uce | 4 | code | init hook | `INIT(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/demo/once-init.uce | 10 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/demo/once-init.uce | 44 | code | once hook | `ONCE() and INIT()` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/demo/once-init.uce | 44 | code | init hook | `ONCE() and INIT()` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/demo/once-init.uce | 47 | code | once hook | `This page calls the same named component twice. `ONCE()` should only run once for the request, while `INIT()` should stay stable for the currently loaded worker copy.` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/demo/once-init.uce | 47 | code | init hook | `This page calls the same named component twice. `ONCE()` should only run once for the request, while `INIT()` should stay stable for the currently loaded worker copy.` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/demo/task-status.uce | 11 | code | background task | `String task_name = first(context.get["task-name"], "example-task");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task-status.uce | 13 | code | background task | `print("Task Name: ", task_name, "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task-status.uce | 14 | code | background task | `print("Task ID: ", task_pid(task_name), "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task-status.uce | 15 | code | background task | `print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task.uce | 13 | code | background task | `String task_name = first(context.get["task-name"], "example-task");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task.uce | 37 | code | background task | `` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task.uce | 46 | code | background task | `load(document.getElementById('task-status'), 'task-status.uce?task-name== uri_encode(task_name) ?>');` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task.uce | 56 | code | background task | `print("Task Name: ", task_name, "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task.uce | 57 | code | background task | `print("Task ID: ", task_pid(task_name), "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task.uce | 58 | code | background task | `print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task.uce | 71 | code | background task | `print("New Task ID: ", task(task_name, []() {` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task_repeat.uce | 13 | code | background task | `String task_name = first(context.get["task-name"], "example-task");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task_repeat.uce | 37 | code | background task | `` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task_repeat.uce | 46 | code | background task | `load(document.getElementById('task-status'), 'task-status.uce?task-name== uri_encode(task_name) ?>');` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task_repeat.uce | 56 | code | background task | `print("Task Name: ", task_name, "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task_repeat.uce | 57 | code | background task | `print("Task ID: ", task_pid(task_name), "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task_repeat.uce | 58 | code | background task | `print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/demo/task_repeat.uce | 71 | code | background task | `print("New Task ID: ", task_repeat(task_name, 5, []() {` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/examples/uce-starter/components/data/widgets.uce | 3 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/examples/uce-starter/components/workspace/primitives.uce | 3 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/examples/uce-starter/lib/user.class.h | 13 | code | static local/global | `static String session_key()` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/examples/uce-starter/lib/user.class.h | 18 | code | static local/global | `static String normalize_email(String email)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/examples/uce-starter/lib/user.class.h | 23 | code | static local/global | `static String hash_id(String raw)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/examples/uce-starter/lib/user.class.h | 45 | code | static local/global | `static String password_hash(String password, String salt)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/examples/uce-starter/lib/user.class.h | 53 | code | static local/global | `static DValue read_json_file(String file_name)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/examples/uce-starter/lib/user.class.h | 64 | code | static local/global | `static bool write_json_file(String file_name, DValue data)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/examples/uce-starter/views/dashboard.uce | 3 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/info/index.uce | 269 | code | static local/global | `
nginx serves static files directly from `site/`
` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. |
-| site/tests/preprocessor.uce | 3 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. |
-| site/tests/tasks.uce | 25 | code | background task | `pid_t repeat_existing = task_pid("site-tests-repeat");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 27 | code | background task | `task_kill(repeat_existing, 15);` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 37 | code | background task | `repeat_pid = task_repeat("site-tests-repeat", 1.0, []() {` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 55 | code | background task | `pid_t seen_short_pid = task_pid("site-tests-short");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 56 | code | background task | `pid_t seen_repeat_pid = task_pid("site-tests-repeat");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 57 | code | background task | `pid_t seen_timeout_pid = task_pid("site-tests-timeout");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 58 | code | background task | `pid_t seen_unsafe_key_pid = task_pid("site-tests/../unsafe key");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 59 | code | background task | `int short_alive = seen_short_pid == 0 ? -1 : task_kill(seen_short_pid, 0);` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 60 | code | background task | `int repeat_alive = seen_repeat_pid == 0 ? -1 : task_kill(seen_repeat_pid, 0);` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 72 | code | background task | `check("task_pid() + task_kill(pid, 0)", short_alive == 0, "kill(0) result=" + std::to_string(short_alive));` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 73 | code | background task | `check("task_repeat()", repeat_pid != 0 && seen_repeat_pid != 0, "started=" + std::to_string(repeat_pid) + " seen=" + std::to_string(seen_repeat_pid));` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
-| site/tests/tasks.uce | 77 | code | background task | `check("task_kill() rejects negative pid", task_kill(-1, 0) == -1, "kill(-1, 0) rejected");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. |
diff --git a/docs/wasm-toolchain-findings.md b/docs/wasm-toolchain-findings.md
deleted file mode 100644
index 6d2eb94..0000000
--- a/docs/wasm-toolchain-findings.md
+++ /dev/null
@@ -1,197 +0,0 @@
-# Phase 0 findings — toolchain & runtime spike
-
-- **Status: EXIT CRITERION PASSED** (2026-06-12, on k-uce)
-- **Runtime selected: Wasmtime** (v45.0.1, C API). WAMR rejected — evidence below.
-- Exceptions decision (§11.1, error codes / `-fno-exceptions`) **confirmed viable**:
- both the stubs and two real generated units compile with `-fno-exceptions`,
- no try/catch blocker anywhere.
-
-The exit criterion ran end-to-end: a core stub (libc/libc++ statically linked,
-owns memory/allocator) and a unit stub (PIC, `dylink.0`) were linked **at
-runtime** by `loader.cpp` and produced:
-
-```
-hello from unit; unit-data-segment-ok; counter=7; mapsum=3; core-string+unit[cb:42][got-func-ok][fn:42]
-core_counter (in linear memory): before=7 after=8
-PHASE0 EXIT CRITERION: PASS
-```
-
-which validates, in one render call: unit data-segment relocation
-(`__memory_base`), GOT.mem read **and write** of a core global, `std::string`
-/`std::map` in unit code on the core's heap, a heap C++ object created in core
-and mutated/read by the unit, function pointers crossing unit→core→unit
-through the shared table, GOT.func resolution, and a `std::function` lambda
-allocated in the unit and invoked by core. That is the §3.4 contract
-("DValue inside the workspace: no serialization, ever") demonstrated at the
-ABI level.
-
-## Toolchain pins
-
-| What | Version | Where on k-uce |
-|---|---|---|
-| wasi-sdk | 33 (clang 22.1.0-wasi-sdk) | `/opt/wasi-sdk` |
-| target triple | `wasm32-wasip1` (`wasm32-wasi` is deprecated) | — |
-| Wasmtime C API | v45.0.1 (prebuilt x86_64-linux release) | `/opt/wasmtime` |
-| WAMR (rejected) | WAMR-2.4.4, built from source | `/opt/wamr` |
-| cmake / ninja | 3.31.6 / 1.12.1 (apt) | — |
-
-## Runtime selection: why not WAMR
-
-WAMR was tried first per §9 ("preferred ... use Wasmtime only if blocked").
-We are blocked, on the load-bearing requirement itself:
-
-1. **WAMR's wasm-c-api ignores imported memories and tables.** At unit
- instantiation it logs `"doesn't support import memories and tables for
- now, ignore them"` (`wasm_c_api.c`) and gives the instance its own
- memory/table — which silently destroys the shared-workspace model.
-2. **Host-side `wasm_table_grow` / `wasm_memory_grow` are explicitly
- unsupported** ("Only allow growing a table via the opcode table.grow").
-3. Its build banner lists *Import/Export of Mutable Globals* as unsupported —
- the dylink ABI imports `__stack_pointer` and every `GOT.*` entry as a
- mutable global.
-
-Its multi-module feature is name-based auto-resolution, not host-orchestrated
-dylink (no host-computed `__memory_base`/`__table_base`, no GOT). Making WAMR
-fit means implementing import binding through the c-api layer and runtime
-internals — a runtime-development project, not a patch.
-
-**Wasmtime v45.0.1 passed everything on the first run** through the standard
-`wasm.h` C API: host-created funcref table shared by both instances, exported
-memory imported by the unit, host-created (mutable) globals, cross-instance
-export→import wiring. The remaining §9 criteria also favor it: AOT artifacts
-(`.cwasm` precompilation) for the unit cache, epoch interruption for CPU
-limits, and built-in copy-on-write memory-image instantiation for the §6 core
-snapshot (machinery we'd have had to build ourselves on WAMR).
-
-Trade-off accepted (was already in §10): Rust codebase, heavier to
-vendor/patch. Pin the release artifact (lib + headers, checksummed) the way
-sqlite is vendored; building from source stays possible but is not the
-default path. The C API .so is ~27 MB.
-
-## Module build recipe (what `build_modules.sh` settled on)
-
-Core (non-PIC reactor, owns libc/libc++/allocator):
-```
-clang++ --target=wasm32-wasip1 -mexec-model=reactor -O1 -fno-exceptions \
- core.cpp -o core.wasm \
- -Wl,--export-all -Wl,--import-table \
- -Wl,--export=__stack_pointer -Wl,--export=__heap_base \
- -Wl,--undefined=_ZTVN10__cxxabiv117__class_type_infoE
-```
-
-Unit (PIC side module):
-```
-clang++ --target=wasm32-wasip1 -fPIC -fvisibility=default \
- -fvisibility-inlines-hidden -O1 -fno-exceptions -c unit.cpp
-wasm-ld -shared --experimental-pic --unresolved-symbols=import-dynamic \
- --Bsymbolic unit.o -o unit.wasm --export=
-```
-
-Hard-won flag findings:
-
-1. **`--unresolved-symbols=import-dynamic`** is required for the side-module
- link; undefined symbols then become `env.*` function imports and `GOT.*`
- globals exactly per the Emscripten dylink ABI.
-2. **`-fvisibility-inlines-hidden` is mandatory.** Without it one libc++
- vague-linkage lambda (`std::map` tree-emplace internals, missing libc++'s
- usual hide-from-ABI attribute) is emitted as *both* an export and an
- import of the unit — a self-import the loader cannot satisfy at
- instantiation time without lazy-binding trampolines. `--Bsymbolic` alone
- did **not** bind it.
-3. **Core symbol closure**: `--export-all` only exports what got *linked*.
- The unit needed `__cxxabiv1::__class_type_info`'s vtable (RTTI machinery
- behind `std::function`), which the core never references — forced in with
- `--undefined=`. The production core needs a closure strategy:
- `--whole-archive` for libc/libc++/libc++abi, or a curated keep-list. The
- loader also implements the complementary fallback (resolve `GOT.mem` of
- weak data from the unit's *own* exports post-instantiation, patching the
- provisional GOT global).
-4. **`--import-table` on the core** + a **host-created table** is the right
- shape (see loader notes); `--export-table`/`--growable-table` was the
- first attempt and died on WAMR's host-grow limitation, but host-created
- stays the better design under Wasmtime too: the loader picks table size
- (core's declared minimum + headroom) before any instantiation.
-5. `wasm32-wasi` triple is deprecated in wasi-sdk 33 → use `wasm32-wasip1`.
-
-## Loader notes (`loader.cpp`, ~450 lines, standard wasm-c-api)
-
-Sequence proven: instantiate core (45 WASI imports satisfied with named trap
-stubs — none was ever called) → parse `dylink.0` (`mem_info`: memsize/align,
-tablesize) → `__memory_base` = call core's exported `malloc` → `__table_base`
-= bump pointer starting at core's table-import minimum → build the unit's
-import vector (memory/table/`__stack_pointer` shared from core; `env.*`
-functions from core exports; `GOT.mem.*` as host mutable i32 globals holding
-addresses read from core's exported data-symbol globals) → instantiate →
-patch deferred GOT entries → `__wasm_apply_data_relocs` → `__wasm_call_ctors`
-→ call the entry export.
-
-- **Erratum (found in Phase 3): self-resolved `GOT.mem` values must add
- `__memory_base`.** A PIC module's exported data symbols are i32 globals
- holding offsets *relative to its `__memory_base`*, not absolute addresses —
- the linker adds the base when patching deferred GOT entries (there is no
- `__wasm_apply_global_relocs` export to do it). Copying the export verbatim
- reads/writes core memory at low addresses and renders silently wrong values;
- the Phase 3 fixture's `self-got`/`callback` markers exist to catch exactly
- this. `GOT.mem` entries resolved from the *core's* exports are absolute
- already (the core is non-PIC) and need no adjustment.
-- **GOT.func is resolved guest-side**: the core exports a helper returning
- `(intptr_t)&func` — taking the address forces a link-time elem entry and a
- wasm function pointer *is* its table index. No host-side funcref injection
- is needed at all (it was WAMR-unsupported; under Wasmtime it would work but
- the guest-side registry is simpler and runtime-agnostic). The production
- core should carry a name→funcptr registry (dlsym-shaped) for its API
- surface.
-- **wasi-libc gotcha**: `_initialize` has a double-init guard ending in
- `__builtin_trap()`. WAMR runs `_initialize` automatically at instantiation
- (so calling it again traps "unreachable"); Wasmtime does not (so you must
- call it). Cost one debugging round; recorded here so it never costs another.
-- Export-name `wasm_name_t` may include the trailing NUL in `size` (WAMR
- did); trim when indexing exports by name.
-
-## Real generated units (delegated grind — full log in `realunit-report.md`)
-
-`site/demo/collections.uce.cpp` and `hello.uce.cpp` (taken verbatim from the
-live unit cache at `/tmp/uce/work/...`) both compile and link as PIC side
-modules with `dylink.0`, **no allocator definitions**, with only shim-level
-intervention. `collections.wasm`: 42 KB, 52 imports — including exactly the
-predicted `GOT.mem.context` for the global `Request*`. Friction points that
-become Phase 2 work items:
-
-1. **`types.h` defines global `operator new/delete` in every unit** — must be
- gated (`#ifdef`) out of side-module builds; allocator ownership belongs to
- the core (§3.2: "the one fatal misconfiguration").
-2. **`sys.h` includes ``** → wasi needs `-D_WASI_EMULATED_SIGNAL`
- (+ `-lwasi-emulated-signal` in the core) or an `#ifdef __wasm__` carve-out;
- signals/fork/exec/sockets in `sys.h` have no wasi equivalent and move
- behind hostcalls anyway (§5.1).
-3. **Generated units include `uce_lib.h` by absolute path** — the
- preprocessor should emit a logical include so the wasm build can supply
- its own include order.
-4. **Header-inline connector wrappers (MySQL etc.) get pulled into every
- unit** regardless of use; the §3.3 membrane split (thin wasm-side shim,
- host-side implementation) resolves this and shrinks unit import lists.
-5. The real-unit compile used the pre-`-fvisibility-inlines-hidden` flag set
- (parallel work); the final unit flag set above should be used from
- Phase 2 on.
-
-## Implications for the next phases
-
-- **Phase 1 (DValue C ABI, native)**: unaffected by any of this; proceed as
- written.
-- **Phase 2 (core module + membrane)**: add the closure strategy
- (whole-archive), the GOT.func name→funcptr registry, the `types.h`
- allocator gate, the signal emulation define, and the preprocessor include
- change. Compile `uce_lib` with the core recipe above.
-- **Phase 3 (loader)**: `loader.cpp` here is the skeleton — dylink parsing,
- base allocation, GOT resolution, and init sequencing are all proven; what
- remains is the registry/dispatch layer, ABI stamping, and multi-unit
- placement.
-- **Phase 4**: use Wasmtime's epoch interruption for CPU limits and its
- memory-image/CoW instantiation for the core snapshot rather than building
- either by hand.
-
-## Artifacts (on k-uce, not in git)
-
-- `/tmp/uce/wasm-phase0/{core.wasm,unit.wasm,loader}` — exit-criterion run
-- `/tmp/uce/wasm-phase0/realunit/` — real-unit compiles + shim tree + inspector dumps
-- `/opt/wasi-sdk`, `/opt/wasmtime`, `/opt/wamr` — toolchains/runtimes
diff --git a/site/doc/pages/1_RENDER.txt b/site/doc/pages/1_RENDER.txt
index 61ebc83..da71a96 100644
--- a/site/doc/pages/1_RENDER.txt
+++ b/site/doc/pages/1_RENDER.txt
@@ -32,7 +32,7 @@ For a normal direct page request, `context.props` starts empty.
If the page is invoked from another UCE file via `unit_render(file_name, context)`, the callee receives that same `context`.
-Pages intended to serve WebSocket traffic may expose both `RENDER(Request& context)` and `WS(Request& context)`. Files may also define `COMPONENT()` handlers when they intentionally need both page and component behavior in one unit.
+Pages that serve WebSocket traffic may expose both `RENDER(Request& context)` and `WS(Request& context)`. Files may also define `COMPONENT()` handlers when one unit needs both page and component behavior.
In that case:
diff --git a/site/doc/pages/base64_decode.txt b/site/doc/pages/base64_decode.txt
new file mode 100644
index 0000000..3fdc599
--- /dev/null
+++ b/site/doc/pages/base64_decode.txt
@@ -0,0 +1,30 @@
+:sig
+String base64_decode(String raw, bool& ok)
+
+:params
+raw : Base64 encoded string
+ok : set to `true` when decoding succeeds; set to `false` for invalid input
+return value : decoded binary-safe string, or an empty string when decoding fails
+
+:see
+>string
+base64_encode
+
+:content
+Decodes a Base64 string.
+
+Pass a `bool` variable for `ok` so callers can distinguish invalid input from a valid empty decoded value.
+
+Example:
+
+```uce
+bool ok = false;
+String decoded = base64_decode("aGVsbG8=", ok);
+// ok == true
+// decoded == "hello"
+```
+
+Related:
+
+- PHP: `base64_decode($value, true)`
+- JavaScript / Node.js: `Buffer.from(value, "base64")`
diff --git a/site/doc/pages/base64_encode.txt b/site/doc/pages/base64_encode.txt
new file mode 100644
index 0000000..a825959
--- /dev/null
+++ b/site/doc/pages/base64_encode.txt
@@ -0,0 +1,27 @@
+:sig
+String base64_encode(String raw)
+
+:params
+raw : binary-safe source string
+return value : Base64 encoded string
+
+:see
+>string
+base64_decode
+
+:content
+Encodes a string with Base64.
+
+UCE strings can contain binary data, so `raw` may include NUL bytes and non-text bytes.
+
+Example:
+
+```uce
+String encoded = base64_encode("hello");
+// encoded == "aGVsbG8="
+```
+
+Related:
+
+- PHP: `base64_encode()`
+- JavaScript / Node.js: `Buffer.from(value).toString("base64")`
diff --git a/site/doc/pages/coming_from_react.txt b/site/doc/pages/coming_from_react.txt
index 8f6ae29..e99884c 100644
--- a/site/doc/pages/coming_from_react.txt
+++ b/site/doc/pages/coming_from_react.txt
@@ -27,7 +27,7 @@ UCE is server-first C++ with a small template preprocessor. It does not try to b
- `ONCE(Request& context)` is per-request setup for a unit before its first render/component entry.
- `INIT(Request& context)` is worker-local setup when a unit is loaded.
- `= expression ?>` is escaped interpolation; prefer it for user-visible text.
-- `` is trusted raw markup output, closer to a deliberate `dangerouslySetInnerHTML` decision.
+- `` writes trusted raw markup, similar to `dangerouslySetInnerHTML` in React.
- `unit_render()` renders another page unit; `component()` returns component HTML as a string.
## Routes and Layouts
@@ -51,11 +51,11 @@ DValue app_items = dv_filter(menu, [](DValue item, String key) { return(item["se
DValue by_section = dv_group_by(menu, [](DValue item, String key) { return(item["section"].to_string()); });
```
-Use these when the transformation communicates intent. Prefer explicit loops when side effects or multi-step validation are the main concern.
+Use these when a short transformation is clearer than a loop. Prefer explicit loops for side effects or multi-step validation.
## Assets and Islands
-Global runtime APIs for assets and islands are intentionally not part of UCE core. The starter emits CSS and JavaScript from the owning unit's `ONCE(Request& context)` hook, with a few shared sibling asset components when multiple components need the same files. The only starter web-affordance helper left is `COMPONENT:island` in `components/theme/web_affordances.uce` for small progressive-enhancement modules. This keeps app policy in the app without an asset registry layer.
+UCE core does not provide a global asset or island registry. The starter emits CSS and JavaScript from the owning unit's `ONCE(Request& context)` hook, with a few shared sibling asset components when multiple components need the same files. The starter's `COMPONENT:island` helper in `components/theme/web_affordances.uce` covers small progressive-enhancement modules while keeping app policy in the app.
## Debugging
@@ -66,4 +66,4 @@ When a unit fails to compile, UCE reports the source path, generated C++ path, c
- No client-side virtual DOM is built into UCE.
- No global file-router is imposed by the runtime.
- No JSX-like component tags are required for this workflow.
-- Component children/slot syntax is intentionally deferred; use explicit props and component calls for now.
+- Component children/slot syntax is not part of UCE yet; use explicit props and component calls for now.
diff --git a/site/doc/pages/dv_filter.txt b/site/doc/pages/dv_filter.txt
index 97f41e5..f74de2c 100644
--- a/site/doc/pages/dv_filter.txt
+++ b/site/doc/pages/dv_filter.txt
@@ -12,7 +12,7 @@ filter
:content
Keeps children for which f returns true. List-like input stays list-like.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue visible = dv_filter(items, [](DValue item, String key) { return(item["hidden"].to_bool() == false); });
diff --git a/site/doc/pages/dv_group_by.txt b/site/doc/pages/dv_group_by.txt
index 4d99686..c1b8e9b 100644
--- a/site/doc/pages/dv_group_by.txt
+++ b/site/doc/pages/dv_group_by.txt
@@ -12,7 +12,7 @@ filter
:content
Groups children into list-like buckets by the string returned from f.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue by_section = dv_group_by(menu, [](DValue item, String key) { return(item["section"].to_string()); });
diff --git a/site/doc/pages/dv_keys.txt b/site/doc/pages/dv_keys.txt
index 643f60e..8b23c0f 100644
--- a/site/doc/pages/dv_keys.txt
+++ b/site/doc/pages/dv_keys.txt
@@ -12,7 +12,7 @@ filter
:content
Returns map keys from a DValue. Scalar values produce an empty list.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
StringList keys = dv_keys(context.cfg["menu"]);
diff --git a/site/doc/pages/dv_map.txt b/site/doc/pages/dv_map.txt
index 7f74aee..20caf99 100644
--- a/site/doc/pages/dv_map.txt
+++ b/site/doc/pages/dv_map.txt
@@ -12,7 +12,7 @@ filter
:content
Transforms each child. List-like input stays list-like; map input keeps keys.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue titles = dv_map(items, [](DValue item, String key) { DValue out; out = item["title"].to_string(); return(out); });
diff --git a/site/doc/pages/dv_omit.txt b/site/doc/pages/dv_omit.txt
index 9c64ae3..b278b50 100644
--- a/site/doc/pages/dv_omit.txt
+++ b/site/doc/pages/dv_omit.txt
@@ -12,7 +12,7 @@ filter
:content
Copies a DValue map except for selected keys.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue safe_user = dv_omit(user, {"password_hash"});
diff --git a/site/doc/pages/dv_pick.txt b/site/doc/pages/dv_pick.txt
index 1dea1c1..4524f4a 100644
--- a/site/doc/pages/dv_pick.txt
+++ b/site/doc/pages/dv_pick.txt
@@ -12,7 +12,7 @@ filter
:content
Copies only selected keys from a DValue map.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue public_user = dv_pick(user, {"name", "avatar"});
diff --git a/site/doc/pages/dv_values.txt b/site/doc/pages/dv_values.txt
index 838157e..ae1da6b 100644
--- a/site/doc/pages/dv_values.txt
+++ b/site/doc/pages/dv_values.txt
@@ -12,7 +12,7 @@ filter
:content
Returns child values as a list-like DValue.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue menu_items = dv_values(context.cfg["menu"]);
diff --git a/site/doc/pages/filter.txt b/site/doc/pages/filter.txt
index 873fc8e..86a81e0 100644
--- a/site/doc/pages/filter.txt
+++ b/site/doc/pages/filter.txt
@@ -4,7 +4,7 @@ vector filter(vector items, function f)
:params
items : list of items to be filtered
-f : a function that decides which items should be in the new list
+f : predicate function; items are kept when this returns `true`
return value : a new list
:see
diff --git a/site/doc/pages/is_list.txt b/site/doc/pages/is_list.txt
index ff9af51..d504a35 100644
--- a/site/doc/pages/is_list.txt
+++ b/site/doc/pages/is_list.txt
@@ -27,7 +27,7 @@ items["custom"] = "x";
// items.is_list() == false, items.is_array() == true
```
-`dv_map()` and `dv_filter()` use this distinction to decide whether results re-index from zero or keep their original keys.
+`dv_map()` and `dv_filter()` use this distinction: list inputs re-index from zero, while map inputs keep their original keys.
## Related Concepts
diff --git a/site/doc/pages/list_every.txt b/site/doc/pages/list_every.txt
index 2ea8864..c37e269 100644
--- a/site/doc/pages/list_every.txt
+++ b/site/doc/pages/list_every.txt
@@ -12,7 +12,7 @@ filter
:content
Returns true when every item matches.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
bool all_named = list_every(routes, [](String s) { return(s != ""); });
diff --git a/site/doc/pages/list_find.txt b/site/doc/pages/list_find.txt
index 9912f8a..9b84fff 100644
--- a/site/doc/pages/list_find.txt
+++ b/site/doc/pages/list_find.txt
@@ -12,7 +12,7 @@ filter
:content
Returns the first matching item or fallback.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
String route = list_find(routes, [](String s) { return(str_starts_with(s, "dashboard")); }, "index");
diff --git a/site/doc/pages/list_some.txt b/site/doc/pages/list_some.txt
index f1ea7a0..3398548 100644
--- a/site/doc/pages/list_some.txt
+++ b/site/doc/pages/list_some.txt
@@ -12,7 +12,7 @@ filter
:content
Returns true when any item matches.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
bool has_dashboard = list_some(routes, [](String s) { return(s == "dashboard"); });
diff --git a/site/doc/pages/list_sort.txt b/site/doc/pages/list_sort.txt
index 1d9813f..d18d9f7 100644
--- a/site/doc/pages/list_sort.txt
+++ b/site/doc/pages/list_sort.txt
@@ -12,7 +12,7 @@ filter
:content
Returns a sorted copy of the list.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
auto sorted = list_sort(tags);
diff --git a/site/doc/pages/list_unique.txt b/site/doc/pages/list_unique.txt
index 5d59cdf..999f7ee 100644
--- a/site/doc/pages/list_unique.txt
+++ b/site/doc/pages/list_unique.txt
@@ -12,7 +12,7 @@ filter
:content
Returns the first occurrence of each string, preserving input order.
-These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary.
+These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
auto tags = list_unique({"uce", "docs", "uce"});
diff --git a/site/doc/pages/split_kv.txt b/site/doc/pages/split_kv.txt
new file mode 100644
index 0000000..0f6739d
--- /dev/null
+++ b/site/doc/pages/split_kv.txt
@@ -0,0 +1,29 @@
+:sig
+StringMap split_kv(String s, char separator = '=', bool trim_whitespace = true, bool uppercase_keys = false)
+
+:params
+s : input containing one key/value pair per line
+separator : character separating each key from its value
+trim_whitespace : trim keys and values when true
+uppercase_keys : uppercase keys when true
+return value : map of parsed keys and values
+
+:see
+>string
+split
+split_http_headers
+
+:content
+Parses simple line-based key/value text into a `StringMap`.
+
+Each non-empty line is split on the first `separator`. Lines without the separator are kept with an empty value.
+
+Example:
+
+```uce
+StringMap cfg = split_kv("host = localhost\nport = 8080");
+// cfg["host"] == "localhost"
+// cfg["port"] == "8080"
+```
+
+This is useful for small config files, metadata blocks, and tests that need predictable key/value parsing.
diff --git a/site/doc/pages/task.txt b/site/doc/pages/task.txt
index 997dc31..b31ec68 100644
--- a/site/doc/pages/task.txt
+++ b/site/doc/pages/task.txt
@@ -20,7 +20,7 @@ If a process with the same `key` is already running anywhere in the runtime inst
Task keys may contain ordinary user-facing text. UCE hashes the key before using it as an internal lock/status filename so slashes and other path-like characters cannot escape the task state directory.
-`timeout` is enforced in the child process with an alarm. The default is ten minutes. Pass `0` only for tasks that are intentionally unbounded and have their own shutdown path.
+`timeout` is enforced in the child process with an alarm. The default is ten minutes. Pass `0` only for tasks that have their own shutdown path.
Related:
diff --git a/site/doc/pages/task_repeat.txt b/site/doc/pages/task_repeat.txt
index ed73080..7bce267 100644
--- a/site/doc/pages/task_repeat.txt
+++ b/site/doc/pages/task_repeat.txt
@@ -21,7 +21,7 @@ Starts a repeating background worker process.
If a process with the same `key` is already running anywhere in the runtime instance, `task_repeat()` does not start a second worker and instead returns the PID of the existing one. Coordination is through the same shared task state used by `task()`.
-`timeout` bounds the lifetime of the repeating worker. The default is ten minutes. Pass `0` only for intentionally unbounded workers with another shutdown path.
+`timeout` bounds the lifetime of the repeating worker. The default is ten minutes. Pass `0` only for workers that have another shutdown path.
Related:
diff --git a/site/doc/pages/ws_message.txt b/site/doc/pages/ws_message.txt
index c81a542..13abe36 100644
--- a/site/doc/pages/ws_message.txt
+++ b/site/doc/pages/ws_message.txt
@@ -12,7 +12,7 @@ Returns the payload of the current WebSocket message being handled by `WS(Reques
For text frames this is the decoded text payload. For binary frames this `String` contains the raw message bytes.
-Use `ws_is_binary()` or `ws_opcode()` to decide how the payload should be interpreted.
+Use `ws_is_binary()` or `ws_opcode()` to choose how to parse the payload.
Related:
diff --git a/site/doc/pages/xml_decode.txt b/site/doc/pages/xml_decode.txt
index f4a1dae..00a6aa6 100644
--- a/site/doc/pages/xml_decode.txt
+++ b/site/doc/pages/xml_decode.txt
@@ -15,7 +15,7 @@ String
:content
Parses a simple XML document into a structured `DValue`.
-`xml_decode()` is intentionally small. It does not validate schemas, DTDs, namespaces, or document types. It parses the first root element and returns the same structural element shape accepted by `xml_encode()`.
+`xml_decode()` parses a compact XML subset. It does not validate schemas, DTDs, namespaces, or document types. It parses the first root element and returns the same structural element shape accepted by `xml_encode()`.
Try the live example in the [XML demo](../demo/xml.uce).
diff --git a/site/doc/pages/yaml_decode.txt b/site/doc/pages/yaml_decode.txt
index 259c4c4..148063d 100644
--- a/site/doc/pages/yaml_decode.txt
+++ b/site/doc/pages/yaml_decode.txt
@@ -15,7 +15,7 @@ xml_decode
:content
Parses a practical YAML subset into a `DValue`.
-`yaml_decode()` is designed for concise UCE config files. It intentionally avoids full YAML schema behavior and does not support anchors, aliases, tags, directives, or complex inline collection syntax.
+`yaml_decode()` is designed for concise UCE config files. It supports a practical YAML subset and does not implement anchors, aliases, tags, directives, or complex inline collection syntax.
Try the live example in the [YAML demo](../demo/yaml.uce).