Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,17 @@ jobs:
node-version: ['22', '24']
steps:
# QNBS-v3: fetch-tags — check-doc-metrics.mjs's stale-PLANNED-status check reads `git tag`
# and silently no-ops without one; a default shallow checkout has no tags at all.
# and silently no-ops without one; a default shallow checkout has no tags at all. fetch-depth:
# 0 (full history) is additionally required by scanUnreleasedTruth's completeness check
# (audit F-2): fetch-tags alone makes the tag ref resolvable but does not deepen the commit
# graph, so `git log v<tag>..HEAD` could still fail or truncate on a shallow checkout — which
# getPostReleaseCommitSubjects() would silently treat as "no history" and skip the entire
# check, defeating it without ever reporting a failure.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-tags: true
fetch-depth: 0
- uses: ./.github/actions/setup
with:
node-version: ${{ matrix.node-version }}
Expand Down
71 changes: 71 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,77 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **Project schema-version classification (Slice A, #553):** `PROJECT_SCHEMA_V1` as a fresh
production version marker, disjoint classification
(`LEGACY_UNVERSIONED`/`SUPPORTED_OLDER`/`UNSUPPORTED_OLDER`/`CURRENT`/`FUTURE`/`MALFORMED`),
raw/header parsing before typed parsing so a `FUTURE` document with a breaking shape still
classifies `FUTURE` rather than `MALFORMED`, and the `LEGACY_TO_V1` in-memory admission
primitive. PR #618.
- **Schema-version classification observed on IDB project load (Slice B, 1/N, #553):** the first
ingress path wired to Slice A's classifiers, observation-only (classify and log, never alter
load behavior, never throw), matching the established Core shadow-validation pattern. PR #619.
- **Canonical document projection foundation:** establishes the canonical parser/import/admission
boundary that retains the original JSON text alongside a bounded typed projection, the
foundation Slice C's admission primitive builds on.
- **Explicit legacy-to-v1 admission primitive (#653):** the non-destructive `LEGACY_TO_V1`
in-memory admission path recognizes, fully validates, losslessly overlays the V1 version marker,
and revalidates the destination. PR #653.
- **Filesystem admission converged after #654 (#658):** carries forward canonical filesystem
admission and closes legacy editable-admission authority gaps. PR #658.

### Fixed

- **`schemaVersion` now enforces raw integer grammar (#553):** rejects rounded fractional schema
versions and non-number schema-version tokens instead of silently coercing them. PR #621.
- **Canonical document projection now reuses its own parsed input** instead of re-parsing,
removing a redundant-parse divergence risk between the raw and typed projections.
- **Projection failure now retains the raw header verdict** instead of discarding the
already-classified version header when the typed projection itself fails.
- **Unsupported-project startup copy added** alongside the existing migration-gap copy, so a
project whose schema version this build cannot open at all gets its own distinct startup
message rather than reusing the migration-gap wording.
- **Migration-gap startup copy now distinguished from the unsupported-project case (#656):** the
two failure classes previously shared wording; each now gets copy specific to its actual cause.
PR #656.
- **PR-size exception governance hardened (#657):** closes control gaps in how per-PR size-ceiling
exceptions are recorded and validated. PR #657.
- **Security docs no longer cite closed PR #356 as active desktop-encryption remediation:**
`docs/SECURITY-THREAT-MODEL.md` and `docs/IDB-ENCRYPTION.md` both asserted PR #356 was the
active/pending remediation for desktop plaintext storage after it was closed as superseded on
2026-08-18. Rewritten to anchor on the living Ledger-row-9/R-15 state instead of a PR number, and
`check-doc-metrics.mjs` now mechanically rejects an unqualified live/pending-remediation claim
tied to a bare PR number in these two files. PR #673.
- **CHANGELOG completeness-check upgrade, backfill Unreleased (audit F-2, PR #674):**
`scanUnreleasedTruth` previously accepted any non-empty `[Unreleased]` section forever, so a
single unrelated bullet let arbitrarily many later `feat`/`fix`/`perf` commits go completely
undocumented — 13 real commits since `v1.28.4` had gone unrecorded. It now requires every
governed commit to be individually referenced by PR number or subject slug, naming any that
aren't; this section was backfilled with all 13 currently-undocumented entries above.
- **Completeness-check review findings addressed:** PR-number matching now requires a non-digit
boundary (a bare substring check let `#65` satisfy `#656`), subject-slug matching is scoped to
one changelog entry at a time instead of the whole section, each matched entry is claimed so one
generic bullet can't simultaneously "document" multiple different commits, a numbered commit now
requires its exact PR number rather than ever falling back to a slug match, and full per-commit
completeness is enforced only outside `pull_request` CI context (an un-numbered commit there is
exempted — a PR's own git-log range enumerates every commit unique to that branch, not the one
commit that will exist after squash-merge, so a review-fix follow-up commit can't reference
itself in advance — while an already-numbered commit from a separate, already-merged PR sitting
in the same range stays fully enforced).
- **Stop reading the GitHub event name as the completeness check's own default:** the
`pull_request`-context parameter defaulted to reading `GITHUB_EVENT_NAME` directly, but that
environment variable is ambiently visible to the Vitest test process too when the whole suite
runs inside a `pull_request`-triggered job — the pure function's default is now hardcoded
`false`; only the real CLI invocation reads the actual environment.

### Security

- **AI/session attribution now rejected in commits and PRs:** a fail-closed guard
(`check-commit-attribution.mjs`, a commit-msg hook, a pre-push scan of outgoing commits, and a
CI check on the commit range and PR title/body) blocks Claude/Anthropic attribution trailers,
session URLs, and generated-by footers before they can land in repository history. PR #672.

### Documentation

- **Post-release v1.28.4 truth sync:** removed the now-stale release-candidate markers from
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,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-2942_keys-0EA5E9" alt="i18n 19 locales — 2942 keys">
<img src="https://img.shields.io/badge/Tests-7593%2B_%2F_603_files-22C55E" alt="7593+ tests / 603 files">
<img src="https://img.shields.io/badge/Tests-7635%2B_%2F_603_files-22C55E" alt="7635+ tests / 603 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 @@ -511,7 +511,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`) | 2942 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 (7593+ tests / 603 files) + Playwright E2E | Unit/integration + cross-browser E2E; Stryker mutation (manual workflow) |
| **Testing** | Vitest 4.x (7635+ tests / 603 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 @@ -549,7 +549,7 @@ WorldScript-Studio/
│ ├── sw.js # PWA Service Worker
│ └── manifest.json # PWA Web App Manifest v3
├── tests/
│ ├── unit/ # Vitest unit tests (7593+ tests, 603 files) — count spans tests/, components/, packages/*/tests/, not just this folder
│ ├── unit/ # Vitest unit tests (7635+ tests, 603 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 @@ -714,7 +714,7 @@ The main pipeline is [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Opt
Raw bundle-budget ceilings (KB per uncompressed asset): entry **2500 KB**, vendor **6200 KB**, other JavaScript **2500 KB**, and WASM **30000 KB**.

**Current test metrics (2026-09-07, source-synchronized; CI remains authoritative for pass/fail):**
- **7593+ unit tests** across **603 test files** — CI is authoritative for pass/fail
- **7635+ unit tests** across **603 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: **2942 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
18 changes: 15 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,21 @@ Status: 🔄 in progress | ⬜ open | ✅ done
`TODO.md`/`README.md` reconciled, `v1.28.4` tag and GitHub Release published with all expected
Desktop artifacts; post-merge main CI and CodeQL green (CodeQL has no tag trigger), tag-triggered
CI/Tauri/Docker all green, and `AUDIT.md` updated with real evidence.
- ⬜ Close the outstanding Wave 2 prerequisite (ledger row 9: project state-shape compatibility
adapter) — currently in progress, not complete. Wave 3/4 R-15 implementation stays blocked
(`S5_IMPLEMENTATION_READY=NO`) until this and `S5_TERMINAL=YES` are both true.
- ✅ Attribution-hygiene guard merged (PR #672): fail-closed rejection of AI/session attribution
trailers and footers in commits, tags, and PR titles/bodies (commit-msg hook, pre-push scan, CI).
- ✅ Post-audit truth/governance remediation PR #673 merged: stale `#356`-active-remediation
citations in `docs/SECURITY-THREAT-MODEL.md`/`docs/IDB-ENCRYPTION.md` rewritten to anchor on the
living ledger state, with a mechanical `check-doc-metrics.mjs` guard against recurrence (first
slice of the 2026-09-08 external audit's S1–S10 remediation sequence; further slices in progress).
- 🔄 Ledger row 9 (project state-shape compatibility adapter) — five `feat`/`fix` PRs landed
(#618 Slice A persisted schema-version classification, #619 Slice B IDB-load observation, #621
raw-integer-grammar hardening, #653 the non-destructive `LEGACY_TO_V1` admission primitive, #658
filesystem-admission convergence) but **still not complete**: durable source-generation fencing,
Comment thread
qnbs marked this conversation as resolved.
writer integration, universal ingress/egress, and authority switch remain open per
`docs/native/CORE-MIGRATION-LEDGER.md` row 9. Wave 3/4 R-15 implementation stays blocked
(`S5_IMPLEMENTATION_READY=NO`, recorded on row 10) until row 9 and `S5_TERMINAL=YES` are both
true — no target release is set for that yet (tracked, not invented; see the S1–S10 remediation
plan's exit-criterion work).
- ⬜ #614 (narrow concurrent-first-install multi-tab race, requires cross-tab coordination) and
#532 (WelcomePortal E2E entry nondeterminism root cause) remain open, tracked separately —
not part of this sprint unless they directly block release or R-15 work.
Expand Down
2 changes: 2 additions & 0 deletions scripts/check-doc-metrics.d.mts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export function scanUnreleasedTruth(
postReleaseCommitSubjects: string[] | null,
packageVersion?: string,
taggedVersions?: Set<string>,
isFeatureBranchContext?: boolean,
branchLocalIndices?: Set<number>,
): string[];
export function scanForDrift(
content: string,
Expand Down
Loading
Loading