Skip to content

Land rcode: 6 lanes - #210

Merged
XrxcGH merged 61 commits into
betafrom
train-rcode
Nov 15, 2026
Merged

XrxcGH merged 61 commits into
betafrom
train-rcode

Conversation

@XrxcGH

@XrxcGH XrxcGH commented Nov 15, 2026

Copy link
Copy Markdown
Owner

Merge train for six Beta 12 lanes: one integration branch off beta, one CI run.

Lanes (merge order)

Lane PR What it adds
b12-code-L05 #207 Python runtimes: quick (MicroPython) and full (Pyodide), shared session, tracebacks, compile() check
b12-code-L06 #0 (none) JavaScript and TypeScript runtime on QuickJS with Sucrase, Jest-compatible test API
b12-code-L10 #0 (none) Code diagnostics: scheduler, Lezer syntax layer, own checkers, engine checks (ADR 0074)
b12-code-L12 #0 (none) Completion without a model: ranked list, pattern ghost text, explicit request
b12-shared-x-blockmerge #0 (none) Block-level merge on sync, merger and conflict view registries (ADR 0089)
b12-text-review #0 (none) Comments, suggesting mode, accept and reject, formatting in Compare (ADR 0086)

Conflicts (7 files over 4 merges)

  • app/bundle-budget.json (L05, text-review): kept beta's higher budgets (start-up 620/30, KitAeroTool 230).
  • run/adapters.ts (L06): union of the Python and JavaScript/TypeScript adapters.
  • codestudio/index.ts (L10, L12): union of loadCodeProjects, loadDiagnostics, and loadCompletion.
  • docs/tools/featureCommands.ts (text-review): union of beta's format and layout sentences and review's 19.
  • docs/tools/registry.json, docs/FEATURE-LIST.md (text-review): regenerated.

Fixes on the train

  • ADR index: row for 0089; 0074 shows Accepted as its file does. Format spec 14.1 notes block mergers. No ADR number clashes.
  • Cross-lane hooks: diagnostics/runtimeChecks.ts registers L05's compile() check (full Python, lazy, no marks without the pack) for L10. L10's no-undef knows L06's runtime globals (readline, prompt, process, module, exports, require). Both with tests.
  • Track changes (Ctrl+Shift+E, Word) clashed with the Google Docs set's center; it is now unbound in the Docs set.
  • featureCommands.ts went over its 800-line limit: review sentences moved to featureCommandsReview.ts.
  • Budgets: start-up 620 to 622 KB (review registers at start-up), Sucrase lazy chunk 46 KB.
  • tests/tsconfig.json includes MicroPython's module declaration, since the tests program now reaches the Python runtime.
  • README, docs/FEATURES.md, help pages: new rows, a Python help page, and a comments and suggestions section.

Waiting on later lanes

  • code:L04 (studio window): create diagnostics with registerRuntimeChecks, and attachCompletion per editor.
  • Tables and code projects: register block mergers and conflict views.

Gates (local)

  • typecheck 0, lint 0, prettier clean, checks 0 errors (warnings only)
  • unit 17,591 passed; components 2,213 passed, 12 load flakes passed on rerun (59/59)
  • node suites (checks, design, lint rules, matrix, extensions, hardware, soak, nvda) pass
  • app build and bundle check pass
  • cargo fmt, clippy -D warnings (core, sync, opennote), cargo test core and sync pass

🤖 Generated with Claude Code

XrxcGH and others added 30 commits November 7, 2026 03:57
merge_block(type, base, mine, theirs) merges two changed versions of one
block through the merger its type registered. With no merger the result
is a conflict, which keeps today's conflict copy. merge_page uses it only
when a changed block opted in, every block changed on both sides merges,
and the other page fields changed on one side at most. The merged
revision comes from the two revisions alone, so every device reaches the
same bytes in any order (ADR 0089).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before the page rule, sync tries the block merge for two concurrent live
versions of page.json, from the newest .history snapshot both list as an
ancestor. The process-wide mergers take part; merge_with takes explicit
ones for tests. With no merger registered the merged manifest and its
conflict copies are byte-identical to before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
conflict.text now also returns the blocks that differ between the two
versions, with their types. A block type registers a compare and resolve
view with registerConflictView, and the conflict dialog shows it for
that type's blocks and the side-by-side text for the rest. Tables and
code projects register theirs in later lanes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The follow-up that ADR 0049 named: one merger and one conflict view
registry per block type, and when sync merges a page by block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The block merge on sync used an older shared snapshot as the base when the
newest one had none. A merged revision never had a snapshot, so the next
concurrent round merged against a stale base and brought undone changes
back. Now only the newest shared revision is the base, and the merge
publishes a snapshot of the merged revision.

