Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Toolchain upgrades must preserve discovery, coverage and consumer behavior

Date: 2026-10-27. Issue #185; source based on reviewed auth stack `d83fd9a`.
Searches of the symptom/security indices found dependency-scope and consumer
lessons but no current Vite/Vitest migration procedure.

## Evidence

The initial Vite 7.3.7 / Vitest 4.1.12 build failed DOM matcher types. A nested
runner layout prevented the matcher augmentation resolving its consumer. The
supported Vitest matcher import and deduplication fixed the build. Unrelated
lint patch drift was restored rather than accepted as part of the upgrade.

`npm run test` initially reported 237 server cases rather than 211: Vitest 4
also discovered the built copies of 26 source tests. The explicit dist exclusion
restored exactly the tracked source file set. Discovery with and without build
outputs is identical: core 3, server 16, MCP 11, web 45. Coverage explicitly keeps
unimported source and the executable offline-shell plugin at zero hits.

Node 22 and 24 coverage exposed expensive editor queries and asynchronous test
proof solving. The five-second editor and 20-second CAPTCHA deadlines were
retained. Direct accessible-label queries preserve all editor assertions; a
native SHA-256 search still solves the actual signed 100,000-bound challenge.
A local CPU-protected database run failed six cases on the busy host; a separate
CI-equivalent run passed all 136, without claiming production CPU acceptance.
All earlier failures remain under the dated evidence directory on the test host.

Current harness: 192/192; actual unified web adapter: 353/353. An owned browser
contract exercises real HMR/state, HTTP/WebSocket proxying, secret denial and
compiled offline loading. Supported-Node coverage is a required CI matrix check.
See [the migration evidence and limits](../../docs/quality/TOOLCHAIN_MIGRATION_2026-10-27.md)
for commands, versions, authoritative references and remaining work.

`node --test tests/lib/adapters/execution-evidence.test.mjs` initially failed
the new optional-skip case: real Vitest 4 reports an explicitly skipped assertion
inside `numPendingTests`. The adapter now reconciles that count against actual
skipped assertions while still rejecting unfinished pending assertions, required
skips and counter contradictions. Real installed-runner controls separately
reported pass, failure, required-skip failure and optional skip, with zero
passes for either skip. The complete harness then passed 192/192.

## Cause and corrective rule

A successful install or lower audit count does not preserve runner behavior.
Upgrade runner, coverage, transformer and Vite as compatible consumers. Compare
actual source discovery, inspect zero-hit coverage, exercise real reporter output
through the strict harness, and test worker/database and browser behavior. Never
count compiled duplicates, skipped required cases or earlier failed attempts as
passes. Do not replace timeouts with retries to mask migration failures.

## Limits

This is a toolchain slice, not runtime-image security clearance. Turbo and
Mermaid/KaTeX remain separately scoped; image packaging/scanning, hosted acceptance,
main promotion and deployment are unfinished. Native browser/database checks
used owned Linux fixtures. Final exact-head review and CI remain required.
1 change: 1 addition & 0 deletions .agents/issues/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

| Symptom | Canonical lesson and regression |
| --- | --- |
| A toolchain upgrade changes test counts, loses matcher types or drops unimported coverage | [Preserve discovery and consumer behavior](../inbox/2026-10-27-toolchain-upgrades-must-preserve-test-contracts.md); [migration evidence](../../docs/quality/TOOLCHAIN_MIGRATION_2026-10-27.md) |
| OAuth links an unverified email, changes accounts after a provider email change, or leaves an orphan user after a link failure | [Provider proof and isolated atomic linking](../inbox/2026-10-27-oauth-linking-needs-provider-proof-and-atomicity.md); `oauth-identity`, `oauth-profiles` and actual `oauth-strategies` tests |
| Login and reset links cross-use, or a reset/reactivation revives old sessions | [Bind purpose and session revision at the write](../inbox/2026-10-27-email-purpose-and-revision-are-auth-boundaries.md); real SQLite/HTTP/WebSocket `auth-link-boundary.test.ts` |
| CAPTCHA widget is visible but an immediate verification event leaves proof empty | [Attach widget listeners during commit](../inbox/2026-10-27-widget-events-need-commit-phase-listeners.md); `AuthCaptcha.test.tsx` Profiler regression |
Expand Down
1 change: 1 addition & 0 deletions .agents/issues/security-audit-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

