# Browser Coder: product stories and implementation review

Source pinned to [7d3cae91](https://github.com/ninasokolov8/browser-coder/tree/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb). Captured 12 September 2026.

The new tour follows four real workflows: compiler explanation → visible repair → teacher-defined checks; loop state and pause history; Python Turtle drawing and replay; multi-file web preview and an independent shared copy. All run against the original application and local services with fictional programs. A fresh share recipient was edited, and the original server snapshot was read back unchanged.

The original preview route was forwarded from the local Vite capture server to the existing API, matching the production route arrangement. This is capture infrastructure, not a replacement preview implementation. The original explorer rename input failed through both menu and keyboard paths; its focus/blur conflict was traced in source and no successful rename is claimed.

Screens were captured at 1600×1000 with DPR2 (3200×2000). Films use lossless native PNG frames, original-pixel detail crops, concise chapter slates, slowed actions for readability and H.264 delivery at 1920×1280. Web-preview and shared-copy stills form deliberate holds within the edited web story. No app UI or result was generated. Full original screenshots remain available in the portfolio viewer.

The old run/debug and Turtle recordings are retained in the original manifest as historical evidence; the visible new stories use the sharper new edits. Presence of source/tests is not a current all-language test-pass claim. Repository-wide inventory, full file-hash verification and subsystem reading are documented below; curated catalogue data and unrelated tests are not represented as manually reviewed line by line.

## Frontend and learning evidence

The audit covers 402 tracked files (4,994,259 bytes): 286 source/configuration files, 104 tests, 11 documents and one generated dependency lock. All paths were inventoried and all file hashes verified against the pinned source tree. Semantic review concentrated on the runtime, editor commands, diagnostics, learner feedback, debugging, sharing, preview and persistence paths described below. This does not mean every catalogue entry, styling line or unrelated test assertion received manual review.

### Captured workflows

1. **Understand and repair.** A missing Python colon produces an explanation and an explicit source edit. A teacher-defined harness then reports two passing checks and one failure. Changing integer division to ordinary division makes all three checks pass; the film preserves the learner's actual edit and returned result.
2. **Inspect program state.** A running Python loop stops at breakpoints. Recorded pauses show changing values and can be inspected without moving the suspended process backward.
3. **Connect code to geometry.** An executed Python Turtle program produces a drawing. Replay and scrubbing inspect recorded drawing commands and their source locations.
4. **Preview and share a project.** A fictional multi-file HTML/CSS website renders through the original preview service. Its published share opens in a fresh recipient context as an editable local copy. Editing that copy leaves the original server snapshot unchanged.

These are the four published stories. The web design is sample student input, not a built-in product template. Explorer rename failed through both menu and keyboard paths and is not presented as a successful workflow.

### Implementation distinctions

- StopHistory preserves at most 500 recorded pauses. Historical navigation changes the display; the suspended engine remains at its current stop. Ending resets history. `src/features/debug/history.ts`.
- Variable comparison is keyed by file/function, uses displayed value text, and suppresses comparisons on a frame change. Same function recursion is deliberately treated as the same frame. `variable-diff.ts`.
- Output mapping uses print-site text anchors and a cursor heuristic; it is not complete runtime tracing. The capture uses a supported loop; this heuristic is not evidence of universal source mapping. `src/features/output-trace.ts`.
- First-aid repairs are deliberately narrow: missing Python header colon, prnt typo, one unclosed same-line parenthesis/bracket. Curly braces, multiline/ambiguous repairs excluded; edits use Monaco operations and revalidate before applying. `error-first-aid.ts`.
- Check-my-work discovers one hidden test-named harness for the active language, refuses ambiguity, reports cases with pass/fail/skip and distinguishes a crashed harness. BCTEST is a cross-language stdout protocol avoiding external package installation. Rendering is bounded at 500 cases while totals track all; status bar has a potential capped-count inconsistency, so large-harness count agreement is not established. All three `features/tests` files were included in semantic review.
- X_HIDDEN_ is a UI/export visibility convention, not a confidentiality boundary: files remain in client storage and execution payload. `workspace-visibility.ts`.
- Shared snapshots are immutable on the server, but the recipient opens an editable local copy. This is not collaborative editing or a locked editor. `share.ts`, `share-link.ts`, `workspace-init.ts`. The README's 'read-only project' wording therefore describes the published snapshot rather than the recipient's editor.
- Live preview supports HTML, CSS, Markdown; publishes immutable files with 22-character random IDs. CSS association refuses zero/multiple HTML entry ambiguity. HTML/CSS URLs retain the /coder mount, while share API uses root URLs (standalone route behavior was checked during capture). `live-preview.ts`.
- Sandbox CSP travels on active preview asset responses, including direct navigation. Active SVG/XML scripts disabled, HTML scripts enabled with opaque origin; connect-src permits external network in browser preview, so preview pages are not network-isolated. `server/previews/headers.mjs`.
- Commands centrally check capability on every invocation from toolbar, keys, palette, host/API. Refusal outcome structured. `commands/registry.ts`.
- Diagnostic producers remain separate; old revision results rejected/invalidation subscriptions wired; compiler suppresses advisory scanner on same line. `diagnostics/store.ts` and `staleness.ts`.
- Import refactoring supports JS/TS,Python,PHP,Java,C#,HTML,CSS, warns on unhandled/ambiguous cases rather than silently declaring all clean. `import-refactor.ts` was partially reviewed; its middle section was not included in semantic coverage.

## Execution, adapters, host protocol and persistence

Audit date: 2026-09-12. Pinned repository: `ninasokolov8/browser-coder`, revision `7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb`.

The server and host findings below are source evidence, not a penetration test or a claim that all languages were demonstrated locally. This part of the review involved source and GitHub metadata inspection; the separately documented capture scenarios establish runtime behavior. The source archive has no independent Git checkout: `source-verification.json` compares the actual Git-blob hash of **all 402 local files** against the pinned GitHub tree; all 402 match. This avoids accidentally attributing the enclosing portfolio's Git history to the archive.

## Coverage and interpretation

- Inventoried and hash-verified all 402 paths. Deep-read runtime/control logic in all 47 `server/**/*.mjs` modules plus the two declaration files, `server.mjs`, all 18 non-starter runtime files under `languages/` (14 MJS, four Python), both Step-Up integration modules, persistence coordinator, IndexedDB store, and the targeted files/tests listed in `server-coverage.json`.
- `languages/` has 62 files: 18 runtime/bridge files, 14 instructional starter programs, six language configurations, and 24 keyword/error catalogues. Configurations were parsed for capabilities and version profiles; catalogue payloads and starters were structurally classified, not every entry semantically verified. These are curated data, not automatically classified as generated code. The dependency lock is generated; there is no vendored language engine in this subtree.
- Read all assertions in `workspace-persistence.test.ts`, `workspace-store-indexeddb.test.ts`, the embedded browser test, C# offline-restore tests, capacity derivation tests, adapter-check tests, and shared debugger channel tests. Additional contract files were indexed for scenarios; their presence is **not a test-pass result**. The final capture manifest records which UI behavior was actually demonstrated.
- Source comments are useful decision context but are not treated as stronger evidence than the actual implementation. Several comments overstate behavior; distinctions follow.

## Architectural decisions

1. **One editor, multiple real toolchains.** A single validation/admission/job lifecycle feeds six language adapters and two transport forms. Each adapter returns either compile diagnostics or a launch description; execution, cancellation, output limits, and cleanup stay in shared code. The processing boundary is project snapshot → validation/admission → disposable workspace → language adapter → streamed output/debug events → cleanup. [Pipeline](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/server/execution/pipeline.mjs#L127), [registry](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/server/languages/registry.mjs#L22).
2. **Debugging without changing the student's line numbers.** Python's guard/turtle shim is bootstrapped separately, and TypeScript maps generated JavaScript stops back to source lines. Output and debugger logpoints share the debug transport to preserve ordering. The captured state-inspection scene uses actual debugger values. [Python bootstrap](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/server/languages/adapters/python.mjs#L288), [source-map bridge](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/languages/javascript/source-map.mjs#L122), [ordered Python output](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/languages/python/debug_adapter.py#L874).
3. **Protect the working copy while saves finish.** Autosave owns one debounce timer and one write drain per document. A completed write marks only the revision it saved; typing during that write triggers a follow-up. Metadata writes cannot carry content, so a rename does not overwrite the editor buffer. Race-focused tests exercise this revision/content distinction. [Coordinator](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/src/workspace/persistence.ts#L229), [race tests](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/tests/unit/workspace-persistence.test.ts#L23), [metadata/content separation](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/src/workspace/types.ts#L63).
4. **Embed a classroom editor without resetting its identity.** Host messages wait for initialization, then serialize. Replacement reconciles documents by path, reuses their IDs, removes absent files, and writes files/folders/state together. The host can independently restrict editing, structure, running, and panels. [Host queue](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/src/integrations/stepup.ts#L178), [reconciliation](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/src/workspace/service.ts#L531), [embedded assertions](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/tests/browser/embedded.ts#L278).
5. **A real offline C# build choice.** The service supplies the project, rejects user MSBuild control files, clears NuGet package/audit feeds, reuses a restored template, and uses `--no-restore` for builds/runs. This directly addresses predictable classroom execution without package downloads at run time. [Build setup and offline configuration](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/server/languages/adapters/csharp.mjs#L49), [dedicated check path](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/server/languages/adapters/csharp.mjs#L439).

## Execution architecture and actual boundaries

### Request → project → process

`pipeline.mjs:127-221` resolves language/version, validates the complete file set, chooses the entry point, applies adapter-specific validation, and checks source policy. `paths.mjs:86-215,249-326` rejects absolute/drive/traversal/control-character/reserved paths, normalizes separators, and rejects duplicate, case/Unicode, and file-directory collisions. File, path, and aggregate source limits apply to the whole submitted project. A second resolved-path containment check exists when writing the job (`job.mjs`).

Admission is a synchronous increment/refusal, shared by checks and runs (`pipeline.mjs:98-111`). Excess execution gets 503; checks have an additional 429 limit (`routes/check.mjs:37-42`). There is **no execution queue, worker pool, or demonstrated concurrency benchmark** in this pipeline. The capacity estimate derives from explicit memory budget, then cgroup v2/v1, then reported host memory, with a server reserve and configurable per-run assumption (`config.mjs:35-140`). Default per-run 50 MB is an estimate, not a memory hard limit for each process. TypeScript compilation occurs synchronously in the API process (`typescript.mjs:193-206`), so a child timeout does not bound that compiler work.

Each job gets a random private directory; source files use 0600 and the directory 0700. **All jobs still run as the same OS user.** Those modes do not isolate one job from another running under that UID. `languages/python/fs_guard.py:11-41` explicitly records that limitation. `sandbox-env.mjs` constructs an allowlisted environment and points HOME/TMP locations into the job; it does not spread server secrets into student runs.

The managed process uses argument arrays, `shell:false`, bounded UTF-8 sinks, process-group termination on POSIX and tree termination on Windows. It records a first explicit termination reason instead of translating every signal into a timeout. Output caps are separate stdout/stderr character caps, not a combined byte limit. Cleanup is shared and idempotent; the periodic reaper excludes live job directories. [Process runner](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/server/execution/process-runner.mjs), [termination vocabulary](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/server/domain/termination.mjs#L80).

### Interactive execution is deliberately different from a short batch run

The original IDE uses interactive NDJSON with stdout/stderr, status, debug events, graphics, and final exit. The interactive route passes `timeoutMs:0` (`run.mjs:328-336`), and pipeline precedence preserves zero (`pipeline.mjs:434-443`). This disables the normal runtime wall limit; it does not disable bounded compile/preflight work or output limits. The code deliberately has no interactive idle/absolute-lifetime timer. The connection, explicit Stop, and output cap govern a live run. A quiet infinite loop can therefore retain a slot while its stream remains open. A universal ten-second execution limit is therefore not supported.

Session IDs include a replica routing token plus 128 random bits (`session-registry.mjs:129-133`). Control requests use possession of this ID; there is no per-account ownership check in these routes. Cross-replica commands forward once to a same-port private IPv4 destination; the prefix is routing metadata, not a signed credential (`session-registry.mjs:157-185`, `run.mjs:495-550`). The registry tracks total/per-IP session counts and live job directories. Source-derived suitability: stateful interactive runs behind replica routing, not stateless resumable sessions.

### Deployment containment is configured around the API replica

`Dockerfile.production` installs real Node, Python, Java, PHP and .NET toolchains, plus Xdebug and pinned dncdbg. The entrypoint starts as root for writable mount permissions, then `su-exec`s UID1001. The checked-in production compose sets read-only root filesystem, no-new-privileges, PID512 limit, restricted capabilities, writable tmpfs, and an API-only internal network. Those are **replica-level** controls, not a container per student run. They are configuration evidence; the Windows native capture does not demonstrate those Linux Docker safeguards. [Dockerfile](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/Dockerfile.production), [production service](https://github.com/ninasokolov8/browser-coder/blob/7d3cae91ff9cc73001d7fcfa0aa3b315cebb2aeb/docker-compose.prod.yml#L121).

Production compose shares all three durable stores - previews, binary cache, share snapshots - between replicas. This avoids a link or asset disappearing when the next request reaches another replica (`docker-compose.prod.yml:54-67,92-117`). Health has separate liveness/readiness: saturation degrades health, returns 503 readiness, and does not imply process death (`routes/health.mjs:36-77`). Shutdown stops sessions and background stores, closes the server, and has a ten-second force-exit backstop (`lifecycle.mjs:46-87`). No load or autoscaling effectiveness was measured here.

## Language adapters: real implementation, unequal capabilities

| Language | Execution/check implementation | Debug implementation | Important limit |
| --- | --- | --- | --- |
| Python | Isolated flags `-I -S -B`, conservative AST preflight, separate filesystem guard; deterministic project import directories | `bdb`, reader thread for live Stop/breakpoint commands, paused-frame variables/evaluation, post-mortem exceptions, turtle snapshots | Guard is interpreter-level defense with source policy, not hostile-code OS isolation. AST preflight is entry-file only and fails open on checker failure; regex policy still checks supplied files. |
| JavaScript | `node --check`, generated module package, Node permission mode allowing project reads, heap128 MB | Worker-thread V8 Inspector controls main-thread execution; filesystem wrapper replaces permission mode for debug bootstrap | Debug and ordinary paths have different enforcement mechanisms. Chosen ES profile is not an old Node runtime or a server-enforced ES5 parser. |
| TypeScript | Installed TS compiler; selected target/strictness; diagnostics block launch; multi-file emits CommonJS, single-file ES modules | Same Inspector bridge plus generated/source line maps | Compiler lives in the server process. Missing compiler falls back to Node launch of source; independent TS runtime availability is not established. |
| Java | `javac --release`, all `.java` sources, generated `.classes`, selected main class; JVM heap128 MB | Custom binary JDWP client, line tables, locals, class preparation, stepping | Watches support variable/field paths; conditions support a limited comparison grammar, not arbitrary Java expressions. Basename-based source identification can conflate packages. |
| PHP | Entry `php -l`; runtime `open_basedir`, memory64 MB, disabled functions, URL includes/opens off | Xdebug/DBGp bridge; serialized engine commands; per-run loader probe | Filesystem/network/function restrictions differ from Python's workspace file-I/O allowance. Other source files may still fail only when included. |
| C# | Service-owned net8 project, source LangVersion, offline restore/template, dedicated compiler `check()`; ordinary `dotnet run --no-restore` | dncdbg over DAP, symbols, scoped variables/evaluation, deferred breakpoint verification | Real debugger binary must be installed. Source policy is a blocklist; no per-job CLR/OS sandbox exists. |

Citations: `server/languages/adapters/{python,javascript,typescript,java,php,csharp}.mjs`; `languages/{python/debug_adapter.py,javascript/debug_worker.mjs,java/debug_adapter.mjs,java/condition.mjs,php/debug_adapter.mjs,csharp/debug_adapter.mjs}`. All at the pinned revision above.

The debugger control channel is loopback-only, requires an initial random token, limits frames/buffer, and validates command shape, paths, count/line bounds, and expression length (`server/debug/channel.mjs:57-201,248+`). Validated strings do not imply side-effect-free watches: Python calls `eval` in the stopped frame (`debug_adapter.py:700-714`), JS uses `Debugger.evaluateOnCallFrame` (`debug_worker.mjs:611-640`), PHP delegates to DBGp eval, and C# to DAP. No source-policy pass is applied to those expressions in the channel. Java intentionally uses a narrower field/comparison evaluator. No bypass was attempted.

## Turtle is a graphics bridge, not desktop tkinter

The custom shim records lines, fills, text, headings, cursor changes, polygons and SVG cursor data. Shapes carry the originating student line where available. A final JSON file is separate from stdout; debug pauses can carry snapshots. It emits nothing when a project merely contains another turtle file but the running program has not drawn (`turtle_shim.py:1301-1408`). The server requires an actual regular file, enforces 8 MB before parsing, and sanitizes shape kinds, counts, coordinates, text, cursor/SVG budgets (`graphics/turtle.mjs:50-73,304-445`).

Full CPython turtle event parity is not implemented: `onkey`, `onclick`, `ontimer`, mainloop and related callbacks are no-ops; input dialogs return placeholders (`turtle_shim.py:837-855`). Some advertised settings are not transmitted: `setworldcoordinates` only stores `_cfg['world']` (`1567-1574`), while `_snapshot` omits it (`1345-1359`) and server sanitizer has no world field. Source-derived conclusion: supported drawing/replay and debugging are the strong story; interactive turtle games/custom coordinate transforms require separate verification.

## Persistence and embedding

- Standalone uses `BrowserCoderDB`; each embedded instance receives a unique IndexedDB name, starts empty, and requests database deletion on `beforeunload` (`main.ts:85-96`, `tabs.ts:139-144`). Cleanup is best effort. There is no Browser Coder account/task database or cross-device sync shown here; the Arc host owns longer-lived student context.
- `PersistenceCoordinator` defaults to 1 s debounce per document, coalesces writes, retains dirty state on failures, and avoids retry spinning. Visibility-hidden/pagehide triggers a best-effort flush; save failures and recovery get a status and accessibility announcement (`save-status.ts:78-134`). This improves resilience; it is not a guarantee against browser termination or storage eviction.
- IndexedDB files/folders/state replace in one transaction; folder rename updates descendant compatibility paths; late writes for a deleted document return without resurrecting it (`store-indexeddb.ts:202-217,278-393`). Tests use fake-indexeddb, not proof of every real browser's storage lifecycle.
- Incoming `stepup:init`, set/get-code, set/get-files, run, set-readonly, show/clear-output are supported. Init can auto-run through the same policy-aware command registry. `ide:ready`, code-change, files, and run-result are emitted. Code-change is debounced 300 ms, full workspace 500 ms (`stepup.ts:195-365`).
- Incoming messages check allowed origin, but **do not check `event.source === window.parent`** (`stepup.ts:180-192`). Allowed host subdomains are shared with CORS, although browser `isStepUpOrigin` does not enforce the production CORS rule requiring HTTPS for dynamically accepted subdomains. Outgoing target origin uses the learned allowed origin or `'*'` (`stepup-bus.ts:15-34`). This is an integration trust convention, not a cryptographic/identity authorization boundary. File-change messages are gated by embedded/editable state, but read-only is an editor policy, not secret source protection.

## Publishing, assets, rate limits

Immutable shares use random 128-bit IDs and exclusive create; they store a JSON snapshot and expire through a sweeper (`shares/store.mjs:109-220`). Read routes are possession-of-link, unauthenticated. Recipients work on editable local copies, as verified in the captured recipient-copy scenario. Share size accumulation is content string length despite the `maxBytes` name; the implementation does not establish a precise Unicode byte guarantee.

Binary cache uses SHA-256 content addressing, verifies upload bytes against the claimed digest, checks max upload size, and does atomic temporary-file rename. Cache miss during execution is a 409, not silently missing files (`blobs/{digest,resolve,store}.mjs`, `routes/blobs.mjs:54-107`). TTL/size eviction is periodic, not a hard allocation reservation; storage must be shared across replicas. A digest is content identity, not user authorization.

Previews stage all files into a temporary directory then rename to publish; failure cleans staging and sweepers handle abandoned staging/TTL/cap (`previews/store.mjs:123-181,219-367`). Active HTML responses get CSP sandbox without same-origin; SVG/XML scripts are denied even for direct navigation. HTML preview scripts can still access permitted external network destinations: `connect-src` allows HTTP(S)/WS(S) (`previews/headers.mjs:139-169`). Browser preview egress differs from configured server-container egress.

Global API rate limits are an in-process fixed window, 100/min default, and directly connected private peers without forwarded headers are bypassed (`rate-limit.mjs:103-118`). Correct proxy configuration matters. CORS controls browser readability: a disallowed actual request continues without access-control headers; only a disallowed preflight is refused (`cors.mjs:51-66`). No-origin clients are allowed. The special large-body admission gate only applies to known Content-Length≥256 KiB; general pipeline admission remains the final control (`capacity-gate.mjs:46-65`). CORS and rate limiting do not provide account authentication.

## Unresolved implementation issues

1. **Rename focus conflict: reproduced UI failure and source explanation.** `tree.ts:317-318` attaches handlers, then applies roving tabindex. Handler setup focuses/selects the rename input (`559-562`), which sets `treeHadFocus` (`637-640`). Roving focus immediately focuses its parent row (`348-349`); the input's blur commits the unchanged name (`614`) and clears rename state (`610`). The issue remains unfixed in the audited source and was excluded from the successful workflow film.
2. **Python nested debugger scope.** `_workspace = dirname(entry)` (`debug_adapter.py:289`), rather than `BROWSER_CODER_WORKSPACE`, despite broader import directories later. Nested entry points and sibling modules can fall outside debug-file recognition, and pause filenames use basename (`554`, `513`). The demonstrated contract uses files at the workspace root; complete nested/package parity was not established.
3. **Full turtle API language is too strong.** Event callbacks are stubs and some state never reaches the renderer, described above.
4. **JS version note overstates checking.** `catalog.mjs:313` says compiled/checked at the selected ES target; JS adapter uses installed `node --check` without a target-specific compiler. TS/Java/C# actually apply their compiler profiles. The supported description is selectable learning profiles with installed toolchains.
5. **Admission edge on storage failure.** `pipeline.start` admits at323 and constructs `new Job` at325 before its cleanup `try` at337. If directory creation throws, that slot has no release path in this function. Source-derived rare infrastructure edge; no failure was induced.

These limitations bound the claims made by the published stories. They do not invalidate the separately observed error explanation, visible repair, teacher-harness feedback, supported debugging, Turtle replay and multi-file preview scenarios.

## Contribution evidence and safe attribution

The pinned HEAD is Nina Sokolov's README-only live-link update (2026-09-09). GitHub identifies substantive code commits by `tomernay`, including `a91b62221e571c02413bbce5391a89ea183b56a3` (C# restore/debugger stabilization, actual file list includes C# adapter, execution UI, debug state/toolbar, tests), `330d9b422536e48046319915d0ac36345e7ca95e` (debugger teardown), `0fcd4ac9b3990e9e36173aeee424c7d6f960b6a9` (show turtle window only when drawing), and `da696fdb558e963afdf4f86efb8f2bdd8c837394` (Step-Up diagnostic rendering). `tnaydnov` also appears as merge author for those PRs. This supports a collaborating engineer role and concrete maintenance/integration work; it does not justify sole ownership of the repository or every subsystem. Commit messages include co-author credits. Repository-wide architecture is presented as team work; the cited commits establish specific contributions rather than individual authorship of every subsystem.

[C# and debugger stabilization commit](https://github.com/ninasokolov8/browser-coder/commit/a91b62221e571c02413bbce5391a89ea183b56a3), [debugger lifecycle commit](https://github.com/ninasokolov8/browser-coder/commit/330d9b422536e48046319915d0ac36345e7ca95e), [turtle behavior commit](https://github.com/ninasokolov8/browser-coder/commit/0fcd4ac9b3990e9e36173aeee424c7d6f960b6a9), [host diagnostics commit](https://github.com/ninasokolov8/browser-coder/commit/da696fdb558e963afdf4f86efb8f2bdd8c837394).
