Skip to content

chore(release): merge v2/main into main for v2.9.0 - #2536

Merged
cliffhall merged 187 commits into
mainfrom
v2/chore/milestone-merge-v2.9.0
Sep 30, 2026
Merged

cliffhall merged 187 commits into
mainfrom
v2/chore/milestone-merge-v2.9.0

Conversation

@cliffhall

@cliffhall cliffhall commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Closes #2530

Milestone merge of v2/main into main for the v2.9.0 release — step 2 of the release skill.

This is a pure merge

No commits of its own, only merge commits:

$ git rev-list HEAD --not origin/main origin/v2/main
87bb185e chore: merge v2/main into main for the v2.9.0 release
36f3d819 chore: merge v2/main into main for the v2.9.0 release
e78127a5 chore: merge v2/main into main for the v2.9.0 release
#   three merges: two smoke findings (#2534, #2535) and a CodeQL finding (#2541), each fixed on v2/main and re-merged

$ git rev-parse origin/v2/main^{tree}
fa73c5946a6cc4f3f92cb2c701c57791e807bc32
$ git rev-parse HEAD^{tree}
fa73c5946a6cc4f3f92cb2c701c57791e807bc32

The merged tree is byte-identical to origin/v2/main. Version on the tree: 2.9.0 (bumped on v2/main in #2528 / PR #2529).

Verification

Ledger: https://claude.ai/artifact/3cWteCtohesQrCSgujccvn (maintainer review artifact; one row per closed milestone issue with what was run and what was observed)

  • npm run local:gate → green (exit 0, 4m54s) on the final tree
  • npm run pack:verify → OK (23 files, 4.53 MB unpacked)
  • npm audit → 0 vulnerabilities in all five installs
  • 42 / 42 completed milestone issues driven from the production build

Found by the smoke, fixed on v2/main

Follow-up filed for v2.10.0: #2533 (same opener defect in the TUI and web launcher).

🤖 Generated with Claude Code

BobDickinson and others added 30 commits September 23, 2026 17:03
Every process (web backend, daemon, CLI) that persists OAuth state used to
flush its whole in-memory snapshot over the shared oauth.json — so a writer
holding a stale snapshot erased entries other processes wrote after it last
read the file (observed live: a background EMA flow wiping a fresh login).

Every write already enters through a mutation scoped to named entries, so
persistence now names what changed and merges only that:

- oauth-persist.ts: OAuthPersistSections + pure mergeOAuthSections (named
  servers/idpSessions keys overlaid onto a fresh read; absent = deletion),
  parseOAuthPersistSections for the wire form; backends accept an optional
  sections arg; the remote backend forwards it as a ?sections= query param.
- oauth-storage.ts: persist(sections) snapshots inside the queued closure
  (fresh at write time); every mutation passes its sections, with the
  enterprise-managed sweep capturing its URLs before clearing them.
- oauth-persist-file.ts: shared writeOAuthSections = cross-process file
  lock -> fresh read -> merge -> atomic write; lock failures rethrown with
  OAuth wording and the original as cause.
- remote server storage route: sectioned POSTs apply the same shared locked
  merge (400 on bad descriptors or non-OAuth bodies); plain POSTs and the
  client store are unchanged.
- cli.ts refreshStoredAuthToken: its hand-rolled read-modify-write now
  persists through writeOAuthSections — same lock, same merge, merged
  against the file at write time.

Memory is deliberately not refreshed from the merged result: overwriting it
could revert concurrent in-process mutations, and reads staying cached is
fine — correctness comes from the per-mutation read-modify-write.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Bob Dickinson <bob.dickinson@gmail.com>
…opening a PR (#2464)

AGENTS.md now requires the loop, unprompted, after every PR, and requires
findings to be weighed against the issue with scope expansion declined.

pr-flow step 7 becomes the loop: request, wait for a posted review or for
Copilot's pending request to lapse without one (plus a 25-minute cap), then
a stop/continue table. The first clean round is the stopping point; no
confirming round. The description leads with "create a PR for an issue" and
eval cases pin that prompt shape.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
 review)

A timeout fires only while the request is still pending, and requestReviews
with union:true is a no-op for an already-requested bot, so a retry would
start nothing new.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…#2464)

Closing keywords do not link a PR whose base is not the default branch, so
a v2 card showed no linked PR. The GraphQL mutation adds a manual closing
reference, which populates the card's Linked pull requests field. Verified
on #2466/#2464 with the manual link removed first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…ot-review-loop

docs(pr-flow): make an exhaustive Copilot review loop the step after opening a PR
Split oauth.json persistence so secret material (acquired tokens,
client secrets, IdP session tokens) is written to the OS secret store
while non-secret residue stays in the file:

- New core/auth/node/oauth-secrets.ts: pure split/join/policy module
  mapping server entries to oauth:<serverUrl> fields (per-issuer and
  legacy tokens/client-secret/prereg-client-secret) and IdP sessions
  to oauth-idp:<issuer>.
- Rewrite oauth-persist-file.ts: writeOAuthSections splits secrets to
  the store, readOAuthStore joins them back (store wins over file
  plaintext) and lazily migrates plaintext secrets when the store is
  durable, removeOAuthStore purges store entries. Store write failures
  degrade to memory-only with a once-per-reason warning; secrets are
  never written back to the file.
- New MCP_INSPECTOR_PERSIST_TOKENS=all|access|none knob controlling
  which acquired tokens persist (write-side; registration client
  secrets always persist). Invalid values warn and default to all.
- Remote storage routes special-case the oauth store (sectioned and
  full-replace writes via locked merge+split, purge on DELETE);
  sectioned writes on other stores are rejected.
- CLI reads stored auth via the joined readOAuthStore so migrated
  tokens remain visible to --wait-for-auth and refresh.
- Pin MCP_INSPECTOR_SECRET_STORE=memory in web/cli test configs so
  tests never touch the real OS keychain.
- Docs: environment-variables.md and secret-storage.md updated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Bob Dickinson <bob.dickinson@gmail.com>
Step 1 moved the card to In Progress but never assigned the issue, so a card
could sit in progress with nobody on it. gh's @me resolves to the
authenticated user, so an agent assigns the maintainer it works for. The
AGENTS.md 'When work begins' rule says the same.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…ow-self-assign

docs(pr-flow): assign the issue to yourself when work starts
#2404)

The stable ext-skills spec settles the question SEP-2640 left open:
GetSkillResult extends CacheableResult, so ttlMs and cacheScope are
REQUIRED. Add ModernGetSkillEnvelopeSchema and select it from the
negotiated era in InspectorClient.getSkillResult, mirroring skills/list.
Legacy results stay permissive. Update the "left open" comments and the
roadmap's open-gap note.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…nders Apps (#2403)

InspectorClient advertised io.modelcontextprotocol/ui by default in every
client, so the CLI and TUI told servers they support MCP Apps although they
cannot render one. The UI registry entry now requires an App renderer: its
default applies only when the new `rendersApps` option is set (or an
app-elicitation renderer is supplied). The web client sets it; the CLI and
TUI no longer claim the extension. An explicit advertisedExtensions override
still wins, and the CLI exposes it as `--advertise-apps` for probing servers
that gate App tools on the advertisement.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
A reader that exits early (| head, | grep -m1, quitting less) made the
next stdout write fail with EPIPE, and with no 'error' listener on
stdout that was an uncaught event that crashed the CLI. The stream path
now listens on stdout for its lifetime: EPIPE unsubscribes and resolves,
any other stdout error unsubscribes and rejects into the CLI error path,
and every listener is detached on each exit, including start() throwing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
A non-conforming server can send a form-encoded secret with an
un-escaped '&', which split the value into a masked key=prefix pair
plus '='-less tail segments that were shown verbatim in the Network
tab. Fold every '='-less segment following a masked pair into its
placeholder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…-get-cacheable

fix(skills): require ttlMs and cacheScope on modern skills/get results
…with a check (#2470)

Step 1 gave a command for the assignment only, and step 6 ended on a
trailing "Move the card to In Review" sentence, so agents ran the code
block and skipped the card move. Both steps now carry a block that moves
the card and ends by printing its Status; the step is done only when it
prints the target column.

Every id is resolved by name at run time (project, Status field, option)
and the card is found through issue.projectItems, so no /board-ops option
id is copied and a recreated option still resolves.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…2405)

`--verify` gave a `resources: "dynamic"` skill the same verdict as one whose
every file hashed clean: `outcome: "verified"`, `ok: true`, exit 0 — though
nothing was hashed.

- core: a fourth `outcome`, `unverifiable` (SEP-2640's own word for it),
  below `failed` and `incomplete` in precedence; `ok` stays true since
  "dynamic" is a conforming wire form. New `anySkillUnverifiable` helper.
- cli: the headline no longer says "Verified" for such a skill and names how
  many advertised no digests; new opt-in `--require-digests` exits 9
  (`skills_unverifiable`) for a CI job standing in for a host that declines
  unverifiable skills. Exit-code selection moved into one helper shared by
  skills/list and skills/get. Default exit stays 0.
- tui: the Skills pane says UNVERIFIABLE instead of Verified.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…e manifest (#2474 review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot's testing -> test-servers hand-off on the pagination prompt missed
because the first move was never `testing` (a grep, or an issue-triage
detour) — never a loaded `testing` that failed to follow its pointer. So
the description, not the body, was the lever: it now opens with writing a
test or end-to-end/integration coverage of an MCP operation.

Also tells a caller arriving at test-servers that every showcase-config
field (maxPageSize included) is a createTestServerHttp option, and brings
AGENTS.md's listing-cost figures up to 3,900/4,000.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…#2471 review)

Web set rendersApps unconditionally, so with no sandbox URL — where the
Apps screen reports that MCP Apps cannot run — it still advertised the UI
extension. Gate it on the confirmed sandbox URL, as appElicitation already
is. A Server Settings override can still force the extension on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Waiters used to race: every one polled the lock and whichever poll landed
first after a release won, so the gate queued longest could lose race
after race to newer arrivals and hit the 45-minute total budget while
they ran. Each waiter now writes an arrival-time ticket to queue/ beside
the lock, and only the head of the line asks for the lock. Dead tickets
(process gone, or unrefreshed for STALE_MS) are pruned by the next
waiter; a live waiter whose ticket was pruned restores it under the same
name and keeps its place. An unwritable queue costs only the ordering.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…c-skill-unverifiable

fix(skills): report a "dynamic" skill as unverifiable, not verified
…raw JSON fields (#2477 review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…2476 review)

A waiter behind a live ticket can find the lock free while the head is
between polls; the waiting, progress and give-up lines then named a
holder that did not exist. describeWait names the holder only while the
lock is held. Also make the FIFO test wait for the clock to pass b's
arrival before starting c, since both share a pid and a same-millisecond
ticket would be ordered by its random suffix.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…ask-unescaped-amp

fix(web): mask the tail of a form secret split by a raw &
Both relay waits (browser RemoteClientTransport and Node RemoteSession)
are keyed by the numeric request id the SDK stamps as progressToken. A
server that echoes the token back as a string ("4") missed the Map
lookup, so progress never re-armed the flat 60s relay deadline and the
web client timed out a call a direct transport keeps alive.

Add waitForProgressToken, which tries the exact key and then falls back
to Number(token) for a string token, matching the SDK Protocol's own
Number(progressToken) correlation. Both relays use it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…apps (#2471 review)

The Server Settings form resolved the UI extension's checkbox from the raw
registry default, so with no sandbox it showed "advertised" although the
client no longer declares it — and the modal's reconverge-to-default logic
made a true override impossible to save. Both now use the shared
isAdvertisedByDefault(ext, rendersApps), fed from App's sandbox URL.

The CLI rejected other connection-only flags ahead of its short-circuit
paths but accepted --advertise-apps there and ignored it; it is now
rejected on --list-stored-auth, --print-handoff and servers/list|show.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…ease-fifo

fix(gate-lease): serve queued local:gate runs in arrival order
cliffhall and others added 6 commits September 30, 2026 08:47
open() resolves with the ChildProcess as soon as it has called spawn(),
and a spawn failure (ENOENT: the opener missing from PATH) arrives
afterwards as an 'error' event on that child. open only listens for it
with { wait: true }, which on macOS adds `open -W` and waits for the
browser to quit, so it is unusable here. Unlistened, the event crashed
the CLI before #2410's fallback line could print.

openUrl now chains a listener directly onto open's promise: a microtask,
so it attaches ahead of the process.nextTick that emits 'error' or
'spawn'. It settles on 'spawn', rejects on 'error', and stays attached so
a later error is absorbed. The TUI and web launcher share the defect and
are tracked in #2533.

Closes #2531

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
From a timer or I/O callback Node drains process.nextTick before promise
reactions, and on macOS open reaches spawn() with no earlier await, so a
listener chained only on open's promise could attach after a spawn
'error' had already been emitted (Copilot, #2534). Calling open from
Promise.resolve().then(...) puts the spawn and the listener in the same
microtask drain whatever the caller's context. The new test enters from
setImmediate and failed before this change.

Refs #2531

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
joinSensitiveContinuations appended a continuation to the last entry in
out, so an empty segment kept in between (access_token=abc&&def) became
the fold target and the tail leaked as %26def= (Copilot, #2535). Track
the sensitive pair's index instead, matching the display masker's
existing behavior for this shape.

Refs #2532

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…en-spawn-error

fix(cli): catch an opener that cannot be spawned instead of crashing
…body-raw-ampersand

fix(core): redact a form secret's raw-& tail in recorded bodies
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Sep 30, 2026
Copilot AI balanced review requested due to automatic review settings September 30, 2026 13:38
Comment thread clients/cli/src/error-handler.ts Fixed
@cliffhall cliffhall linked an issue Sep 30, 2026 that may be closed by this pull request
5 tasks done

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Prototype-inherited server names remain accepted despite at least one bare map lookup treating an absent name as a configured server.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
What changed in this PR

Merges the complete v2.9.0 release tree into main, covering OAuth secret storage, MCP protocol fixes, client UX, dependency updates, and release/CI hardening.

Changes:

  • Moves OAuth secrets into dedicated secret stores with safer persistence.
  • Improves CLI, TUI, web behavior, diagnostics, and MCP extension handling.
  • Hardens release workflows, dependency checks, smoke tests, and packaging.
File Description
specification/​v2_storage.md Documents split-secret OAuth persistence.
specification/​v2_auth_ema.md Updates EMA persistence design.
scripts/​smoke-web-app.mjs Adds non-fatal console diagnostics.
scripts/​smoke-tui.mjs Enables hermetic TUI smoke in CI.
scripts/​smoke-cli.mjs Isolates secret storage and parses final error envelopes.
scripts/​sdk-watch.test.mjs Covers SDK devDependencies.
scripts/​sdk-watch.mjs Watches SDK packages across dependency sections.
scripts/​lib/​workflow-gate.test.mjs Updates TUI smoke expectation.
scripts/​lib/​workflow-gate.mjs Documents TUI CI execution.
scripts/​lib/​deep-link-connect.test.mjs Tests improved timeout diagnostics.
scripts/​lib/​deep-link-connect.mjs Reports the application’s connection error.
scripts/​dependency-refresh.test.mjs Tests SHA-pinned action tracking.
scripts/​dependency-refresh.mjs Ranks action SHAs using release comments.
scripts/​dependabot-alerts.mjs Fails safely on incomplete alert listings.
package.json Bumps v2.9.0, dependencies, and validation guards.
docs/​publishing.md Documents split package/publish jobs.
docs/​environment-variables.md Documents token-persistence policy.
Dockerfile Uses the dedicated mode-aware healthcheck.
core/​storage/​store-id.ts Reserves __proto__ store IDs.
core/​storage/​own-entry.ts Adds safe dynamic property helpers.
core/​react/​useServers.ts Documents fetch-function stability.
core/​mcp/​types.ts Adds MCP Apps renderer capability.
core/​mcp/​remote/​remoteClientTransport.ts Handles stringified progress tokens.
core/​mcp/​remote/​progressToken.ts Adds compatible progress-token lookup.
core/​mcp/​remote/​node/​remote-session.ts Re-arms waits for stringified IDs.
core/​mcp/​fetchTracking.ts Redacts raw-ampersand secret tails.
core/​auth/​revocation.ts Supports partial stored token grants.
core/​auth/​requestTimeout.ts Guards against reused request deadlines.
core/​auth/​remote/​storage-remote.ts Fixes OAuth storage to its shared endpoint.
core/​auth/​node/​storage-node.ts Shares storage load/write coordination.
core/​auth/​node/​secret-store-selection.ts Forwards strict bulk reads.
core/​auth/​node/​file-lock.ts Introduces a typed lock-contention error.
clients/​web/​vite.config.ts Prevents tests from touching host keychains.
clients/​web/​src/​utils/​transportHeaders.ts Compares effective wire headers.
clients/​web/​src/​utils/​transportHeaders.test.ts Tests header normalization and comparison.
clients/​web/​src/​utils/​toasts/​toastIds.ts Adds reconnect-toast IDs.
clients/​web/​src/​utils/​toasts/​toastIds.test.ts Tests reconnect-toast IDs.
clients/​web/​src/​utils/​maskSecrets.ts Masks malformed form-secret continuations.
clients/​web/​src/​utils/​maskSecrets.test.ts Covers malformed form bodies.
clients/​web/​src/​test/​setup.ts Configures React’s act environment.
clients/​web/​src/​test/​reactActEnvironment.test.ts Verifies the act flag.
clients/​web/​src/​test/​integration/​storage/​store-id.test.ts Covers prototype-like store IDs.
clients/​web/​src/​test/​integration/​storage/​own-entry.test.ts Tests safe property helpers.
clients/​web/​src/​test/​integration/​mcp/​remote/​remote-session.test.ts Tests string progress tokens.
clients/​web/​src/​test/​integration/​mcp/​inspectorClient-skills.test.ts Covers skill caching and verification outcomes.
clients/​web/​src/​test/​integration/​mcp/​inspectorClient-oauth-remote-storage-e2e.test.ts Adapts fixed OAuth storage endpoint tests.
clients/​web/​src/​test/​integration/​mcp/​inspectorClient-oauth-e2e.test.ts Verifies tokens are absent from plaintext files.
clients/​web/​src/​test/​integration/​mcp/​inspectorClient-ema-e2e.test.ts Verifies split EMA token storage.
clients/​web/​src/​test/​integration/​mcp/​extensions-mimetype.test.ts Enables renderer-aware extension testing.
clients/​web/​src/​test/​integration/​auth/​node/​storage.test.ts Tests coordinated split persistence.
clients/​web/​src/​test/​integration/​auth/​node/​secret-store-selection.test.ts Tests strict bulk-read forwarding.
clients/​web/​src/​test/​integration/​auth/​node/​file-lock.test.ts Requires confirmed secret deletion.
clients/​web/​src/​test/​core/​mcp/​remote/​remoteClientTransport.test.ts Covers relayed string progress tokens.
clients/​web/​src/​test/​core/​mcp/​remote/​progressToken.test.ts Tests progress-token matching rules.
clients/​web/​src/​test/​core/​mcp/​inspectorClient-skills.test.ts Enforces modern skill caching attributes.
clients/​web/​src/​test/​core/​mcp/​inspectorClient-client-info.test.ts Tests advertised client identity.
clients/​web/​src/​test/​core/​mcp/​inspectorClient-app-elicitation.test.ts Tests renderer-aware Apps advertisement.
clients/​web/​src/​test/​core/​mcp/​fetchTracking.test.ts Tests complete secret-tail redaction.
clients/​web/​src/​test/​core/​auth/​store.test.ts Covers prototype-key OAuth state.
clients/​web/​src/​test/​core/​auth/​storage-remote.test.ts Tests fixed remote OAuth endpoint.
clients/​web/​src/​test/​core/​auth/​storage-browser.test.ts Tests partial token handling.
clients/​web/​src/​test/​core/​auth/​revocation.test.ts Tests refresh-only revocation.
clients/​web/​src/​test/​core/​auth/​requestTimeout.test.ts Tests per-call deadline stamping.
clients/​web/​src/​hooks/​useServerJsonImport.test.tsx Flushes timer updates inside act.
clients/​web/​src/​hooks/​useServerCommands.test.tsx Extends persisted-settings test doubles.
clients/​web/​src/​hooks/​useOAuthRecovery.test.tsx Awaits async disconnect finalization.
clients/​web/​src/​hooks/​useLastPersistedSettings.ts Exposes pending settings writes.
clients/​web/​src/​hooks/​useLastPersistedSettings.test.tsx Tests pending-write tracking.
clients/​web/​src/​components/​screens/​PromptsScreen/​PromptsScreen.test.tsx Keeps debounce updates within act.
clients/​web/​src/​components/​screens/​AppsScreen/​AppsScreen.test.tsx Uses React-aware asynchronous waits.
clients/​web/​src/​components/​groups/​ServerSettingsModal/​ServerSettingsModal.tsx Applies renderer-aware extension defaults.
clients/​web/​src/​components/​groups/​ServerSettingsModal/​ServerSettingsModal.test.tsx Tests explicit Apps overrides.
clients/​web/​src/​components/​groups/​ServerSettingsForm/​ServerSettingsForm.tsx Updates extension and header behavior text.
clients/​web/​src/​components/​groups/​ServerSettingsForm/​ServerSettingsForm.test.tsx Tests renderer-aware toggles.
clients/​web/​src/​components/​elements/​Toasts/​Toasts.test.tsx Tests reconnect toast interaction.
clients/​web/​src/​components/​elements/​Toasts/​Toasts.stories.tsx Adds reconnect-toast story.
clients/​web/​src/​components/​elements/​Toasts/​HeadersReconnectToastMessage.tsx Adds saved-but-unsent header notice.
clients/​web/​package-lock.json Updates brace-expansion.
clients/​tui/​src/​utils/​bodyLines.ts Bounds displayed HTTP body size.
clients/​tui/​src/​components/​SkillsTab.tsx Displays unverifiable skill status.
clients/​tui/​src/​components/​RequestsTab.tsx Uses bounded body rendering.
clients/​tui/​src/​components/​HistoryTab.tsx Clarifies effect dependency suppression.
clients/​tui/​src/​components/​BodyLines.tsx Adds bounded body-line rendering.
clients/​tui/​package-lock.json Updates brace-expansion.
clients/​tui/​__tests__/​SkillsTab.test.tsx Tests unverifiable status.
clients/​tui/​__tests__/​BodyLines.test.tsx Tests body formatting and limits.
clients/​launcher/​README.md Documents TUI smoke behavior in CI.
clients/​launcher/​__tests__/​parse-launcher-argv.test.ts Covers Windows path forwarding.
clients/​cli/​vitest.config.ts Isolates tests from host keychains.
clients/​cli/​src/​open-url.ts Handles opener timeouts and spawn errors.
clients/​cli/​src/​handlers/​skills-verify.ts Adds unverifiable verification outcomes.
clients/​cli/​src/​handlers/​run-method.ts Centralizes skill verification exit codes.
clients/​cli/​src/​handlers/​method-types.ts Adds Apps and digest CLI options.
clients/​cli/​src/​handlers/​consume-outcome.ts Handles stdout pipe failures.
clients/​cli/​src/​cli-oauth-navigation.ts Reports browser-open failures.
clients/​cli/​__tests__/​skills-verify-cli.test.ts Tests digest requirements and exit envelopes.
clients/​cli/​__tests__/​cli-oauth-navigation.test.ts Tests manual browser fallback messaging.
clients/​cli/​__tests__/​app-info.test.ts Tests explicit Apps advertisement.
.github/​workflows/​sdk-watch.yml Pins actions in credentialed jobs.
.github/​workflows/​dependabot-alerts.yml Pins workflow actions to SHAs.
.claude/​skills/​testing/​SKILL.md Broadens testing skill triggers.
.claude/​skills/​test-servers/​SKILL.md Documents in-process resolved configurations.
.claude/​skills/​release/​SKILL.md Documents split release publishing.
.claude/​skills/​pr-flow/​evals/​evals.json Adds PR-flow trigger evaluations.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread core/storage/store-id.ts
Comment thread clients/web/src/test/core/auth/storage-browser.test.ts
@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 1: 2 findings, both confirmed, neither a regression or a release risk, and no change made on this branch (merge-PR rule: the tree must stay identical to origin/v2/main).

Since nothing changed, no further round was requested. Whether either should hold the release is a maintainer call.

redactUrlsInText stripped a URL's trailing sentence punctuation with the
unanchored regex /[.,;:!?)\]']+$/, which rescans a punctuation run from
every start position when the run does not end the match: quadratic.
The text is server-controlled (an HTTP error body lands in the error
message), so a body of http://a/? plus a long run of '!' before an 'x'
stalled the CLI's error path: 60k characters took 3s, against 0s on
2.8.0. CodeQL flagged it high on the v2.9.0 merge PR (#2536).

Replace it with a backward scan over the same character set. Behavior is
unchanged; the same probe now answers in ~180ms at 60k and at 400k.

Closes #2540

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…-trailing-punctuation

fix(cli): split trailing punctuation off redacted URLs in linear time
Copilot AI balanced review requested due to automatic review settings September 30, 2026 20:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

OAuth profiles can exchange credentials across state files, and dependency reports incorrectly claim SHA-pinned actions are untested.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (2)
Previously missed (1)

In code that hasn't changed since last review

Low severity Update report to reflect SHA-pinned dependency coverage

scripts/​dependency-refresh.mjs:175

The cleared report still states that commit-SHA refs are “deliberately not covered,” but this branch now ranks those refs from their exact # vX.Y.Z comments. Consequently a successful monthly sweep publishes a false limitation precisely when SHA-pinned updates were checked. Update the generated note and its existing assertion in dependency-refresh.test.mjs to describe the new coverage.

Comment thread core/auth/node/oauth-secrets.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Release 2.9.0 step 2: merge v2/main into main and cut the release

4 participants