Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/agents/rename-detector.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ identifier deleted elsewhere in the diff. For each candidate:
- String interpolations, log templates, `nameof()` calls.
- Test data, `[InlineData]` arrays.
- Doc comments and inline `///` references.
- `docs/site/` (incl. `docs/site/src/components/SyncExplainer/syncScenarios.ts`): the user docs quote UI labels verbatim, so a changed `msgid` in `frontend/viewer/src/locales/en.po` or value in `frontend/src/lib/i18n/locales/en.json` can leave a stale label there.
3. Report each straggler with `file:line`.

## Blast radius
Expand All @@ -42,6 +43,7 @@ the old name." Helps the author verify completeness.
- Stale generated TS → ⚠️ important (auto-fixable by rebuild).
- Snapshot fixtures with old name → ⚠️ important.
- String interpolations → ⚠️ important.
- Old UI label in `docs/site/` → ⚠️ important (users follow the docs to a button that's gone).
- Comments / doc strings → 💭 nit.

## Finding format
Expand Down
16 changes: 16 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,22 @@ updates:
- nx
- "@nx/*"

# --- docs site ---
- package-ecosystem: npm
directory: /docs/site
target-branch: develop
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 10
labels:
- dependencies
groups:
production-dependencies:
dependency-type: production
development-dependencies:
dependency-type: development

# --- GitHub Actions ---
- package-ecosystem: github-actions
# Workflows plus composite actions (.github/actions/*/action.y*ml)
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Docs
on:
push:
paths:
- 'docs/site/**'
- '.github/workflows/docs.yaml'
branches:
- develop
pull_request:
paths:
- 'docs/site/**'
- '.github/workflows/docs.yaml'
branches:
- develop
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: ./docs/site
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Full history for each page's "Last updated" date.
fetch-depth: 0
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
package_json_file: 'docs/site/package.json'
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: './docs/site/package.json'
cache: 'pnpm'
cache-dependency-path: './docs/site/pnpm-lock.yaml'
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck
# Build for this repo's GitHub Pages URL until docs.lexbox.org is set up.
- run: pnpm build
env:
DOCS_URL: https://${{ github.repository_owner }}.github.io
DOCS_BASE_URL: /${{ github.event.repository.name }}/
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
if: ${{ github.ref == 'refs/heads/develop' }}
with:
path: docs/site/build

deploy:
name: Deploy to GitHub Pages
if: ${{ github.ref == 'refs/heads/develop' }}
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
12 changes: 8 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ languageforge-lexbox/
│ └── Testing/ # Test projects
├── frontend/ # Lexbox SvelteKit web app
├── frontend/viewer/ # FieldWorks Lite frontend Svelte code
├── docs/site/ # Public docs site: fw-lite/, lexbox/, technical/ (see docs/site/AGENTS.md)
├── docs/dev/ # Developer docs: setup guides, troubleshooting, ADRs
└── deployment/ # K8s/Docker configs
```

Expand All @@ -36,9 +38,10 @@ Key documentation for this project:
- `README.md` - Project overview and setup
- `AGENTS.md` - You are here! Agent instructions
- `.github/AGENTS.md` - **CI/CD and deployment guide** (workflows, K8s, Docker)
- `docs/DEVELOPER-win.md` - Windows development setup
- `docs/DEVELOPER-linux.md` - Linux development setup
- `docs/DEVELOPER-osx.md` - macOS development setup
- `docs/site/AGENTS.md` - **Docs site** (user guides + technical docs): which page covers what, writing rules, links that must not break
- `docs/dev/DEVELOPER-win.md` - Windows development setup
- `docs/dev/DEVELOPER-linux.md` - Linux development setup
- `docs/dev/DEVELOPER-osx.md` - macOS development setup
- `backend/README.md` - Backend architecture
- `backend/AGENTS.md` - General backend guidelines
- `backend/LexBoxApi/AGENTS.md` - API & GraphQL specific rules
Expand Down Expand Up @@ -84,7 +87,7 @@ Do not run `kubectl`, `helm`, `k9s`, port-forwards, or anything else that reads
### Questions?

- Check existing issues: `gh issue list --limit 30`
- Read the docs in `docs/` directory
- Read `docs/site/technical/` (architecture, sync) and `docs/dev/adr/` (decisions)
- Create a GitHub issue if unsure (public! — see 🔒 Privacy rules above)
- Ask the user to clarify

Expand All @@ -102,6 +105,7 @@ Before implementing any change that will touch many files or is in a 🔴 **Crit
- ✅ If the user asks about "the" PR, but does not explicitly name a PR or branch, assume they mean the PR associated with the current branch.
- ✅ Use **Mermaid diagrams** for flowcharts and architecture (not ASCII art)
- ✅ When handling a user prompt ALWAYS ask for clarification if there are details to clarify, important decisions that must be made first or the plan sounds unwise
- ✅ **Update the docs in the same PR** when you change what FW Lite or lexbox.org users see or do (a label, a flow, sync behavior, roles) or how the system is built or run: `git grep` the old wording under `docs/site/` and fix every hit. Page map and writing rules: `docs/site/AGENTS.md`.
- ❌ Do NOT git commit or git push without explicit user approval
- ❌ Do NOT post project/user identifiers or any real-project data online, and do NOT touch a k8s cluster without explicit permission — see **🔒 Privacy and Production Access** above
- ⚠️ **Pre-commit hook prettier-formats staged `*.{js,ts,svelte,css,md,json}` files** (yes, incl. `.md`/docs) via lint-staged; the glob lives in `.husky/pre-commit`. The hook first checks staged names in shell and skips Node when none match (e.g. backend-only). If some match and `frontend/viewer/node_modules` is missing (fresh worktree/clone), the hook runs `pnpm install` itself, so the first such commit takes ~45s — no need to install up front. Don't `--no-verify` to dodge the hook, that just defers the format failure to CI.
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ There are some exceptions:

Summary of setup steps below. See the appropriate file for your operating system for more details:

* [Windows](docs/DEVELOPER-win.md)
* [Linux](docs/DEVELOPER-linux.md)
* [Mac](docs/DEVELOPER-osx.md)
* [Windows](docs/dev/DEVELOPER-win.md)
* [Linux](docs/dev/DEVELOPER-linux.md)
* [Mac](docs/dev/DEVELOPER-osx.md)

### Prerequisites
* docker and compose
Expand Down Expand Up @@ -87,8 +87,8 @@ The SvelteKit UI will be available at http://localhost:3000.
- `task ui:only` starts the ui locally

**If the k8s deployments are already running**
- `infra-forward` forwards the infrastructure ports for the API
- `backend-forward` forwards the infrastructure + backend ports for the UI
- `task k8s:infra-forward` forwards the infrastructure ports for the API
- `task k8s:backend-forward` forwards the infrastructure + backend ports for the UI

---
### Project urls
Expand Down Expand Up @@ -194,7 +194,7 @@ Traces can be accessed directly with a URL like this: [https://ui.honeycomb.io/s

In the application, a trace ID (aka "Error code") shown at the bottom of an error message can be Ctrl+clicked to navigate to the trace in Honeycomb.

![Error example](./docs/img/error-example.png)
![Error example](./docs/dev/img/error-example.png)

## Testing

Expand Down
4 changes: 4 additions & 0 deletions backend/FwHeadless/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ flowchart TD

**Critical steps highlighted** - Steps 4 and 5 are where data loss can occur.

**User docs promise:** the merge runs only when someone presses Sync (no schedule), one project at a time, and a project's first sync can take minutes. Change that? Update `docs/site/fw-lite/how-sync-works.mdx`, `docs/site/src/components/SyncExplainer/syncScenarios.ts`, `docs/site/lexbox/fw-lite.md` and `docs/site/technical/sync/fwheadless-merge.md` in the same PR.

Job outcomes are `SyncJobResult.Status` (`LexCore/Sync/SyncJobResult.cs`); the `SyncJob*`/`TimedOut*` ones are polling outcomes, and results stay cached 30 s. A Chorus rollback blocks the project (HTTP 423 at the trigger, re-checked in the worker); unblock with `POST /api/fw-lite/sync/unblock?projectId=...` (no UI; any user who can sync the project). The snapshot is `{project}_snapshot.json`; why it's written after the post-merge Send/Receive and from the Harmony API is in `SyncWorker.ExecuteSync` (`Services/SyncHostedService.cs`).

### Key Services

| Service | Purpose | Danger Level |
Expand Down
4 changes: 4 additions & 0 deletions backend/FwLite/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ flowchart LR

---

**User docs promise:** device ↔ Lexbox sync runs by itself (after each edit, on project open, on server push), and a field edited on both sides keeps the FieldWorks Classic value. Change either? Update `docs/site/fw-lite/how-sync-works.mdx`, `docs/site/src/components/SyncExplainer/syncScenarios.ts`, `docs/site/lexbox/fw-lite.md` and `docs/site/technical/sync/` in the same PR.

Sync triggers: local edit (`MiniLcmJsInvokable.OnDataChanged`), project open (`ProjectServicesProvider`), `OnProjectUpdated` push and listener (re)connect (`LexboxHubConnection`), listener restart after a successful sync (`SyncService.ExecuteSync`), 5-minute `PushListenerRecoveryService`. `BackgroundSyncService` runs them one at a time with no dedup. `SafeExecuteSync` swallows exceptions; statuses are `NoServer`, `NotLoggedIn` (reported before `Offline`), `Offline`, `UnknownError`.

## 🔴 CRITICAL AREA: FwData ↔ CRDT Sync

**This is where data loss happens.** The sync between CRDT (SQLite) and FwData (FieldWorks XML) is the most complex code.
Expand Down
2 changes: 1 addition & 1 deletion backend/FwLite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ dotnet test FwLiteOnly.slnf
```

### Logs and settings (desktop/mobile app)
See [docs/FWLITE-TROUBLESHOOTING.md](../../docs/FWLITE-TROUBLESHOOTING.md) for where the app log lives, the
See [docs/dev/FWLITE-TROUBLESHOOTING.md](../../docs/dev/FWLITE-TROUBLESHOOTING.md) for where the app log lives, the
optional `fw-lite-settings.json`, and how to turn on verbose logging.

### Building for a specific framework
Expand Down
8 changes: 0 additions & 8 deletions docs/DEVELOPER-osx.md

This file was deleted.

File renamed without changes.
16 changes: 16 additions & 0 deletions docs/dev/DEVELOPER-osx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
### Prerequisites
* [Docker Desktop for Mac](https://docs.docker.com/desktop/setup/install/mac-install/)
* enable Kubernetes in the Docker Desktop settings

### Setup details
* to install [Taskfile](https://taskfile.dev/installation/) and [Tilt](https://docs.tilt.dev/), for example with Homebrew:
* `brew install go-task tilt`
* check with `task --version` and `tilt version`
* install ICU4C (macOS does not have ICU installed by default):
* `brew install icu4c`
* add the following lines to your ~/.zshrc (Apple Silicon):
```
export DYLD_LIBRARY_PATH=/opt/homebrew/opt/icu4c/lib:$DYLD_LIBRARY_PATH
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
```
* run `task setup` and continue with the [main instructions](../../README.md)
2 changes: 1 addition & 1 deletion docs/DEVELOPER-win.md → docs/dev/DEVELOPER-win.md
Comment thread
myieye marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* to install [Tilt](https://docs.tilt.dev/) and add it to your path:
* the Tilt installer will create a `bin` folder in your home folder and put the Tilt binary there
* you may then need to add `C:\Users\YOUR_USER_NAME\bin` to your PATH
* run `task setup` as per main instructions
* run `task setup` as per the [main instructions](../../README.md)
* seed Mercurial repos are pulled automatically from the published `ghcr.io/sillsdev/lexbox-test-repos` image on first startup — no manual download needed
* add the following lines to your `C:\Windows\system32\drivers\etc\hosts` file:

Expand Down
File renamed without changes.
22 changes: 22 additions & 0 deletions docs/dev/adr/0002-docs-site.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ADR 0002: Product docs site in the monorepo

Status: Accepted

## Context

There was no documentation home. Technical content (sync architecture, integrations, CI/CD) lived in `AGENTS.md` files and the root README, written for coding agents. User documentation didn't exist in the repo; the apps link out to the FieldWorks help site, Scribe how-tos and the community forum.

Other SIL products (surveyed 2026-07): Bloom, Scripture Forge and Paratext use Docusaurus with Crowdin for translation; Bloom adds Algolia DocSearch and its Ask AI chat. The Combine uses MkDocs Material for offline in-app bundling. FieldWorks uses WordPress plus GitHub wikis that have gone stale.

FieldWorks Lite ↔ FieldWorks Classic sync is the hardest thing to explain: several static diagrams and UI iterations still confused people.

## Decision

- **One Docusaurus site** with product sections: `/fw-lite/` (FieldWorks Lite app users), `/lexbox/` (lexbox.org and Send/Receive users) and `/technical/` (how it works, for anyone curious; not how to develop it). One pipeline, one search index. "Which app do you use?" is a factual question; persona labels ("I'm a manager") are a known audience-navigation failure (NN/g). The homepage routes with product tiles; `/lexbox/fw-lite` bridges project managers to the FieldWorks Lite guide.
- **Source in this monorepo**, so the docs change in the same PR as the code and reviewers see drift in the diff. The site lives in `docs/site/` and everything there is published; developer docs (setup, troubleshooting, ADRs) live in `docs/dev/`. Rules for keeping the site current: `docs/site/AGENTS.md`.
- **Terminology follows the shipped UI.** CRDT, Harmony, Mercurial and FwHeadless are technical-section vocabulary only.
- **Link to FieldWorks Classic docs, don't duplicate them.** Sibling products, sibling doc sites.
- **The sync explainer** (`/fw-lite/how-sync-works`) is an interactive, question-driven stepper over one fixed picture (device → Lexbox holding two copies → colleague's FieldWorks Classic), mirroring the app's Sync dialog. Users pick their question instead of a persona tab; each answer is a learner-paced sequence of short steps (Mayer's segmenting principle; no autoplay). All content is in `syncScenarios.ts`. Its caps are load-bearing: they fix the overwhelm that sank the static diagrams.
- **React for interactive components** (Docusaurus), accepted though the products are Svelte. Content stays in framework-neutral files. Revisit if product-like components pile up: compile Svelte to web components, or move to Astro Starlight.
- **Deployment**: GitHub Pages from `.github/workflows/docs.yaml` (build on PR, deploy on develop). Final URL: `docs.lexbox.org`, a Cloudflare CNAME `docs` → `sillsdev.github.io`. Until that's set up, CI builds for the repo's own Pages URL (the site URL is env-configurable).
- **Later**: Crowdin for the user guides, Algolia DocSearch and Ask AI, screenshots, app help links pointing here, moving architecture content out of `AGENTS.md` files into `technical/` and linking back.
File renamed without changes
4 changes: 4 additions & 0 deletions docs/site/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/node_modules
/build
.docusaurus
.cache-loader
39 changes: 39 additions & 0 deletions docs/site/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Docs site

Docusaurus site with three sections: `fw-lite/` (FieldWorks Lite users), `lexbox/` (lexbox.org and Send/Receive users) and `technical/` (how the system works, for anyone curious; not how to develop it). Everything in this folder is published. Developer docs (setup, troubleshooting, ADRs) go in `docs/dev/`, CI/CD in `.github/AGENTS.md`. Check a change with `pnpm install && pnpm build` in this folder; broken links and anchors fail the build. `pnpm typecheck` covers the React components.

## Which page to update

| You changed | Update |
|---|---|
| FW Lite UI (`frontend/viewer/`) | the `fw-lite/` page that names it; Sync dialog changes also `src/components/SyncExplainer/syncScenarios.ts` |
| When sync runs or which edit wins (`backend/FwLite/`, `backend/FwHeadless/`) | `fw-lite/how-sync-works.mdx`, `syncScenarios.ts`, `lexbox/fw-lite.md`, `technical/sync/` |
| lexbox.org UI, roles, invites, orgs, project requests (`frontend/src/`, `backend/LexBoxApi/`, `backend/LexCore/`) | the `lexbox/` page that names it |

`git grep` the old label or behavior under `docs/site/` to find every page that mentions it.

## Adding pages

- A feature gets its own page in its section (e.g. `fw-lite/views.md` for choosing and customizing views), titled by what the user does, with headings that answer the questions support gets. Order it with `sidebar_position` between the existing pages; the FAQ sits last at 99.
- No stub pages: every page, section index pages included, must answer something on its own (at least a short intro plus content or a contents list). Fold thin content into its parent page instead.
- Add the page to its section's `index.md` "In this guide" list. Once a section has more than about six feature pages, group them in a folder with a `_category_.json`.
- Downloads always link to `https://lexbox.org/fw-lite`, never to files. Only exception: the Mac early-tester build (GitHub releases), with its warning.

## Writing rules

- Name buttons, menus and tabs exactly as the app shows them: FW Lite text is a `msgid` in `frontend/viewer/src/locales/en.po`, lexbox.org text a value in `frontend/src/lib/i18n/locales/en.json`. `fw-lite/` says FieldWorks Classic (FW Lite's name for the desktop app); `lexbox/` says FieldWorks (lexbox.org's name). No doc-only names.
- `fw-lite/` and `lexbox/` are for non-technical readers: plain words, short sentences, no em-dashes. CRDT, Harmony, Mercurial and FwHeadless belong in `technical/` and the "Under the hood" box. Call the sync engine Harmony; say CRDT only where you explain what Harmony is.
- Say who can see a feature when not everyone can (role, beta flag, admin only).
- No real project codes, names, people or project data, in text or screenshots. Example project codes use the private-use language code `qaa`. Screenshots come from FW Lite's example project or a local dev Lexbox.
- `technical/` explains how the system works; `AGENTS.md` files keep agent procedure (commands, gotchas) and link here. The docs never link to `AGENTS.md` files. Don't copy content between them.
- The sync explainer's limits are in the `syncScenarios.ts` header.

## Links must keep working

Support replies link to pages, headings and explainer questions (`/fw-lite/how-sync-works#not-seen`: scenario ids in `syncScenarios.ts` are URLs). The apps are meant to link to `/fw-lite/` (FW Lite help), `/lexbox/` (lexbox.org help), `/fw-lite/how-sync-works` (Sync help) and `/lexbox/fw-lite` (turning on FW Lite).

- Moving or renaming a page: add `@docusaurus/plugin-client-redirects` with a redirect from the old path.
- Rewording a heading changes its anchor. Keep the old one with `## New wording {/* #old-anchor */}` (`{#id}` doesn't build here because of `future.v4`).
- Don't rename a scenario id.

Decisions behind the site: `docs/dev/adr/0002-docs-site.md`.
1 change: 1 addition & 0 deletions docs/site/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
Loading
Loading