| Symptom | Evidence and corrective rule |
| --- | --- |
| A toolchain upgrade changes test counts, loses matcher types or drops unimported coverage | [Preserve discovery and consumer behavior](../inbox/2026-10-27-toolchain-upgrades-must-preserve-test-contracts.md); [migration evidence](../../docs/quality/TOOLCHAIN_MIGRATION_2026-10-27.md) |
| OAuth email metadata loses verification or callbacks log credential-bearing redirects | [Provider proof and isolated atomic linking](../inbox/2026-10-27-oauth-linking-needs-provider-proof-and-atomicity.md); [provider/recovery contract](../../docs/quality/OAUTH_IDENTITY.md) |
| Login and reset links cross-use, or a reset/reactivation revives old sessions | [Bind purpose and session revision at the write](../inbox/2026-10-27-email-purpose-and-revision-are-auth-boundaries.md); real SQLite/HTTP/WebSocket `auth-link-boundary.test.ts` |
| CAPTCHA validates only a client string's format or skips missing GraphQL proof | [Server policy, signed action/expiry and bounded replay](../inbox/2026-10-27-captcha-proof-must-be-server-issued.md); `packages/server/tests/captcha*.test.ts`, `qc:captcha` |
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ jobs:
run: npm run build
- name: Install Playwright (chromium)
run: npx playwright install --with-deps chromium
- name: Vite proxy, hot reload and offline-shell contract
run: npm run test:toolchain
- name: Start GraphDone (dev stack)
run: |
npm run dev > /tmp/dev.log 2>&1 &
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/comprehensive-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- name: Install dependencies
run: npm ci --legacy-peer-deps
- name: Run all unit suites (core, web, server, mcp-server)
run: npm run test && npm run test:unified:lib
run: npm run test:coverage && npm run test:unified:lib

# Genuinely-real checks: the installer exists, deployment compose parses
installer-and-deploy-config:
Expand Down
119 changes: 119 additions & 0 deletions docs/quality/TOOLCHAIN_MIGRATION_2026-10-27.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Patched build and test toolchain — 2026-10-27

This is the compatible-toolchain slice of #185. Runtime-image packaging and
Turbo/Mermaid updates remain separate. This evidence does not establish image,
Windows, hosted-backend or deployment clearance.

## Versions and compatibility

| Consumer | Previous lock | Reviewed line |
| --- | --- | --- |
| Vite, declared in all four tested packages | 5.4.21 | 7.3.7 |
| React transform | 4.7.0 | 5.2.0 |
| Vitest and matching V8 coverage | 1.6.1 | 4.1.12 |
| Worker implementation | tinypool 0.8.4 | Vitest 4 native pool; tinypool removed |

Node 22.18 and 24 satisfy the installed engine/peer contracts. Vite 7 and
Vitest 4 are patched compatible lines; this change does not require adopting
Vite 8's different bundler. All locked and actually resolved copies are guarded
by `tests/lib/dependencies/toolchain.test.mjs`. The guard fails ten cases against
the old lock/install, with one compatibility control still passing.

Authoritative references:

