Files
2026-07-22 15:54:59 -05:00

120 lines
6.5 KiB
Markdown

# Project SDE Meeting Toolkit
A shared, multi-user web app with two meeting tools:
- **Field Problem Workshop** — capture field problems, cluster them into root problems, dot-vote, build "breadcrumb" trails from problem to measurable outcome, and assign actions. A "Suggest fields" button fills each breadcrumb from built-in construction-execution methodology (no external service).
- **Scope Lock Meeting Suite** — Scope Boundary Board, MVP Priority Ranker, Decision Registry, and Micron Pilot Readiness.
Everyone who opens the same link works from the **same data**, stored on a shared server. Edits autosave, and a version check stops two people from silently overwriting each other. The original single-file HTML versions are kept in [`legacy/`](./legacy) for reference.
## Tech stack
- **SvelteKit (Svelte 5) + TypeScript** — frontend and server API in one project
- **Node** (`@sveltejs/adapter-node`) — runs as a plain server, easy to containerize
- **PostgreSQL** — shared storage for all users (accessed with `pg`, no ORM)
- **Docker + docker-compose** — one command to run the whole thing internally
## Quick start (Docker)
Requires Docker Desktop / Docker Engine.
```bash
docker compose up -d --build
```
Open **http://localhost:3000**. To let others on your network in, they use `http://<your-machine-ip>:3000`.
```bash
docker compose down # stop
docker compose down -v # stop AND erase all saved data
docker compose logs -f app # watch app logs
```
Data persists in a Docker volume (`db_data`), so it survives restarts.
There are no API keys or external accounts to set up. The "Suggest fields" button runs entirely in-app from a built-in methodology table.
## Local development (without Docker)
Needs Node 20+ and a PostgreSQL you can reach.
```bash
cp .env.example .env # edit DB_USER / DB_PASSWORD / DB_HOST etc.
npm install
# create the schema once (use the same values you put in .env):
psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -f db/init.sql
npm run dev # http://localhost:5180
```
Scripts: `npm run build` (production build) · `npm run preview` (serve the build) · `npm run start` (run the built server) · `npm run check` (type-check).
## Configuration
All config is via environment variables — see [`.env.example`](./.env.example):
| Variable | Purpose |
|---|---|
| `DB_HOST` | Database host (default `localhost`; `db` inside docker-compose). |
| `DB_PORT` | Database port (default `5432`). |
| `DB_USER` | Database username. |
| `DB_PASSWORD` | Database password. |
| `DB_NAME` | Database name. |
| `DATABASE_URL` | Optional. A full connection string that overrides the `DB_*` values above if set. |
| `PORT` | Server port (default 3000). |
| `ORIGIN` | Public URL the app is served from; used to validate POST origins. |
## Project layout
```
src/
app.css shared design tokens + base styles
app.html page shell
lib/
types.ts data shapes for both tools
breadcrumbs.ts methodology table + keyword classifier ("Suggest fields")
workspace.svelte.ts client sync: load / autosave / poll / conflict handling
toast.svelte.ts shared toast controller
components/ Toast.svelte, SyncBadge.svelte
defaults/ seeded baseline state for each tool (index, workshop, scope-lock)
server/
db/index.ts Postgres connection pool (lazy)
db/workspaces.ts load / save / reset with optimistic concurrency
routes/
+layout.svelte imports global CSS
+page.svelte home / tool picker
workshop/+page.svelte Field Problem Workshop
scope-lock/+page.svelte Scope Lock Meeting Suite
api/
workspaces/[tool]/[name]/+server.ts GET (load) + PUT (save)
workspaces/[tool]/[name]/reset/+server.ts POST (reset to baseline)
db/init.sql database schema (runs once on first DB startup)
static/favicon.svg
Dockerfile, docker-compose.yml
.gitea/workflows/build-image.yml CI: build + push image on push to main
legacy/ the original single-file HTML tools + the .skill file
```
## How the shared-workspace model works
- Each tool has a named workspace (the UI uses `default`) stored as one JSON document with a `version` number.
- Editing autosaves after a short pause, sending your changes **plus the version you started from**.
- If a teammate saved first, the server rejects your save (HTTP 409) and returns their current copy, which your page loads — so no one's work is silently overwritten. The header status badge reflects this.
- Open pages poll every few seconds, so a teammate's saved changes appear without a manual refresh.
This fits turn-taking meeting use. It is **not** live character-by-character co-editing (like Google Docs); the version/polling foundation could be upgraded to websockets if that's ever needed.
## Continuous integration (Gitea Actions)
[`.gitea/workflows/build-image.yml`](./.gitea/workflows/build-image.yml) builds the Docker image and pushes it to Gitea's built-in container registry on every push to `main`, tagged `:latest` and `:<commit-sha>`.
It requires, on your Gitea instance: Actions enabled with a registered `act_runner`, that runner able to build images (host Docker socket mounted, or docker-in-docker), and the package registry enabled (on by default). The workflow uses the auto-injected token; if that lacks package-write scope, add a Personal Access Token with `write:package` as a repo secret named `REGISTRY_TOKEN`. Details are in the workflow file's header comments.
## The `.skill` file
`legacy/construction-breadcrumbs.skill` is a **Claude skill** (a zip of instructions + a methodology knowledge base). It is not part of the website — it teaches Claude how to draft breadcrumb fields using construction methodology (EVM, CII AWP, Last Planner, first-time quality). The same methodology is encoded, deterministically and offline, in [`src/lib/breadcrumbs.ts`](./src/lib/breadcrumbs.ts), which powers the app's "Suggest fields" button. Install the skill in Claude (via "Save skill") to use the methodology in any chat.
## Possible next steps
- **Authentication** — none yet; anyone with the link can view and edit. The `workspaces.updated_by` column is already there for when it's added.
- **Multiple named workspaces** — the API accepts any name (`/api/workspaces/workshop/<name>`); the UI currently hardcodes `default`. A workspace picker would be a small addition.