From e76a705eb711a01b54af9c7723db4a8e639e961a Mon Sep 17 00:00:00 2001 From: Ryota Murakami Date: Mon, 26 Oct 2026 05:26:52 +0900 Subject: [PATCH 1/9] feat: Cycle 0053: a test inside a workspace package runs with that package's Vitest and config The node-test run now starts Vitest in the cited test's Vitest root (the nearest folder above it with a Vitest or Vite config), with the Vitest Node resolves from there, unless the root config already selects the test exactly once (root claim). The plan is made before the history, re-verified before the launch, and named in the run manifest. codebook_status searches the root and two levels below it (vitestSearch); a start no longer waits on that search. Refs #128 --- extension/README.md | 35 +- extension/src/constants.ts | 27 +- extension/src/environment.ts | 89 ++- extension/src/nativeNodeTestBootstrap.ts | 19 + extension/src/ownedNodeTestRun.ts | 6 +- extension/src/resources.ts | 8 +- extension/src/runtime.ts | 24 +- extension/src/service.ts | 50 +- .../src/tests/decideNodeTestRootClaim.test.ts | 52 ++ extension/src/tests/environment.test.ts | 44 +- .../tests/environmentNodeTestCheck.test.ts | 89 ++- extension/src/tests/launchNodeTest.test.ts | 31 +- .../src/tests/listVitestForRootClaim.test.ts | 132 ++++ .../src/tests/listVitestTestFiles.test.ts | 22 +- extension/src/tests/nodeTestHelp.test.ts | 22 +- .../src/tests/nodeTestRunLifecycle.test.ts | 20 +- .../tests/nodeTestVitestRootLaunch.test.ts | 393 ++++++++++++ .../tests/nodeTestVitestRootService.test.ts | 351 +++++++++++ extension/src/tests/ownedNodeTestRun.test.ts | 4 +- .../src/tests/planNodeTestExecution.test.ts | 566 ++++++++++++++++++ .../src/tests/prepareNodeTestLaunch.test.ts | 120 +++- .../src/tests/readVitestPrerequisite.test.ts | 92 ++- .../src/tests/searchRepositoryVitest.test.ts | 196 ++++++ .../src/tests/utils/rootModeExecutionPlan.ts | 31 + .../src/utils/decideNodeTestRootClaim.ts | 57 ++ extension/src/utils/isMissingEntryError.ts | 15 + extension/src/utils/launchNodeTest.ts | 14 +- extension/src/utils/listVitestForRootClaim.ts | 75 +++ extension/src/utils/listVitestTestFiles.ts | 20 +- .../src/utils/partitionListedTestFiles.ts | 31 + extension/src/utils/planNodeTestExecution.ts | 197 ++++++ extension/src/utils/prepareNodeTestLaunch.ts | 132 ++-- .../src/utils/readVitestConfigEntries.ts | 79 +++ extension/src/utils/readVitestPrerequisite.ts | 120 ++-- extension/src/utils/repositoryPosixPath.ts | 15 + extension/src/utils/requireOwnedVitest.ts | 120 +++- .../src/utils/resolveNodeTestVitestRoot.ts | 58 ++ extension/src/utils/searchRepositoryVitest.ts | 138 +++++ .../src/utils/unreadableFolderRefusal.ts | 29 + extension/src/utils/vitestSearchField.ts | 20 + 40 files changed, 3262 insertions(+), 281 deletions(-) create mode 100644 extension/src/tests/decideNodeTestRootClaim.test.ts create mode 100644 extension/src/tests/listVitestForRootClaim.test.ts create mode 100644 extension/src/tests/nodeTestVitestRootLaunch.test.ts create mode 100644 extension/src/tests/nodeTestVitestRootService.test.ts create mode 100644 extension/src/tests/planNodeTestExecution.test.ts create mode 100644 extension/src/tests/searchRepositoryVitest.test.ts create mode 100644 extension/src/tests/utils/rootModeExecutionPlan.ts create mode 100644 extension/src/utils/decideNodeTestRootClaim.ts create mode 100644 extension/src/utils/isMissingEntryError.ts create mode 100644 extension/src/utils/listVitestForRootClaim.ts create mode 100644 extension/src/utils/partitionListedTestFiles.ts create mode 100644 extension/src/utils/planNodeTestExecution.ts create mode 100644 extension/src/utils/readVitestConfigEntries.ts create mode 100644 extension/src/utils/repositoryPosixPath.ts create mode 100644 extension/src/utils/resolveNodeTestVitestRoot.ts create mode 100644 extension/src/utils/searchRepositoryVitest.ts create mode 100644 extension/src/utils/unreadableFolderRefusal.ts create mode 100644 extension/src/utils/vitestSearchField.ts diff --git a/extension/README.md b/extension/README.md index eb3e6282a..90370a0a1 100644 --- a/extension/README.md +++ b/extension/README.md @@ -10,7 +10,7 @@ Before final acceptance, open `/Users/ryotamurakami/repository/nextjs-dashboard` ## Set up editor AI chat -The same VSIX includes `skills/codebook/SKILL.md` with its version in the skill frontmatter. VS Code registers it through `contributes.chatSkills`. Cursor's setup command installs those exact bytes to `~/.cursor/skills/codebook/SKILL.md` only when the target is absent or an existing directory is empty, or updates a skill whose file identity and bytes still match Codebook's private ownership receipt. The VSIX manifest records the packaged file's SHA-256. A pre-existing nonempty unowned directory, later manual edit, symlink or mismatched receipt is preserved and reported instead of overwritten. +The same VSIX includes `skills/codebook/SKILL.md` with its version in the skill frontmatter. VS Code registers it through `contributes.chatSkills`. Cursor's setup command installs that skill to `~/.cursor/skills/codebook/SKILL.md`, with one difference from the packaged bytes: the placeholder `{{CODEBOOK_PUBLISHER_PATH}}` is replaced by the path of this installation's Wiki publisher (`skills/codebook/publish-wiki.mjs`), since a skill copied out of the extension cannot find it beside itself. It installs only when the target is absent or an existing directory is empty, or updates a skill whose file identity and bytes still match Codebook's private ownership receipt. The VSIX manifest records the packaged file's SHA-256. A pre-existing nonempty unowned directory, later manual edit, symlink or mismatched receipt is preserved and reported instead of overwritten. 1. Install the **same identified** preview VSIX in the editor being used, then open the intended trusted local Git repository in a **new** dedicated work window. If that repository was open while updating the VSIX, save its work, close only that work window, and reopen the repository in a new window. Keep other work windows and files intact. 2. Run **Codebook: Set Up AI Chat** in that window. VS Code starts its workspace-local server and advertises it through its MCP provider. Cursor installs the bundled user skill and registers a local stdio bridge for this window's authenticated MCP instance. To wake Cursor's MCP catalog after dynamic registration, setup updates only the modification time of an existing nonempty `~/.cursor/mcp.json`, preserving its bytes; if absent, it creates a private `0600` file containing `{"mcpServers":{}}` and records that exact file's identity in private extension storage. An existing empty file is preserved and reported for inspection before retrying setup. It does not add a static Codebook server or place a credential in the skill, MCP configuration or chat. @@ -68,7 +68,7 @@ A Vitest test run that the chat explained (the `node-test` profile) is saved the - **Recording finished · Save learning replay** once the run has ended: Save reads the sealed history and the chat's learning material. - **Learning saved · Open replay** when the test passed, every stop's recorded debugger state verifies, and the learning material explains every stop and the result. - **Learning partially saved · Open replay** otherwise. The tooltip names the result and every missing part, and a notification says what to ask the chat for (a missing explanation) or that only a new test run records it (missing capture). A failing test's replay is saved too: it holds the failing run. -- A test run that ends before it records any output, stop or result leaves no test replay: it is not listed in Saved studies and takes no place of the saved study limit. A read error that a retry can clear (a busy or locked file, too many open files) shows a replay as unavailable only until the next refresh. Codebook runs a root Vitest only from inside the repository; a `node_modules/vitest` that links outside it is named as such in `codebook_status`. A history sealed with no entry is removed when it seals, also when a list or a count meets one that an earlier version sealed; a history that holds a saved learning material or understanding marks is kept, and so is one whose seal records entries but whose journal was lost: a status call or a list never removes it. +- A test run that ends before it records any output, stop or result leaves no test replay: it is not listed in Saved studies and takes no place of the saved study limit. A read error that a retry can clear (a busy or locked file, too many open files) shows a replay as unavailable only until the next refresh. Codebook runs a Vitest only from inside the repository; a `node_modules/vitest` that links outside it is named as such in `codebook_status` and refuses the start. A history sealed with no entry is removed when it seals, also when a list or a count meets one that an earlier version sealed; a history that holds a saved learning material or understanding marks is kept, and so is one whose seal records entries but whose journal was lost: a status call or a list never removes it. - Saved studies in a second editor window of the same workspace lists a test replay that another live window is still recording as `recording`, not interrupted, and refreshes it by reading only what was appended; once the recording window seals it, the window shows the sealed status, and a replay whose recording window crashed reads as interrupted, with no verified final seal. A history that is being recorded carries a small writer record (`writer.json`) in its folder, which is removed when the history seals; earlier versions ignore it, a record that could not be read for a moment is judged again within 5 seconds, and test histories, learning materials and reader positions open in both directions unchanged. - A stop before the first stop reads stopped, not refused. A run stopped before its history began, or whose history sealed with no entry, offers no `read-history`. After a stop whose release failed, the run offers only `stop`, and a continue is refused with `STALE_PAUSE`. - **Learning save failed · Retry** when stopping the run did not finish (Retry stops it again) or the sealed history could not be read back (Retry reads it again; the test does not run again), and **Learning save failed · Open retained history** when the history could not be sealed (the recorded part opens read-only). Each tooltip names the cause; the details are in **Output › Codebook**. @@ -104,12 +104,37 @@ The bundled skill (1.0.47) routes a question about a test, or about library beha #### Quick start: a test replay -1. Open the repository in VS Code or Cursor and run **Codebook: Set Up AI Chat** once. The repository needs Vitest 4 or later installed at its root. +1. Open the repository in VS Code or Cursor and run **Codebook: Set Up AI Chat** once. The test needs Vitest 4 or later in its package or in a folder above it inside the repository ([Tests inside a workspace package](#tests-inside-a-workspace-package)). 2. In Copilot Chat (in VS Code, a new chat in a **Local** session) or Cursor Agent, ask `/codebook` your own question about a test, or about library behavior a test exercises. 3. Follow the chat: it pauses the test at the stops it chose and explains each from the captured values, which you can inspect in the native debugger meanwhile. 4. When the test has run, click **Save learning replay** in the status bar. 5. Click **Open replay**, or open the test replay from Saved studies, and step through its stops with **Previous stop** and **Next stop** in the transport at the bottom: the Application pane shows the test output up to each moment, the recorded-source tab the source as it ran, the native debugger the recorded values and stack, and the Learning summary, in the chat's place, the moment's explanation with its step marked on the diagram. +#### Tests inside a workspace package + +A test runs the way its package's own `vitest` command would: in its **Vitest root**, the nearest folder above the test that holds a Vitest or Vite config (`vitest.config.*` or `vite.config.*`), with that config and the Vitest 4 or later that Node resolves from there (the package's own, or one hoisted to a folder above it inside the repository), unless the repository root's own config already selects the test exactly once, which keeps it running from the root as before. A test with no config above it, up to the repository root, runs from the root. Nothing above the opened folder is consulted. + +1. Open the repository root (the monorepo, not the package) in VS Code or Cursor. The test's package, or a folder above it, needs Vitest 4 or later in its `node_modules`. Yarn Plug'n'Play installs have no `node_modules/vitest` and are not supported. +2. Ask `/codebook` about the code a package's test exercises, for example `/codebook explain how QueryCache decides to reuse a query, running its test`. +3. The run's manifest (the run resource's `manifestUri`) names the folder it ran in as `vitestRoot` (`packages/query-core`, or `.` for the root), why (`vitestRootReason`: `nearest-config`, `root-config`, `root-claim` or `no-config`), the root claim's result (`rootClaim`), the config files there (`vitestConfigFiles`) and config-named entries it passed over on the way (`skippedConfigFiles`). +4. Unlike the package's `test` script, the run uses Codebook's fixed environment (`CI=true`, `NODE_ENV=test`, no inherited credentials) and none of the script's own variables or options. Vite and Vitest still load the chosen config and read the Vitest root's `.env*` files for `import.meta.env`, as the package's own `vitest` would. + +`codebook_status` reports `node-test` ready when it finds a usable Vitest at the repository root or in a folder one or two levels below it, looking at no more than 256 folders and never inside `node_modules`, a folder whose name starts with `.`, or a linked folder; its `vitestSearch` reads `usable`, `none`, `unusable` or `incomplete`. A start does not wait on that search: it checks the cited test's own Vitest, so a test in a deeper package starts when its own Vitest is usable, while a missing Node or debugger still refuses it. + +| What the run's `error` or `codebook_status` says | Code | Cause | Fix | +| --- | --- | --- | --- | +| `node-test needs Vitest 4 or later for ; none was found in /node_modules/vitest or above it inside the repository.` | `PREREQUISITE_MISSING` | No Vitest in the test's Vitest root or any folder above it up to the repository root. | Install Vitest in that package or at the repository root, then start again. | +| `node-test needs Vitest 4 or later for ; /node_modules/vitest is .` | `PREREQUISITE_MISSING` | The Vitest Node resolves from the Vitest root is older than 4; Codebook never falls back to another one above it. | Upgrade that Vitest. | +| `node-test cannot read the version of /node_modules/vitest for : …` | `PREREQUISITE_MISSING` | Its `package.json` is missing, unreadable or not a Vitest package file. | Reinstall the dependencies. | +| `node-test needs the Vitest program /node_modules/vitest/vitest.mjs for ; …` | `PREREQUISITE_MISSING` | An incomplete Vitest install. | Reinstall Vitest there. | +| `… /node_modules/vitest links outside it (for example to a package store outside the repository), which Codebook does not run.` | `PREREQUISITE_MISSING` | pnpm's global virtual store, or another package store outside the repository. | Install the dependencies so that Vitest lies inside the repository. | +| `node-test does not start Vitest in for : there is a folder or a link that leaves the repository, …` | `PREREQUISITE_MISSING` | A folder, or a link out of the repository, named like a config in the folder the test would run from; Vitest's own config search could load it. This also refuses such a root, which earlier versions ran from. | Remove or replace that entry. | +| `node-test cannot read for ().` | `PREREQUISITE_MISSING` | A folder on the way to the test's Vitest cannot be read. | Make that folder readable. | +| `node-test cannot pass to Vitest from : its path there starts with "-", …` | `INVALID_CONFIGURATION` | Vitest would read the path as an option. | Rename that file or folder. | +| `node-test's plan for changed while the run was preparing (…)` | `INVALID_CONFIGURATION` | A config or the Vitest changed between the start's check and the launch. | Start the test again. | +| `Vitest does not select the cited test file …` or `The cited test file belongs to Vitest projects, …` | `INVALID_CONFIGURATION` | The Vitest root's config (its `include`, `root` or `dir`) leaves the test out, or several of its projects include it. The error's `cause` names the test, the Vitest root, the root claim's result and the filter. | Check that config. A test whose root config lists it under several projects runs in its package instead (`rootClaim` `several-projects`). | +| `codebook_status`: `… none was found.`, `… the first one found, /node_modules/vitest, …` or `… did not look further.` | data, not an error | The readiness search looks only at the root and two levels below it. | A deeper package still starts; otherwise install or upgrade the Vitest it names. | + #### Terms - **Learning replay**: a saved explained run that you can replay after it ended; a browser run's holds the application video. @@ -275,7 +300,7 @@ Run **Codebook: Set Up AI Chat**, then use `/codebook` in the editor's standard ## First study through MCP -Read `codebook_status` and `codebook://help/repository-browser`. Prepare an article from actual workspace source, then supply its exact immutable revision and source-bound configuration to `codebook_start`. `repository-browser` drives one configured browser action. The `node-test` profile (0.2.12+) instead runs one cited repository Vitest test with ordered native Node source stops. It needs Vitest 4 or later and cites the test by `testTitles` (enclosing `describe` titles, then the test title); the retired `testName` is refused. Codebook runs only that file and fails the run unless Vitest reports exactly the cited test as executed. Read `codebook://help/node-test` for its configuration. Saved Node test histories are listed at `codebook://node-history`. Follow returned `nextActions`, snapshot/source resources and exact continuation tuples. A saved article or recording never launches the application by itself. +Read `codebook_status` and `codebook://help/repository-browser`. Prepare an article from actual workspace source, then supply its exact immutable revision and source-bound configuration to `codebook_start`. `repository-browser` drives one configured browser action. The `node-test` profile (0.2.12+) instead runs one cited repository Vitest test with ordered native Node source stops. It needs Vitest 4 or later in the test's package or above it, runs the test in its Vitest root ([Tests inside a workspace package](#tests-inside-a-workspace-package)) and cites the test by `testTitles` (enclosing `describe` titles, then the test title); the retired `testName` is refused. Codebook runs only that file and fails the run unless Vitest reports exactly the cited test as executed. Read `codebook://help/node-test` for its configuration. Saved Node test histories are listed at `codebook://node-history`. Follow returned `nextActions`, snapshot/source resources and exact continuation tuples. A saved article or recording never launches the application by itself. ## Learn this repository @@ -338,7 +363,7 @@ For actual-editor Playwright CLI video, set `CODEBOOK_EDITOR_CDP=1` only on the ## Run one configured repository action -Minimum versions: **extension 0.2.0+ / MCP API 1.4+**. The current preview is the version in this package's manifest (`extension/package.json`) with **MCP API 1.8** (`codebook_status` reports the installed build). API 1.8 changed what a `node-test` run reports: it reads `demonstrated` in `finalOutcome` once its test passed after every declared stop, and in `studyOutcome` and `recordedStudyOutcome` once its history sealed complete (before, it read `partial` at most); its run resource adds `nodeTest.testResult` (`passed`, `failed`, `signal` or `none`), `nodeTest.learningCoverage` (`complete`, `partial` or `none`) and `nodeTest.learningMissing`, and `nextActions` lists `save-node-learning` until the coverage is complete (and only for a history that holds an entry). Both the run resource and the node-history resource also list `nodeTest.captureMissing`, the recorded evidence the replay lacks (a stop whose debugger state was not captured, or an execution that ended before its result), which no explanation adds and which makes Save read partially saved; an agent claims saved learning only when `captureMissing` is empty. A node-history resource (`codebook://node-history/`, its events or its learning material) or a `codebook_explain_node_history` call that names a study id this workspace holds no history for (a mistyped id, for example, or a history removed while it is read) is refused as `NOT_FOUND` with a sentence that names the id and tells the agent to copy the `studyId` or `historyUri` from `codebook://node-history`; the published recovery of `NOT_FOUND` says the same. Causes the run resource shows (`nodeTest.historyReason`, `recording.reason`, `recording.contextReason`) are cut to 300 characters and name no private storage path. A node-test stop whose position does not fit its retained citation is refused during preparation with a sentence that names the stop (`source` or `additionalStops[n]`), its `anchorIndex`, the cited path and lines and the one condition that failed (anchor, path, hash, line or column), and what to send. The column of a declared node-test stop is a preference: when the debugger binds the declared line at another column, the stop is accepted and captured at that column, and only a binding on another line refuses it; that refusal names the declared and the bound position and says that the stop is captured on its declared line. A declared node-test stop in the cited test file itself is reached as well when the debugger reports no entry pause before it: its first pause is the stop, and Codebook captures it instead of continuing past it. A node-test start's checks (a changed stop source, a cited source the editor holds with other text, a changed test file, a path that is not a file, a root Vitest missing, older than 4 or without its program) run during preparation and end the run with a typed `error` before its first stop, which records no history and frees the run's slot and key; `codebook_status` names a missing or older root Vitest before any start, and a root `node_modules/vitest` that links outside the repository is not run. `codebook_stop` answers `HISTORY_SEAL_FAILED` when it could not seal the history of the run it ended, and `codebook_playback` refuses a test replay with `PLAYBACK_PROFILE_UNSUPPORTED`. `codebook_explain_node_history` refuses a step that cites another event than it claims with `INVALID_REFERENCE`, naming what that event is, and the node-test help (`codebook://help/node-test`) holds a complete start example and the ordered walkthrough of the calls. A test that ends with a non-zero exit code or a signal keeps that result even without Vitest's report, and a passing run whose first release failed seals complete once a retried Stop releases it. A test history whose seal fails with a transient file-system code (`EMFILE`, `ENFILE`, `EBUSY`, `EAGAIN` or `EPERM`) is sealed once more after 200 ms before the run reports `HISTORY_SEAL_FAILED`; a repeated `codebook_stop` makes no further attempt. No stored format changed in 1.8: test histories, learning materials and reader positions written by an earlier 0.2 version open unchanged. API 1.7 changed two answers: `codebook_source`, and `codebook_read_resource` for a book's cited source, answer the source's lines as `numberedText` and no longer as `text`, because the two copies doubled every source answer. An MCP `resources/read` of a cited source's URI still answers its exact text. No request states an API version, so a client written for API 1.6 gets these answers too: where it read `text`, it reads `numberedText` (each line after its one-based number and a tab, the lines joined by a line feed). API 1.7 also added a second reason a run resource names, and its evidence is refused with, once the run's saved study was removed: `The archive of this run's saved study is no longer in the study library: its files were removed by hand or by another Codebook window.`, beside `The learner moved this saved study to the Trash.` for a study deleted in this window (see [Delete a saved study](#delete-a-saved-study)). API 1.6 added, without changing the meaning of an existing field: `declaredSource`, the position you declared (`requestedSource` is its older name); `armed`, the place Codebook requested the breakpoint at when that differs; `place`, one sentence that names the line a stop paused on when the debugger bound it on another line than you declared; and `answer: "next"` in the run resource's `continueArguments`, with which a continuation answers with the next state (the next observation, the observed result or the failure). The same version also added `numberedText` to the answer of `codebook_source` and to the answer of `codebook_read_resource` for a book's cited source: the source's lines, each after its one-based number and a tab. Cycle 0029 delivered private preview **0.2.12** and Cycle 0018 **0.2.2**. `repository-browser` appears in `codebook_status.profiles` and its exact input schema at `codebook://help/repository-browser`. External-client execution, source/value pauses, recording/restart, interrupted evidence and native reader flows have separate development evidence in both editors. [Cycle 0017 installed evidence](https://github.com/laststance/codebook/blob/273660c49bb1aa5a88082ab6d43cd1ebae6ee7cf/docs/cycles/evidence/0017/native-validation.md) adds one unchanged real-repository action, live/replay modes and Wiki returns in both editors; its Cursor restart failure was later repaired in the bounded Cycle 0018 schedule. Neither result establishes G12 editor-chat use or full-product acceptance. +Minimum versions: **extension 0.2.0+ / MCP API 1.4+**. The current preview is the version in this package's manifest (`extension/package.json`) with **MCP API 1.8** (`codebook_status` reports the installed build). API 1.8 changed what a `node-test` run reports: it reads `demonstrated` in `finalOutcome` once its test passed after every declared stop, and in `studyOutcome` and `recordedStudyOutcome` once its history sealed complete (before, it read `partial` at most); its run resource adds `nodeTest.testResult` (`passed`, `failed`, `signal` or `none`), `nodeTest.learningCoverage` (`complete`, `partial` or `none`) and `nodeTest.learningMissing`, and `nextActions` lists `save-node-learning` until the coverage is complete (and only for a history that holds an entry). Both the run resource and the node-history resource also list `nodeTest.captureMissing`, the recorded evidence the replay lacks (a stop whose debugger state was not captured, or an execution that ended before its result), which no explanation adds and which makes Save read partially saved; an agent claims saved learning only when `captureMissing` is empty. A node-history resource (`codebook://node-history/`, its events or its learning material) or a `codebook_explain_node_history` call that names a study id this workspace holds no history for (a mistyped id, for example, or a history removed while it is read) is refused as `NOT_FOUND` with a sentence that names the id and tells the agent to copy the `studyId` or `historyUri` from `codebook://node-history`; the published recovery of `NOT_FOUND` says the same. Causes the run resource shows (`nodeTest.historyReason`, `recording.reason`, `recording.contextReason`) are cut to 300 characters and name no private storage path. A node-test stop whose position does not fit its retained citation is refused during preparation with a sentence that names the stop (`source` or `additionalStops[n]`), its `anchorIndex`, the cited path and lines and the one condition that failed (anchor, path, hash, line or column), and what to send. The column of a declared node-test stop is a preference: when the debugger binds the declared line at another column, the stop is accepted and captured at that column, and only a binding on another line refuses it; that refusal names the declared and the bound position and says that the stop is captured on its declared line. A declared node-test stop in the cited test file itself is reached as well when the debugger reports no entry pause before it: its first pause is the stop, and Codebook captures it instead of continuing past it. A node-test start's checks (a changed stop source, a cited source the editor holds with other text, a changed test file, a path that is not a file, a test's Vitest missing, older than 4, unreadable, outside the repository or without its program, a Vitest root that holds a config-named folder or an outside link, a test path from the Vitest root that starts with `-`, a plan that changed before the launch) run during preparation and end the run with a typed `error` before its first stop, which records no history and frees the run's slot and key; `codebook_status` names a missing or older Vitest at the root or up to two levels below it before any start (`profiles['node-test'].vitestSearch`), and a `node_modules/vitest` that links outside the repository is not run. The node-test manifest's `vitestRoot`, `vitestRootReason`, `rootClaim`, `vitestConfigFiles` and `skippedConfigFiles` and `codebook_status`'s `vitestSearch` are additive output fields of API 1.8, and a node-test start no longer waits on `codebook_status`'s Vitest search. `codebook_stop` answers `HISTORY_SEAL_FAILED` when it could not seal the history of the run it ended, and `codebook_playback` refuses a test replay with `PLAYBACK_PROFILE_UNSUPPORTED`. `codebook_explain_node_history` refuses a step that cites another event than it claims with `INVALID_REFERENCE`, naming what that event is, and the node-test help (`codebook://help/node-test`) holds a complete start example and the ordered walkthrough of the calls. A test that ends with a non-zero exit code or a signal keeps that result even without Vitest's report, and a passing run whose first release failed seals complete once a retried Stop releases it. A test history whose seal fails with a transient file-system code (`EMFILE`, `ENFILE`, `EBUSY`, `EAGAIN` or `EPERM`) is sealed once more after 200 ms before the run reports `HISTORY_SEAL_FAILED`; a repeated `codebook_stop` makes no further attempt. No stored format changed in 1.8: test histories, learning materials and reader positions written by an earlier 0.2 version open unchanged. API 1.7 changed two answers: `codebook_source`, and `codebook_read_resource` for a book's cited source, answer the source's lines as `numberedText` and no longer as `text`, because the two copies doubled every source answer. An MCP `resources/read` of a cited source's URI still answers its exact text. No request states an API version, so a client written for API 1.6 gets these answers too: where it read `text`, it reads `numberedText` (each line after its one-based number and a tab, the lines joined by a line feed). API 1.7 also added a second reason a run resource names, and its evidence is refused with, once the run's saved study was removed: `The archive of this run's saved study is no longer in the study library: its files were removed by hand or by another Codebook window.`, beside `The learner moved this saved study to the Trash.` for a study deleted in this window (see [Delete a saved study](#delete-a-saved-study)). API 1.6 added, without changing the meaning of an existing field: `declaredSource`, the position you declared (`requestedSource` is its older name); `armed`, the place Codebook requested the breakpoint at when that differs; `place`, one sentence that names the line a stop paused on when the debugger bound it on another line than you declared; and `answer: "next"` in the run resource's `continueArguments`, with which a continuation answers with the next state (the next observation, the observed result or the failure). The same version also added `numberedText` to the answer of `codebook_source` and to the answer of `codebook_read_resource` for a book's cited source: the source's lines, each after its one-based number and a tab. Cycle 0029 delivered private preview **0.2.12** and Cycle 0018 **0.2.2**. `repository-browser` appears in `codebook_status.profiles` and its exact input schema at `codebook://help/repository-browser`. External-client execution, source/value pauses, recording/restart, interrupted evidence and native reader flows have separate development evidence in both editors. [Cycle 0017 installed evidence](https://github.com/laststance/codebook/blob/273660c49bb1aa5a88082ab6d43cd1ebae6ee7cf/docs/cycles/evidence/0017/native-validation.md) adds one unchanged real-repository action, live/replay modes and Wiki returns in both editors; its Cursor restart failure was later repaired in the bounded Cycle 0018 schedule. Neither result establishes G12 editor-chat use or full-product acceptance. The current adapter is `next-dev-webpack`: a trusted local Git root with dependencies already installed, an executable Next development server, a same-origin `entryPath` (default `/`), default `.next/dev` output, and webpack modules/maps that retain the exact original selected source. Codebook starts the owned server on `127.0.0.1` at the project's own port (Next is started with `--hostname 127.0.0.1`) and opens the app in the recorded browser at `http://localhost:`, the origin that `AUTH_URL`, OAuth callbacks and cookies usually name. `localhost`, `127.0.0.1` and `[::1]` at that port count as the same application origin, so a sign-in redirect between them keeps the run going while another port or host still ends it. Because a host may resolve `localhost` to `::1`, Codebook also listens on `[::1]:` for the whole run and forwards its bytes to the owned server (each direction ends as its source ends, so a client that half-closes still gets the reply), so no other process can answer there. The listener starts after the owned `next dev` has bound its port and reported ready, and never starts when that readiness fails or is cancelled; it closes with the run. If something else already holds `[::1]:`, preparation is refused as a busy port naming `[::1]`; if the system does not let Codebook listen there it is refused as not permitted, and any other listen failure is refused with its error code. On a host without IPv6 loopback no `[::1]` listener is made and `[::1]` is not accepted. The recorded browser resolves `localhost` as the system does, so the app's own calls to your other local services, on `::1` or `127.0.0.1`, reach them as they do in your own browser; Chrome itself runs on a dynamic loopback endpoint. The port is the one the project's own `dev` script would start `next dev` on, and Codebook asserts it only for a fixed set of forms. The `dev` script, and each script it runs, may be one or more statements joined by `&&` or `;`, each one of these: `export PORT=4000`, or a `PORT=4000` or plain `NAME=value` assignment as a prefix before a command; a `next dev` command reached by `next`, `npx next`, `bunx next`, `pnpm exec next`, `yarn next` or a `node_modules/.bin/next` path (the path form only on its own or after `pnpm exec`, since `npx` and `bunx` take a package name and fail on a path), optionally after `PORT=4000`, `cross-env` or `env` with assignments only, with Next's own `dev` flags and literal values (`-p`/`--port` as `-p 4000`, `-p4000`, `--port 4000` or `--port=4000`, the last one winning, `-H`/`--hostname`, `--turbo`, `--turbopack`, `--webpack`, `--experimental-https` and its `--experimental-https-*` options); a script run with no arguments (`npm run x`, `pnpm run x`, `yarn run x`, `bun run x`; `pnpm x` and `yarn x` without `run` are followed only for `dev`, `start`, `serve`, `web`, `app`, `client`, `frontend` and a name with a `:`, since pnpm and yarn have many commands and aliases of their own (`deploy`, `ci`, `t`, `access`, `rebuild`) that run before or instead of a script of that name, and some of them change between versions: write `pnpm run deploy`), followed two levels deep at most; or one `concurrently` whose quoted commands are each read the same way and exactly one of which is a `next dev` command. The port is the last `-p`/`--port`, else the `PORT` the script exports or assigns before the command, else Next's default 3000, and `PORT` from the editor's own environment is not consulted. The `dev` script is read as text and never run. A `package.json` that is not a regular file (a FIFO, for example) or is larger than 1 MiB is not read, and `applicationPortSource` says so. A `dev` script that runs more than 256 statements in all, counting every line of it and of each script it runs, is unread, and `applicationPortSource` says the dev script runs more statements than Codebook reads. The last statement may end with one `;`. The script is read with the meaning of the shell that runs it: `sh` on macOS and Linux, `cmd` on Windows, where npm, pnpm and yarn run scripts with `cmd`. On Windows only the forms that mean the same under `cmd` are read: bare `next` or an `npx`-style launcher with `next dev` and literal flags (a program written as a path with `/`, such as `node_modules/.bin/next`, is unread), `cross-env PORT=4000 next dev`, delegations and a `concurrently` with double-quoted commands; single quotes, a `PORT=4000` or `NAME=value` prefix, `export`, `env`, `;` and a variable named `port` in any case other than `PORT` (Windows ignores the case of variable names) are unread there, and `applicationPortSource` names `cmd`. An empty `dev` script is told as empty. A word that has a quote before its `=` (`"PORT=4000" next dev`) is no assignment. Everything else is unread: any other program or option, a pnpm command whose meaning depends on the installed pnpm version (`pnpm rebuild`), a pipe, `&` or `||`, a group or subshell, `$`, a backtick, a redirection or heredoc, a comment, a backslash or a line break, `if`/`while`/`!`/`time`, `sh -c`, `cmd /c`, `dotenv`, `node`, `set`, `unset`, a bare `PORT=4000;`, a `${PORT:-4000}` default, arguments given to a delegated script, and a script that never runs `next dev`. For an unread script no port is asserted: Next's default 3000 is taken and `applicationPortSource` says so and quotes the statement that left the forms above by its structure only. Only names from fixed lists are quoted (the words of the forms above by their base name, Next's `dev` flags, `PORT=…` and plain `NAME=…` assignment names before the program, and `package.json` script names, without control characters and cut); every value, operand, path, token, header, `user:pass@host` and base64 text is shown as `…`, so declare the port with `-p` in the `dev` script in one of the forms above to name it. `.env` files are not read for the port, because Next reads `PORT` before it loads them. Bun is the exception: it loads `.env` files into the environment of the scripts it runs, so a `next dev` without a `-p`/`--port` flag and without a `PORT` assigned in the script is unread (Next's default 3000 is taken and `applicationPortSource` names Bun's `.env` loading) when it is reached by `bun run