Skip to content

Commit d0fcae2

Browse files
Web-first resurrection: UI builds and runs on the WASM Rust core, real CI, docs truth reset (#22)
* build: resurrect the toolchain — Deno-only JS deps, esbuild pipeline, root cargo workspace - deno.json: nodeModulesDir + pinned npm: specifiers (rescript 11.1.4, @rescript/core 1.6.1, @rescript/react 0.13.1, react 18.3.1, esbuild 0.28.1); real deno.lock (was 0 bytes); tasks for setup/dev/build/ build:wasm/test that actually run. Removes the npm-CLI setup task that violated the Deno-only MUST (see docs/adr/0002). - ui/rescript.json: drop unused rescript-tea and unresolvable cadre-tea-router; open RescriptCore (was a hidden compile blocker); esmodule output; compile ui/tests as dev sources. - scripts/: esbuild bundle + dev server + wasm build (cargo + wasm-bindgen) entry points. - Root Cargo.toml workspace: members=[core], exclude=[desktop] so the missing gossamer sibling can never break root builds; release profile opt-level=z + lto for the wasm artifact. - Justfile: real setup/build/build-wasm/test/run/check recipes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps * core: wasm bindings, backlink hardening, contract + property tests - core/src/wasm.rs (feature "wasm"): WasmNotebook browser API. Granular mutations return only the changed note, or a {changed, removed, backlinks} delta for topology ops — never the whole notebook. View types serialize camelCase to match the UI records; the snake_case on-disk format is untouched. Errors are real JS Errors. - fix(notebook): link_notes(a, a) corrupted the backlink index — Note::add_link refused the self-link silently but the backlink was recorded anyway. Now rejected with the existing CircularLink error. Found by the new property test on its first run. - notebook: rebuild_backlinks() — loading never trusts a stored index (storage::load and wasm from_json both rebuild). - core/tests/golden.rs + tests/fixtures/notebook.golden.json: on-disk format contract, shared with ui/tests/contract.test.js. - core/tests/invariants.rs (proptest): backlinks are the exact inverse of outgoing links after any op sequence; serde roundtrip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps * ui: run on the wasm core — store seam, real persistence, tests The UI no longer reimplements notebook logic client-side. All mutations delegate to the Rust core (compiled to WebAssembly) through a store seam and patch a read model with what the core returns; ids and timestamps come from the core (Types.res no longer fabricates random ids). - ui/src/store/WasmStore.res: bindings + current-notebook handle; the operation set deliberately mirrors the desktop shell commands so a GossamerStore can swap in later with zero UI changes. - ui/src/store/Persist.res (+ idb.js, fileio.js): debounced autosave to IndexedDB; Save downloads .nexia.json; Load via file picker — the former SaveNotebook/LoadNotebook no-ops now work. - ui/src/Update.res: delegates to the store; self-link/delete guards; fix: update was recursive without rec (never compiled). - ui/src/Main.res: async boot (init wasm -> restore autosave -> mount); local DomBindings.res replaces the undeclared rescript-webapi dep and the %raw handler hack; the delete-while-editing guard moved into update where it sees current state (was a stale closure). - ui/tests/: TEA update suite (UpdateTests.res) and golden-fixture contract tests, run by deno test against the real wasm core. - web/index.html: loads the bundled ./app.js; noscript + aria-live. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps * ci: add product build/test workflows (rust-ci, ui-ci) The repo previously had no workflow that built or tested the product — all eleven existing workflows are estate governance/scanning (and governance.yml's own comment referenced a rust-ci.yml that didn't exist). Now: - rust-ci.yml: cargo fmt --check, clippy -D warnings (incl. the wasm feature), cargo test (unit + golden + property), plus a wasm32 build + wasm-bindgen job. Never touches desktop/ (external sibling dep). - ui-ci.yml: Deno 2.9.1 + ReScript compile, wasm core build, deno test (TEA update + contract tests), esbuild bundle, deno lint. - workflows/README.md: which checks verify the product vs. which are estate plumbing expected to no-op outside hyperpolymath. - dependabot: track cargo deps for /core (desktop excluded — its path dep cannot resolve here). Actions are SHA-pinned, reusing the pins already present in the repo; no new third-party actions introduced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps * docs: truth reset — make every document match the code TOPOLOGY claimed ~70% complete with petgraph/tantivy/Nickel at 100%; none of those exist. STATE.a2ml said 0%. ROADMAP had everything unchecked. Several governance files still carried {{PLACEHOLDER}} tokens, and Quickstarts/CONTRIBUTING referenced deleted files (flake.nix, .scm state, nonexistent just recipes). - README/TOPOLOGY/ROADMAP: describe the real web-first architecture (ReScript UI -> wasm Rust core -> IndexedDB/file persistence; desktop = optional external Gossamer shell); honest completion dashboard (~35%); aspirational tech moved to a labelled Future section. - STATE.a2ml 35%, ai.txt pointers fixed to 6a2/*.a2ml, CONTRIBUTING and all three Quickstarts now give commands that exist, placeholders filled in INTENT.contractile/methodology.a2ml/Intentfile.a2ml. - New: READINESS.md (Grade D, per the 2026-04-15 audit), desktop/README (external-sibling containment + resume path), ADR-0001 (wasm core, web-first) and ADR-0002 (Deno-only interpretation). - TEST-NEEDS.md: real test matrix (was "0 test files" — wrong even then). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent fcd76c2 commit d0fcae2

60 files changed

Lines changed: 3311 additions & 722 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/dependabot.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,14 @@ updates:
88
actions:
99
patterns:
1010
- "*"
11+
# Rust core crate. NOTE: deliberately no entry for /desktop — its path
12+
# dependency on an external sibling checkout (../../gossamer) would make
13+
# dependabot error.
14+
- package-ecosystem: "cargo"
15+
directory: "/core"
16+
schedule:
17+
interval: "weekly"
18+
groups:
19+
cargo:
20+
patterns:
21+
- "*"

.github/workflows/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!-- SPDX-License-Identifier: CC-BY-SA-4.0 -->
2+
# Workflows
3+
4+
## Product CI (required checks)
5+
6+
These build and test the actual product and are self-contained — they run
7+
anywhere, including forks:
8+
9+
| Workflow | What it verifies |
10+
|---|---|
11+
| `rust-ci.yml` | `cargo fmt --check`, `clippy -D warnings`, `cargo test` (unit + golden contract + property tests), wasm32 build + wasm-bindgen |
12+
| `ui-ci.yml` | ReScript compile under Deno, wasm core build, `deno test` (TEA update tests + golden-fixture contract tests), esbuild bundle, `deno lint` |
13+
14+
The same commands run locally: `just test`, `just build`, `just check`
15+
(or the underlying `deno task …` / `cargo …` equivalents).
16+
17+
## Estate workflows (expected to no-op or fail outside hyperpolymath)
18+
19+
Everything else in this directory is governance/scanning plumbing tied to
20+
private or sibling `hyperpolymath` repos (`standards` reusable workflows,
21+
`hypatia`, `casket-ssg`, `a2ml`/`k9` validate actions, `.git-private-farm`,
22+
`boj-server`). On forks or in isolated environments they cannot run and are
23+
**not** indicators of product health:
24+
25+
`boj-build.yml`, `casket-pages.yml`, `dogfood-gate.yml`, `governance.yml`,
26+
`hypatia-scan.yml`, `instant-sync.yml`, `mirror.yml`,
27+
`push-email-notify.yml`, `secret-scanner.yml`
28+
29+
`codeql.yml` and `scorecard.yml` are standard GitHub scanning and run
30+
anywhere.
31+
32+
Branch protection should require `rust-ci` and `ui-ci`; estate workflows
33+
should stay non-required.

.github/workflows/rust-ci.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Product CI (Rust): the load-bearing build/test workflow for core/.
3+
# desktop/ is intentionally NOT built here — it needs a sibling checkout of
4+
# hyperpolymath/gossamer (see desktop/README.md) and is excluded from the
5+
# workspace in the root Cargo.toml.
6+
name: rust-ci
7+
8+
on:
9+
push:
10+
branches: [main]
11+
pull_request:
12+
workflow_dispatch:
13+
14+
permissions:
15+
contents: read
16+
17+
concurrency:
18+
group: rust-ci-${{ github.ref }}
19+
cancel-in-progress: true
20+
21+
jobs:
22+
test:
23+
name: fmt + clippy + test
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
27+
- name: Install stable toolchain
28+
run: |
29+
rustup toolchain install stable --profile minimal --component rustfmt --component clippy
30+
rustup default stable
31+
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
32+
with:
33+
path: |
34+
~/.cargo/registry
35+
target
36+
key: rust-test-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
37+
- name: Format check
38+
run: cargo fmt --check
39+
- name: Clippy (including the wasm feature, compiled for host)
40+
run: cargo clippy --all-targets --features wasm -- -D warnings
41+
- name: Tests (unit + golden contract + property)
42+
run: cargo test
43+
44+
wasm:
45+
name: wasm build
46+
runs-on: ubuntu-latest
47+
steps:
48+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
49+
- name: Install stable toolchain with wasm target
50+
run: |
51+
rustup toolchain install stable --profile minimal
52+
rustup default stable
53+
rustup target add wasm32-unknown-unknown
54+
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
55+
with:
56+
path: |
57+
~/.cargo/registry
58+
~/.cargo/bin
59+
target
60+
key: rust-wasm-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
61+
- name: Install wasm-bindgen-cli
62+
run: command -v wasm-bindgen >/dev/null || cargo install wasm-bindgen-cli --version 0.2.126 --locked
63+
- name: Build core for wasm32
64+
run: cargo build --release --target wasm32-unknown-unknown --features wasm -p nexia-core
65+
- name: Generate JS bindings
66+
run: wasm-bindgen target/wasm32-unknown-unknown/release/nexia_core.wasm --target web --no-typescript --out-dir web/wasm

.github/workflows/ui-ci.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Product CI (UI/web): compiles the ReScript UI under Deno, builds the wasm
3+
# core, runs the UI + contract tests, and produces the web bundle.
4+
name: ui-ci
5+
6+
on:
7+
push:
8+
branches: [main]
9+
pull_request:
10+
workflow_dispatch:
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: ui-ci-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
build-test:
21+
name: rescript + wasm + deno test + bundle
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
25+
26+
- name: Install Deno
27+
run: |
28+
curl -fsSL https://deno.land/install.sh | DENO_INSTALL="$HOME/.deno" sh -s v2.9.1
29+
echo "$HOME/.deno/bin" >> "$GITHUB_PATH"
30+
31+
- name: Install stable Rust with wasm target
32+
run: |
33+
rustup toolchain install stable --profile minimal
34+
rustup default stable
35+
rustup target add wasm32-unknown-unknown
36+
37+
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
38+
with:
39+
path: |
40+
~/.cargo/registry
41+
~/.cargo/bin
42+
target
43+
key: rust-wasm-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
44+
45+
- name: Install wasm-bindgen-cli
46+
run: command -v wasm-bindgen >/dev/null || cargo install wasm-bindgen-cli --version 0.2.126 --locked
47+
48+
- name: Install JS dependencies (Deno-only, npm registry)
49+
run: deno task setup
50+
51+
- name: Compile ReScript
52+
run: deno task build:res
53+
54+
- name: Build wasm core
55+
run: deno task build:wasm
56+
57+
- name: UI + contract tests
58+
run: deno task test:ui
59+
60+
- name: Bundle web app
61+
run: deno task build:web
62+
63+
- name: Lint
64+
run: deno lint

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ erl_crash.dump
4040
# ReScript
4141
/lib/bs/
4242
/.bsb.lock
43+
ui/lib/
44+
ui/src/**/*.res.js
45+
ui/tests/**/*.res.js
46+
47+
# Generated web artifacts
48+
web/dist/
49+
web/wasm/
4350

4451
# Python (SaltStack only)
4552
__pycache__/

.machine_readable/6a2/STATE.a2ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
[metadata]
66
project = "nexia-list"
77
version = "0.1.0"
8-
last-updated = "2026-03-15"
8+
last-updated = "2026-07-02"
99
status = "active"
1010

1111
[project-context]
1212
name = "nexia-list"
13-
completion-percentage = 0
14-
phase = "In development"
13+
completion-percentage = 35
14+
phase = "active development — web-first build resurrection"
15+
focus = "wasm core integration (wasm-bindgen bridge to browser), then product CI, tests, canvas drag-and-drop, PWA (service worker)"

.machine_readable/INTENT.contractile

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,38 +35,36 @@
3535

3636
; === Purpose (what this repo IS) ===
3737
(purpose
38-
"{{ONE_PARAGRAPH_PURPOSE}}"
38+
"Nexia is a local-first, spatial personal knowledge management tool: notes arranged on a spatial canvas, first-class bidirectional links between them, and (planned) agents implemented as persistent queries that continuously organise the notebook. It is web-first — a ReScript TEA-style UI drives a Rust core compiled to WASM in the browser — and all user data lives on the user's device in human-readable JSON."
3939
)
4040

4141
; === Anti-Purpose (what this repo is NOT — prevents scope creep) ===
4242
(anti-purpose
43-
"{{ONE_PARAGRAPH_ANTI_PURPOSE}}"
44-
; Examples:
45-
; "This is NOT a general-purpose database — it solves one specific problem."
46-
; "This is NOT a framework — it is a library with a focused API."
47-
; "This does NOT handle authentication — that is delegated to [other repo]."
43+
"This is NOT a cloud or SaaS product — no server-side component, no account, no required network. It does NOT do real-time collaboration — local-first means single-user focus. It must NOT introduce vendor lock-in — data is always exportable in open formats. It is NOT an outliner clone — the spatial canvas and link graph are the primary organising metaphors, not a hierarchy."
4844
)
4945

5046
; === Key Architectural Decisions That Must Not Be Reversed ===
5147
(architectural-invariants
52-
; *REMINDER: List the foundational decisions*
53-
; ("Idris2 for ABI definitions — dependent types prove interface correctness")
54-
; ("Zig for FFI — zero-cost C ABI compatibility")
55-
; ("Elixir for supervision — OTP fault tolerance")
48+
("Web-first: the browser is the primary target; desktop (Gossamer) is an optional external shell over the same bundle")
49+
("The Rust core is the single source of truth for note/notebook semantics — the UI must not fork the data model")
50+
("Rust core is compiled to WASM (wasm-bindgen) so the browser runs the real engine")
51+
("Deno is the only JS runtime/package manager — no npm/bun/yarn/pnpm CLIs")
52+
("Local-first: no core feature may require network access")
53+
("User data stored in human-readable JSON")
5654
)
5755

5856
; === Sensitive Areas (if in doubt, ask) ===
5957
(ask-before-touching
60-
; *REMINDER: List areas where LLMs should check before modifying*
61-
; "src/abi/ — formal proofs, changes require re-verification"
62-
; "ffi/zig/ — C ABI boundary, changes affect all language bindings"
63-
; ".machine_readable/ — checkpoint files, format is specified"
58+
".machine_readable/ — checkpoint files, format is specified"
59+
".github/workflows/ — estate governance workflows; removal requires approval"
60+
"LICENSE and SPDX headers — never remove or modify"
61+
"core/src/ data model — semantics mirrored by the UI; changes ripple across the WASM boundary"
6462
)
6563

6664
; === Ecosystem Position ===
6765
(ecosystem
68-
(belongs-to "{{MONOREPO_OR_STANDALONE}}")
69-
(depends-on ("{{DEP1}}" "{{DEP2}}"))
70-
(depended-on-by ("{{CONSUMER1}}" "{{CONSUMER2}}"))
66+
(belongs-to "standalone repo within the hyperpolymath estate")
67+
(depends-on ("gossamer (optional — desktop shell only, external sibling checkout)"))
68+
(depended-on-by ())
7169
)
7270
)

.machine_readable/agent_instructions/methodology.a2ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ perfective = 10 # % for SPDX headers, doc updates, formatting, style
5555
# Customise this per project — the template default is generic.
5656

5757
[methodology.unique-strength]
58-
description = "{{PROJECT_UNIQUE_STRENGTH}}"
58+
description = "Typed end-to-end: the Rust core compiled to WASM is the single source of truth for note/notebook semantics, driven by an exhaustively pattern-matched TEA-style ReScript UI — no type drift between engine and interface"
5959
deepen-not-broaden = true
6060

6161
# ============================================================================

.tool-versions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
just 1.36.0
2+
deno 2.9.1

.well-known/ai.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ Allow: /
1212
Manifest: /0-AI-MANIFEST.a2ml
1313

1414
# Machine-readable metadata
15-
Metadata: /.machine_readable/STATE.scm
16-
Metadata: /.machine_readable/META.scm
17-
Metadata: /.machine_readable/ECOSYSTEM.scm
15+
Metadata: /.machine_readable/6a2/STATE.a2ml
16+
Metadata: /.machine_readable/6a2/META.a2ml
17+
Metadata: /.machine_readable/6a2/ECOSYSTEM.a2ml
1818

1919
# License
2020
License: MPL-2.0

0 commit comments

Comments
 (0)