diff --git a/README.md b/README.md index a3210c8..daa6cf6 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Local-first Vietnamese/English speech-to-text PWA with private voice-model workf ## Current status -The current implementation target is the v0.6.2 CDN/Xet CSP hotfix on package version `0.6.2`, superseding the v0.6.1 CSP hotfix without moving the v0.6.0 or v0.6.1 tags/assets. It preserves the v0.5.0 speech, enrollment, browser-training, profile, portability, privacy, security, and compatibility contracts while keeping the v0.6 task-first interface and fixing production model-download CSP for the explicit **Install model** action: +The current implementation target is the v0.6.3 real-ASR production dictation hotfix on package version `0.6.3`, superseding the v0.6.2 CDN/Xet CSP hotfix without moving the v0.6.0, v0.6.1, or v0.6.2 tags/assets. It preserves the v0.5.0 enrollment, browser-training, profile, portability, privacy, security, and compatibility contracts while keeping the v0.6 task-first interface and publishing the worker-owned real ASR path verified by an opt-in production fake-microphone dictation smoke: - **Dictate** — install the required speech model, record, edit, copy, download, or clear local transcript text. - **Vocabulary** — create sets, enable/disable them, add words, and use Advanced only for steering and diagnostics. - **Models** — create a voice model, record prompts, train/check candidates, activate or roll back, import `.speechmodel`, export encrypted `.speechmodel`, and manage local model data. - **Settings menu** — Audio, Storage, Privacy, Keyboard shortcuts, Diagnostics, About, and install/update actions. -v0.6.2 changes only deployment/release metadata around the Hugging Face CDN/Xet CSP redirect hotfix. It does not change UI, model-pack, profile, adapter, portable-bundle, storage, training, or privacy schemas. +v0.6.3 changes the production dictation runtime path and release metadata only. Dictate now sends captured PCM to the ASR worker, which reads installed model files through model-manager storage, runs worker-owned ONNX encoder/predictor/joiner inference, decodes RNN-T output, and commits final transcript text. It does not change model-pack, profile, adapter, portable-bundle, vocabulary, training, import/export, or privacy schemas. Production ASR weights, private recordings, speech corpora, and personal profiles are intentionally not committed. The current VietASR catalog entry is a metadata-only external candidate; it is not yet advertised as a low-latency streaming model because its inspected encoder does not expose streaming cache tensors. @@ -113,7 +113,7 @@ pnpm chromium-smoke PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/path/to/chrome pnpm chromium-smoke ``` -Release-validation coverage includes accessibility naming/section checks, keyboard focus through primary actions, rapid push-to-talk stress cycles, no fetch/XHR/websocket requests while push-to-talk is active, offline reload, model lifecycle inspection, screenshot-attachment visual regression checks, and diagnostics/support bundle schema checks. Shared CI timing is informational; hard latency gates require declared reference hardware. +Release-validation coverage includes accessibility naming/section checks, keyboard focus through primary actions, rapid push-to-talk stress cycles, no fetch/XHR/websocket requests while push-to-talk is active, offline reload, model lifecycle inspection, screenshot-attachment visual regression checks, diagnostics/support bundle schema checks, and an opt-in deployed-app production dictation smoke. Shared CI timing is informational; hard latency gates require declared reference hardware. ## Documentation map @@ -171,7 +171,7 @@ v0.6 documentation must keep the release-usability gate open until issue #255 ha ## Known limitations -- v0.6.0/v0.6.1/v0.6.2 release-usability participant evidence remains open in issue #255. The v0.6.2 hotfix may be published as a CSP-only patch over the human-approved v0.6.0 release, but closing #255 still requires aggregate participant evidence or a separate explicit decision recorded in `docs/research/v0.6-release-usability-study.json`. +- v0.6.0/v0.6.1/v0.6.2/v0.6.3 release-usability participant evidence remains open in issue #255. The v0.6.3 hotfix may be published as a production dictation patch over the human-approved v0.6.0 release, but closing #255 still requires aggregate participant evidence or a separate explicit decision recorded in `docs/research/v0.6-release-usability-study.json`. - v0.5.0 does not yet publish user-approved 30-speaker bilingual cohort evidence; do not claim production Personal Voice Model accuracy or quality gates pass until ADR 0004 is resolved. - v0.5.0 does not yet publish declared reference-hardware Personal Voice Model benchmark evidence; do not claim production memory, storage, latency, RTF, export/import, offline, or zero-network performance gates pass until ADR 0005 is resolved. - Synthetic fixtures, CI smoke tests, local diagnostics, semantic tests, and contract tests are regression evidence only. diff --git a/apps/web/package.json b/apps/web/package.json index 358ba21..37035f1 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@speech/web", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Installable local-first speech-to-text PWA shell.", "type": "module", diff --git a/apps/web/src/app/PersonalModelsPanel.tsx b/apps/web/src/app/PersonalModelsPanel.tsx index 15488b8..82e9cd1 100644 --- a/apps/web/src/app/PersonalModelsPanel.tsx +++ b/apps/web/src/app/PersonalModelsPanel.tsx @@ -779,7 +779,7 @@ export function PersonalModelsPanel() { const result = await exportPortableSpeechModel({ profileId: primarySummary.profile.id, exactBaseModel: portableExportBaseModel.exactBaseModel, - sourceAppVersion: '0.6.2', + sourceAppVersion: '0.6.3', mode: portableExport.encrypted ? 'encrypted' : 'unencrypted', timeoutMs: 60_000, ...(portableExport.encrypted ? { passphrase: portableExport.passphrase } : {}), diff --git a/apps/web/src/app/about-screen.test.ts b/apps/web/src/app/about-screen.test.ts index 42b5cbe..4b31edb 100644 --- a/apps/web/src/app/about-screen.test.ts +++ b/apps/web/src/app/about-screen.test.ts @@ -13,9 +13,9 @@ const basePwa: PwaLifecycleSnapshot = { describe('about-screen helpers', () => { it('builds source, license, provenance, acknowledgements, and update state without marketing copy', () => { - const model = buildAboutScreenModel({ appVersion: '0.6.2', pwa: basePwa }); + const model = buildAboutScreenModel({ appVersion: '0.6.3', pwa: basePwa }); - expect(model.version).toBe('0.6.2'); + expect(model.version).toBe('0.6.3'); expect(model.sourceRepository).toEqual({ label: 'WilsonLe/speech', href: speechSourceRepository, diff --git a/docs/instructions/privacy-data.instructions.md b/docs/instructions/privacy-data.instructions.md index ba093f3..3f1044a 100644 --- a/docs/instructions/privacy-data.instructions.md +++ b/docs/instructions/privacy-data.instructions.md @@ -65,3 +65,4 @@ applyTo: 'apps/**,packages/**,model-packs/**,tools/**,training/**,MODEL_LICENSES - #258 release publication artifacts must remain aggregate and public: release notes, checklist placeholders, checksums, tag names, GitHub release URLs, Vercel deployment URLs, and validation command names are allowed, but do not include participant data, real transcripts/audio/vocabulary/profile/model artifacts, private storage paths, tokens, browser storage dumps, support bundles, screenshots, or #255 pass claims without consented aggregate evidence. - Patch-release artifacts for CSP/model-download fixes may include public origin names, tag names, checksums, deployment URLs, and validation command names only. Do not broaden network allowances beyond exact committed model origins or imply telemetry, analytics, participant evidence, audio/transcript/profile/model artifact publication, or closure of unrelated release-usability gates. +- v0.6.3 production dictation release artifacts may record public release metadata, exact public model origins, validation command names, and aggregate pass/fail evidence only. Do not commit generated WAVs, browser profiles, model files, raw audio, raw transcripts, signed model URLs, support bundles, private vocabulary, profile artifacts, or participant data; keep the production dictation smoke opt-in and its failure attachments redacted. diff --git a/docs/instructions/release-process.instructions.md b/docs/instructions/release-process.instructions.md index d3a7ea2..4f4ea7e 100644 --- a/docs/instructions/release-process.instructions.md +++ b/docs/instructions/release-process.instructions.md @@ -18,4 +18,4 @@ applyTo: 'package.json,apps/web/package.json,packages/*/package.json,docs/instru - #257 may record local release-candidate PWA update/offline/rollback evidence before publication, but hosted production deployment, GitHub release assets, checksums, tag publication, and production alias verification remain release issue #258 work unless they are explicitly performed and validated on the published URL. - #258 final release-prep artifacts must distinguish prepared-before-publication placeholders from completed tag/GitHub-release/checksum/Vercel evidence. Do not tag or publish until the release-prep PR is merged, required CI passes on clean `main`, and any hosted deployment evidence is recorded from the production URL; keep #255 open unless aggregate participant evidence or a separate explicit human close decision exists. -- Patch releases such as v0.6.1 or v0.6.2 must not move earlier release tags/assets. Add patch-specific release notes/checklists, bump package metadata consistently, tag the corrected commit, publish new checksums/assets, verify production headers and the exact hotfix behavior. For model-download CSP hotfixes, verify both the manifest origin and any exact redirect origin under the production document policy; keep unrelated open gates such as #255 explicit. +- Patch releases such as v0.6.1, v0.6.2, or v0.6.3 must not move earlier release tags/assets. Add patch-specific release notes/checklists, bump package metadata consistently, tag the corrected commit, publish new checksums/assets, verify production headers and the exact hotfix behavior. For model-download CSP hotfixes, verify both the manifest origin and any exact redirect origin under the production document policy. For production dictation hotfixes, run the opt-in deployed-app fake-microphone smoke on a fresh profile and record any stale service-worker update guidance separately; keep unrelated open gates such as #255 explicit. diff --git a/docs/instructions/testing.instructions.md b/docs/instructions/testing.instructions.md index 1d34379..a1367e7 100644 --- a/docs/instructions/testing.instructions.md +++ b/docs/instructions/testing.instructions.md @@ -45,3 +45,4 @@ Performance gates must be measured from audio timestamps and documented with bro - Keep `tests/static/test_v06_release_usability_study.py` green when changing release usability artifacts. The #255 artifact must preserve required final tasks, 90% completion gates, comprehension gates, no-hover and high-severity gates, privacy flags, zero participant evidence counts, and `mayCloseIssue255: false` until consented participant evidence or an explicit human release decision is recorded. - Patch-release validation must include a static artifact/version consistency test for the new release notes/checklist, unchanged base-release immutability, exact production header expectations, and open release gates. Keep package versions, app About/version tests, and current-state gates aligned with the patch version. +- Keep `training/tests/test_v0_6_3_real_asr_dictation_release.py` green when changing v0.6.3 patch-release artifacts, package/app version metadata, production dictation smoke behavior, ASR worker storage/decoder wiring, or current-state release gates. It must preserve v0.6.0/v0.6.1/v0.6.2 immutability, #255 openness, opt-in deployed-app fake-microphone dictation verification, redacted install-vs-ASR diagnostics, and exact model-download CSP origins. diff --git a/docs/planning/CURRENT_STATE.json b/docs/planning/CURRENT_STATE.json index 0396277..260d862 100644 --- a/docs/planning/CURRENT_STATE.json +++ b/docs/planning/CURRENT_STATE.json @@ -1,15 +1,16 @@ { "schemaVersion": 1, "title": "Current state for wilsonle/speech", - "asOf": "2026-06-29", - "status": "v0.6.2 CDN/Xet CSP hotfix prepared for publication; #255 usability evidence remains open", - "packageVersion": "0.6.2", - "currentRelease": "v0.6.2-cdn-csp-hotfix", + "asOf": "2026-06-30", + "status": "v0.6.3 real ASR production dictation hotfix prepared for publication; #255 usability evidence remains open", + "packageVersion": "0.6.3", + "currentRelease": "v0.6.3-real-asr-dictation-hotfix", "lineage": [ "v0.5.0-browser-personal-models", "v0.6.0-minimal-ui-ux", "v0.6.1-csp-hotfix", - "v0.6.2-cdn-csp-hotfix" + "v0.6.2-cdn-csp-hotfix", + "v0.6.3-real-asr-dictation-hotfix" ], "appIdea": "A local-first installable browser PWA for Vietnamese/English dictation, vocabulary steering, guided voice-model enrollment, browser-side personal-model training, encrypted portable voice-model import/export, offline use, and privacy-preserving diagnostics.", "informationArchitecture": { @@ -28,7 +29,7 @@ "legacyRoutesRedirected": true }, "workflows": { - "dictate": "The default screen is Dictate. If the required speech model is missing, setup appears in place with install/verify/activate progress and model details disclosed. When ready, the user records by holding the microphone button or Space, edits transcript text locally, and uses the transcript menu for new/download/clear actions.", + "dictate": "The default screen is Dictate. If the required speech model is missing, setup appears in place with install/verify/activate progress and model details disclosed. When ready, the user records by holding the microphone button or Space; captured PCM is sent to the worker-owned ASR runtime, final transcript text is committed locally, and transcript menu actions handle new/download/clear.", "vocabulary": "Vocabulary uses a compact set list with search, on/off state, word counts, row menus, basic word editing, and a single Advanced disclosure for steering, category, prompt inclusion, pronunciation/diagnostic tokenization details, and expert controls.", "models": "Models uses a compact voice-model list with state-dependent actions, focused create/enroll/train/results/import/export routes, model detail accordions, blocker-first readiness, four-stage training progress, outcome-first candidate results, and dedicated storage/deletion routes.", "enrollment": "Enrollment is one prompt at a time with current voice condition, progress, one recording control, concise quality feedback, and Recording details for SNR, clipping, VAD, alignment, language, storage, and diagnostics.", @@ -79,7 +80,7 @@ }, "capabilitiesPreserved": { "speech": [ - "local Vietnamese/English/automatic/mixed-language streaming transcription", + "local Vietnamese/English/automatic/mixed-language dictation through worker-owned ASR over installed model graphs", "stable committed/provisional transcript rendering", "offline model-pack installation and atomic activation", "WebGPU inference with multithreaded WASM fallback where supported" @@ -137,9 +138,10 @@ "prototypeUsabilityBlocker": "docs/research/v0.6-prototype-usability-study.json", "releaseUsabilityBlocker": "docs/research/v0.6-release-usability-study.json", "updateOfflineRollback": "docs/planning/v0.6.0-update-offline-rollback-verification.json", - "releaseNotes": "docs/planning/v0.6.2-cdn-csp-hotfix-release-notes.json", - "releasePublicationChecklist": "docs/planning/v0.6.2-release-publication-checklist.json", - "issue255VerificationChecklist": "docs/planning/v0.6.0-issue-255-verification-checklist.json" + "releaseNotes": "docs/planning/v0.6.3-real-asr-dictation-release-notes.json", + "releasePublicationChecklist": "docs/planning/v0.6.3-release-publication-checklist.json", + "issue255VerificationChecklist": "docs/planning/v0.6.0-issue-255-verification-checklist.json", + "productionDictationSmoke": "apps/web/e2e/production-dictation.spec.ts" }, "intentionalProductBoundaries": [ "No production telemetry, account system, cloud sync, remote support collection, browser extension, native companion, new language/localization, model-quality claim, decorative marketing dashboard, gamification, or generic feature-card home screen was added.", @@ -149,7 +151,7 @@ "openReleaseGates": [ "Issue #255 remains open for final moderated release-usability participant evidence or a separate explicit human release decision that updates the #255 artifact.", "Production accuracy/performance claims remain bounded by ADR 0004 and ADR 0005 until evidence exists.", - "Publication/deployment evidence is issue #314 work and must be completed on the tagged v0.6.2 hotfix release commit before closing #314." + "Publication/deployment evidence is issue #323 work and must be completed on the tagged v0.6.3 hotfix release commit before closing #323." ], "nextPlanningInstruction": "Plan only the user's next requested delta against this current state. Preserve #255, ADR 0004, and ADR 0005 evidence limitations unless the user provides the missing evidence or an explicit human decision. After the next delta completes, generate another cumulative successor snapshot.", "privacy": { @@ -161,7 +163,8 @@ "containsModelWeights": false }, "intentionalBoundaries": [ - "v0.6.2 is a CSP-only patch release over v0.6.1; it does not change UI, model, profile, adapter, portable-bundle, storage, training, or privacy schemas.", - "v0.6.0 and v0.6.1 release tags/assets remain immutable; v0.6.2 supersedes them only for the Hugging Face CDN/Xet CSP redirect fix." + "v0.6.3 is a real-ASR production dictation patch release over v0.6.2; it does not change model-pack, profile, adapter, portable-bundle, vocabulary, training, import/export, or privacy schemas.", + "v0.6.0, v0.6.1, and v0.6.2 release tags/assets remain immutable; v0.6.3 supersedes them for the worker-owned production dictation fix and opt-in deployed-app smoke evidence.", + "Production dictation smoke artifacts must remain opt-in, redacted, and free of committed WAVs, model files, browser profiles, raw audio, signed URLs, and transcript artifacts." ] } diff --git a/docs/planning/v0.6.3-real-asr-dictation-release-notes.json b/docs/planning/v0.6.3-real-asr-dictation-release-notes.json new file mode 100644 index 0000000..1b195d5 --- /dev/null +++ b/docs/planning/v0.6.3-real-asr-dictation-release-notes.json @@ -0,0 +1,52 @@ +{ + "schemaVersion": 1, + "issue": 323, + "release": "v0.6.3", + "title": "v0.6.3 real ASR production dictation hotfix release notes", + "status": "release-preparation artifact before tag publication", + "baseRelease": "v0.6.2", + "baseReleaseImmutable": true, + "plannedTag": "v0.6.3", + "summary": "v0.6.3 is a patch hotfix over v0.6.2 that preserves the task-first UI release and publishes the production dictation fix: Dictate sends real PCM to a worker-owned ASR path, and the opt-in production smoke verifies a deployed real-model download plus generated fake-microphone TTS transcript assertion.", + "changes": [ + "Adds an opt-in Playwright production dictation smoke that opens the deployed Vercel app, downloads the real model, generates local FFmpeg/flite TTS audio, feeds Chromium fake microphone input, records in Dictate, and asserts the normalized transcript matches the source phrase.", + "Wires Dictate push-to-talk PCM into the ASR worker with START_UTTERANCE, AUDIO_CHUNK, and END_UTTERANCE messages instead of ending with an empty transcript.", + "Adds a worker-owned real ASR decode path that reads installed model files through model-manager storage, creates encoder/predictor/joiner ONNX sessions inside the worker, resamples PCM, extracts log-Mel features, runs greedy RNN-T decoding, detokenizes pieces, and emits FINAL transcript text.", + "Keeps production dictation E2E opt-in and diagnostic artifacts redacted so normal CI/dev runs do not download real model weights or commit audio/model/profile artifacts.", + "Adds troubleshooting guidance for production dictation cases where model install succeeds but transcript output is empty." + ], + "unchangedFromV062": [ + "No model-pack, profile, adapter, portable-bundle, vocabulary, training, export, import, or privacy schema contract changes beyond the worker-owned ASR runtime path and test coverage.", + "No telemetry, analytics, remote logging, account service, or broad third-party network allowance was added.", + "v0.6.0, v0.6.1, and v0.6.2 release artifacts and tags are intentionally left immutable; this patch release supersedes them for production dictation behavior." + ], + "knownLimitations": [ + "Issue #255 remains open for final moderated release-usability participant evidence.", + "Production dictation smoke uses a short deterministic generated phrase because local FFmpeg/flite English TTS is not Vietnamese-native and longer phrases are less stable.", + "Existing users with an old service-worker app shell may need to activate the in-app update, hard refresh, or clear site data before retrying dictation.", + "Production accuracy and reference-hardware performance evidence limitations from ADR 0004 and ADR 0005 remain unresolved.", + "Manual Windows NVDA/Edge and macOS VoiceOver/Safari accessibility matrix rows remain accepted limitations until run outside the Linux agent environment." + ], + "releaseAssetsPlanned": [ + "GitHub source archives generated by GitHub from tag v0.6.3", + "speech-web-v0.6.3-dist.tar.gz generated from the tagged commit", + "SHA256SUMS.txt covering release assets" + ], + "verificationPlan": [ + "Required CI passes on the v0.6.3 release-prep PR and clean main commit.", + "Production alias returns direct routes with CSP `connect-src 'self' https://huggingface.co https://us.aws.cdn.hf.co`.", + "Production About screen shows app version 0.6.3.", + "Opt-in production dictation smoke passes on a fresh profile: real deployed model download, generated TTS fake microphone, Dictate recording, and transcript assertion.", + "Record any stale service-worker/update indicator finding as user-update guidance rather than weakening the fresh-profile production smoke." + ], + "privacy": { + "containsParticipantData": false, + "containsAudio": false, + "containsTranscripts": false, + "containsVocabularyTerms": false, + "containsProfileArtifacts": false, + "containsModelWeights": false, + "addsTelemetryOrAnalytics": false, + "allowsBroadThirdPartyNetwork": false + } +} diff --git a/docs/planning/v0.6.3-release-publication-checklist.json b/docs/planning/v0.6.3-release-publication-checklist.json new file mode 100644 index 0000000..6ce271d --- /dev/null +++ b/docs/planning/v0.6.3-release-publication-checklist.json @@ -0,0 +1,50 @@ +{ + "schemaVersion": 1, + "issue": 323, + "release": "v0.6.3", + "title": "v0.6.3 real ASR dictation hotfix release publication checklist", + "status": "prepared-before-publication", + "baseRelease": "v0.6.2", + "baseReleaseImmutable": true, + "preTagChecks": [ + "Release-prep PR merges to clean main.", + "Required CI checks pass on the release-prep PR and clean main commit.", + "All package versions are 0.6.3.", + "Static validation confirms v0.6.3 release notes/checklist and v0.6.0/v0.6.1/v0.6.2 immutability wording.", + "Full local validation passes: pnpm lint && pnpm format-check && pnpm typecheck && pnpm test && pnpm build && pnpm chromium-smoke." + ], + "publicationSteps": [ + "Create annotated tag v0.6.3 from clean main after CI passes.", + "Generate web dist archive from tagged commit: speech-web-v0.6.3-dist.tar.gz.", + "Generate SHA256SUMS.txt for release assets.", + "Create GitHub release v0.6.3 with notes that preserve #255, ADR 0004, and ADR 0005 limitations.", + "Verify production Vercel deployment for the tagged release commit.", + "Verify direct-route headers include `connect-src 'self' https://huggingface.co https://us.aws.cdn.hf.co`.", + "Verify production About shows 0.6.3.", + "Run the opt-in production dictation smoke on a fresh browser profile and record the real model download plus fake-microphone transcript assertion result.", + "Close issue #323 only after GitHub release and production verification evidence are recorded.", + "Do not close issue #255 unless participant evidence or an explicit separate human decision updates that issue." + ], + "postPublicationEvidenceToRecord": { + "tagCommit": null, + "githubReleaseUrl": null, + "distArchiveSha256": null, + "sha256SumsSha256": null, + "productionUrl": null, + "vercelDeploymentUrl": null, + "verifiedHeaders": [], + "aboutVersionVerified": false, + "productionDictationSmokeVerified": false, + "staleServiceWorkerUpdateGuidanceRecorded": false + }, + "privacy": { + "containsParticipantData": false, + "containsAudio": false, + "containsTranscripts": false, + "containsVocabularyTerms": false, + "containsProfileArtifacts": false, + "containsModelWeights": false, + "addsTelemetryOrAnalytics": false, + "allowsBroadThirdPartyNetwork": false + } +} diff --git a/package.json b/package.json index 411fdcc..006ddce 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "speech", "private": true, - "version": "0.6.2", + "version": "0.6.3", "description": "Local-first Vietnamese/English streaming speech-to-text PWA with private voice personalization", "license": "Apache-2.0", "type": "module", diff --git a/packages/audio/package.json b/packages/audio/package.json index 6f66852..8e2bc43 100644 --- a/packages/audio/package.json +++ b/packages/audio/package.json @@ -1,6 +1,6 @@ { "name": "@speech/audio", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Audio transport primitives: capture controller, ring buffer, fallback buffers, and resampling integration.", "type": "module", diff --git a/packages/benchmark/package.json b/packages/benchmark/package.json index 3adc3d8..57518b4 100644 --- a/packages/benchmark/package.json +++ b/packages/benchmark/package.json @@ -1,6 +1,6 @@ { "name": "@speech/benchmark", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Latency, RTF, queue, memory, custom-term, Dictate parity, and benchmark export contracts.", "type": "module", diff --git a/packages/browser-training/package.json b/packages/browser-training/package.json index 03efcf4..0a2a5f4 100644 --- a/packages/browser-training/package.json +++ b/packages/browser-training/package.json @@ -1,6 +1,6 @@ { "name": "@speech/browser-training", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "BrowserTrainingBackend contracts, fixed local adapter-math training loop, and sampler/optimizer diagnostics for in-browser personal models.", "type": "module", diff --git a/packages/context-bias/package.json b/packages/context-bias/package.json index f4f9194..27aca00 100644 --- a/packages/context-bias/package.json +++ b/packages/context-bias/package.json @@ -1,6 +1,6 @@ { "name": "@speech/context-bias", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Vocabulary schemas, token automata, and bounded contextual bias scoring.", "type": "module", diff --git a/packages/decoder/package.json b/packages/decoder/package.json index 289a104..f00646f 100644 --- a/packages/decoder/package.json +++ b/packages/decoder/package.json @@ -1,6 +1,6 @@ { "name": "@speech/decoder", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "RNN-T decoding, stable-prefix control, and contextual-scoring integration.", "type": "module", diff --git a/packages/enrollment/package.json b/packages/enrollment/package.json index 3b49887..da10b08 100644 --- a/packages/enrollment/package.json +++ b/packages/enrollment/package.json @@ -1,6 +1,6 @@ { "name": "@speech/enrollment", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Guided enrollment scheduling, CTC forced alignment, quality reporting, prompt-identity splitting, and coverage accounting.", "type": "module", diff --git a/packages/features/package.json b/packages/features/package.json index 25a94cb..f9ad0e0 100644 --- a/packages/features/package.json +++ b/packages/features/package.json @@ -1,6 +1,6 @@ { "name": "@speech/features", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Streaming acoustic feature extraction contracts and implementations.", "type": "module", diff --git a/packages/formatter/package.json b/packages/formatter/package.json index 5134c79..81d1975 100644 --- a/packages/formatter/package.json +++ b/packages/formatter/package.json @@ -1,6 +1,6 @@ { "name": "@speech/formatter", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Vietnamese/English normalization, detokenization, punctuation, and ITN.", "type": "module", diff --git a/packages/inference/package.json b/packages/inference/package.json index a383a93..73f8334 100644 --- a/packages/inference/package.json +++ b/packages/inference/package.json @@ -1,6 +1,6 @@ { "name": "@speech/inference", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "ONNX Runtime Web session adapters, provider selection, adapter runtime smoke tests, and training feasibility reports.", "type": "module", diff --git a/packages/model-manager/package.json b/packages/model-manager/package.json index 2ea0dfe..454451b 100644 --- a/packages/model-manager/package.json +++ b/packages/model-manager/package.json @@ -1,6 +1,6 @@ { "name": "@speech/model-manager", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Model catalog, install, checksum, storage, activation, rollback, and deletion lifecycle.", "type": "module", diff --git a/packages/personalization/package.json b/packages/personalization/package.json index c09e0cb..f05dc90 100644 --- a/packages/personalization/package.json +++ b/packages/personalization/package.json @@ -1,6 +1,6 @@ { "name": "@speech/personalization", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Speaker profile, personal/anchor evaluation, bilingual cohort release gates, activation decisions, adapter runtime, browser-vs-Python comparison, and residual/LHUC personalization contracts.", "type": "module", diff --git a/packages/portable-model/package.json b/packages/portable-model/package.json index cf7a099..9439e95 100644 --- a/packages/portable-model/package.json +++ b/packages/portable-model/package.json @@ -1,6 +1,6 @@ { "name": "@speech/portable-model", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Portable per-user .speechmodel manifest, deterministic inner bundle, encrypted binary envelope, CLI residual-adapter compatibility, constrained importer, and hostile-import boundary contracts.", "type": "module", diff --git a/packages/profile-manager/package.json b/packages/profile-manager/package.json index 944c680..b6c5352 100644 --- a/packages/profile-manager/package.json +++ b/packages/profile-manager/package.json @@ -1,6 +1,6 @@ { "name": "@speech/profile-manager", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Private profile storage, atomic portable model imports, activation-review guarded enables, frozen training-job revisions, feature shards, frame-label alignment sets, prompt split planning, import/export, rollback, and deletion.", "type": "module", diff --git a/packages/protocol/package.json b/packages/protocol/package.json index ccda78e..e6d014a 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -1,6 +1,6 @@ { "name": "@speech/protocol", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Versioned TypeScript contracts for workers, profiles, manifests, browser training, tools, and tests.", "type": "module", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 020017a..54cb530 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@speech/test-utils", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Shared fixtures and deterministic test helpers.", "type": "module", diff --git a/packages/ui/package.json b/packages/ui/package.json index b95b1f8..4a7ad05 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@speech/ui", - "version": "0.6.2", + "version": "0.6.3", "private": true, "description": "Semantic UI tokens and accessible frontend primitives for the task-first speech PWA.", "type": "module", diff --git a/training/tests/test_v0_5_release_snapshot.py b/training/tests/test_v0_5_release_snapshot.py index 46cc76b..6a89132 100644 --- a/training/tests/test_v0_5_release_snapshot.py +++ b/training/tests/test_v0_5_release_snapshot.py @@ -4,7 +4,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[2] -EXPECTED_CURRENT_VERSION = "0.6.2" +EXPECTED_CURRENT_VERSION = "0.6.3" def read_text(relative_path: str) -> str: diff --git a/training/tests/test_v0_6_2_cdn_csp_hotfix_release.py b/training/tests/test_v0_6_2_cdn_csp_hotfix_release.py index e5dac82..a5a4a4a 100644 --- a/training/tests/test_v0_6_2_cdn_csp_hotfix_release.py +++ b/training/tests/test_v0_6_2_cdn_csp_hotfix_release.py @@ -7,31 +7,13 @@ RELEASE_NOTES = ROOT / "docs/planning/v0.6.2-cdn-csp-hotfix-release-notes.json" CHECKLIST = ROOT / "docs/planning/v0.6.2-release-publication-checklist.json" V061_NOTES = ROOT / "docs/planning/v0.6.1-csp-hotfix-release-notes.json" -CURRENT_STATE = ROOT / "docs/planning/CURRENT_STATE.json" -PACKAGE_FILES = [ - ROOT / "package.json", - ROOT / "apps/web/package.json", - *sorted((ROOT / "packages").glob("*/package.json")), -] VERCEL_CONFIG = ROOT / "vercel.json" -PERSONAL_MODELS_PANEL = ROOT / "apps/web/src/app/PersonalModelsPanel.tsx" def read_json(path: Path) -> dict[str, object]: return json.loads(path.read_text(encoding="utf-8")) -def test_all_package_versions_are_v0_6_2() -> None: - versions = { - path.relative_to(ROOT).as_posix(): read_json(path)["version"] - for path in PACKAGE_FILES - } - - assert versions - assert set(versions.values()) == {"0.6.2"} - assert "0.6.2" in PERSONAL_MODELS_PANEL.read_text(encoding="utf-8") - - def test_v0_6_2_release_notes_describe_cdn_csp_only_hotfix() -> None: notes = read_json(RELEASE_NOTES) @@ -92,31 +74,6 @@ def test_v0_6_2_publication_checklist_keeps_release_gates_explicit() -> None: assert checklist["postPublicationEvidenceToRecord"]["modelDownloadCspVerified"] is False -def test_current_state_points_to_v0_6_2_without_closing_255() -> None: - current = read_json(CURRENT_STATE) - - assert current["packageVersion"] == "0.6.2" - assert current["currentRelease"] == "v0.6.2-cdn-csp-hotfix" - assert "v0.6.2-cdn-csp-hotfix" in current["lineage"] - assert ( - current["verifiedGates"]["releaseNotes"] - == "docs/planning/v0.6.2-cdn-csp-hotfix-release-notes.json" - ) - assert ( - current["verifiedGates"]["releasePublicationChecklist"] - == "docs/planning/v0.6.2-release-publication-checklist.json" - ) - - gates = "\n".join(current["openReleaseGates"]) - assert "Issue #255 remains open" in gates - assert "#314" in gates - - boundaries = "\n".join(current["intentionalBoundaries"]) - assert "CSP-only patch release" in boundaries - assert "does not change UI" in boundaries - assert "v0.6.0 and v0.6.1 release tags/assets remain immutable" in boundaries - - def test_v0_6_1_release_artifacts_remain_immutable() -> None: notes = read_json(V061_NOTES) diff --git a/training/tests/test_v0_6_3_real_asr_dictation_release.py b/training/tests/test_v0_6_3_real_asr_dictation_release.py new file mode 100644 index 0000000..9b03c0c --- /dev/null +++ b/training/tests/test_v0_6_3_real_asr_dictation_release.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +import json +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +RELEASE_NOTES = ROOT / "docs/planning/v0.6.3-real-asr-dictation-release-notes.json" +CHECKLIST = ROOT / "docs/planning/v0.6.3-release-publication-checklist.json" +V062_NOTES = ROOT / "docs/planning/v0.6.2-cdn-csp-hotfix-release-notes.json" +CURRENT_STATE = ROOT / "docs/planning/CURRENT_STATE.json" +PACKAGE_FILES = [ + ROOT / "package.json", + ROOT / "apps/web/package.json", + *sorted((ROOT / "packages").glob("*/package.json")), +] +PERSONAL_MODELS_PANEL = ROOT / "apps/web/src/app/PersonalModelsPanel.tsx" +PRODUCTION_DICTATION_SPEC = ROOT / "apps/web/e2e/production-dictation.spec.ts" +ASR_WORKER = ROOT / "apps/web/src/workers/asr.worker.ts" +TRANSCRIPT_PANEL = ROOT / "apps/web/src/app/TranscriptPanel.tsx" +VERCEL_CONFIG = ROOT / "vercel.json" + + +def read_json(path: Path) -> dict[str, object]: + return json.loads(path.read_text(encoding="utf-8")) + + +def test_all_package_versions_are_v0_6_3() -> None: + versions = { + path.relative_to(ROOT).as_posix(): read_json(path)["version"] + for path in PACKAGE_FILES + } + + assert versions + assert set(versions.values()) == {"0.6.3"} + assert "0.6.3" in PERSONAL_MODELS_PANEL.read_text(encoding="utf-8") + + +def test_v0_6_3_release_notes_describe_real_asr_dictation_hotfix() -> None: + notes = read_json(RELEASE_NOTES) + + assert notes["schemaVersion"] == 1 + assert notes["issue"] == 323 + assert notes["release"] == "v0.6.3" + assert notes["baseRelease"] == "v0.6.2" + assert notes["baseReleaseImmutable"] is True + assert notes["plannedTag"] == "v0.6.3" + + combined = "\n".join( + [ + notes["summary"], + *notes["changes"], + *notes["unchangedFromV062"], + *notes["knownLimitations"], + *notes["verificationPlan"], + ] + ) + assert "production dictation" in combined + assert "fake microphone" in combined or "fake-microphone" in combined + assert "START_UTTERANCE" in combined + assert "AUDIO_CHUNK" in combined + assert "END_UTTERANCE" in combined + assert "RNN-T" in combined + assert "Issue #255 remains open" in combined + assert "old service-worker app shell" in combined + assert ( + "v0.6.0, v0.6.1, and v0.6.2 release artifacts and tags are " + "intentionally left immutable" + in combined + ) + assert "No model-pack, profile, adapter" in combined + assert "privacy schema contract changes" in combined + + privacy = notes["privacy"] + for key in [ + "containsParticipantData", + "containsAudio", + "containsTranscripts", + "containsVocabularyTerms", + "containsProfileArtifacts", + "containsModelWeights", + "addsTelemetryOrAnalytics", + "allowsBroadThirdPartyNetwork", + ]: + assert privacy[key] is False + + +def test_v0_6_3_publication_checklist_keeps_release_gates_explicit() -> None: + checklist = read_json(CHECKLIST) + + assert checklist["issue"] == 323 + assert checklist["release"] == "v0.6.3" + assert checklist["baseRelease"] == "v0.6.2" + assert checklist["baseReleaseImmutable"] is True + assert any("All package versions are 0.6.3" in check for check in checklist["preTagChecks"]) + assert any("speech-web-v0.6.3-dist.tar.gz" in step for step in checklist["publicationSteps"]) + assert any("production dictation smoke" in step for step in checklist["publicationSteps"]) + assert any("Do not close issue #255" in step for step in checklist["publicationSteps"]) + assert checklist["postPublicationEvidenceToRecord"]["aboutVersionVerified"] is False + assert checklist["postPublicationEvidenceToRecord"]["productionDictationSmokeVerified"] is False + + +def test_current_state_points_to_v0_6_3_without_closing_255() -> None: + current = read_json(CURRENT_STATE) + + assert current["packageVersion"] == "0.6.3" + assert current["currentRelease"] == "v0.6.3-real-asr-dictation-hotfix" + assert "v0.6.3-real-asr-dictation-hotfix" in current["lineage"] + assert current["verifiedGates"]["releaseNotes"] == str( + RELEASE_NOTES.relative_to(ROOT).as_posix() + ) + assert current["verifiedGates"]["releasePublicationChecklist"] == str( + CHECKLIST.relative_to(ROOT).as_posix() + ) + assert current["verifiedGates"]["productionDictationSmoke"] == str( + PRODUCTION_DICTATION_SPEC.relative_to(ROOT).as_posix() + ) + + gates = "\n".join(current["openReleaseGates"]) + assert "Issue #255 remains open" in gates + assert "#323" in gates + + boundaries = "\n".join(current["intentionalBoundaries"]) + assert "real-ASR production dictation patch release" in boundaries + assert "v0.6.0, v0.6.1, and v0.6.2 release tags/assets remain immutable" in boundaries + assert "redacted" in boundaries + + +def test_v0_6_2_release_artifacts_remain_immutable() -> None: + notes = read_json(V062_NOTES) + + assert notes["release"] == "v0.6.2" + assert notes["plannedTag"] == "v0.6.2" + assert "v0.6.3" not in json.dumps(notes) + + +def test_real_asr_and_production_smoke_are_present_and_opt_in() -> None: + spec = PRODUCTION_DICTATION_SPEC.read_text(encoding="utf-8") + asr_worker = ASR_WORKER.read_text(encoding="utf-8") + transcript_panel = TRANSCRIPT_PANEL.read_text(encoding="utf-8") + + assert "SPEECH_PRODUCTION_DICTATION_E2E" in spec + assert "chromium --use-fake-device-for-media-stream" not in spec + assert "--use-file-for-fake-audio-capture" in spec + assert "SPEECH_PRODUCTION_DICTATION_PROFILE_DIR" in spec + assert "SPEECH_PRODUCTION_DICTATION_TEXT" in spec + assert "production-model-install-state.json" in spec + assert "production-dictation-failure-state.json" in spec + assert "START_UTTERANCE" in transcript_panel + assert "AUDIO_CHUNK" in transcript_panel + assert "END_UTTERANCE" in transcript_panel + assert "createOrtInferenceSession" in asr_worker + assert "GreedyRnntDecoder" in asr_worker + assert "detokenizePieces" in asr_worker + assert "createDefaultModelStorageBackend" in asr_worker + + +def test_vercel_csp_still_allows_exact_model_download_origins_only() -> None: + vercel = read_json(VERCEL_CONFIG) + headers = next(rule for rule in vercel["headers"] if rule["source"] == "/(.*)")["headers"] + csp = next(header["value"] for header in headers if header["key"] == "Content-Security-Policy") + + connect_src = next( + directive.strip().split()[1:] + for directive in csp.split(";") + if directive.strip().startswith("connect-src ") + ) + + assert connect_src == [ + "'self'", + "https://huggingface.co", + "https://us.aws.cdn.hf.co", + ] + assert "https:" not in connect_src + assert "https://*.huggingface.co" not in connect_src + assert "https://*.hf.co" not in connect_src + assert "*" not in connect_src diff --git a/training/tests/test_v0_6_documentation_screenshots.py b/training/tests/test_v0_6_documentation_screenshots.py index c2c3d8e..6f5bfaf 100644 --- a/training/tests/test_v0_6_documentation_screenshots.py +++ b/training/tests/test_v0_6_documentation_screenshots.py @@ -99,8 +99,8 @@ def test_current_state_and_snapshots_preserve_release_gate_boundaries() -> None: archive = read_json(V0_5_ARCHIVE) candidate = read_json(V0_6_CANDIDATE) - assert current["currentRelease"] == "v0.6.2-cdn-csp-hotfix" - assert current["packageVersion"] == "0.6.2" + assert current["currentRelease"] == "v0.6.3-real-asr-dictation-hotfix" + assert current["packageVersion"] == "0.6.3" assert current["informationArchitecture"]["persistentPrimaryDestinations"] == [ "Dictate", "Vocabulary", @@ -131,7 +131,7 @@ def test_public_docs_match_v0_6_ui_without_overclaiming_release_evidence() -> No components_adr = read_text(ADR_COMPONENTS) for phrase in [ - "v0.6.2 CDN/Xet CSP hotfix", + "v0.6.3 real-ASR production dictation hotfix", "**Dictate**, **Vocabulary**, and **Models**", "Encrypted `.speechmodel` export is the default", "Screenshot PNGs are captured locally under `/tmp` and are not committed", diff --git a/training/tests/test_v0_6_update_offline_rollback.py b/training/tests/test_v0_6_update_offline_rollback.py index 4de3b49..2c35f7b 100644 --- a/training/tests/test_v0_6_update_offline_rollback.py +++ b/training/tests/test_v0_6_update_offline_rollback.py @@ -75,11 +75,11 @@ def test_current_state_points_to_update_offline_rollback_evidence() -> None: gates = "\n".join(current["openReleaseGates"]) assert "Issue #255 remains open" in gates assert "Production accuracy/performance claims" in gates - issue_310_gate = ( - "Publication/deployment evidence is issue #314 work and must be completed " - "on the tagged v0.6.2 hotfix release commit before closing #314." + issue_323_gate = ( + "Publication/deployment evidence is issue #323 work and must be completed " + "on the tagged v0.6.3 hotfix release commit before closing #323." ) - assert issue_310_gate in gates + assert issue_323_gate in gates def test_update_prompt_and_offline_restart_tests_are_present() -> None: