Skip to content

feat(core): LambdaDelta L0 kernel — reader, value model, evaluator, budget#35

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

feat(core): LambdaDelta L0 kernel — reader, value model, evaluator, budget#35
hyperpolymath merged 3 commits into
mainfrom
claude/project-scope-planning-5zoaze

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

What this is

Phase L0 of ADR-0003 — the first implementation of the λδ substrate, built to the just-merged spec v0.1 (#34). A small, homoiconic, Clojure-flavoured Lisp living in the Rust core.

It is headless and carries no UI change — the ADR calls this "the ideal first PR because it carries no UI risk." Nexia-List stays a Tinderbox-like tool you can use for life without ever seeing a parenthesis; this is the engine power users will later reach through opt-in doors (fx fields, a REPL, computational notes).

A note is a letter we send to our future self. — the North Star. λδ exists to enlarge that correspondence, never to fence it in.

The kernel / host seam (spec §7) — from day one

  • core/src/lambdadelta/ is a self-contained language that knows nothing about notes: reader · value model · evaluator · budget · pure builtins.
  • Interp::register_builtin is the seam. A host (the notebook — the first of possibly many) registers its own builtins as native functions carrying their capability. Nexia-List is simply the first host. This is what makes an SDK, embedding, and the plugin ecosystem (LambdaDelta plugin system + dev design/deploy wizard (minter · provisioner · configurator · harness) #33) cheap rather than a later rewrite. A doc-test exercises the seam end-to-end.

What's in this PR

Piece File Notes
Reader reader.rs Total, panic-free. () [] {} #{}, #uuid/#inst tagged literals, sugar ' ` ~ ~@, #(… % …), ; comments, commas-as-whitespace. Guards so nan/inf/-> stay symbols.
Value model value.rs nil/bool/int/float/string/symbol/keyword/list/vector/set/map/tagged/fn/builtin; truthiness (only nil/false falsy); reader-compatible printer; lexical Scope chain for closures.
Evaluator eval.rs Special forms quote if do let fn def + quasiquote/unquote/unquote-splicing (spec §3); closures w/ lexical capture and & rest; keyword-as-function (:k m) (powers multimethod dispatch & sort-by); collection literals evaluate their elements.
Sandbox budget (in eval.rs) Per-eval reduction-step ceiling + recursion-depth limit (spec §6); exhaustion aborts cleanly — never hangs the tab.
Errors as values error.rs unbound / not-callable / arity / type / syntax / divide-by-zero / budget — structured, never panics.
Pure builtins builtins.rs Arithmetic, comparison, predicates, sequences (incl. map/filter/reduce/sort/sort-by), sets, strings, maps, reflection (eval/read/type). No notebook I/O — that's a host's job.
WASM entrypoint wasm.rs lambdadeltaEval proves the kernel runs in the browser.

Forks I resolved (flagged, not silent)

The spec left two things open; I picked and documented both in code:

  • = compares numbers across int/float ((= 1 1.0) → true). Notebook attributes round-trip through JSON where 2 and 2.0 are indistinguishable in intent — strict variant-equality there is a quiet footgun.
  • Collection transformers return vectors (the spec's "data" form, §9.4); conj/cons preserve the natural structure.

Deliberately deferred (to keep this reviewable)

Hygienic defmacro + gensym/macroexpand; multimethods (defmulti/defmethod); let/fn destructuring; and the notebook host bindings (notes, set-attr!, …) a host registers through the seam. Each layers on top without changing the seam.

Verification

  • cargo test66 green (34 kernel unit tests + 2 property tests [reader never panics on arbitrary input; print→read round-trip] + the existing core suite + the host-seam doc-test).
  • cargo clippy --all-targets -- -D warnings — clean.
  • cargo fmt --check — clean.
  • cargo check --features wasm — clean (browser binding compiles).

Note: the known-red estate governance checks (workflow staleness, ReScript "package anti-pattern policy", Hypatia baseline) are pre-existing estate policy, out of scope for this PR. Product CI (rust-ci) is the relevant gate here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps


Generated by Claude Code

…udget

Phase L0 of ADR-0003: the notebook-agnostic λδ language kernel, built to the
confirmed spec v0.1. Headless and fully tested; no UI change. This is the piece
everything else (formulas, agents-as-programs, REPL, plugins) will sit on.

The kernel / host seam (spec §7), from day one:
- `core/src/lambdadelta/` is a self-contained Clojure-flavoured Lisp that KNOWS
  NOTHING ABOUT NOTES. Reader · value model · evaluator · budget · pure builtins.
- `Interp::register_builtin` is the seam: a host (the notebook — the first of
  possibly many) registers its own builtins as native functions. Nexia-List is
  simply the first host. This is what makes an SDK/embedding/plugin ecosystem
  (#33) cheap rather than a later rewrite. A doc-test exercises the seam.

What's in:
- Reader (reader.rs): total, panic-free. Literals, `()` `[]` `{}` `#{}`,
  `#uuid`/`#inst` tagged literals (generic `Value::Tagged` — kernel keeps the
  form, hosts assign meaning), reader sugar `' ` `~` `~@`, `#(… % …)`, `;`
  comments, commas-as-whitespace. Guards so `nan`/`inf`/`->` stay symbols.
- Value model (value.rs): nil/bool/int/float/string/symbol/keyword/list/vector/
  set/map/tagged/fn/builtin; truthiness (only nil/false falsy); a reader-
  compatible printer; a lexical `Scope` with parent chain for closures.
- Evaluator (eval.rs): special forms `quote if do let fn def` + quasiquote/
  unquote/unquote-splicing exactly per spec §3; closures with lexical capture
  and `& rest`; keyword-as-function (`(:k m)`) powering multimethod dispatch and
  `sort-by`; collection literals evaluate their elements.
- Sandbox budget (spec §6): per-eval reduction-step ceiling and recursion-depth
  limit; exhaustion aborts cleanly with an error value — never hangs.
- Errors as values (error.rs): unbound/not-callable/arity/type/syntax/
  divide-by-zero/budget — structured, never panics.
- Pure builtins (builtins.rs, spec §4): arithmetic, comparison, predicates,
  sequence ops incl. map/filter/reduce/sort/sort-by, sets, strings, maps, and a
  little reflection (`eval`/`read`/`type`). No notebook I/O — that's a host's job.
- WASM: `lambdadeltaEval` browser entrypoint proves the kernel runs in the tab.

Deliberate resolutions the spec left open (flagged, not silent):
- `=` compares numbers across int/float (`(= 1 1.0)` → true) — JSON attributes
  make strict variant-equality a footgun.
- Collection transformers (map/filter/rest/sort/…) return vectors (the spec's
  "data" form, §9.4); `conj`/`cons` preserve the natural structure.

Deferred to follow-up L0/L1 slices (kept out to keep this reviewable): hygienic
`defmacro` + `gensym`/`macroexpand`, multimethods (`defmulti`/`defmethod`),
`let`/`fn` destructuring, and the notebook host bindings (`notes`, `set-attr!`,
…) that a host registers through the seam.

Verification: `cargo test` 66 green (34 kernel unit + 2 property [reader never
panics; print→read round-trip] + existing core suite + the seam doc-test);
`cargo clippy --all-targets -- -D warnings` clean; `cargo fmt --check` clean;
`cargo check --features wasm` clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps
Comment thread core/src/lambdadelta/reader.rs Fixed
Comment thread core/src/lambdadelta/tests.rs Fixed
Comment thread core/src/lambdadelta/tests.rs Fixed
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 65 issues detected

Severity Count
🔴 Critical 1
🟠 High 33
🟡 Medium 31

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Issue in scorecard.yml",
    "type": "missing_workflow",
    "file": "scorecard.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "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"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

Hypatia code_safety flagged unwrap/panic sites on the L0 kernel:
- reader.rs `read_one` used `forms.pop().unwrap()` on a length-1 vec. Replaced
  with `forms.remove(0)` (total) so the reader stays strictly panic-free — a
  real strengthening of the "reader never panics" guarantee, not a suppression.
- tests.rs `unwrap()`/`panic!` are the idiomatic assertion mechanism for unit
  tests (a failed unwrap IS the test failure), not a runtime DoS surface. Added
  a scoped, documented .hypatia-ignore carve-out for the test file ALONE; the
  production kernel deliberately stays under the code_safety scanner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 62 issues detected

Severity Count
🔴 Critical 1
🟠 High 30
🟡 Medium 31

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Issue in scorecard.yml",
    "type": "missing_workflow",
    "file": "scorecard.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "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"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

…est module

Rewrites the kernel test module to compare whole `Result`s
(`assert_eq!(run(x), Ok(y))`) instead of unwrapping. Two wins:
- Clears the Hypatia code_safety alerts (unwrap_without_check, panic_macro) on
  the test file — the gating SARIF check does not consult .hypatia-ignore, so
  the fix belongs in the code, not the exemption list. Reverts the speculative
  .hypatia-ignore carve-out; the governance file is back to its prior state.
- Assertions now cover the error variant too, and the whole kernel (production
  AND tests) is free of unwrap/panic.

No behaviour change; 32 kernel tests still green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps
@sonarqubecloud

sonarqubecloud Bot commented Jul 4, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 62 issues detected

Severity Count
🔴 Critical 1
🟠 High 30
🟡 Medium 31

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Issue in scorecard.yml",
    "type": "missing_workflow",
    "file": "scorecard.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "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"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath marked this pull request as ready for review July 4, 2026 11:57
@hyperpolymath
hyperpolymath merged commit eb8aa4e into main Jul 4, 2026
24 checks passed
@hyperpolymath
hyperpolymath deleted the claude/project-scope-planning-5zoaze branch July 4, 2026 11:57
hyperpolymath added a commit that referenced this pull request Jul 9, 2026
…lude (#43)

## What this is

The **language core, completed** (spec §3) — the second half of "1 then
2": **full macro hygiene**, procedural `defmacro`, **multimethods**, and
a **sugar prelude**. Builds on the L0 kernel (#35) and notebook host
(#36); the kernel/host seam is untouched, so the notebook host inherits
all of this for free.

You chose **B — full set-of-scopes hygiene now, in one pass** — and
that's what this is: not the weaker Clojure auto-gensym model.

## Full hygiene (and it's genuinely full)

Kohlbecker-style **marks**, encoded in symbol names. Each macro
expansion gets a fresh mark; quasiquote tags the symbols a *template
introduces* with it. Both hygiene properties are implemented **and
tested**:

| Property | Test | Result |
|---|---|---|
| Introduced bindings never capture | `(defmacro m [e] \`(let [x 2] ~e))
(let [x 1] (m x))` | **1**, not 2 |
| Free identifiers are referentially transparent | `(defmacro inc [n]
\`(+ ~n 1)) (let [+ -] (inc 5))` | **6**, not 4 |

So the prelude's `and`/`or`/`if-let` use plain `v`/`tmp` with **no
`foo#`** — "macro-introduced bindings never capture… *without the author
asking*" (spec §3), delivered literally.

Mechanics: marked free identifiers fall back to the definition (global)
env; special forms **and** macros dispatch on the *base* name (so a
marked `let`/`if`/user-macro in a template still works); `Display`
strips marks so output stays readable.

## What's in this PR

- **`defmacro`** — procedural transformer over *unevaluated* forms;
`gensym`, `macroexpand`, `macroexpand-1`.
- **Multimethods** (spec §3, open dispatch — not class-OO): `defmulti
name dispatch-fn` + `defmethod name dispatch-val [args] …` with
`:default`. Dispatch on *any* function of the args — e.g. a note's
`:type`:
  ```clojure
  (defmulti describe :type)
  (defmethod describe "task" [n] (str "task:" (:title n)))
  (defmethod describe :default [n] "other")
  ```
- **Prelude** (`prelude.rs`) — hygienic sugar written in λδ itself,
loaded at `Interp::new()`: `when unless cond and or -> ->> if-let
when-let`.

## Files

`value.rs` (mark primitives + `MultiMethod` + mark-stripping display) ·
`mod.rs` (macro/multimethod registries, mark counters, prelude load) ·
`eval.rs` (hygiene-aware symbol resolution, base-name dispatch,
`expand_macro`, `defmacro`/`defmulti`/`defmethod`, quasiquote marking) ·
`builtins.rs` (`gensym`/`macroexpand`) · `prelude.rs` (new) · `tests.rs`
(11 new tests).

## Deliberately deferred

`let`/`fn` destructuring; an explicit unhygienic escape hatch (if ever
justified); `case`/`match` (pattern matching). Each layers on without
touching the seam.

## Verification

- `cargo test` — **81 lib tests green** (11 new: user macros, both
hygiene properties, multimethods, all prelude sugar, `gensym`,
`macroexpand`) + host + golden + invariants + doc-tests.
- `cargo clippy --all-targets -- -D warnings` — clean (default **and**
`--features wasm`).
- `cargo fmt --check` — clean. Production kernel stays **panic-free**
(Hypatia-clean).

> **Note:** estate governance `workflow_audit` findings are pre-existing
estate policy, out of scope; `rust-ci` is the gate here.

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

https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps

---
_Generated by [Claude
Code](https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps)_

Co-authored-by: Claude <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jul 16, 2026
Verified against the code rather than TOPOLOGY.md, which is stale:

  claim                     TOPOLOGY/wiki       reality
  λδ substrate              "planned"           built (#35, #36, #43 merged)
  core size                 ~623 LOC, 12 tests  5,344 LOC, 90 tests green

λδ is the largest subsystem in the core (~4,050 LOC, ~76%): reader, value
model, evaluator + Budget, hygienic macros, multimethods, prelude, and the
notebook host seam. Calling it "planned" materially misrepresents the project
to every audience the wiki serves.

- Home.md: substrate row planned -> built; status paragraph now cites measured
  figures and flags that TOPOLOGY.md predates the three LambdaDelta merges.
- Developer.md: add the λδ substrate as a first-class stack layer; Track A
  marked substantially landed, with the outstanding work named (.ld packages,
  surfacing L2+ through the disclosure ladder).

Toolchain verified end-to-end while checking this: `just build-wasm` (with
wasm-bindgen-cli 0.2.126, matching Cargo.lock), `just build`, and `just test`
all green — 90 Rust + 10 UI tests, including "TEA update delegates to the
wasm core".

Co-Authored-By: Claude Opus 4.8 (1M context) <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