Skip to content

Web-first resurrection: UI builds and runs on the WASM Rust core, real CI, docs truth reset#22

Merged
hyperpolymath merged 5 commits into
mainfrom
claude/project-scope-planning-5zoaze
Jul 2, 2026
Merged

Web-first resurrection: UI builds and runs on the WASM Rust core, real CI, docs truth reset#22
hyperpolymath merged 5 commits into
mainfrom
claude/project-scope-planning-5zoaze

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

What this is

The repo had three disconnected layers (a working Rust core, a ReScript UI that didn't compile and reimplemented all logic client-side, and a desktop shell blocked on the external gossamer sibling), no working build pipeline, and no CI that touched the product. This PR makes the web app the primary, fully self-contained target: the ReScript UI now runs the real Rust core compiled to WebAssembly, in the browser, with working persistence — and every piece of it is built and tested by CI from this repo alone.

Architecture decision recorded in docs/adr/0001-wasm-core-web-first.md; the Deno-only package-management interpretation in docs/adr/0002-deno-only-interpretation.md.

Highlights, by commit

  • build: Deno 2 toolchain with pinned npm: specifiers and a real deno.lock (was 0 bytes); esbuild bundle/dev-server scripts; root cargo workspace that excludes desktop/ so the missing gossamer sibling can never break builds; the npm install task that violated the repo's own Deno-only MUST is gone.
  • core: new wasm.rs bindings (granular mutations, camelCase views for the UI, snake_case disk format unchanged); rebuild_backlinks() on every load; fixes a real index-corruption buglink_notes(a, a) recorded a backlink for a link that was never created (found by the new property test on its first run); golden-fixture contract tests + proptest invariants.
  • ui: all notebook logic now delegates to the core through a store seam (WasmStore.res) whose operations mirror the desktop shell's commands, so a future GossamerStore swaps in with zero UI changes; IDs/timestamps come from the core (the UI previously fabricated Math.random() IDs); debounced IndexedDB autosave + file Save/Load replace the former no-ops; fixes for rec update, the undeclared Webapi dependency, and a stale-closure delete guard; TEA update tests + contract tests under deno test.
  • ci: rust-ci.yml (fmt, clippy -D warnings incl. the wasm feature, all tests, wasm32 build) and ui-ci.yml (ReScript compile, wasm build, deno tests, bundle, lint) — the first workflows in the repo that build or test the product. SHA-pinned, no new third-party actions.
  • docs: truth reset — TOPOLOGY claimed ~70% done with petgraph/tantivy/Nickel at 100% (none exist); now every document describes what the code actually does, placeholders are filled, and dead references (flake.nix, .scm state files, nonexistent just recipes) are fixed.

Verified

  • cargo test: 14 unit + 2 golden + 2 property tests green
  • deno task test:ui: TEA update suite + both contract tests green against the real wasm binary
  • cargo fmt --check, cargo clippy --all-targets --features wasm -- -D warnings, deno lint: clean
  • Browser E2E (headless Chromium): create two notes → titles/content via the core → search returns the right note → reload → both notes restored from IndexedDB autosave, stored in the same snake_case format the desktop shell reads

Not in this PR (next phases per ROADMAP)

Canvas drag-and-drop, WCAG keyboard canvas, PWA service worker, wiki-links, graph view, agents, import/export — see the revised ROADMAP.adoc sequencing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps


Generated by Claude Code

claude added 5 commits July 2, 2026 15:29
… 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/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
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
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
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
@sonarqubecloud

sonarqubecloud Bot commented Jul 2, 2026

Copy link
Copy Markdown

@@ -0,0 +1,111 @@
// SPDX-License-Identifier: MPL-2.0
Comment thread ui/src/store/Persist.res
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MPL-2.0
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 62 issues detected

Severity Count
🔴 Critical 1
🟠 High 23
🟡 Medium 38

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Issue in boj-build.yml",
    "type": "missing_timeout_minutes",
    "file": "boj-build.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in codeql.yml",
    "type": "missing_timeout_minutes",
    "file": "codeql.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in hypatia-scan.yml",
    "type": "missing_timeout_minutes",
    "file": "hypatia-scan.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath marked this pull request as ready for review July 2, 2026 15:33
@hyperpolymath
hyperpolymath merged commit d0fcae2 into main Jul 2, 2026
21 of 24 checks passed
@hyperpolymath
hyperpolymath deleted the claude/project-scope-planning-5zoaze branch July 2, 2026 15:33
hyperpolymath added a commit that referenced this pull request Jul 2, 2026
- ui: replace deprecated Js.Dict.t / Js.Nullable with RescriptCore
  Dict.t / Nullable (Hypatia code-scanning alerts 143, 144)
- ci: add timeout-minutes to the rust-ci and ui-ci jobs
  (Hypatia workflow_audit: missing_timeout_minutes)


Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps

Co-authored-by: Claude <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jul 17, 2026
…fix the Justfile (#46)

## Why

`TOPOLOGY.md` predated the three LambdaDelta merges (#35, #36, #43) and
never mentioned λδ. Because it is *the* status source, every downstream
doc inherited its stale figures — including the wiki landed in #45. This
re-derives the countable facts from the tree and corrects the cascade,
then fixes the Justfile gaps found along the way.

## TOPOLOGY was understating the project on six axes

| Claim | Reality |
|---|---|
| λδ absent entirely | **Built, not planned** — reader, value model,
evaluator + Budget sandbox, hygienic macros, multimethods, prelude, host
seam. ~3,400 LOC excl. tests, **~64% of the core**, its largest
subsystem. Now its own dashboard row |
| "12 passing unit tests" | **90** (81 unit + 3 exchange + 2 golden + 2
property + 2 doc) |
| WASM bridge "40%, in progress" | **Wired and exercised** — `Main.res`
loads the wasm at boot, `WasmStore` binds 21 of 31 exports, ui-ci runs a
TEA↔WASM contract test against the real bundle |
| CI "10%, landing in a parallel workstream" | `rust-ci.yml` +
`ui-ci.yml` **load-bearing since #22**, SHA-pinned, green on main |
| Web/PWA "no service worker yet (planned)" | Service worker +
webmanifest + icon **ship and are registered** (`Main.res:88`, #27) |
| UI "no drag-and-drop; GraphView placeholder" | Note drag
**implemented** (`View.res:397`); GraphView renders a circular layout |

**Overall ~35% → ~65%.** The critical path has **moved off the WASM
bridge onto the UI surface**: 10 exports are unbound in ReScript,
including both λδ entry points (`lambdadeltaEval`, `evalLambdadelta`) —
the substrate is reachable from JS but nothing calls it.

Also corrects **a mixed-basis error I introduced in #45**: λδ was
reported as "~76% of the core" by dividing 4,049 LOC (*with* tests) by
5,344 (*without*). Like-for-like it is 3,407/5,344 = **~64%**.

## Justfile

- **`just test` could not work on a clean checkout** — the UI tests
import generated `*.res.js` and the wasm bindings, so type-check failed
with 4 `TS2307` errors. Now `test → build → build-wasm`. Warm no-op
costs ~0.45s, so it is not a tax on the inner loop; added `test-rust`
for the Rust-only loop.
- **`just check` was weaker than CI** (no `--all-targets`, no
`--features wasm`, ran from `core/`) — it could pass locally while CI
failed. Now mirrors `rust-ci.yml` exactly.
- **`just doctor` ignored the wasm toolchain** — the one thing that
actually blocks the browser build. Now checks the wasm32 target and
compares the wasm-bindgen CLI against `Cargo.lock`, printing the exact
fix command on drift.
- **`crg-grade` and `crg-badge` were dead on arrival** — Make-style
`$$(...)` under just/sh expanded `$$` to the shell PID, so `(` was a
syntax error. They failed 100% of the time. Rewritten as bash shebang
recipes.

## Verification

- `just check` — pass (now compiles the `wasm` feature, which the old
one never did)
- `just test` from a **fully cleaned tree** (no `web/wasm`, no
`*.res.js`) — rebuilds and passes **90 Rust + 10 UI**
- `just doctor`, `crg-grade` (→ `D`), `crg-badge` — all run; doctor's
FAIL path verified by hiding wasm-bindgen from `PATH`
- `asciidoctor` renders README/ROADMAP/QUICKSTART-DEV clean; `just
wiki-sync dry` still previews correctly

## Deliberately not done — needs your call

- **`READINESS.md` grade D is probably stale.** All three *Path to C*
criteria now appear met (product CI on every PR ✅, UI tests in CI ✅,
WASM bridge built + smoke-tested ✅). The grade is assigned by audit, so
I corrected the facts and flagged a re-audit rather than self-promoting.
`just crg-badge` publishes that letter, so the badge is currently
understating the project.
- **`must-spdx-headers` is a broken gate** (pre-existing, untouched
here). It runs `find . | head -20`, so it checks an arbitrary,
readdir-order-dependent fifth of the tree *and* walks vendored
`node_modules/`. It currently fails on `@rescript/react`'s own `.res`
files — **every tracked source file does have SPDX**. Worse,
`contractile.just` has drifted from its source:
`contractiles/must/Mustfile.a2ml` contains no `spdx-headers` rule at
all, and its `no-hardcoded-paths` rule (severity: **critical**) is
missing from the generated file. The `contractile` tool isn't installed
to regenerate, and this is your standards ecosystem — so I left it
alone.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants