W7 done done
This commit is contained in:
@@ -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.
|
||||
@@ -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": ""
|
||||
}
|
||||
]
|
||||
@@ -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`.
|
||||
@@ -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 | `<input type="text" name="task-name" value="<?= task_name ?>"/>` | 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 | `<input type="text" name="task-name" value="<?= task_name ?>"/>` | 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 | `<li>nginx serves static files directly from `site/`</li>` | 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. |
|
||||
@@ -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=<entry>
|
||||
```
|
||||
|
||||
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 `<signal.h>`** → 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
|
||||
Reference in New Issue
Block a user