merge_page also keeps the conflict copy when a side is read-only or the two
sides were read at different version keys (R-VER-5). The conflict dialog
keeps the whole-page text under a disclosure when every changed block has
its own view, so page-field differences stay visible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The merge base is the first revision each side's ancestor list shares
  with the other, and both sides must agree. A device whose clock runs
  behind can no longer make an older shared save the base.
- Sync uploads a .history snapshot of each page.json revision it sends
  that has none yet, so pages synced while open keep their base.
- The conflict list hides a copy the current page already includes.
- Split long merge functions into helpers and move the block merge into
  crates/sync/src/merge/blocks.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comments and suggestions are marks in a text block's Markdown with
their records in data.comments and data.suggestions (ADR 0086, spec
6.10). A review model rides on the text sync, so a mark and its record
change in one undo step, and records of marks that left are dropped.

Suggesting mode turns typing into insertion marks, deletions into
deletion marks, and formatting commands into format changes that keep
the old Markdown. Accept and reject settle one change, all changes, or
one author's changes. The marks get styles for the All, Simple, and
None markup views, and a screen reader hears "Insertion by Sam".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comments pane lists a page's comments in reading order. It posts a
comment's text, replies, resolves, reopens, deletes, and goes to the
text. The Review tab gets New comment, Show comments, Previous and Next
comment, Resolve, Delete, Track changes (Ctrl+Shift+E), the markup
views, Accept and Reject (one, all, or by author), and Previous and
Next change, all behind text.review. The person's display name for
comments and changes is typed in the pane and kept on this device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A paragraph whose text is the same but whose Markdown differs now says
what changed, such as "Formatting changed: bold added to 'grow'".
Paragraph formatting compares key by key, so an alignment change alone
makes a changed paragraph. This prepares a later per-key paras merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records, the rider that keeps a mark and its record in one undo step,
suggesting mode, accept and reject, markup views, the comments pane,
the local author name, and what is not tracked yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review's chunks load only after the page view, so they join the page
group and count without the editor (about 10 KB at most). Its start-up
registrations and strings add about 2.8 KB: origin/beta builds 542.1 KB
and this branch 544.9 KB, so the start-up limit goes from 542 to 545.
The aero tool chunk grows from 222.8 to 223.1 KB as shared chunks
split differently, so its limit goes from 223 to 224.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/tools/registry.json gains the Review tab commands, and
featureCommands.ts gives each one a sentence, so the feature list
generator can place them. FEATURE-LIST.md is left to the docs owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The feature list test fails when FEATURE-LIST.md is not what
docs:features makes, so the generated page gains the 19 Review tab
commands. Nothing else changed in it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Accept or reject removes a suggestion's paragraph breaks with it, so a
  deletion across paragraphs joins them and a rejected paste joins the split.
- The comments pane refuses a comment over 64 KiB and a reply past 256
  replies, as the core's reader does (spec 16).
- Under forced colors comment marks drop their tint behind CanvasText.
- The pane parses a block without an editor once per Markdown, and a caret
  move only updates the marked comment.
