Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
226b0b4
docs(theory): add correspondence model — the engine that classifies, …
claude Jun 18, 2026
ddd3d44
chore(standards): complete 6a2 manifest set + panic-attack gate + eNS…
claude Jun 18, 2026
c7a1281
Merge main (#19 spec + #20 hygiene) before identity re-point
claude Jun 18, 2026
734c4b0
docs(identity): re-point repo to the correspondence (classify-not-tra…
claude Jun 18, 2026
6a3bd0a
Merge main (#21 identity) before abstraction-model ABI
claude Jun 18, 2026
c8b2859
feat(abi): Idris2 ABI core — Concept/Form/Transition + six Correspond…
claude Jun 18, 2026
22935e1
Merge main (#22 ABI core) before Zig FFI seam
claude Jun 18, 2026
dd13b84
feat(ffi): Zig FFI seam — C-ABI mirror of the correspondence ABI
claude Jun 18, 2026
021b227
feat(cartridges): correspondence cartridge contract + validator (veri…
claude Jun 18, 2026
47e19a6
docs(verify): record ABI typecheck; make cartridge validator lockfile…
claude Jun 18, 2026
ec1f5dd
feat(ci+host): Idris2 ABI typecheck CI + AffineScript host binding
claude Jun 18, 2026
bc66d0a
Merge main (#23 track-2 wave) before multi-pane GUI
claude Jun 27, 2026
1aa75bd
feat(gui): multi-pane correspondence workspace (cartridge-driven)
claude Jun 27, 2026
d5dc360
Merge main (#32 GUI) before removing ReScript
claude Jun 27, 2026
abe6330
Remove legacy ReScript host; Deno + cartridges is the runtime surface
claude Jun 27, 2026
48bf632
Dedup cartridge loader into src/cartridges.js (SonarCloud duplication…
claude Jun 27, 2026
e3a2a8a
Revert cosmetic GUI reformatting; keep only the shared-loader change
claude Jun 27, 2026
bb5c4b7
Merge merged main into branch before handoff doc
claude Jun 27, 2026
7dd94fe
Add HANDOFF.adoc — orientation for resuming (e.g. in the desktop app)
claude Jun 27, 2026
969731d
Merge merged main before preexisting-cleanup work
claude Jun 27, 2026
6930568
Purge stale TS/VS Code-era docs; finish .adoc migration where unambig…
claude Jun 27, 2026
fbc89e6
Merge merged main before licence alignment
claude Jun 27, 2026
cc8377b
Align licensing: MPL-2.0 (code) / CC-BY-SA-4.0 (docs); retire MIT+Pal…
claude Jun 27, 2026
4318d69
Merge merged main before .adoc doc consolidation
claude Jun 27, 2026
c3e1810
Consolidate CONTRIBUTING + CHANGELOG to .adoc (retire the .md duplica…
claude Jun 27, 2026
cb0c74d
Merge merged main before polish batch
claude Jun 27, 2026
0f56af6
Polish batch: 2nd cartridge, GUI smoke test, finish .adoc tidy
claude Jun 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,16 @@ gui:

# === TESTING ===

# Run all tests
# Run all tests (cartridge invariants + GUI smoke test)
test:
@echo "🧪 Running tests..."
deno task test

# Run just the GUI smoke test (server handler: shell, bundle, cartridge API)
test-gui:
@echo "🖥️ GUI smoke test..."
@deno task test:gui

# === VALIDATION ===

# Run full validation
Expand Down
45 changes: 45 additions & 0 deletions TEST-NEEDS.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: CC-BY-SA-4.0
= TEST-NEEDS — nextgen-languages-evangeliser

CRG Grade: C (achieved 2026-04-04).

== Current test state

[cols="2,1,3",options="header"]
|===
| Category | Count | Notes

| Cartridge invariant tests | 1 suite | `test/run_all.js` (Deno) — semantic invariants over every cartridge
| GUI smoke test | 1 suite | `test/gui_smoke_test.js` (Deno) — server handler: shell, bundle, cartridge API
| Cartridge schema validation | 1 | `scripts/validate-cartridges.js` (Deno + ajv, JSON-Schema 2020-12)
| Idris2 ABI typecheck | 1 | `abi.ipkg` / `src/interface/Abi/*.idr` (CI: `idris2-abi.yml`)
| Test framework | Deno + ajv + Idris2 | host-language unit tests were removed with the ReScript host
|===

== What's covered

* Every correspondence is classified by one of the six CorrespondenceKinds
* Well-formedness: Forms (language + surface), residue shapes, strata names/booleans
* False-friend signature (surface holds ∧ intention diverges) — mirrors `Abi.Correspondence.isFalseFriendShape`
* Residue direction for novel (absent-source) / vanished (absent-target)
* The worked-examples reference cartridge demonstrates all six kinds
* Cartridge JSON-Schema conformance (ajv)
* GUI server handler: static shell, `/app.js` bundle, `/api/cartridges` shape, and a clean 404
* Idris2 ABI typechecks (no `believe_me` / `assert_total`)

== Still missing (for CRG B+)

* Headless-DOM rendering test of `gui/app.js` (the smoke test covers the server / API, not the browser render)
* Property-based generation over cartridge facts
* Performance benchmarks
* End-to-end "walk a correspondence" flow test

== Run tests

[source,bash]
----
just test # cartridge invariants + GUI smoke test
just test-gui # GUI smoke test only
deno task test # same as `just test`
deno task validate # cartridge schema validation
----
37 changes: 0 additions & 37 deletions TEST-NEEDS.md

This file was deleted.

51 changes: 51 additions & 0 deletions TOPOLOGY.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: CC-BY-SA-4.0
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
= TOPOLOGY — nextgen-languages-evangeliser

== Purpose

Cross-language *comprehension and transfer* engine — Duolingo / Rosetta Stone for
programming languages. It *classifies* correspondences between a known language and
a target so learning transfers (it does not translate, lint, or build IDEs).
Flagship / future host: AffineScript. Legacy target: ReScript (host removed). Runs
on Deno; the browser multi-pane workspace is the primary surface.

== Module map

[source]
----
nextgen-languages-evangeliser/
├── bin/evangeliser.js # Offline CLI (Deno; reads cartridges)
├── src/
│ ├── cartridges.js # Shared cartridge loader (Deno glue)
│ └── interface/ # Engine spine: Idris2 ABI + Zig FFI + AffineScript host binding
├── cartridges/ # Correspondence facts: schema + reference pack
├── gui/ # Browser multi-pane workspace (primary surface)
├── scripts/ # Deno utilities (cartridge validator, GUI launcher)
├── test/ # Cartridge invariant tests + GUI smoke test
├── config.ncl # Nickel configuration
├── docs/ # Documentation (AsciiDoc)
├── Justfile # Task runner recipes
└── deno.json # Deno module config
----

== Data flow

[source]
----
cartridges/**/*.cartridge.json (authored correspondence facts)
shared loader (src/cartridges.js)
├─► gui/server.js ──► browser workspace (index · forms · classification)
├─► bin/evangeliser.js ──► CLI (--kind / --find / --json)
└─► test + scripts ──► invariants + schema validation
(downstream, planned) octads ──► VeriSimDB ──► PanLL panels
----

The classification vocabulary (six CorrespondenceKinds, strata, residue shapes) is
specified in the Idris2 ABI (`src/interface/Abi/`) and mirrored by the cartridge
schema. The engine *classifies* correspondences; it does not transform source.
30 changes: 0 additions & 30 deletions TOPOLOGY.md

This file was deleted.

122 changes: 122 additions & 0 deletions cartridges/reference/python-ruby.cartridge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{
"$schema": "../correspondence-cartridge.schema.json",
"spdx": "MPL-2.0",
"name": "python-ruby",
"version": "0.1.0",
"description": "Second reference cartridge: Python -> Ruby. Dogfoods the cartridge contract on a fresh language pair to show the engine is language-agnostic. Covers cognate / false-friend / alien-realization / novel / vanished from a Python learner's standpoint.",
"languages": ["python", "ruby"],
"transitions": [
{
"concept": "function-definition",
"from": { "language": "python", "surface": "def greet(name):\n ..." },
"to": { "language": "ruby", "surface": "def greet(name)\n ...\nend" },
"kind": "cognate",
"residue": {
"shape": "none",
"note": "Same keyword (`def`) and intent; Ruby closes the body with `end` where Python uses indentation."
},
"strata": [
{ "stratum": "surface", "holds": true },
{ "stratum": "intention", "holds": true }
],
"narrative": {
"celebrate": "You already define functions with `def` — that word is the same in Ruby.",
"better": "Your `def` intuition transfers directly.",
"example": "def greet(name): ... <-> def greet(name) ... end"
}
},
{
"concept": "string-interpolation",
"from": { "language": "python", "surface": "f\"Hi {name}\"" },
"to": { "language": "ruby", "surface": "\"Hi #{name}\"" },
"kind": "cognate",
"residue": {
"shape": "none",
"note": "Same intent (interpolate a value into a string); Ruby uses `#{}` and needs no `f` prefix."
},
"strata": [
{ "stratum": "surface", "holds": true },
{ "stratum": "intention", "holds": true }
],
"narrative": {
"celebrate": "f-strings are second nature to you — interpolation carries straight over.",
"better": "Drop the `f` and write `#{}` instead of `{}`.",
"example": "f\"Hi {name}\" <-> \"Hi #{name}\""
}
},
{
"concept": "empty-collection-truthiness",
"from": { "language": "python", "surface": "if items: # empty list is falsy" },
"to": { "language": "ruby", "surface": "if items # empty array is TRUTHY" },
"kind": "false-friend",
"residue": {
"shape": "lossy",
"note": "In Ruby only `nil` and `false` are falsy; `[]`, `\"\"`, and `0` are all truthy. The Python habit of testing a collection for emptiness via truthiness silently breaks."
},
"strata": [
{ "stratum": "surface", "holds": true },
{ "stratum": "intention", "holds": false }
],
"narrative": {
"celebrate": "You've leaned on truthiness to check for empty/zero for years — that's a real, useful instinct.",
"safety": "In Ruby that instinct misfires: `if items` is true even when empty. Test emptiness explicitly.",
"example": "Python: if items: -> Ruby: if items.any? (or: unless items.empty?)"
}
},
{
"concept": "map-over-collection",
"from": { "language": "python", "surface": "[f(x) for x in xs]" },
"to": { "language": "ruby", "surface": "xs.map { |x| f(x) }" },
"kind": "alien-realization",
"residue": {
"shape": "lossy",
"note": "Same intention (transform each element) realised through different machinery: a comprehension expression vs a method taking a block."
},
"strata": [
{ "stratum": "intention", "holds": true },
{ "stratum": "structure", "holds": false }
],
"narrative": {
"celebrate": "You think in comprehensions — that mapping intent is exactly right.",
"better": "Ruby spells it as a block passed to `map`; once it clicks, blocks are everywhere.",
"example": "[f(x) for x in xs] -> xs.map { |x| f(x) }"
}
},
{
"concept": "symbol",
"from": { "language": "python", "surface": "(no analog)" },
"to": { "language": "ruby", "surface": ":status" },
"kind": "novel",
"residue": {
"shape": "absent-source",
"note": "Python has no symbol type. The nearest habits — interned strings, enum members, dict keys — are not the same thing; learn the symbol fresh."
},
"strata": [
{ "stratum": "intention", "holds": false }
],
"narrative": {
"celebrate": "Coming from Python you have great instincts for dict keys and enums.",
"safety": "A Ruby symbol (`:status`) is its own immutable, interned identifier — not a string. Learn it as a new thing rather than mapping it onto strings.",
"example": "{ status: :active } # :active is a symbol, not \"active\""
}
},
{
"concept": "explicit-self-parameter",
"from": { "language": "python", "surface": "def m(self):\n ..." },
"to": { "language": "ruby", "surface": "def m\n ...\nend" },
"kind": "vanished",
"residue": {
"shape": "absent-target",
"note": "Ruby has no explicit `self` parameter; the receiver is implicit. The Python habit of writing `self` first in every method has nowhere to go — re-route it."
},
"strata": [
{ "stratum": "intention", "holds": false }
],
"narrative": {
"celebrate": "Writing `self` first is muscle memory from Python — totally reasonable.",
"safety": "In Ruby that parameter vanishes: methods take only their real arguments, and `self` is implicit (use it bare when you need the receiver).",
"example": "Python: def m(self): ... -> Ruby: def m ... end"
}
}
]
}
5 changes: 3 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"gui": "deno run --allow-read --allow-net gui/server.js",
"lint": "deno lint",
"fmt": "deno fmt",
"test": "deno run --allow-read test/run_all.js",
"test": "deno run --allow-read test/run_all.js && deno run --allow-read test/gui_smoke_test.js",
"test:gui": "deno run --allow-read test/gui_smoke_test.js",
"pre-commit": "deno task lint && deno task fmt --check"
},
"fmt": {
Expand All @@ -20,7 +21,7 @@
"semiColons": false,
"singleQuote": false,
"proseWrap": "preserve",
"include": ["bin/", "src/cartridges.js", "test/run_all.js", "deno.json"]
"include": ["bin/", "src/cartridges.js", "test/", "deno.json"]
},
"lint": {
"rules": {
Expand Down
Loading
Loading