Development
Run the app, refresh demo data, build the docs, and check changes.
Run the app
Install packages and start the self-contained demo:
corepack enable
corepack pnpm run setup
corepack pnpm run demo
Open http://localhost:2299. The demo serves the checked-in todo-list data
from public/demo-diff-data.json. It does not start a snapshot, coding agent,
or remote Git request. Vite refreshes the page as you edit the app or fixture.
pnpm run dev starts the same local Vite server.
Develop live reviews
Run a local worktree review through the app without building production files:
pnpm run dev:live -- --repo /path/to/repo
The command starts the Git watcher, live JSON route, event stream, and Vite.
It picks the next free port when 2299 is busy and prints the ready URL. Stop
it with Ctrl-C; it stops its child processes and removes its temporary data.
Use a deterministic local agent sequence when you do not need Git, agent credentials, or network access:
pnpm run dev:mock
The mock starts with delayed note generation, reports file progress, fails,
retries, then completes. It reads public/demo-diff-data.json; pass
--fixture PATH to use another local JSON fixture. Vite updates source edits,
and fixture changes update the browser through the event stream.
Run checks
corepack pnpm run check
corepack pnpm run check is the product gate. It runs React and TypeScript
lint, builds the app, runs the Node unit and integration tests, checks and
builds the docs, then installs the packed package in a temporary folder and
runs its CLI. Each stage names its failure before the command stops.
Npm release verification skips docs and saves the tarball from that same package smoke test. Publishing uses that verified tarball without rebuilding it. PR checks and site deployment still check and build the docs.
Install Chromium once after setup, and again when Playwright changes:
corepack pnpm run test:browser:install
Pull requests run this same gate from pnpm-lock.yaml on Node 22.13.0 and
the current Node 24 line. The changed-code Fallow audit and the site deployment
remain separate checks; neither replaces the product gate.
pnpm run setup uses pnpm install --frozen-lockfile, so it installs only what pnpm-lock.yaml
pins. It is safe to run again and needs no ignored or generated files. Node
22.13.0 and pnpm 11.18.0 are pinned in .nvmrc and package.json; the
corepack pnpm commands use the pinned pnpm version.
For a shorter local loop, run one named lane:
corepack pnpm run lint
corepack pnpm run test:unit
corepack pnpm run test:integration
corepack pnpm run test:coverage
corepack pnpm run test:browser
corepack pnpm run test:platform
| Command | What it checks |
|---|---|
pnpm run test:unit |
Fast parser, provider, doctor, release, config, and path checks. It does not build the app. |
pnpm run test:integration |
The production build plus local Git, agent note, presenter, and server checks. |
pnpm run test:coverage |
Unit and integration tests once, with per-file coverage checks. |
pnpm run test:browser |
The review journey in Chromium. It does not download a browser. |
pnpm run test:platform |
Host command lookup and npm launcher rules with local fake tools. |
pnpm test |
Unit and integration tests with coverage, then browser and platform tests. |
Coverage wraps test:core, which runs the unit and integration commands. It
does not repeat the integration tests. The fast unit job also runs on its own
in CI to report failures early.
The coverage lane writes text, JSON summary, and LCOV reports under
coverage/. Each covered file must keep at least 80% statement and line
coverage, 60% branch coverage, and 90% function coverage. The check applies
the floor to each file.
Pull request jobs run a fast unit check, the combined core coverage check, and browser tests from a clean Ubuntu checkout. They use local Git repos, fake coding agents, and checked-in browser data. A weekly job runs the platform lane on macOS 15 and Windows 2025. You can also start the Test lanes workflow by hand.
For the supported browser, screen, and device checks, see Mobile support.
Use a clean checkout
Each checkout owns its dependencies and live data. Use the same commands in the main checkout, a linked worktree, and a Codex-managed worktree:
corepack enable
corepack pnpm run setup
corepack pnpm run check
Do not copy node_modules, .cache/, or a generated diff snapshot into a
worktree. pnpm run setup creates the dependency tree from the lock file. The
app falls back to checked-in demo data until you create live data locally.
Linked Git worktree
Create and verify a linked worktree from the main checkout:
git worktree add -b feature/my-change ../diffsplain-my-change HEAD
cd ../diffsplain-my-change
corepack enable
corepack pnpm run setup
corepack pnpm run check
Before removal, inspect and commit, move, or discard its changes. Then remove the worktree from the main checkout:
git -C /path/to/diffsplain status --short
git -C /path/to/diffsplain worktree remove ../diffsplain-my-change
Use git worktree remove --force only after you have dealt with changes.
Codex-managed worktree
Choose Worktree when starting a Codex task, then run the same setup and
check commands above. Codex worktrees start from tracked files. This project
does not need .worktreeinclude, because setup does not depend on ignored
files. Use Codex handoff when you need the same branch in the main checkout.
Codex cloud
Set the Codex cloud setup and maintenance scripts to:
corepack enable
corepack pnpm run setup
Then run this from the cloud task:
corepack pnpm run cloud:check
cloud:check runs the clean-checkout gate and the provider/browser tests.
Those tests use fake coding providers and a fake browser command. Real Codex,
Claude, Copilot, Cursor, OpenCode, GitHub, or browser login is optional and is
needed only for a live integration task. Without a configured default or
--agent, an interactive terminal lists usable agents in this order: Codex,
Claude, Copilot, Cursor, then OpenCode. You choose one. Non-interactive runs need a configured default,
--agent NAME, or --no-agent. Cursor Agent 2026.08.11 or newer uses
the signed-in Cursor CLI in the user’s home. It still contacts the Cursor
service. Keep credentials in Codex environment settings, not checked-in rules
or scripts.
To test the linked-worktree path itself, run:
corepack pnpm run setup:smoke
It creates a temporary linked worktree, checks that no dependencies or live snapshot were copied, runs setup, then removes the worktree. Commit or stash changes to package install files first, so the test checks the same files.
Trust repo automation
The checked-in Codex hook manifest runs no commands. This keeps a branch from
using that manifest to approve its own code. Review .codex/, .agents/,
AGENTS.md, and skills-lock.json at the commit you plan to use. After that
review, you may run the vendored UI check yourself:
node .agents/skills/impeccable/scripts/hook.mjs
Do not put this command back in the repo-owned hook manifest. Use a user-owned hook outside the checkout if you want it to run on each edit.
skills-lock.json records the source, version, path, and content hash for
vendored skills. Pull requests that change a hook manifest, vendored automation,
agent instructions, or this lock file request the automation owner’s review.
The Automation trust review check reads the pull request through the GitHub
API without checking out branch code. It passes after a maintainer adds the
automation-reviewed label.
Codex tool profiles
Diffsplain keeps developer integrations separate from product summary agents.
The checked-in .codex/config.toml has no secrets or enabled integrations.
After you trust the checkout, copy the developer template to your Codex home:
cp .codex/developer.config.toml.example "$CODEX_HOME/developer.config.toml"
Use codex --profile developer for optional OpenAI developer docs MCP access
and the GitHub plugin. Docs tools can run without a prompt; GitHub tools ask
first.
Do not use a named profile alone as a no-tool boundary. Profiles merge MCP servers and plugins from the base user config. For a manual no-tool Codex run, replace those maps at runtime and skip user config:
codex exec --ephemeral --ignore-user-config --ignore-rules \
--sandbox read-only \
--config 'mcp_servers={}' \
--config 'plugins={}' \
--config 'web_search="disabled"'
diffsplain uses snapshot-only access for remote targets, exact ranges, and
--no-checkout-access. It runs agents in the temporary snapshot folder with
provider-specific snapshot safeguards, a small runtime-only environment, and
no credential variables. Authentication remains available to the client
itself.
For the current checkout, --worktree, and --base REF without --head, it
uses checkout-read-only access. The agent runs in the chosen repo with normal
user settings so it can inspect ignored files, Git history, and symlink
targets. The prompt forbids edits and mutating commands; approval stays with
the user. Codex keeps its read-only sandbox, Claude uses permission mode
plan, and Cursor uses Ask mode with its sandbox, trust, and workspace.
Copilot and OpenCode have no proven native read-only mode, so Diffsplain warns
before either gets checkout access.
Measure the pipeline
Run the deterministic speed and note-quality gate locally:
pnpm run benchmark:gate
It measures build, summary, presentation transport, agent startup, and restart
separately. The presentation metric stops when the local transport is ready;
it is not a browser rendering measure. Agent startup covers the interval from
that ready snapshot to the first coding-agent request. The gate runs one
warm-up and five measured samples for both the working and held-out fixtures.
It compares each median with the limit in
benchmarks/performance-baseline.json and records all samples plus their
minimum, median, p95, and maximum.
The same gate scores fixed useful, incomplete, false, duplicate, and empty note
cases from benchmarks/quality-fixtures.json. Full and summary runs also score
the notes that the fixed fake provider produced for the working and held-out
pipelines. A failure names the fixture, speed measure, limit, or rubric item
that missed its target.
To run one speed measure, pass its name:
pnpm run benchmark:gate -- --case summary
The Live provider canary workflow runs one paid-provider sample each week and
on manual request. It has a ten-minute job limit, needs the
OPENAI_API_KEY repository secret, and does not run on pull requests. Run the
same bounded canary from an authenticated checkout with:
pnpm run benchmark:canary -- --runs 1 --reasoning minimal
Measure live updates
Run the live-update benchmark by hand when changing event delivery or polling:
pnpm run benchmark:live-update -- --mode events
pnpm run benchmark:live-update -- --mode poll
These commands report timing samples. They do not enforce a speed threshold and are not part of the test suite. Server and browser tests check live-update behavior and recovery.
Publish a release
Use the GitHub Actions Release workflow for normal releases. Run it from
main and enter one exact version such as 0.11.0 or 0.11.0-beta.1. The input
must be canonical SemVer: ranges, patch or minor aliases, surrounding
whitespace, build metadata, and shell syntax are rejected.
Before the first run, configure the npm package’s trusted publisher with this exact tuple:
- provider: GitHub Actions
- organization or user:
itsjling - repository:
diffsplain - workflow filename:
release.yml - environment:
npm-publish - allowed action:
npm publish
Create the protected npm-publish environment in GitHub. Allow this workflow’s
GITHUB_TOKEN to update main and release tags under your branch and tag
rules. Configure any required environment reviewers. These npm and GitHub
settings are prerequisites; the repository does not prove that they exist.
The workflow prepares and verifies the release without repository write access
or an npm identity. It passes the tarball, verification receipt, release commit,
and tag to a separate protected job. Only that final job receives GitHub write
access and the npm OIDC identity. It independently checks the artifact, pushes
the exact version commit and tag atomically, and publishes the tarball with
package lifecycle scripts disabled. Stable versions use the latest npm tag
and prereleases use next. Releases are serialized so two workflow runs cannot
publish concurrently.
Recover a workflow release
Rerun the failed publish job in the existing workflow run, or start a new run
with the same exact version after a failure. The publish-job rerun retains the
original verified artifact and dispatch commit, and is accepted when that
commit is the parent of the pushed release. Rerunning every job from an older
dispatch is rejected after main advances; start a new workflow run instead.
The workflow accepts only these states:
- Create:
mainstill contains the previous package version, the requested tag and npm version do not exist, and localmainmatchesorigin/main. - Resume: the requested package version and tag are already on the same
commit at
origin/main, but npm does not contain the version. - Complete: Git is in the resume state and npm contains the version. The
workflow rebuilds the verified tarball and succeeds only when its SHA-512 SRI
matches npm’s
dist.integrity.
A moved branch, conflicting tag, local-only version commit, or mismatched npm tarball is a mixed state and stops the workflow. Inspect and reconcile Git and npm before rerunning. If the atomic push failed and left only a local commit and tag in the runner, start a new run; if reproducing locally, delete or reconcile those local refs without overwriting remote history.
Publish locally as a fallback
Use local publication only when the GitHub workflow cannot be used. Start from
a clean main checkout, update the version, and create its commit and tag. Pass
the exact intended version to pnpm version:
corepack pnpm version 0.11.0
Do not push yet. Verify the tagged commit and build the release tarball:
corepack pnpm run release:verify
This command requires a clean tree and a v<version> tag on HEAD. It runs the
product gate without docs, installs and tests the exact tarball in a temporary consumer,
and records the commit and tarball hash in .cache/.
Sign in to npm when needed, then publish by naming the exact version again:
COREPACK_ENABLE_PROJECT_SPEC=0 corepack npm@11.5.1 login --auth-type=web
corepack pnpm run release:publish -- 0.11.0
The publish command checks the clean tree, tag, commit, tarball hash, npm login, and registry before it publishes. Local publication does not use the workflow’s trusted-publisher identity or add npm provenance.
After npm confirms the version, push the version commit and tag:
git push origin main --follow-tags
If verification fails, fix the cause and run it again. If publication fails,
keep the same version and tag, fix the npm login or named error, and rerun only
release:publish. If npm accepted the package but the Git push failed, push the
existing commit and tag without publishing again.
Dependency security overrides live in pnpm-workspace.yaml. Their advisory
references and re-evaluation steps are recorded in
the dependency override register.
Run pnpm audit and pnpm audit --prod for current findings; docs builds use
committed content and do not handle user submissions.
Repository security
For dependency updates, main branch rules, the release push identity, and admin recovery, see the repository security runbook.
Refresh the demo
site/todo-demo.js holds the ten sample files used by the landing page and the
app demo. After changing it, rebuild the checked-in data:
node scripts/write-todo-demo.mjs
To build a live snapshot from another repo:
corepack pnpm run snapshot -- --repo /path/to/repo
The writer stores live data in the ignored .cache/diff-data.json. The local
server exposes it at /diff-data.json.
Run the docs
The docs use Blume and live under docs/.
corepack pnpm run docs:dev
Build or preview the static output:
corepack pnpm run docs:build
corepack pnpm run docs:preview
Blume writes its output to docs/dist/. The Pages workflow publishes it under
/diffsplain/docs.
Project paths
| Path | Purpose |
|---|---|
app/ |
React review page. |
scripts/ |
CLI, Git data writer, note writer, and local server. |
public/ |
Checked-in app demo data. |
site/ |
Static GitHub Pages landing page. |
docs/ |
Blume product docs. |
data/ |
Checked-in demo notes. |