- A refused record-only change puts back what the core holds and says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each change of a file starts one timer per layer (syntax 50 ms, checkers
and engine checks 300 ms, code tools 500 ms, clang 800 ms, DESIGN 6.1).
Every provider result carries the version it read, and a result for an
older version is dropped, so a slow checker never paints marks on text
that has moved (F18). A mapped change keeps shown marks on their text
until the layers answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The checker walks the Lezer tree and reports F401, F811, F821, F841,
E711, E712, F632, W191, and E101, the codes Ruff documents, so people
see familiar names. Scopes follow Python's name resolution: module code
runs in order, function bodies later, and class bodies are hidden from
their methods. The tests use Ruff's documented rule examples.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The checker reports no-undef, no-unused-vars, no-const-assign,
no-unreachable, eqeqeq, no-cond-assign, and no-fallthrough with
ESLint's default options, and reads /*global*/ comments as ESLint
does. TypeScript type names count as uses but are never undefined.
The tests use the examples in ESLint's rule documentation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
C and C++ get clang's undeclared-identifier and unused-variable over
the Lezer tree. The checker stays quiet where a header could declare a
name: calls, qualified names, class members, project headers, and
using namespace other than std. Lua has no Lezer grammar in the app,
so its checker reads tokens and gives luacheck's W113 and W211.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lezer error nodes become messages that name the unclosed bracket and
its line, a Python header without its colon, the unexpected token, or
an early end of file. The syntax and checker layers share one worker,
and the page runs the same code when no worker is there. Runtime lanes
register engine checks (SQLite, wasmoon, Python compile, clang), whose
line answers become offsets. The worker sends message ids; the page
words them from the diagnostics strings. loadDiagnostics loads it all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The repository checks limit sentences to 30 words and test blocks in
length, so the long comment lists become short sentences and each long
describe becomes several.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The benchmark harness builds a 1,000-line file of ordinary code per
checker language and times one run, parse included, as the worker does
it. The test holds each language under 50 ms (DESIGN 6.1, gate G-D2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The layers before the code tools pack are built: the scheduler, the
syntax layer, the checkers, and engine check registration. The tools
pack is the lane's Beta 13 work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the page the grammars now come from the lang-* packs, which the
editor has already loaded, so diagnostics add no new chunks and stay
inside the bundle budget. The worker keeps the bare Lezer grammars,
since it has no use for CodeMirror.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Completion must work on every build, with no model (DESIGN 10.2). This adds
the ranked list (scope names from the Lezer tree, members of known types and
modules, keywords and built-ins by bundled frequency, snippets with fields,
words from open files, and a recency boost) with a deterministic score.

It also adds the pattern predictor for one line of ghost text (idioms, a
def colon, numeric steps, and earlier lines with the same start) and the
explicit request rule: the predictor at a lower threshold, then the list's
top item when it leads the next by a fixed margin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
attachCompletion gives an editor the ranked list and the pattern ghost
source, and records accepted items for the recency boost. loadCompletion
keeps it in a lazy chunk for the studio window and code cells.

Tests run a real editor: the Python for snippet's fields move with Enter
and Shift+Enter while Tab indents, and explicit requests insert the leader.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Beta's run contract now resolves with how the run ended and takes a
context, so the test stubs reject instead of resolving with nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vitest's module runner turns each import into a property read, which
made the parser about ten times slower than in the app. The gate now
bundles the harness as the app does and times it in its own Node
process, as the diagnostics worker runs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code studio can now run JavaScript and TypeScript projects in the run
worker. Each run gets a fresh QuickJS runtime with the page's limits.

- Sucrase strips TypeScript and turns import and export into require.
  It keeps every line in place, so errors map to the line in the file.
- A loader inside QuickJS serves require from the mounted project:
  relative paths only, .ts for .js names, JSON, and a cache so each
  module runs once. A missing file or a package gives a clear error.
- console methods, print, readline, prompt, process.argv and exit, and
  a Math.random seeded from the run. The main file's last value shows
  after an arrow, as in code cells.
- QuickJS stacks and Sucrase syntax errors become RuntimeDiagnostic
  items on the first project frame.
- A Jest-compatible test API (describe, it, test, beforeEach,
  afterEach, expect with .not) runs every *.test and *.spec file when
  a run asks for framework tests, and sends one test message per test.
  Its semantics follow Jest's documentation.

Both runtimes are registered in run/adapters.ts, as the run core's
hand-over asks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
XrxcGH and others added 28 commits November 11, 2026 12:22
The members type rules read lines nearest the caret first, with string
operations and linear patterns. Lines over 500 characters are skipped, so
a line of spaces can no longer hang the main thread. Parameter and bare
annotations now name a receiver's type.

A dot with no named receiver offers nothing. The list keeps 200 items,
and the words, scope, and pattern sources read a window around the caret.
The scope walk skips scopes away from the caret, so a cold list costs
about 2 ms in a 1,000-line file. The perf test now samples cold.

Ghost text keeps nested brackets and strings before a closer. The step
rule keeps padded widths and stops at zero and at 15 digits. Digits
outside ASCII tokenize as single characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Cap the QuickJS heap at the page memory limit, and end the run when the
  heap grows past the run's own limit.
- Check the time and memory limits after promise jobs, in main runs and
  test runs. A test run stops at a limit and marks the rest as not run.
- Cap a test run at 500 tests and number equal names with a map.
- Find error places in paths with spaces and parentheses.
- Stop with a clear message at a namespace with code and at an export
  with several names, which Sucrase 3.35.1 compiles wrong.
- Print any thrown value without failing the run.
- Keep plain scripts in sloppy mode unless Sucrase changes them.
- Keep line-1 columns right after Sucrase's prefix.
- Add beforeAll and afterAll, and clear errors for .only, .each, and a
  test inside a test. Compare symbol keys and Map keys in toEqual.
- Add a help page for JavaScript and TypeScript in code projects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	app/src/features/codestudio/index.ts
- Lua: a local whose value holds a function is declared in the scope of
  its own statement, not inside the function.
- C/C++: declare default arguments, parameter packs, using declarations,
  namespace names, lambda init captures, names a file macro declares, and
  brace-initialized template variables.
- Python syntax: "expected ':'" only for a header that has no colon of
  its own, at the error's line; match/case only in a match statement.
- Python F811/F401: submodule imports compare full names; `x as x`
  re-exports and `__all__ +=` count.
- JS/TS: `print` from the sandbox is a known global; bodiless signatures
  keep their parameters quiet; `declare function` declares its name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The signature rule scans the line once for brackets, strings, and
comments. It shows the colon only when the parameters close at depth
zero. An annotation that may still be typed shows only on request.

The word counts keep their tail anchored to the caret line, so typing in
a large file reuses them. The predictor window is cut by characters too,
and the step rule never tokenizes a long line.

SQL offers the columns of the nearest CREATE TABLE after "table.", also
through a FROM alias. Idioms start at the line start. The step rule
skips hex digits and "-0". Tables are read by own key only. Snippets
say "snippet" in their note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Give the capped heap headroom above the run limit, scaled to Emscripten's growth steps,
  and set QuickJS's own limit above the cap, so many small allocations end as "memory"
  rather than "Uncaught null"; any error with the heap past the limit is the memory limit.
- Make the TypeScript namespace scan linear on blank lines, and catch every Sucrase
  several-declarator export form (multi-line, no space, no initializer).
- The test API reports unfinished tests without program-replaceable built-ins and has a
  hard closing deadline; toEqual compares array lengths as Jest does.
- Known answers for the toBeCloseTo bound, small-object memory runs, and the new guards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both Python runtimes run a project the same way: the files go into the
runtime's in-memory file system, a Python prelude sets argv, sys.path,
the working folder, prefilled stdin, and the seed, and the run's end
comes back as JSON. Tracebacks drop the prelude's frames and the mount
folder, and the mapper turns the innermost project frame into a
RuntimeDiagnostic. checkPython runs compile() for the L-c checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MicroPython 1.27.0 loads in the warm worker before lockdown with its
heap fixed at the memory limit. Its JavaScript table is cut off at load,
so the js module gives the program nothing to call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pyodide 314.0.7 (CPython 3.14.2) loads from the opennote-pack scheme
while the worker is warm. After lockdown the js, pyodide_js, pyodide,
and _pyodide modules are dropped and refused, so the program cannot
reach JavaScript or pyfetch. A missing pack gives a plain message.
Pyodide is a dev dependency only, for tests; the app never bundles it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
run/adapters.ts loads python-quick and python-full lazily. Tests: the
20-program suite byte-exact in both runtimes, CPython 3.12+ known
answers, traceback mapping across files, compile() checks, exit codes,
stdin and seeds, and the sandbox escapes after lockdown in a real
browser worker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The worker now sends the first 1,000 output messages as soon as the
program writes them. A synchronous runtime keeps the worker busy until
the run ends, so held output was lost when the time limit or Stop ended
the worker. Past that, output goes in 4,096-character pieces as before.

A runtime can now say it reads only the prefilled input
(RuntimeAdapter.prefilledOnly). The worker then reports the run as not
interactive, so the console says interactive input is not available
(DESIGN 4.4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Exit codes: a bool counts as an int, and a code past 32 bits is
  clamped, so sys.exit(False), sys.exit(True) and sys.exit(2**40) end
  the run at once. The session refuses any other exit the page would
  refuse.
- Memory limit in both runtimes: heap.ts caps WebAssembly memory growth
  at the request's memoryBytes. MicroPython starts with a 1 MiB heap and
  its growth is capped from the top of what it used at load.
- The prelude imports its own modules with the project folders off
  sys.path, so a project random.py, io.py or json.py no longer replaces
  them. The program's own import still finds its file.
- Full Python: a plain asyncio event loop before the cut-off, and the
  cut-off also drops Pyodide's js import hook and refuses
  _imp.create_builtin for the cut modules.
- Prefilled-only input in both runtimes; a byte order mark is treated
  the same in a run, a check, and the written files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Count bytes written to MEMFS against the run's memory limit; a write
  past it fails with ENOSPC (OSError) in both runtimes.
- The cut-off now destroys and drops every JavaScript proxy Python
  holds (the Pyodide API object among them), removes _pyodide_core's
  functions from every module and from the copy create_builtin uses,
  and makes Pyodide's classes refuse to make JavaScript values.
- The reach test (shared by both test files) tries the closure route,
  walks referents and attributes, and walks JavaScript values deeply.
- Links in the project are removed, never followed; the walk skips
  them and a walk error keeps the run's status.
- The check reports errors without a frame and parser give-ups; a
  parser stack overflow ends a run as "error", not "memory".
- os._exit gives its exit code; raw runtime text and the start
  failure text come from the strings file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts:
#	app/bundle-budget.json
# Conflicts:
#	app/src/features/codestudio/run/adapters.ts
# Conflicts:
#	app/src/features/codestudio/index.ts
# Conflicts:
#	app/src/features/codestudio/index.ts
# Conflicts:
#	app/bundle-budget.json
#	docs/FEATURE-LIST.md
#	docs/tools/featureCommands.ts
#	docs/tools/registry.json
ADR 0074 is Accepted in its file, so the index says so too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L05's adapters carry check(); L10 waited for runtime lanes to register one. registerRuntimeChecks registers
compile() in full Python as the pyodide engine check, loaded on first use, with no marks when the pack is missing.
L06 sets readline, prompt, process, module, exports and require; no-undef now knows them, and a test reads the
runtime's sources to keep the list true.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tests program now reaches the Python runtime through the diagnostics barrel, so it needs the same
declaration file the app program has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
featureCommands.ts went over its 800-line limit with both trains' sentences in it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ctrl+Shift+E is Word's Track Changes and Google Docs' center. The review lane bound it in every set, which
clashed with the Docs set's para.alignCenter from the text shortcuts lane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rows for comments and suggestions, Python and JavaScript runs, error detection, completion, and block merge.
A help page for Python in code projects, and a comments and suggestions section in Typed notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comments and suggestions register with the page at start-up: 620.7 KB gzipped against 620. Sucrase loads only
in the run worker for TypeScript runs, at 44 KB gzipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@XrxcGH
XrxcGH merged commit c405db4 into beta Nov 15, 2026
40 of 42 checks passed
@XrxcGH
XrxcGH deleted the train-rcode branch November 15, 2026 06:47
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.

1 participant