- [Vite 7 migration](https://v7.vite.dev/guide/migration): explicit build targets
retain ES2020, Chrome 87, Edge 88, Firefox 78 and Safari 14.
- [Vite non-Windows file boundary advisory](https://github.com/advisories/GHSA-rq7h-c2jc-7f22):
7.3.7 is patched; network exposure and path conditions matter.
- [Vite Windows alternate-path advisory](https://github.com/advisories/GHSA-fx2h-pf6j-xcff):
the selected line also contains its fix; this is not a Windows execution test.
- [Vitest UI advisory](https://github.com/advisories/GHSA-5xrq-8626-4rwp):
4.1.0 and later on this line are patched; ordinary run mode does not prove UI exposure.
- [Vitest 4 migration](https://v4.vitest.dev/guide/migration): worker pool,
test discovery and coverage behavior require explicit migration.
- [Tinypool advisory](https://github.com/advisories/GHSA-85c8-ppgw-ccpr):
removing its actual dependency paths avoids replacing a worker major under Vitest 1.

## Preserved contracts

- Explicit `**/dist/**` exclusion prevents compiled server tests being counted
twice. Exact discovered files match tracked sources both with and without
compiled outputs: core 3, server 16, MCP 11, web 45.
- Coverage explicitly includes every package's source plus executable
`packages/web/offline-shell.ts`. Unimported modules remain zero-hit entries;
tests and compiled output are excluded. V8's new mapping changes percentages,
so compare scope and exercised behavior rather than equating old percentages.
- MCP remains isolated forks, one worker, no parallel test files. Web/server
use two workers; existing test deadlines and retry policy are unchanged.
- DOM matcher setup uses the Vitest entry point. Lock deduplication makes its
augmentation resolve the same installed runner; unrelated ESLint patches and
their placements were restored.
- The editor lifecycle fixture queries the controls' explicit accessible labels
and still asserts native button semantics, all writes, pending refetches,
drafts and undo snapshots. It avoids repeatedly computing roles across the
entire dialog during the same five-second test.
- The HTTP CAPTCHA fixture searches the actual signed SHA-256 challenge using
native hashing with the unchanged 100,000 bound. Server verification, action
binding, replay rejection and the 20-second deadline remain unchanged. Real
browser acceptance still uses ALTCHA's browser solver.

## Validation and retained failures

Pinned clean install, build, types and lint passed. Current Node 22 coverage
runs pass core 41, server 211, web 353 and MCP 110; the 26 explicitly opted-in
MCP database cases are not counted as passes in ordinary unit mode. The actual
owned Neo4j run passed all 136 MCP cases with the existing CI CPU-monitor mode.

Node 24 core/server/MCP coverage passed; current web coverage passed all 353
cases after correcting the fixture costs below. Final branch CI runs coverage
for both supported Node versions and remains required before integration.

The current harness passes 192/192. The real unified web adapter passes 353
with no skips or warnings. The installed runner's real pass/fail/skip JSON is
also checked through the adapter. Vitest 4 includes explicitly skipped assertions
in `numPendingTests`; the adapter accepts that documented-by-output shape and
the prior zero-pending shape, while unfinished assertions and required skips
still fail. Optional skips remain skipped and never become passes.

`npm run test:toolchain` uses an owned browser/backend/build fixture and proves
React hot reload preserves state and document identity, HTTP and GraphQL
WebSocket proxy paths, denied `.env` content, a compiled offline shell that
works offline, and exclusion of API/auth responses from shell caching. It runs
as a required Smoke Gate step. The normal application, LOD, mobile and runtime
checks remain required too.

Retained failures and corrections:

1. Initial build lost DOM matcher types with nested runner installations;
the supported matcher import and deduplicated runner fixed resolution.
2. Initial Vitest 4 discovery ran 26 compiled duplicate server cases;
explicit dist exclusion restores source-only discovery, not fewer source tests.
3. Unbounded coverage hit the editor fixture's five-second budget. Bounded
workers passed on Node 22, but Node 24 still exposed costly repeated role
queries. Direct accessible-label queries preserve all lifecycle assertions;
the corrected 18-case file and complete web suite passed with unchanged budgets.
4. Node 24 HTTP CAPTCHA coverage hit 20 seconds while using the asynchronous
WebCrypto search loop. The independent native-hash fixture solver passed all
four cases without changing the issued work factor or production code.
5. A local real-DB run was blocked in six cases by the host CPU monitor at
95–100% load. The full 136-case CI-mode run passed; this proves CI parity,
not production CPU-exhaustion behavior. The failed run is retained separately.
6. Initial dependency guards assumed a coverage-package engine field and an
exported React-plugin manifest. Corrected guards check the actual contracts;
they do not weaken supported-runner or version checks.

7. Real Vitest 4 intentional-skip output initially produced a counter mismatch
for an optional sequence. Explicit skipped-assertion accounting corrected
it; the regression failed before the fix. Real pass/failure/required-skip/
optional-skip controls now preserve all four outcomes.

Fresh isolated-cache audit: full inventory has three residual package entries
(Turbo moderate, Mermaid/KaTeX low); production-only has zero. Historical counts
remain dated evidence, not distinct exploit counts or a claim about why registry
metadata changed. Runtime images can still contain development packages and
require the separate packaging and image inventory work in #185.

Vite's existing all-interface development binding is retained for the repo's
LAN/container workflows. Keep access within an intentionally trusted development
network or authenticated tunnel; do not expose Vite/Vitest UI/API as a public
production service. No host allowlist, credential scope or deployment is changed.
Loading
Loading