- This file, to the end: state of the branch, what was decided, how to resume.
00-assessment.mdsection 0 (errata and decisions), then sections 1, 2, 7 and 11: why Node, what depends on Deno, the phased plan, the runtime-layer design.REVIEW-2026-10-15.mdsections 1, 2, 3 and 6: readiness per phase, the structural findings that shaped the rework, the landing strategy, the decisions table. 3b.REVIEW-2026-10-16.md(the Codex review of the reworked plans, 21 findings) andTRIAGE-2026-10-16.md(every finding verified; the work split into maintainer decisions A1–A8, taken; a cross-plan reconciliation pass B1–B9, landed, one commit per item listed in the triage; and per-plan local corrections, bucket C, applied, and its step-4 re-read, done). Its A9–A11 are still open: see "Known loose ends" below for where each is written.README.md: the executable sequence, conventions, dependency graph and ratchet baseline. The index you consult while working, not the context._TEMPLATE.md: the normative vocabulary every plan uses.SPIKES.md, then the plan file for the PR being executed.
The meeting summary with the roadmap and architecture drawings is a Claude doc (private until shared): https://claude.ai/code/artifact/4868893e-2d5f-4b0c-8035-bfbaa5f264fd. The assessment is also published as a page: https://claude.ai/artifact/9thKkB7s15659uYgtwYUmW. Both derive from the files here; the files are canonical.
Written 2026-10-15 at the end of the planning session, updated 2026-10-16 at the end of the reconciliation pass, for whoever (human or agent) picks this up in a fresh session.
- The plans merged to
mainas #15133 on 2026-10-20. - Everything durable is in this directory (
dev-docs/runtime-migration/):README.md: entry point, the ordered table of 45 PRs, two spikes and one milestone, dependency graph, conventions the plans decided, progress metric.00-assessment.md: decision record (why Node, impact, phased plan, spike results, module-by-module refactoring plan)._TEMPLATE.md: plan structure and the normative shared vocabulary forsrc/deno_ral/(every plan uses these names).00a-*.mdto39-*.md: one plan per PR (45 PRs), plusSPIKES.md.REVIEW-2026-10-15.md,REVIEW-2026-10-16.md,TRIAGE-2026-10-16.md: the two reviews and the triage of the second one (what was decided, what was reconciled, what is still to correct locally).spike/: the Node bundler, shim, launcher and test documents from the spike that ran the unmodified CLI on Node 22.
- The published assessment page (same content as
00-assessment.mdwith the spike and refactoring sections): https://claude.ai/artifact/9thKkB7s15659uYgtwYUmW is private to its owner until shared from its Share menu; the canonical copy is00-assessment.mdhere.
These were throwaway and are recreated by the plans when needed:
.spike-node/(18 MB of GitHub clones of cliffy/deno_dom/etc., 146 MB ofnode_modules,dist/quarto.js, the Deno-baseline import map). The scripts that matter are committed underspike/; PR 26 turns them intotools/node-bundle/.spike-node-tests/outputs (rendered HTML, Deno-baseline renders).package/dist/bin/tools/x86_64/binaries (Deno 2.7.14, Pandoc 3.10, Dart Sass, Typst, typst-gather, esbuild) downloaded by hand because the planning sandbox's proxy blockeddeno.land. A normal checkout gets them from./configure.sh.
REVIEW-2026-10-15.md is the principal review of the first draft (13 structural findings, per-plan defects, the landing strategy, the decisions taken in its section 6).
Every plan was then reworked against it: six new PRs (00a, 00b, 00c, 02b, 14b, 20b, 27b), PR 21 folded into 25, PR 07 and PR 39 rewritten and renamed, SPIKES.md added, _TEMPLATE.md vocabulary updated, 00-assessment.md given an errata section.
README.md carries the new table, dependency graph, conventions and baseline.
The plans are now executable in the README order; PR 02 (#15135), 02c (#15153) and 02d (#15218) are merged; 00a is in progress as the Deno 2.9.8 security bump (#15152), and 00b runs next.
All nine items are committed and pushed, one commit per item (verified with git log on the branch).
README.md "Shared rules and tables" is the index of where each rule now lives; this table adds the commit.
| Item | Commit | Where the rule or table lives |
|---|---|---|
| B1 | 11d115f |
PR 02, Context "The std boundary rule" (kStdAllowListed; PR 22's table is the source); baseline src 49, package/src 3 |
| B2 | 22993e6 |
PR 25, Context "Runtime residue table" (17 rows); PR 39 before PR 25; PR 33 Task 16; PR 35 Task 6 |
| B3 | 0480a47 |
_TEMPLATE.md (env keys, exit, KV file rule); PR 28 acceptance; PRs 33/37 lose --unstable-kv and the stale KV-backend sentences |
| B4 | 7023b19 |
PR 26, Interfaces "The builder contract"; PR 33 Task 2 step 4 keeps it |
| B5 | 91aa3ef |
PR 37, Context "The CI graph after this PR" (decision 5: render-parity = dev bundle vs distribution bundle) |
| B6 | 63b94d8 |
SPIKES.md Spike V (after PR 30; rule 0 classification; rule 4 inconclusive) |
| B7 | be2ea17 |
PR 35, Spec "Supported migration surface" (the matrix, the "not available on Node" table, the version rule, the governing-lockfile table); PR 35 Decision 3 (module-local Deno binding for the stdlib/* bundles); PR 36 Decisions 3 and 4 (@quarto/std-* packages; root lockfile versions, the version rule as a warning); PRs 00c, 34, 37 refer to the matrix; README graph 35 → 36 |
| B8 | 99a5328 |
PR 33, "Dual-runtime combinations until PR 37" (after Task 13) and the execPath() rule; PR 35 runOnNode() and PR 36 typeCheck() on architectureToolsPath("node"); doc comment on execPath() in PR 28 and _TEMPLATE.md; acceptance greps in 33, 35, 36, 37 |
| B9 | the commit that updated this file | README (review bullet, "Shared rules and tables", rows 33 and 35), this file, the resolution list in TRIAGE-2026-10-16.md |
The README table, dependency graph and progress metric were checked once against every plan's **Depends on:** line after B8: they agree (B2 and B6 had already moved PR 39 and Spike V; B7 added 35 → 36).
Three "Decision needed" entries were added by B7 where the triage did not cover a mechanism, each with its recommendation picked and the plan written for it: PR 35 Decision 3, PR 36 Decisions 3 and 4 (above).
The C-row correction of aliasTarget() in PR 36 is subsumed by PR 36 Decision 4.
Two notes for whoever continues: the B1 commit carries the maintainer's author identity by mistake (an amend and force-push was refused; the attribution line is in the message); the triage's C rows for PRs 33, 35 and 36 overlapped files that B2, B4, B7 and B8 edited, and were run after the pass landed (all three are done).
- Work from an up-to-date
main: the plans are there. - Read
README.md, then00-assessment.mdsections 10 and 11, then_TEMPLATE.md. - The section 9 decisions are taken (
REVIEW-2026-10-15.mdsection 6); the test runner is decided by Spike V inSPIKES.mdbefore PR 31. - Apply the local corrections of
TRIAGE-2026-10-16.mdbucket C (one task per plan file, in parallel where the triage marks it), then the re-read of 02, 25, 26, 27, 29, 33, 35, 36 the triage's step 4 asks for (02, 25, 26, 27 and 29 done 2026-10-16, 33, 35 and 36 done 2026-10-17: step 4 is complete). - PR 02, 02c and 02d are merged, and 00a is in progress; open the remaining plans as PRs against
mainin the README order: 00b, then 03–06 in parallel, then 07. Each plan names its branch, executor and verification commands. For the per-worktree session ritual (window model, trust, fresh conversation, concurrency ceiling), checklocal/multi-worktree-workflow.mdif it exists — it is a maintainer-local, untracked note. - Keep the README status column current as PRs merge.
A management pre-validation of the plans (report in French under /mnt/project-files/revue-organisation/) found the content complete and the organisation incomplete: no named roles, no release dates in the index, no merge rule for shared files, two stale hand-offs (00b → 07, 22 → 27b).
Its corrections are in README ("Roles", "Release calendar", "Open decisions with an owner", ground rule 8, step 0 of "How to execute a plan"), SPIKES.md (decider column), _TEMPLATE.md and the plan headers it lists.
The "Roles" table has since been filled in for a solo maintainer (Chris) plus LLM-driven agents; the cut dates remain Chris's to confirm before Phase 0 starts.
00-assessment.mdstill cites the inventory-era "71 files with direct std imports" and "166testing/assertsimports"; PR 02's script measures 75srcfiles before the std boundary allow-list and 49 after it (B1), and 161 intests, and says so. Not reconciled on purpose (narrative vs. tool output).- Bucket C of
TRIAGE-2026-10-16.mdis applied for 00b (R20: lock scope,tools/check-test-imports.ts, Decision 2) and for 14b/28/34 (R10: the Windowstaskkill /Tcase inprocess-group.test.ts, both preview tests on three OSes, a website preview test, the "Windows coverage" table; the Windows branch ofkillProcessmoved frompreview-server.tsinto PR 08's layer so that PR 28 keeps one contract) and for 26 (R03: the "Resolver scope" rule in Interfaces, thenode_modulesimporter check andresolveDirchoice in the plugin,resolveBare'smappedflag,build.test.mjs; the R04 interop prelude was already done by B4) and for 27 (R05:openFlagBits()and the "Open flags" rule, writes through the descriptor withtruncate: !append; R06: the one-descriptor-ownerNodeFsFile, streams closing through the object, idempotentclose(), decision A8; R19: the Windows link type fromstatof the source with a dangling-link fallback; tests for each in Tasks 2 and 4, checked on Node 22 only, Deno covered by the tests) and for 29 (R07: the "Server lifecycle" rule in Interfaces, decision A7;shutdown()awaitslisteningbefore closing, a per-server set of ownedwssockets closed with 1001 and terminated in the forced path,Request.signalfrom the response'sclosewithwritableFinished === false, the body loop racingdrainwithcloseand cancelling the reader; five R07 tests in Task 3; the body and all ten cases were run on Node 22.22 withws8.22, Deno not run) and for 30 (R18, decision A6: the "Lock ownership" rule in Interfaces and an exportedlockIndex();<lock>/ownerwith pid, host and nonce written on acquire, aprocess.kill(pid, 0)probe before breaking so a live owner's lock is never broken by age, a gone owner's at once and an unknown owner's after 10 s;release()and the finalrenameonly while the marker matches; readers no longer delete a corrupt index; the Decision and Risks say best-effort exclusion; three R18 cases in Task 1; the body and the nine cases run on Node 22.22, plus akill -STOPof a live writer for 12 s with a second writer waiting, Deno not run) and for 31 (R13: a "Leak guarantees" table in Interfaces, one mechanism per event; the per-test timeout,QUARTO_TEST_TIMEOUT, is a watchdog for hangs and "per-file" is corrected everywhere;runChecked()intests/harness.tsfails a test that leaves a child, a listening server or a timer open (process.getActiveResourcesInfo()before and twosetImmediateturns after the test) or that callsexit()(process.exitguarded), withsanitizemapped onto it; the forced exit bounds the run and detects nothing; open files, client sockets and pending fs operations are intentionally undetected on Node; a sixthharness-node.test.tscase and four one-file examples in Task 5;runChecked()run on Node 22.22, Deno and Vitest not run) and for 32 (R14: a "Parity evidence" table in Interfaces;parity-compareneedsnode-bucketstoo and fails in its first step on anyneeds.*.resultother thansuccess, thencompare-junit.mjs --buckets N(N_BUCKETS, a new output ofnode-buckets) refuses a runtime with other thanNbucket artifacts, a bucket without a report, a report without a case, a case without a file or a missing path withparity: NO EVIDENCE;compare()counts any failure on either side againstok(failedDenonever allowlisted,failedNodeonly for Julia); a test is<file> :: <name>(caseIdandcasesin PR 26'sjunit.mjs, the file from the case'sfileattribute, written by node:test's reporter on Node 26, a file-lookingclassname, Deno's, or the file suite, Vitest's; Node 22's reporter writes no file, sojunit-finish.mjs, which replacesjunit-prereq.mjs, keeps a--filewrap the runner scripts do not use); the artifact download patterns ared[0-9][0-9]/n[0-9][0-9]so thatdenosanityandnbindo not count as buckets; fivecompare-junit.test.mjscases and two more injunit.test.mjs; the three scripts and their cases run on Node 26.12.0 and 22.22, the reporter'sfileattribute measured on both, the workflow not run; PR 37's file list renamed accordingly) and for 33 (R18:ensure.mjs'swithLock()copies PR 30's "Lock ownership" rule, restated in PR 33 Interfaces, since it cannot importkv.ts:<lock>/ownerwith pid, host and nonce, theprocess.kill(pid, 0)probe, a gone owner's lock broken at once, an unknown owner's after 120 s, a live owner's never, release only while the marker matches, the 300 s timeout naming the owner;bundle()gains an optionalbeforeCommitinput, called before its first write and between the bundle's temporary file and its rename, whichensure.mjspasses as the lock'sassertHeld(), added to PR 26's builder contract table; three R18 cases in Task 2 Step 2 and a by-hand check in Verification; R04's part,banner/definekept and the three-global prelude, was already done by B4 and is now named in the review checklist;ensure.mjsand the cases ran on Node 26.12.0 and 22.22, Deno not run) and for 35 (R05:deno-members.cjscopies PR 27'sopenFlagBits()and write mapping, "Open flags (PR 27's rule, copied)" in Interfaces, no string flag left, so{ create: false }truncates and never creates and{ append, create: false }never creates; R15:Deno.Commandcopies PR 28's rules, "Child processes (PR 28's rules, copied)": an enumerated option subset with aTypeErrorfor anything else, per-method stdio defaults,spawn()handing out Web Streams with aTypeErrorfor a non-piped getter,128 + nfor a signal exit,.bat/.cmdthroughcmd.exewith a copy ofbatchCommandLine(), the differences from Deno stated; R09: the changelog names the member table and theCommandsubset instead of "the commonDenoAPI calls" (B7 had already dropped "keep working"; R08'sarchitectureToolsPath("node")was B8's); six R05/R15 cases in Task 3, the.cmdone Windows-only, a vectors case for the copy, review-checklist items that diff both copies against the layer, two Risks entries; the table and the cases other than the Windows one ran on Node 26.12.0 and 22.22, Deno not involved) and for 36 (R16: a "Config precedence" decision, one rule for the options and the map:package.jsongoverns only through aquarto.buildsection, even an empty one, and adeno.jsonnext to it is ignored whole and named by one warning; apackage.jsonwithoutquarto.builddecides nothing and hides nodeno.json;resolveConfig()rewritten on that rule with anignoredfield the command warns from, a non-objectquarto.buildan error; the Goal, "Which map", "Config", the Interfaces comment, an acceptance bullet for the two mixed cases, a review item and a Risks entry say the same thing; the unit test's precedence case covers both mixed cases, the empty section and the error; the body and the case ran undernode --teston Node 26.12.0 and 22.22; R17'saliasTarget()part was B7's PR 36 Decision 4). The step-4 re-read of 36 (2026-10-17) ran itsfs-logengine as written against the packages PR 35's builder makes from the root pins, on Node 26.12.0 and 22.22, and foundpathserved raw (@std/path@1.0.8'sresolve()readsDeno.cwd(): now@quarto/std-path), the member table's messages puttingDeno.Commandinto every package (nowkDeno), theFileHandlercheck lost underminify: true(now a"quarto-unsupported:<member>"literal), and two defects in PR 35's builder (the gate's loader onunsupported.ts, the gate scanning the inlined table); the R17 forms joined PR 36's version-rule test and the residue inspection its Verification. Every per-plan C row is applied, and the cross-cutting fact-check (triage row, 2026-10-16: pins, Node flags, package versions and OS minimums re-checked against the primary sources; four wrong facts fixed in 00a, 26, 31 and 33, Intel macOS note added to 34). The--reversebackport demo after PR 10 remains (next bullet). The line numbers in the review and triage were measured at075cbf4; re-grep before trusting one. - Still open on 2026-10-17, each written where the session that needs it will read it, so none needs this file to be remembered: triage A9 (who opens the upstream issue with PumasAI, whether the process/network interfaces freeze first) in PR 08 "Decisions needed" and README row J; A10 (owners for the packaging readiness gate, the HTTP publishing contract, distribution compliance, the offline promise) as the README "Decisions and conventions" bullet to fill in; A11 (performance baseline and thresholds) in PR 34 "Decisions needed" 4; the
--reversebackport demo (triage cross-cutting row 2) in PR 10 "Follow-up after merge", repeated by PR 37 Task 6 Step 2 after the flip; PR 25's two-command residue inspection in PR 37's Acceptance criteria and review checklist (PR 37 had repeated only theexecPath()grep). - Spike V may end inconclusive (
SPIKES.mdrule 4): PR 31 then keeps both harness variants and PR 32 cannot make the Node matrix required until the spike is re-run. README "The sequence" now carries the rows27x–30x(fix PRs for the runtime gaps rule 0 records) andSV-rerunbetween SV and 31. - PR 07: decided (
REVIEW-2026-10-15.mddecision 8): the unused puppeteer launch helpers and the deprecatedchromiumtool are deleted, not ported; the plan is written that way. Nothing to decide before executing it. - PR 13 counts 72
Deno.*sites in the Julia engine subtree; PR 02's script rule counts 74. Both are excluded from the ratchet from the start. - Windows behaviour, interactive Cliffy prompts, Posit Connect publishing without a cookie jar, and the deno_dom WASM performance gate (PR 20) were reasoned about, not run.
- The plans were written against
mainatb86f98d(2026-10-14). Line references drift asmainmoves; re-grep before trusting a line number.
This repository has no issue tracker for this work: the files in this directory are the whole record.
The order, dependencies and status of every PR, spike and milestone are in README.md (the sequence tables and the dependency graph), and each plan file holds its own checkbox steps.
Anyone who wants issue-level tracking sets it up in their own tooling, from the README "The sequence" tables: one item per row (45 PRs, two spikes, one milestone), a dependency for each "depends on" entry, and a link to the row's plan file. The maintainer does this in a private tracker that is not committed. Keep the plans as the source for design; a tracker only needs status and ordering.