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
28 changes: 20 additions & 8 deletions .github/workflows/build-ts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ name: build-ts

on:
workflow_dispatch:
schedule:
- cron: "17 8 * * *"
push:
branches: ["main"]
pull_request:
Expand All @@ -15,8 +17,8 @@ on:
branches: ["main"]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name != 'schedule' }}

permissions:
pull-requests: read
Expand All @@ -31,7 +33,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
os: ${{ github.event_name == 'schedule' && fromJSON('["macos-latest"]') || fromJSON('["ubuntu-latest", "windows-latest", "macos-latest"]') }}
version: ["22.23.4", "24.22.1", "26.12.1"]

runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -142,17 +144,27 @@ jobs:
working-directory: ts
run: |
pnpm run postinstall:better-sqlite3-node-restore
# Runs the unit suite once, then reruns only the packages that owned a
# failing test up to two times (serially), stopping early on success.
# Flaky tests are reported as annotations / a step summary but do not fail
# the build; the final retry package suite must otherwise pass.
# macOS gates use an explicit compatibility selection. Scheduled and
# manual runs retain full macOS coverage on all supported Node versions.
- name: Test
if: ${{ github.event_name != 'pull_request' || steps.filter.outputs.ts != 'false' }}
working-directory: ts
shell: pwsh
env:
TYPEAGENT_SKIP_POWERSHELL_BROKER_TESTS: ${{ runner.os == 'Windows' && '1' || '0' }}
TYPEAGENT_TEST_PROFILE: ${{ runner.os == 'macOS' && github.event_name != 'schedule' && github.event_name != 'workflow_dispatch' && 'macos-compat' || 'full' }}
run: |
node tools/scripts/runTestLocalWithSummary.mjs
$log = Join-Path $env:RUNNER_TEMP 'typeagent-test.log'
node tools/scripts/runTestLocalWithSummary.mjs 2>&1 | Tee-Object -FilePath $log
exit $LASTEXITCODE
- name: Upload test log
if: ${{ always() && (github.event_name != 'pull_request' || steps.filter.outputs.ts != 'false') }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: test-log-${{ matrix.os }}-${{ matrix.version }}
path: ${{ runner.temp }}/typeagent-test.log
if-no-files-found: ignore
retention-days: 14
- name: UI tests (requires display)
if: ${{ (github.event_name != 'pull_request' || steps.filter.outputs.ts != 'false') && runner.os == 'Linux' }}
working-directory: ts
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/memory-regression.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

name: memory-regression

on:
workflow_dispatch:
schedule:
- cron: "23 8 * * 1"

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

env:
NODE_OPTIONS: --max_old_space_size=8192

jobs:
memory:
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
runs-on: ${{ matrix.os }}
timeout-minutes: 60
defaults:
run:
shell: pwsh
working-directory: ts
steps:
- name: Setup Git LF
working-directory: .
run: git config --global core.autocrlf false
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Linux prerequisites
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y libsecret-1-dev
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
package_json_file: ts/package.json
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: "24.22.1"
cache: "pnpm"
cache-dependency-path: ts/pnpm-lock.yaml
- name: Install dependencies
run: pnpm install --frozen-lockfile --strict-peer-dependencies
- name: Build memory service
id: build
run: |
if (Test-Path $PROFILE) { . $PROFILE }
pnpm run build @typeagent/memory-service
- name: Restore better-sqlite3 for Node.js
run: pnpm run postinstall:better-sqlite3-node-restore
- name: Memory correctness tests
timeout-minutes: 30
run: pnpm --filter @typeagent/memory-service run test:local --runInBand --json --outputFile=memory-local-results.json
- name: Memory scaling regressions
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
timeout-minutes: 15
run: pnpm --filter @typeagent/memory-service run test:regression --json --outputFile=memory-regression-results.json
- name: Upload test results
if: ${{ !cancelled() }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: memory-results-${{ matrix.os }}
path: ts/packages/memory/service/memory-*-results.json
if-no-files-found: warn
retention-days: 14
60 changes: 60 additions & 0 deletions ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,66 @@ pnpm run test:live # Integration tests (*.test.ts) — requires API keys in t
pnpm run test # Both local + live + shell tests
```

CI uses `node tools/scripts/runTestLocalWithSummary.mjs`. It runs
`test:local:packages`, retries failing files or packages serially up to twice, then runs
`test:local:scripts` independently. A failed workspace phase cannot skip the
script phase or be hidden by a successful script phase; both phases must pass.
Infrastructure failures (such as a process signal or spawn error) stop the run.
Root script failures are not retried.

For recognized single-invocation Jest package scripts, retries run the accumulated
failed files through that package's `jest-esm` wrapper. Packages with chained
phases, setup hooks, or unrecognized runners explicitly fall back to their
complete `test:local` command. This preserves setup and does not assume every
package is a standalone Jest suite.

Shared and standalone Jest configurations use one worker when `CI=true`, and
disable their Jest cache when `TYPEAGENT_JEST_NO_CACHE=true` during package
retries. These settings do not change local worker defaults.

#### macOS blocking and nightly coverage

The [build-ts workflow](../.github/workflows/build-ts.yml) keeps full local
correctness coverage on Ubuntu and Windows across all supported Node versions.
For pull requests, merge queues, and pushes, macOS runs the explicit
`macos-compat` selection in
[runLocalTestPackages.mjs](./tools/scripts/runLocalTestPackages.mjs), plus **all
root script tests**, across the same Node versions:

| Area | Selected package suites |
| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Config/file URLs | `fileLink` |
| Native SQLite | `sqliteCollection`, `sqliteDataFrame` |
| Memory locking, publication, persistence, privacy and Git history | `corpusAccess`, `filesystemPublication`, `markdownPersistence`, `revisionTextStore`, `viewHistoryRecords` |
| Dispatcher locking and session lifecycle | `instanceLock`, `sessionContext`, `codingSessionLifecycle` |
| Cache round trips | `constructionCacheRoundTrip1`, `constructionCacheRoundTripMerge1` |

Root script tests retain generated-file healing, aliased-directory containment
and subprocess/installer coverage. The smaller package selection is deliberate;
it is not full macOS correctness coverage. Keep it a positive list with contract
tests, and update it when new platform-sensitive behavior is introduced.

Every day at **08:17 UTC**, the same workflow runs the **full** local correctness
suite on macOS for all supported Node versions. Scheduled runs use the default
branch. Manual dispatch runs full coverage on all three operating systems.
These runs exclude the separately scheduled memory scaling regressions.
Test logs are uploaded for 14 days and phase/failure summaries appear in GitHub.
Scheduled failures do not block an already merged PR: TypeAgent maintainers
should triage them before subsequent merges and add a blocking regression to
the compatibility selection when a missing platform-sensitive case is found.

The default developer profile remains `full`. To validate the compatibility
selection locally after building, run from `ts` in PowerShell:

```powershell
$env:TYPEAGENT_TEST_PROFILE = 'macos-compat'
try {
pnpm run test:local
} finally {
Remove-Item Env:TYPEAGENT_TEST_PROFILE
}
```

To run tests for a single package:

```bash
Expand Down
4 changes: 3 additions & 1 deletion ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@
"test": "pnpm run test:local && pnpm run test:live && pnpm run shell:test",
"test:keys": "npx tsx tools/scripts/testServiceKeys.ts",
"test:live": "pnpm -r ---no-bail -no-sort --stream --workspace-concurrency=1 run test:live",
"test:local": "pnpm -r --no-bail --no-sort --stream --workspace-concurrency=3 run test:local && node --test tools/scripts/test/*.spec.mjs",
"test:local": "pnpm run test:local:packages && pnpm run test:local:scripts",
"test:local:packages": "node tools/scripts/runLocalTestPackages.mjs",
"test:local:scripts": "node --test tools/scripts/test/*.spec.mjs",
"test:ui": "pnpm -r --no-bail --no-sort --stream --if-present run test:ui"
},
"devDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions ts/packages/agents/browser/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { fileURLToPath } from "node:url";

/** @type {import('ts-jest').JestConfigWithTsJest} */
export default {
...(process.env.CI === "true" ? { maxWorkers: 1 } : {}),
...(process.env.TYPEAGENT_JEST_NO_CACHE === "true" ? { cache: false } : {}),
preset: "ts-jest/presets/default-esm",
extensionsToTreatAsEsm: [".ts"],
testEnvironment: "jsdom",
Expand Down
2 changes: 2 additions & 0 deletions ts/packages/agents/browserExtension/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { fileURLToPath } from "node:url";

/** @type {import('ts-jest').JestConfigWithTsJest} */
export default {
...(process.env.CI === "true" ? { maxWorkers: 1 } : {}),
...(process.env.TYPEAGENT_JEST_NO_CACHE === "true" ? { cache: false } : {}),
preset: "ts-jest/presets/default-esm",
extensionsToTreatAsEsm: [".ts"],
testEnvironment: "jsdom",
Expand Down
7 changes: 7 additions & 0 deletions ts/packages/cli/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import sharedConfig from "../../jest.config.js";

/** @type {import("ts-jest").JestConfigWithTsJest} */
export default {
...(process.env.CI === "true" ? { maxWorkers: 1 } : {}),
...(process.env.TYPEAGENT_JEST_NO_CACHE === "true" ? { cache: false } : {}),
...(sharedConfig.reporters === undefined
? {}
: { reporters: sharedConfig.reporters }),
preset: "ts-jest/presets/default-esm",
extensionsToTreatAsEsm: [".ts"],
testEnvironment: "node",
Expand Down
24 changes: 24 additions & 0 deletions ts/packages/memory/service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,30 @@ snapshot; actual qualification runs still require an explicit fixture root.
No generated memory stores, recordings, provider responses or run receipts are
included in these fixtures.

## Test suites

Build from the TypeScript workspace root before running compiled tests:

```powershell
pnpm run build @typeagent/memory-service
pnpm --filter @typeagent/memory-service run test:local
pnpm --filter @typeagent/memory-service run test:regression
```

`test:local` runs the offline `.spec.ts` correctness suite used by PR CI.
View-history ordering, exact records, authorship and selective parsing are checked
with ten commits; privacy-purge, recovery and concurrent-read cases remain in
that suite. The same assertions run against 100 and 1,000 commits in
`test:regression`, which selects only `.regression.ts` files and runs serially.
These scaling cases are excluded from normal Jest discovery and `test:local`;
no tests are skipped.

The [memory-regression workflow](../../../../../.github/workflows/memory-regression.yml)
runs both suites every Monday at 08:23 UTC and on manual dispatch, on Ubuntu,
Windows and macOS with Node 24.22.1. Each OS uploads Jest JSON results, including
individual test durations. No model credentials or external fixture checkout
are required. Model-backed `.test.ts` rehearsals remain separately opt-in.

## Automatic maintenance foundation

When a Wiki candidate overlaps an existing page, the service checks the page's
Expand Down
1 change: 1 addition & 0 deletions ts/packages/memory/service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
"test": "npm run test:local",
"test:local": "pnpm run jest-esm --testPathPattern=\".*[.]spec[.]js\"",
"test:regression": "pnpm run jest-esm --runInBand --testMatch=\"**/dist/test/**/*.regression.js\"",
"tsc": "tsc -b"
},
"dependencies": {
Expand Down
22 changes: 22 additions & 0 deletions ts/packages/memory/service/test/viewHistoryRecords.regression.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { mkdtemp, rm } from "node:fs/promises";
import path from "node:path";
import { checkHistoryRecords } from "./viewHistoryRecordsTestCommon.js";

test.each([100, 1000])(
"%i commits match legacy ordering and exact records without parsing unrelated state",
async (count) => {
const root = await mkdtemp(
path.join(process.cwd(), ".b12-view-records-regression-"),
);
try {
await checkHistoryRecords(root, count);
} finally {
import.meta.jest.restoreAllMocks();
await rm(root, { recursive: true, force: true });
}
},
180_000,
);
Loading
Loading