Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<img src="https://img.shields.io/badge/Storage-IndexedDB_v8-F59E0B" alt="IndexedDB v8">
<img src="https://img.shields.io/badge/PWA-v3.0-5BB974?logo=pwa" alt="PWA v3.0">
<img src="https://img.shields.io/badge/i18n-19_locales-2925_keys-0EA5E9" alt="i18n 19 locales — 2925 keys">
<img src="https://img.shields.io/badge/Tests-6984%2B_%2F_577_files-22C55E" alt="6984+ tests / 577 files">
<img src="https://img.shields.io/badge/Tests-6987%2B_%2F_578_files-22C55E" alt="6987+ tests / 578 files">
<img src="https://img.shields.io/codecov/c/github/qnbs/WorldScript-Studio?logo=codecov&label=Coverage" alt="Codecov Coverage">
<img src="https://img.shields.io/badge/License-MIT-22C55E" alt="License MIT">
<img src="https://img.shields.io/github/actions/workflow/status/qnbs/WorldScript-Studio/.github/workflows/ci.yml?branch=main&logo=github" alt="CI Status">
Expand Down Expand Up @@ -512,7 +512,7 @@ The Settings → AI panel shows a live GPU status badge with adapter details and
| **Document Export** | docx + jszip | Word-compatible `.docx` generation (lazy-loaded) |
| **PWA** | Service Worker + Web App Manifest v3 | Offline support, installability, Workbox chunking |
| **i18n** | Custom React Context (`I18nContext.tsx`) | 2925 keys × 19 locales (de/en/es/fr/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu/ru/ko Beta); EN fallback; `localStorage` persistence |
| **Testing** | Vitest 4.x (6984+ tests / 577 files) + Playwright E2E | Unit/integration + cross-browser E2E; Stryker mutation (manual workflow) |
| **Testing** | Vitest 4.x (6987+ tests / 578 files) + Playwright E2E | Unit/integration + cross-browser E2E; Stryker mutation (manual workflow) |
| **Code Quality** | Biome (lint + format) + TypeScript 7 (tsgo) strict | `--error-on-warnings` in CI; zero `any` policy |
| **Visualization** | Force-directed graph | Interactive character relationship network |
| **Desktop** | Tauri v2 | Cross-platform installer; auto-updater via `latest.json` |
Expand Down Expand Up @@ -550,7 +550,7 @@ WorldScript-Studio/
│ ├── sw.js # PWA Service Worker
│ └── manifest.json # PWA Web App Manifest v3
├── tests/
│ ├── unit/ # Vitest unit tests (6984+ tests, 577 files) — count spans tests/, components/, packages/*/tests/, not just this folder
│ ├── unit/ # Vitest unit tests (6987+ tests, 578 files) — count spans tests/, components/, packages/*/tests/, not just this folder
│ │ ├── ai/ # aiSmallModules, aiCoreFallbackPaths
│ │ └── settings/ # WebLlmPanel, AiSections
│ └── e2e/ # Playwright specs + helpers.ts
Expand Down Expand Up @@ -712,7 +712,7 @@ The main pipeline is [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Opt
| `scorecard` | weekly + `main` push | OpenSSF Scorecard — SARIF uploaded to GitHub Code Scanning |

**Current test metrics (2026-08-21, source-synchronized; CI remains authoritative for pass/fail):**
- **6984+ unit tests** across **577 test files** — CI is authoritative for pass/fail
- **6987+ unit tests** across **578 test files** — CI is authoritative for pass/fail
- Coverage thresholds: lines ≥ 80 · branches ≥ 66 · functions ≥ 72 · statements ≥ 78 — enforced in CI (see Codecov badge for live metrics)
- i18n: **2925 keys × 19 locales** (en/de/fr/es/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu/ru/ko Beta)

Expand Down
28 changes: 14 additions & 14 deletions scripts/ci-prepush-lowend.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ function report(name, status, detail = '') {
return status;
}

function runCheck(name, run) {
const status = run();
async function runCheck(name, run) {
const status = await run();
report(name, status === 0 ? 'PASS' : 'FAIL');
if (status !== 0) process.exit(status ?? 1);
}

function main() {
async function main() {
const evidenceIndex = process.argv.indexOf('--prepush-evidence-file');
const evidenceFile = evidenceIndex >= 0 ? process.argv[evidenceIndex + 1] : undefined;
const fullRequested = process.argv.includes('--full');
Expand Down Expand Up @@ -51,18 +51,18 @@ function main() {
}
report('Dependency state', 'PASS');

runCheck('Toolchain', () => runNodeScript('scripts/check-pnpm-toolchain.mjs', ['--hook']));
runCheck('Docs/release truth', () => runNodeScript('scripts/check-doc-metrics.mjs'));
runCheck('CSP policy', () => runNodeScript('scripts/check-csp-policy.mjs'));
runCheck('Desktop import boundary', () =>
await runCheck('Toolchain', () => runNodeScript('scripts/check-pnpm-toolchain.mjs', ['--hook']));
await runCheck('Docs/release truth', () => runNodeScript('scripts/check-doc-metrics.mjs'));
await runCheck('CSP policy', () => runNodeScript('scripts/check-csp-policy.mjs'));
await runCheck('Desktop import boundary', () =>
runNodeScript('scripts/check-tauri-import-boundary.mjs'),
);
runCheck('Native readiness', () => runNodeScript('scripts/check-native-readiness.mjs'));
await runCheck('Native readiness', () => runNodeScript('scripts/check-native-readiness.mjs'));

if (shouldRunAdmissionCheck('i18n', classification.files) || full) {
runCheck('i18n key parity', () => runNodeScript('scripts/check-i18n-keys.mjs'));
runCheck('i18n bundle rebuild', () => runNodeScript('scripts/build-i18n.mjs'));
runCheck('i18n translation quality', () =>
await runCheck('i18n key parity', () => runNodeScript('scripts/check-i18n-keys.mjs'));
await runCheck('i18n bundle rebuild', () => runNodeScript('scripts/build-i18n.mjs'));
await runCheck('i18n translation quality', () =>
runNodeScript('scripts/i18n-quality-report.mjs', [
'--strict',
'--min-coverage',
Expand All @@ -74,10 +74,10 @@ function main() {
}

if (shouldRunAdmissionCheck('contentGuard', classification.files) || full)
runCheck('Content guard', () => runNodeScript('scripts/content-guard.mjs'));
await runCheck('Content guard', () => runNodeScript('scripts/content-guard.mjs'));

if (typecheckRequired) {
runCheck('TypeScript (single checker)', () =>
await runCheck('TypeScript (single checker)', () =>
// QNBS-v3: one checker bounds memory use on constrained developer machines.
runLocalBinary('tsgo', ['--project', 'tsconfig.tsgo.json', '--noEmit', '--checkers', '1']),
);
Expand All @@ -92,4 +92,4 @@ function main() {
}

// QNBS-v3: guard execution so this module can be imported for testing without running the CLI.
if (isMainModule(process.argv[1], import.meta.url)) main();
if (isMainModule(process.argv[1], import.meta.url)) await main();
4 changes: 2 additions & 2 deletions scripts/hooks/pre-commit.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import process from 'node:process';
import { ensureDependencyState, runLocalBinary, runNodeScript } from './shared.mjs';

if (runNodeScript('scripts/signing/doctor.mjs', ['--hook']) !== 0) process.exit(1);
if ((await runNodeScript('scripts/signing/doctor.mjs', ['--hook'])) !== 0) process.exit(1);
if (!ensureDependencyState()) process.exit(1);
process.exit(runLocalBinary('lint-staged'));
process.exit(await runLocalBinary('lint-staged'));
4 changes: 2 additions & 2 deletions scripts/hooks/pre-push.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ try {
evidenceFile = join(evidenceDir, 'evidence.json');
writePrePushEvidenceFile(evidenceFile, updates);
const childArgs = [...process.argv.slice(2), '--prepush-evidence-file', evidenceFile];
exitCode = runNodeScript('scripts/signing/verify-outgoing.mjs', childArgs);
if (exitCode === 0) exitCode = runNodeScript('scripts/ci-prepush-lowend.mjs', childArgs);
exitCode = await runNodeScript('scripts/signing/verify-outgoing.mjs', childArgs);
if (exitCode === 0) exitCode = await runNodeScript('scripts/ci-prepush-lowend.mjs', childArgs);
} catch (error) {
console.error(
`pre-push evidence capture failed closed: ${error instanceof Error ? error.message : 'invalid input'}`,
Expand Down
50 changes: 50 additions & 0 deletions scripts/hooks/shared.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export interface BoundedResult {
status: number | null;
signal: string | null;
error: Error | null;
timedOut: boolean;
interrupted: boolean;
command: string;
}

export interface RunOptions {
timeoutMs?: number;
env?: NodeJS.ProcessEnv;
input?: string;
shell?: boolean;
cwd?: string;
root?: string;
detached?: boolean;
}

export function ensureDependencyState(root?: string): boolean;

export function runBounded(
command: string,
args: string[],
options?: RunOptions,
): Promise<BoundedResult>;

export function runNodeScriptDetailed(
script: string,
args?: string[],
options?: RunOptions,
): Promise<BoundedResult>;
Comment thread
sourcery-ai[bot] marked this conversation as resolved.

export function runNodeScript(
script: string,
args?: string[],
options?: RunOptions,
): Promise<number>;

export function runLocalBinaryDetailed(
binary: string,
args?: string[],
options?: RunOptions,
): Promise<BoundedResult>;

export function runLocalBinary(
binary: string,
args?: string[],
options?: RunOptions,
): Promise<number>;
Loading
Loading