Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
5046019
fix(workspace): detect the initial UI language from browser preferences
Green-hats Sep 10, 2026
9e7599b
fix(dashboard): survive unavailable browser storage
Green-hats Sep 10, 2026
5098222
fix(chat): restore workspace choices for gated goals
luw2007 Sep 14, 2026
04cb337
test(chat): cover restored workspace gate selection
luw2007 Sep 14, 2026
4089025
fix(hygiene): treat GitHub's own rendered pages as live surfaces
yuedai-pbc Sep 23, 2026
fdce140
feat(team): trace version-bound correction paths on demand
huangruiteng Sep 24, 2026
e429950
docs(team): record L1 correction readback boundary
huangruiteng Sep 24, 2026
d75ebf7
fix(quota): name the retained selection and the replan obligation tha…
huangruiteng Sep 24, 2026
d5150c5
docs: define reusable conversation work surface and steward adoption
huangruiteng Sep 24, 2026
a2995a6
feat(chat): expose real turn activity and scoped interruption
huangruiteng Sep 24, 2026
1025890
Strengthen PR review scope evidence and published conclusions
huangruiteng Sep 24, 2026
43b65eb
Document review depth policy and scope self-repair pattern
huangruiteng Sep 24, 2026
12283f9
test(chat): exercise conversation activity and interruption in browser
huangruiteng Sep 24, 2026
9779cff
fix(quota): distinguish retained selection from replan identity
huangruiteng Sep 24, 2026
8b00d26
Judge sustained progress and user experience before review approval
huangruiteng Sep 24, 2026
a4c61de
feat(chat): deliver adjustments to the exact running turn
huangruiteng Sep 24, 2026
90f21a5
Merge pull request #4993 from loopx-project/codex/retained-selection-…
huangruiteng Sep 24, 2026
95c6fe5
Ground review fixtures in historical source and causal findings
huangruiteng Sep 24, 2026
401f1b2
fix(windows): gate directory fsync to POSIX
Duang777 Sep 24, 2026
bdf1519
Merge pull request #4987 from loopx-project/codex/live-team-l1-eviden…
huangruiteng Sep 24, 2026
b928cb4
test: select source Python for Node subprocesses
huangruiteng Sep 24, 2026
8e21a90
docs: record Node test Python discovery contract
huangruiteng Sep 24, 2026
f1ef1e5
test: tighten cross-platform Python fallback guard
huangruiteng Sep 24, 2026
0b1de3a
test: route every bare-python test caller through the source selector
huangruiteng Sep 24, 2026
ff6a32c
fix(replan): exclude monitors from agent long-chain thresholds
Jim-jimu Sep 24, 2026
47941d5
fix(chat): refresh registry I/O census after turn controls
huangruiteng Sep 24, 2026
f2f9196
Merge pull request #4980 from loopx-project/codex/python-test-interpr…
huangruiteng Sep 24, 2026
868b564
Merge remote-tracking branch 'origin/main' into fix/monitor-replan-th…
Jim-jimu Sep 24, 2026
d64c4d3
Merge pull request #5002 from Duang777/codex/fix-windows-directory-fsync
huangruiteng Sep 24, 2026
934e704
Merge remote-tracking branch 'origin/main' into fix/monitor-replan-th…
Jim-jimu Sep 24, 2026
91b36be
fix(coordination): carry complete sources through bounded private tra…
huangruiteng Sep 24, 2026
05078b7
docs(authority): reconcile delivered code and remaining default cutov…
huangruiteng Sep 24, 2026
7ae38a0
refactor(coordination): single-source transfer protocol in existing s…
huangruiteng Sep 24, 2026
f2a18a9
fix(windows): skip POSIX-only fchmod in atomic private writers
huangruiteng Sep 24, 2026
d7ff0e2
test(authority): freeze the Python discovery helper with mutation ora…
huangruiteng Sep 24, 2026
dc72dc4
test(replan): align retained selection with advancement threshold
Jim-jimu Sep 24, 2026
93566c2
Merge pull request #5004 from Jim-jimu/fix/monitor-replan-threshold
huangruiteng Sep 24, 2026
0056a31
Merge pull request #4996 from loopx-project/codex/steward-interaction…
huangruiteng Sep 24, 2026
ba8aafb
merge: include complete-source transfer for release qualification
huangruiteng Sep 24, 2026
361539c
merge: include Windows atomic-write fix for release qualification
huangruiteng Sep 24, 2026
bc01dfc
refactor(turn): keep release qualification within owned runtime bound…
huangruiteng Sep 24, 2026
33aa19a
fix(qualification): restore release smoke and CI baselines
huangruiteng Sep 24, 2026
7bb5982
fix(capabilities): keep progress review editor within catalog vocabulary
huangruiteng Sep 24, 2026
2ee12b9
fix(dashboard): show machine catalog load failures and retry
huangruiteng Sep 24, 2026
c088d63
Merge pull request #5009 from loopx-project/codex/fix-machine-capabil…
huangruiteng Sep 24, 2026
46e6859
feat: bind local reports to accepted Todo completions
huangruiteng Sep 24, 2026
adb7e8e
test: prove accepted report readback and stale rejection
huangruiteng Sep 24, 2026
f185eec
fix: recover accepted report replay after source removal
huangruiteng Sep 24, 2026
4f5e7d5
feat: read accepted managed reports in Goal Files
huangruiteng Sep 24, 2026
979da3a
test: verify managed report Files readback
huangruiteng Sep 24, 2026
d552e4b
fix: bound managed report verification to the requested page
huangruiteng Sep 24, 2026
86d4a99
fix: reject malformed managed report inventories without crashing Files
huangruiteng Sep 24, 2026
fd4241d
fix: exclude frontend placeholders from source fingerprint
huangruiteng Sep 24, 2026
226718a
feat(chat): return accepted managed reports to source manager plan
huangruiteng Sep 24, 2026
a0363db
docs(roadmap): distinguish managed report return from adoption
huangruiteng Sep 24, 2026
45a0c45
fix: scope managed report readback to the plan's own Todos
huangruiteng Sep 24, 2026
4b6ebf1
fix: install completion result objects atomically
huangruiteng Sep 24, 2026
c837315
fix: keep Todo heads written before an additive contract field
huangruiteng Sep 24, 2026
8e91b93
test(stage2c): freeze Python launcher for mutation controls
huangruiteng Sep 24, 2026
5ae9843
merge: refresh release qualification on latest main
huangruiteng Sep 24, 2026
e15959b
fix(hygiene): bind the governance exceptions to one record each
yuedai-pbc Sep 24, 2026
46280da
Merge pull request #4978 from loopx-project/codex/managed-result-loca…
huangruiteng Sep 24, 2026
f888257
merge: integrate accepted report readback before release
huangruiteng Sep 24, 2026
a4a54aa
test(dashboard): follow accepted report empty state in browser smoke
huangruiteng Sep 24, 2026
6d29882
Merge pull request #4954 from yuedai-pbc/yuedai/live-surface-github-m…
huangruiteng Sep 24, 2026
79d168f
fix(coordination): carry explicit context through leased handoff
huangruiteng Sep 24, 2026
3a1be5a
docs(authority): qualify leased continuation and remaining default pr…
huangruiteng Sep 24, 2026
f4e6eeb
fix: launch leased-continuation CLI through the selected test Python
huangruiteng Sep 24, 2026
a73b616
fix: ignore hidden HTML comments in PR review evidence
huangruiteng Sep 24, 2026
2eef2b5
Merge current main after GitHub hygiene fix
huangruiteng Sep 24, 2026
e363b96
fix: honor full Markdown fence boundaries in review evidence
huangruiteng Sep 24, 2026
2e1e632
Merge pull request #4994 from loopx-project/codex/leased-continuation…
huangruiteng Sep 24, 2026
23b8c17
Merge current main after leased continuation fix
huangruiteng Sep 24, 2026
fb77315
Merge pull request #4998 from loopx-project/codex/pr-review-evidence-…
huangruiteng Sep 24, 2026
2605c06
Merge current main after review policy update
huangruiteng Sep 24, 2026
3414fc7
Merge pull request #5010 from loopx-project/codex/release-integration…
huangruiteng Sep 24, 2026
b150841
fix(quota): release rejected monitor poll reservations
huangruiteng Sep 24, 2026
a6b79e6
Merge pull request #4991 from loopx-project/codex/monitor-poll-lease-…
huangruiteng Sep 24, 2026
9ec1a1f
fix: transport monitor lease proof through generated CLI route
huangruiteng Sep 24, 2026
3a82f88
test: cover leased monitor transport and crash replay
huangruiteng Sep 24, 2026
0e687fc
docs: explain monitor lease proof and replay contract
huangruiteng Sep 24, 2026
717535c
test(quota): reject foreign monitor lease transport
huangruiteng Sep 24, 2026
e5eda31
Merge current main and retain storage fallback
huangruiteng Sep 24, 2026
989b249
Merge pull request #4995 from loopx-project/codex/monitor-poll-proof-…
huangruiteng Sep 24, 2026
3152998
fix(quota): keep deferred receipt-bound Turn from selecting a success…
huangruiteng Sep 24, 2026
5bba250
Merge current main for workspace locale fix
huangruiteng Sep 24, 2026
193db99
Merge latest main for workspace locale fix
huangruiteng Sep 24, 2026
d665c5c
Merge current main for restored workspace gate
huangruiteng Sep 24, 2026
cecffc9
test(workspace): verify restored gate through confirmed readback
huangruiteng Sep 24, 2026
0fa2c00
Merge pull request #4159 from Green-hats/niu/dashboard-storage-unavai…
huangruiteng Sep 24, 2026
5284f1f
Merge pull request #4156 from Green-hats/niu/issue-3543-workspace-locale
huangruiteng Sep 24, 2026
485ada0
Merge current main before workspace gate qualification
huangruiteng Sep 24, 2026
5a70b6d
Merge pull request #4363 from luw2007/codex/fix-goal-create-workspace…
huangruiteng Sep 24, 2026
2e9892a
release: prepare LoopX 1.2.0 version
huangruiteng Sep 24, 2026
e6cc6c7
Merge pull request #5015 from loopx-project/codex/release-v1-2-0
huangruiteng Sep 24, 2026
1678dc8
chore: remove unused imports blocking release lint
huangruiteng Sep 24, 2026
3b3e92e
chore: refresh project registry I/O census for release
huangruiteng Sep 24, 2026
0ac4122
Merge pull request #5017 from loopx-project/codex/release-ruff-clean
huangruiteng Sep 24, 2026
12990b2
fix(turn): preserve complete signed host actions
huangruiteng Sep 24, 2026
132a26d
Merge pull request #5018 from loopx-project/codex/release-preserve-ac…
huangruiteng Sep 24, 2026
59dae89
fix(release): make qualification deterministic on configured hosts
huangruiteng Sep 24, 2026
116ed4a
Merge pull request #5019 from loopx-project/codex/release-isolate-ope…
huangruiteng Sep 24, 2026
9bb1228
test(reward-memory): isolate preview from host OpenViking
huangruiteng Sep 24, 2026
262a313
docs(release): align book and smoke baselines with v1.2.0
huangruiteng Sep 24, 2026
d14f39a
Merge pull request #5020 from loopx-project/codex/release-isolate-rew…
huangruiteng Sep 24, 2026
dcbf1cf
fix(quota): replay unleased monitor poll receipts
huangruiteng Sep 24, 2026
b73d59e
Merge pull request #5021 from loopx-project/codex/release-monitor-rep…
huangruiteng Sep 24, 2026
c9b3c56
fix(release): stabilize live model qualification (#5022)
huangruiteng Sep 24, 2026
563de3b
fix(release): probe real external-wait fallback decision (#5023)
huangruiteng Sep 24, 2026
2c8f9e5
fix(release): recognize installed wheel in deep doctor (#5024)
huangruiteng Sep 25, 2026
cd87b86
fix(runtime): allow bounded typed responses under host load
huangruiteng Sep 25, 2026
028136a
fix(qualification): complete bounded vision closeout and retain safe …
huangruiteng Sep 25, 2026
785cf06
docs(qualification): align live portfolio and vision limits
huangruiteng Sep 25, 2026
5a4617e
Merge pull request #5025 from loopx-project/codex/release-vision-clos…
huangruiteng Sep 25, 2026
19180e1
fix(chat): reconcile turn controls and safe retry on main
huangruiteng Sep 25, 2026
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
4 changes: 2 additions & 2 deletions .github/GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ but do not replace the approval required from an eligible GitHub reviewer.

## Main Branch Merge Gates

The live [main ruleset](https://github.com/huangruiteng/loopx/rules/18121976)
The live [main ruleset](https://github.com/loopx-project/loopx/rules/18121976)
is the operational authority. It requires a pull request, one approving
review, code-owner approval where applicable, dismissal of stale approvals,
approval of the last push by another reviewer, resolved review threads, and
Expand Down Expand Up @@ -220,7 +220,7 @@ The versioned
[Current Technical Directions](../docs/project/technical-directions.md) page is
the canonical map of active strategic programs, maturity, contribution routes,
and promotion gates. The pinned
[GitHub Discussion](https://github.com/huangruiteng/loopx/discussions/2851) is
[GitHub Discussion](https://github.com/loopx-project/loopx/discussions/2851) is
its community-facing projection; an issue, Discussion, RFC, or integration
branch does not override merged runtime and stable reference contracts.

Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ body:
- type: markdown
attributes:
value: |
Thanks for helping improve LoopX. Do not report an unpatched security vulnerability here; use [Private Vulnerability Reporting](https://github.com/huangruiteng/loopx/security/advisories/new). Remove credentials, private data, raw agent sessions, internal links, and local runtime state before submitting.
Thanks for helping improve LoopX. Do not report an unpatched security vulnerability here; use [Private Vulnerability Reporting](https://github.com/loopx-project/loopx/security/advisories/new). Remove credentials, private data, raw agent sessions, internal links, and local runtime state before submitting.

- type: checkboxes
id: preflight
Expand Down
8 changes: 4 additions & 4 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
blank_issues_enabled: false
contact_links:
- name: Ask a question
url: https://github.com/huangruiteng/loopx/discussions/categories/q-a
url: https://github.com/loopx-project/loopx/discussions/categories/q-a
about: Get community help with usage, configuration, or design questions.
- name: Discord community
url: https://discord.gg/XmGgQyCFZd
about: Join informal onboarding, workflow, and show-and-tell conversations.
- name: Report a security vulnerability
url: https://github.com/huangruiteng/loopx/security/advisories/new
url: https://github.com/loopx-project/loopx/security/advisories/new
about: Privately report suspected unpatched vulnerabilities. Do not open a public issue.
- name: Contributor task board
url: https://github.com/huangruiteng/loopx/blob/main/docs/development/contributor-tasks.md
url: https://github.com/loopx-project/loopx/blob/main/docs/development/contributor-tasks.md
about: Start here for public, claimable work.
- name: Current technical directions
url: https://github.com/huangruiteng/loopx/blob/main/docs/project/technical-directions.md
url: https://github.com/loopx-project/loopx/blob/main/docs/project/technical-directions.md
about: Understand active programs, maturity, ownership boundaries, and promotion gates.
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ baseline/head comparison and a failing-before or mutation check, not just test c
Documentation-only changes may use a static/manual row and explain runtime N/A.
A passing row does not waive required real-path/backend gates. -->

See [validation disclosure guidance](https://github.com/huangruiteng/loopx/blob/main/CONTRIBUTING.md#validation-disclosure).
See [validation disclosure guidance](https://github.com/loopx-project/loopx/blob/main/CONTRIBUTING.md#validation-disclosure).

## Frontend / Visual Evidence

Expand Down
2 changes: 1 addition & 1 deletion .github/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ version-specific regression.
## Reporting A Vulnerability

Please report suspected vulnerabilities through
[GitHub Private Vulnerability Reporting](https://github.com/huangruiteng/loopx/security/advisories/new).
[GitHub Private Vulnerability Reporting](https://github.com/loopx-project/loopx/security/advisories/new).
Do not open a public issue, discussion, or pull request for an unpatched
vulnerability.

Expand Down
18 changes: 9 additions & 9 deletions .github/SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ channels.

| Need | Channel | Use it for |
| --- | --- | --- |
| Reproducible bug or installation failure | [GitHub Issues](https://github.com/huangruiteng/loopx/issues/new/choose) | Public, sanitized reproduction steps for behavior that can be investigated or fixed in the repository. |
| Feature request | [GitHub Issues](https://github.com/huangruiteng/loopx/issues/new/choose) | A concrete problem, desired outcome, alternatives, and the smallest useful product change. |
| Usage or design question | [GitHub Discussions: Q&A](https://github.com/huangruiteng/loopx/discussions/categories/q-a) | Questions, configuration help, and design discussion that do not yet identify a repository bug. |
| Security vulnerability | [Private Vulnerability Reporting](https://github.com/huangruiteng/loopx/security/advisories/new) | Suspected unpatched vulnerabilities. Do not post them in an issue, discussion, or chat. See [`SECURITY.md`](SECURITY.md). |
| Reproducible bug or installation failure | [GitHub Issues](https://github.com/loopx-project/loopx/issues/new/choose) | Public, sanitized reproduction steps for behavior that can be investigated or fixed in the repository. |
| Feature request | [GitHub Issues](https://github.com/loopx-project/loopx/issues/new/choose) | A concrete problem, desired outcome, alternatives, and the smallest useful product change. |
| Usage or design question | [GitHub Discussions: Q&A](https://github.com/loopx-project/loopx/discussions/categories/q-a) | Questions, configuration help, and design discussion that do not yet identify a repository bug. |
| Security vulnerability | [Private Vulnerability Reporting](https://github.com/loopx-project/loopx/security/advisories/new) | Suspected unpatched vulnerabilities. Do not post them in an issue, discussion, or chat. See [`SECURITY.md`](SECURITY.md). |
| Informal peer help | [Discord](https://discord.gg/XmGgQyCFZd) | Onboarding, workflow comparison, show and tell, and community conversation. Chat is not an authoritative support or release record. |

Public contributor work belongs on the
Expand All @@ -25,14 +25,14 @@ requests should follow

## Official Publication Sources

- [GitHub Releases](https://github.com/huangruiteng/loopx/releases) is the
- [GitHub Releases](https://github.com/loopx-project/loopx/releases) is the
authoritative source for published versions and release notes.
- [GitHub Discussions: Announcements](https://github.com/huangruiteng/loopx/discussions/categories/announcements)
- [GitHub Discussions: Announcements](https://github.com/loopx-project/loopx/discussions/categories/announcements)
is the authoritative source for project announcements that are not tied to
one release. The pinned
[Current technical directions and known limitations](https://github.com/huangruiteng/loopx/discussions/2851)
[Current technical directions and known limitations](https://github.com/loopx-project/loopx/discussions/2851)
post is the community-facing projection of the versioned repository map.
- [GitHub Security Advisories](https://github.com/huangruiteng/loopx/security/advisories)
- [GitHub Security Advisories](https://github.com/loopx-project/loopx/security/advisories)
is the authoritative source for coordinated vulnerability disclosures.

Repository documentation describes the current product and contributor
Expand All @@ -49,7 +49,7 @@ official publication source. Reposts, screenshots, personal accounts, and
third-party communities may be useful, but they are not authoritative project
communications. When sources conflict, prefer the official GitHub source for
the relevant topic and ask for clarification in a public
[Discussion](https://github.com/huangruiteng/loopx/discussions).
[Discussion](https://github.com/loopx-project/loopx/discussions).

## Make A Useful Request

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,9 @@ jobs:
- name: Install test dependencies
run: python -m pip install --disable-pip-version-check -e ".[test]"

- name: Verify native Windows test Python discovery
run: node --no-warnings --experimental-strip-types --test tests/control_plane_ts/test_python_runtime.test.ts

- name: Run native Windows lifecycle tests
run: >-
python -m pytest -q
Expand All @@ -567,6 +570,7 @@ jobs:
tests/control_plane/test_effect_runtime_integration.py
tests/control_plane/test_local_authority_shadow_outbox.py
tests/test_self_update_runtime_activation.py
tests/test_windows_atomic_writes.py
tests/test_windows_install.py

- name: Set up the qualified SQLite runtime
Expand Down
22 changes: 22 additions & 0 deletions apps/presentation/dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ Personal Workspace is the operator UI for Goals, Tasks, Chat, and outputs.
The CLI and versioned control-plane projections remain the sources of truth;
the UI does not create a second state authority.

## Workspace Language / 工作区语言

On first use, the workspace follows the browser's ordered language preferences:
English uses English, and Chinese uses the available Simplified Chinese catalog.
If neither language matches or language information is unavailable, it uses
English. This replaces the previous unconditional Chinese default.

Choose **Settings → Language** to override detection. Explicit choices remain in
the existing `loopx-pw-locale` browser storage entry and survive reloads and
upgrades on the same origin. A different host/port, cleared browser storage, or a
fresh desktop webview starts detection again. When storage is blocked, language
changes last for the current session only.

首次打开时,工作区按浏览器语言偏好顺序选择英文或简体中文;没有匹配语言或无法
获取语言信息时使用英文,替代原来固定使用中文的默认行为。
可在 **设置 → 语言** 手动选择,同一站点来源下的刷新和升级会保留选择。
更换主机或端口、清除浏览器存储或使用新的桌面 WebView 后会重新检测;浏览器
禁止存储时,手动选择仅在当前会话有效。

Validate this behavior with `npm run smoke:workspace-locale`; the same cases also
run in the full development and packaged personal-workspace browser smokes.

## Fresh Clone Public Preview

No private LoopX state is required for the first dashboard preview. The
Expand Down
1 change: 1 addition & 0 deletions apps/presentation/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"smoke:home-route": "rm -rf /tmp/loopx-home-route-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-home-route-smoke smoke/home-route-smoke.ts && node /tmp/loopx-home-route-smoke/home-route-smoke.js",
"smoke:delegation-preflight": "rm -rf node_modules/.cache/loopx-delegation-preflight-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --jsx react-jsx --skipLibCheck --strict --rootDir . --outDir node_modules/.cache/loopx-delegation-preflight-smoke smoke/delegation-preflight-smoke.tsx src/data/delegation-preflight.ts src/features/personal-workspace/delegation-preflight-status.tsx && node node_modules/.cache/loopx-delegation-preflight-smoke/smoke/delegation-preflight-smoke.js",
"smoke:personal-workspace": "npm run smoke:goal-order && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser-smoke.mjs",
"smoke:workspace-locale": "LOOPX_PERSONAL_WORKSPACE_SCENARIO=workspace-locale node ../../../examples/personal-workspace-browser-smoke.mjs",
"smoke:personal-workspace-packaged": "npm run smoke:goal-order && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs",
"smoke:personal-workspace-router": "rm -rf /tmp/loopx-personal-workspace-router-smoke && tsc --ignoreConfig --target ES2022 --module commonjs --moduleResolution node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-personal-workspace-router-smoke smoke/personal-workspace-router-smoke.ts src/features/personal-workspace/personal-workspace-router.ts && node /tmp/loopx-personal-workspace-router-smoke/smoke/personal-workspace-router-smoke.js",
"smoke:presentation-surface-schema": "rm -rf /tmp/loopx-presentation-surface-schema-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --resolveJsonModule --esModuleInterop --outDir /tmp/loopx-presentation-surface-schema-smoke smoke/presentation-surface-schema-smoke.ts src/data/status.ts src/data/decision-research.ts src/data/goal-channel-frontstage.ts && NODE_PATH=\"$PWD/node_modules\" node /tmp/loopx-presentation-surface-schema-smoke/apps/presentation/dashboard/smoke/presentation-surface-schema-smoke.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { cleanupBrowserSmoke, launchBrowser, startViteDashboardServer, waitForHttp } from "../../../../examples/dashboard-browser-smoke-support.mjs";
import { acceptance, contract, snapshot, verifiedContract } from "./goal-acceptance-contract-fixture.mjs";
import { resolveTestPython } from "../../../../scripts/test-python.mjs";

const dashboardDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const root = resolve(dashboardDir, "../../..");
const python = process.env.LOOPX_PYTHON ?? "python3";
const python = resolveTestPython();
const port = Number(process.env.LOOPX_ACCEPTANCE_CONTRACT_PORT ?? 5297);
const packaged = process.env.LOOPX_ACCEPTANCE_CONTRACT_PACKAGED === "1";
const payload = JSON.parse(execFileSync(python, ["-c", `
Expand Down
101 changes: 101 additions & 0 deletions apps/presentation/dashboard/smoke/workspace-locale-browser-smoke.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import assert from "node:assert/strict";
import { resolve } from "node:path";

// Exercise the shipped provider and Settings UI against the shared synthetic API.
export async function verifyWorkspaceLocales({ browser, url, installApi, outputDir }) {
const cases = [
{ name: "fresh English", locale: "en-US", expected: "en" },
{ name: "fresh Chinese", locale: "zh-CN", expected: "zh-CN" },
{ name: "Chinese region", locale: "zh-TW", expected: "zh-CN" },
{ name: "unsupported language", locale: "fr-FR", expected: "en" },
{ name: "ordered preferences", languages: ["fr-FR", "en-GB", "zh-CN"], expected: "en" },
{ name: "secondary Chinese", languages: ["fr-FR", "zh-CN", "en"], expected: "zh-CN" },
{ name: "language fallback", languages: [], locale: "zh-CN", expected: "zh-CN" },
{ name: "missing language", languages: [], language: "", expected: "en" },
{ name: "saved English", stored: "en", locale: "zh-CN", expected: "en" },
{ name: "saved Chinese", stored: "zh-CN", locale: "en-US", expected: "zh-CN" },
{ name: "invalid preference", stored: "invalid", locale: "en-US", expected: "en" },
{ name: "locale storage failure English", blocked: true, locale: "en-US", expected: "en" },
{ name: "locale storage failure Chinese", blocked: true, locale: "zh-CN", expected: "zh-CN" },
];
for (const scenario of cases) {
const page = await browser.newPage({ locale: scenario.locale ?? "en-US" });
try {
await installApi(page);
await page.addInitScript(({ stored, blocked, languages, language }) => {
if (stored !== undefined) localStorage.setItem("loopx-pw-locale", stored);
if (languages !== undefined) Object.defineProperty(navigator, "languages", { value: languages });
if (language !== undefined) Object.defineProperty(navigator, "language", { value: language });
if (blocked) {
const getItem = Storage.prototype.getItem;
const setItem = Storage.prototype.setItem;
Storage.prototype.getItem = function (key) {
if (key === "loopx-pw-locale") throw new DOMException("Storage unavailable", "SecurityError");
return getItem.call(this, key);
};
Storage.prototype.setItem = function (key, value) {
if (key === "loopx-pw-locale") throw new DOMException("Storage unavailable", "SecurityError");
return setItem.call(this, key, value);
};
}
}, scenario);
await page.goto(url, { waitUntil: "networkidle" });
await page.getByTestId("personal-goal-home").waitFor();
assert.equal(await page.locator("html").getAttribute("lang"), scenario.expected, scenario.name);
const label = scenario.expected === "en" ? "Settings" : "设置";
assert.ok(await page.getByRole("button", { name: label, exact: true }).isVisible(), scenario.name);
if (!scenario.blocked) {
assert.equal(await page.evaluate(() => localStorage.getItem("loopx-pw-locale")), scenario.stored ?? null,
`${scenario.name}: detection must not overwrite a saved choice`);
}
if (scenario.blocked) {
await page.getByRole("button", { name: label, exact: true }).click();
await page.getByRole("button", { name: /Language|语言/ }).click();
const next = scenario.expected === "en" ? "zh-CN" : "en";
await page.getByRole("radio", { name: next === "en" ? /English/ : /Simplified Chinese/ }).click();
assert.equal(await page.locator("html").getAttribute("lang"), next,
"A locale write failure still permits changing the current session");
}
console.log(`workspace locale: ${scenario.name}: ok`);
} finally {
await page.close();
}
}

const page = await browser.newPage({ locale: "en-US", viewport: { width: 1440, height: 960 } });
try {
await installApi(page);
await page.goto(url, { waitUntil: "networkidle" });
await page.getByTestId("personal-goal-home").waitFor();
await page.screenshot({ path: resolve(outputDir, "locale-default-english.png") });
await page.setViewportSize({ width: 390, height: 844 });
await page.locator(".personal-channel-scroll").evaluate((element) => { element.scrollTop = 0; });
const clippedStats = await page.locator(".personal-digest-stats span").evaluateAll((stats) =>
stats.filter((stat) => {
const rect = stat.getBoundingClientRect();
return rect.left < 0 || rect.right > window.innerWidth;
}).length,
);
assert.equal(clippedStats, 0, "English summary statistics fit the mobile viewport");
await page.screenshot({ path: resolve(outputDir, "locale-default-english-mobile.png") });
await page.setViewportSize({ width: 1440, height: 960 });
await page.getByRole("button", { name: "Settings", exact: true }).click();
await page.getByRole("button", { name: /Language/ }).click();
await page.getByRole("radio", { name: /Simplified Chinese/ }).click();
assert.equal(await page.evaluate(() => localStorage.getItem("loopx-pw-locale")), "zh-CN");
await page.reload({ waitUntil: "networkidle" });
await page.getByTestId("personal-goal-home").waitFor();
assert.equal(await page.locator("html").getAttribute("lang"), "zh-CN");
await page.screenshot({ path: resolve(outputDir, "locale-saved-chinese.png") });
await page.getByRole("button", { name: "设置", exact: true }).click();
await page.getByRole("button", { name: /语言/ }).click();
await page.getByRole("radio", { name: /English/ }).click();
await page.reload({ waitUntil: "networkidle" });
await page.getByTestId("personal-goal-home").waitFor();
assert.equal(await page.locator("html").getAttribute("lang"), "en");
assert.equal(await page.evaluate(() => localStorage.getItem("loopx-pw-locale")), "en");
console.log("workspace locale: Settings choices survive reload in both directions: ok");
} finally {
await page.close();
}
}
Loading
Loading