chore: replace the em-dashes this release added with plain punctuation

The merged PRs (mostly #714's git fetch) added about 120 em-dashes in
comments, docs, test names and three settings strings. Each one is now a
comma, colon, semicolon or parentheses. The English settings strings in
index.html and their i18n.js keys change together, and the zh-CN value
for the fetch interval uses full-width parentheses instead of a double
dash. The one left is the regex in constants.js that matches a dash
typed as a word, which is behaviour, not punctuation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-10-27 09:11:19 +01:00
1 parent de1aab9b86
commit c04fa97f78
28 files changed
+91 -91

No files matched your search

+1 -1
View File
@@ -222,7 +222,7 @@ describe('App Settings modal structure', () => {
// The strip destructure is the other half: an aliased key here never reaches the settings PUT.
expect(settingsUi).not.toMatch(/gitFetch(?:Enabled|IntervalSeconds):\s*_\w+/);
// Declared in the .strict() schema — an undeclared key 400s the whole settings PUT.
// Declared in the .strict() schema: an undeclared key 400s the whole settings PUT.
expect(SettingsUpdateSchema.safeParse({ gitFetchEnabled: true, gitFetchIntervalSeconds: 300 }).success).toBe(true);
// Off by default (only an explicit true loads as on), and an empty box falls back to the default interval.
+2 -2
View File
@@ -2,11 +2,11 @@
* @fileoverview The Git fetch interval rows (App Settings → Bottom bar, Session Options → Git) in Chinese
* (zh-CN): each label and description runs through the real translator over the real index.html in JSDOM and
* reads in Chinese with no English left beyond the product name and the technical names Git / git fetch, and
* every new key — the save toast in session-ui.js included — is in the dictionary exactly once (a repeated key
* every new key (the save toast in session-ui.js included) is in the dictionary exactly once (a repeated key
* in the object literal silently replaces the first).
*
* The Git tab's Status row is not here: its text is written at runtime by updateGitFetchStatus and carries
* data-i18n-skip, so translating it would rewrite the stamp the code builds. 'sec' is not a key either — only
* data-i18n-skip, so translating it would rewrite the stamp the code builds. 'sec' is not a key either: only
* whole unit words are ('hours'), so 'sec' stays as written.
*
* Port: N/A.
+6 -6
View File
@@ -1,12 +1,12 @@
/**
* @fileoverview The Git fetch interval scheduler (src/git-fetch-scheduler.ts): which sessions a tick runs,
* and which it must not. Every rule is checked by calling `tick(now)` with an explicit clock, so nothing here
* depends on fake timers or on how long a real tick takes — the scheduler owns no timers itself.
* depends on fake timers or on how long a real tick takes: the scheduler owns no timers itself.
*
* The run belongs to the FOLDER, so these tests put two sessions on one folder and expect one fetch that
* both see, and they check the tick's two bounds separately: one shared bound for the fetches of the whole
* tick, and the folders bounded by a bound that cannot be the same semaphore. A folder is due when ANY session
* open on it is due, so the order the sessions were read cannot decide it — that is checked in both orders.
* open on it is due, so the order the sessions were read cannot decide it, and that is checked in both orders.
* A run carries the trigger that caused it, and what is published says the run's, not the tick's.
*
* Port: N/A.
@@ -62,7 +62,7 @@ type Harness = {
/** The folders the tick actually ran, in the order they were picked, with the trigger the tick passed. */
calls: Array<{ cwd: string; opts: GitFetchOptions; trigger: GitFetchTrigger }>;
published: Array<{ id: string; status: GitFetchStatus }>;
/** Every path the tick probed, in order — the probe has to happen before anything is fetched. */
/** Every path the tick probed, in order: the probe has to happen before anything is fetched. */
probed: string[];
settings: GitFetchSettings;
dockerReads: number;
@@ -247,7 +247,7 @@ describe('who runs', () => {
expect(h.calls.length).toBe(4);
});
it('runs a session that turned itself on even while the app-wide switch is off — that is what a per-session switch has to mean', async () => {
it('runs a session that turned itself on even while the app-wide switch is off: that is what a per-session switch has to mean', async () => {
const h = harness([VIEW('plain'), VIEW('own', { gitFetchEnabled: true, gitFetchIntervalSeconds: 30 })], {
settings: { enabled: false, intervalSeconds: 300 },
});
@@ -355,7 +355,7 @@ describe('one folder, one run', () => {
expect(await h.scheduler.tick(15_000)).toBe(0);
expect(h.published).toEqual([]);
// The short session is past its own interval, so the folder is fetched — once, for both. The run that
// The short session is past its own interval, so the folder is fetched (once, for both). The run that
// happens covers the long session's interval too, and both still get it because both are open on the
// folder: publishing only the session that made the folder due would leave the other tab on a stale row.
h.published.length = 0;
@@ -410,7 +410,7 @@ describe('one folder, one run', () => {
{
// Every folder is handed the SAME limiter for its work, so that work cannot get its own bound: a
// per-call bound would let 5 folders × 6 items run at once. The folders themselves are bounded by a
// DIFFERENT bound, because one semaphore cannot serve both levels — a folder holding a slot and then
// DIFFERENT bound, because one semaphore cannot serve both levels: a folder holding a slot and then
// waiting for one for its own fetch is waiting on itself, and the folders that fill the bound are
// exactly the ones blocking it. If the tick used one semaphore for both, this test would hang and fail
// as a timeout rather than as an assertion.
+11 -11
View File
@@ -1,24 +1,24 @@
/**
* @fileoverview Tests for the Git panel's Fetch button (src/git-fetch.ts): the only git command the Git panel
* runs that writes, and the only one that can run without being asked — which is why it is a click, or an
* runs that writes, and the only one that can run without being asked, which is why it is a click, or an
* interval the user turned on, and why the writing and the hooks it can run are pinned here. Cloning and
* worktree management write too, but they write when the user asked.
*
* Four halves, mirroring the module:
*
* 1. The PURE half — the fixed argv, the `-c` overrides the runner adds ahead of it, the ssh environment the
* 1. The PURE half: the fixed argv, the `-c` overrides the runner adds ahead of it, the ssh environment the
* runner refuses to override (`fetchEnvFor`), the failure classification, and the two reads the interval
* scheduler depends on: what the settings say (`gitFetchSettingsFrom`) and what a run did
* (`gitFetchStatusFrom`). No spawning.
* 2. The IO half — a REAL git fetching a REAL local bare repo, so that "remote-tracking refs only" is
* 2. The IO half: a REAL git fetching a REAL local bare repo, so that "remote-tracking refs only" is
* proven against git's actual behavior: HEAD, the index and the working tree must be exactly what they
* were while the remote-tracking ref moves. The rest of the half uses an injected runner over real
* repositories on disk, because what is being pinned is WHICH repositories git is allowed to touch.
* 3. The process group — a timed-out fetch has to take its `git-remote-http` helper with it, against a
* 3. The process group: a timed-out fetch has to take its `git-remote-http` helper with it, against a
* server that accepts and never answers, so the assertion is about what is still RUNNING after the answer
* went out. A test that only checked the returned error would pass with the old `execFile` runner, which
* signals the parent only.
* 4. The join (`fetchWorkspaceOnce`) — one run per folder at a time, because two fetches in one clone
* 4. The join (`fetchWorkspaceOnce`): one run per folder at a time, because two fetches in one clone
* collide on git's ref lock, the bound is ONE limiter shared with the scheduler's tick rather than a bound
* each run gets for itself, and the trigger a join reports is the joined run's, not the joiner's.
*
@@ -311,7 +311,7 @@ describe.skipIf(!gitPresent)('fetchGitWorkspaceOverview (real git)', () => {
git(work, 'push', '-q', 'origin', 'main');
// A commit made elsewhere and pushed: work's origin/main cannot hear about it until a fetch runs.
// This has to be a clone of origin, not a fresh init — a commit on its own root commit is no
// This has to be a clone of origin, not a fresh init: a commit on its own root commit is no
// descendant of what origin already holds, so pushing it is rejected as non-fast-forward.
const other = join(dir, 'other');
git(dir, 'clone', '-q', origin, 'other');
@@ -441,7 +441,7 @@ interface Proc {
}
/**
* Every process on the machine, as /proc reports it — so this half can only run on Linux, and the fields are
* Every process on the machine, as /proc reports it, so this half can only run on Linux, and the fields are
* counted from the closing parenthesis because `comm` can hold spaces.
*
* Membership of the GROUP is what is checked, not parentage: a helper that outlives its parent stops being a
@@ -496,8 +496,8 @@ describe.skipIf(!gitPresent)('runFetch (the whole tree, not just the git parent)
const answered = runFetch(repo, buildFetchArgs(), { timeoutMs: 1_500 }).catch((e) => e);
// The tree has to be a tree before the timeout, or nothing is being proven: git itself, and the helper
// it spawns for an http remote. The helper is a grandchild — `git fetch` runs `git remote-http`, which
// spawns `git-remote-http` — so the group, not the parentage, is what proves it. Each process is
// it spawns for an http remote. The helper is a grandchild (`git fetch` runs `git remote-http`, which
// spawns `git-remote-http`), so the group, not the parentage, is what proves it. Each process is
// remembered once found rather than re-scanned: after the kill the group is empty, and a test that kept
// looking would report a tree it had already destroyed. The `git config` read the runner does first is
// a `git` too, so the fetch is found by what it was asked to do.
@@ -527,7 +527,7 @@ describe.skipIf(!gitPresent)('runFetch (the whole tree, not just the git parent)
expect(err.code).toBe('ETIMEDOUT');
expect(err.killed).toBe(true);
// The answer went out before the tree died — that is the whole point of the fix, and it is why the
// The answer went out before the tree died: that is the whole point of the fix, and it is why the
// escalation is given its own time here instead of being cancelled when the promise settles.
const deadline = Date.now() + FETCH_KILL_ESCALATION_MS + 2_000;
while (Date.now() < deadline && scanProc().some((p) => p.pgrp === parent!.pgrp)) await sleep(50);
@@ -631,7 +631,7 @@ describe.skipIf(!gitPresent)('fetchWorkspaceOnce (the join)', () => {
const first = fetchWorkspaceOnce(folder, { git: firstRunner }, 'interval');
await runStarted; // the run is going, and still going
// A click while that run is working the folder: it joins, so it never touches git at all — not even the
// A click while that run is working the folder: it joins, so it never touches git at all, not even the
// resolver's own probe, because it does not resolve anything for itself.
let joinerCalls = 0;
const joinerRunner: GitRunner = async () => {
+3 -3
View File
@@ -523,7 +523,7 @@ describe('POST /api/sessions/:id/git-fetch', () => {
const { app } = await setup({ git: runner });
const data = (await app.inject({ method: 'POST', url: '/api/sessions/test-session-1/git-fetch' })).json().data;
expect(data.state).toBe('not-a-repo');
// The resolver looked once and found nothing, so nothing was fetched — and never guessed a repository.
// The resolver looked once and found nothing, so nothing was fetched, and it never guessed a repository.
expect(fetches()).toEqual([]);
});
@@ -564,7 +564,7 @@ describe('POST /api/sessions/:id/git-fetch', () => {
const second = app.inject({ method: 'POST', url: '/api/sessions/test-session-1/git-fetch' });
// Assert the join BEFORE releasing the run: the lane entry goes away when the run finishes, so releasing
// first would let the run complete before this request reached the lane, and it would then be entitled to
// start its own fetch — a test that passes for the wrong reason. One tick of the loop is enough for both
// start its own fetch: a test that passes for the wrong reason. One tick of the loop is enough for both
// handlers to reach the lane while the run is still held open.
await new Promise((resolve) => setImmediate(resolve));
expect(fetches).toHaveLength(1); // one run, and the second request is waiting on it, not working
@@ -593,7 +593,7 @@ describe('POST /api/sessions/:id/git-fetch', () => {
const { app, ctx } = await setup({ git: runner });
// The interval tick got to this clone first. The click joins it, so the row the other tabs see has to name
// the interval — the user clicked, but nothing this click did was a fetch. The request is left open, not
// the interval: the user clicked, but nothing this click did was a fetch. The request is left open, not
// awaited: awaiting it here would wait on the run this test is holding, and the test would wait on itself.
const intervalRun = fetchWorkspaceOnce(dir, { git: runner }, 'interval');
await runStarted;
@@ -1,6 +1,6 @@
/**
* `POST /api/sessions/:id/git-fetch-settings` (session-routes.ts): the per-case half of the fetch interval
* (src/git-fetch-scheduler.ts) — the case's own switch and its own interval. `null` on either half clears
* (src/git-fetch-scheduler.ts): the case's own switch and its own interval. `null` on either half clears
* that half so the app-wide setting applies again, and a body that names one row leaves the other as it was,
* because the Session Options rows save on their own. Refused for remote and Docker cases, the same rule the
* Git panel and the scheduler use. app.inject(), no port.
+1 -1
View File
@@ -105,7 +105,7 @@ describe('Session Options modal structure', () => {
expect(update).toContain('set-row-disabled');
expect(update).toContain('el.disabled = follows');
// Decided from BOTH fields: a half-choice means the session is not following, and the half that still
// follows shows the app's value while the other shows its own — a switch-only check would gray out a live row.
// follows shows the app's value while the other shows its own; a switch-only check would gray out a live row.
expect(update).toMatch(
/follows = session\.gitFetchEnabled === undefined && session\.gitFetchIntervalSeconds === undefined/
);
+1 -1
View File
@@ -1,4 +1,4 @@
// Port: none (a pure function in a vm context — no browser, no server).
// Port: none (a pure function in a vm context: no browser, no server).
//
// File paths with spaces in them (`OneDrive - Contoso/…`, `90 Sensitive/…`) were
// cut at the first space by every path pattern, so the terminal and the response
+1 -1
View File
@@ -1,4 +1,4 @@
// Port: none (pure functions + the link provider in a vm context — no browser, no server).
// Port: none (pure functions + the link provider in a vm context: no browser, no server).
//
// Absolute file paths codex word-wrapped inside a markdown table cell were not
// clickable: the first fragment has no extension and the continuation has no
+1 -1
View File
@@ -1,4 +1,4 @@
// Port: none (pure functions + the link provider in a vm context — no browser, no server).
// Port: none (pure functions + the link provider in a vm context: no browser, no server).
//
// Terminal URL links that stopped short.
//