Projects become the top-level container; SOPs and Work Packages belong to one. Backend: - New projects table + CRUD (/api/projects). - sops.project_id (FK, cascade) and work_packages.project_id added; list/latest/metrics endpoints accept a project_id filter. Front end (now under html/): - project-data.js: shared API-first ProjectData adapter with localStorage fallback + active-project helpers. - Home page: removed "About This Suite"; added a Project picker (create / use sample / select). Tool cards stay hidden until a project is active and carry &project=<id>; hero shows the active project. - Suite reads ?project, resolves it, shows it in the header, and prefills the SOP project fields; passes &project into the WP-creator iframe. - WP creator stamps projectId onto saved packages. SOP/WP localStorage is not yet namespaced per project (next step). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
12 KiB
Project Handoff & History
A curated record of where this project is, the decisions behind it, and what's left — so a teammate (or a fresh Claude Code session) can pick up cleanly. This is a summary of the working sessions, not a verbatim chat transcript.
Last updated: 2026-06-15.
TL;DR — current status
The Work Package Suite is an internal tool for Prime Controls construction projects: configure a project SOP, then author Work Packages against it.
- Front end: static HTML/CSS/JS, no build step. Two tools surfaced from the
home page — SOP Configuration and Work Package Creator — both living in one
app (work-package-suite.html) as two tabs. The
Creator is embedded via
<iframe>(wp-creation-index.html). - Back end (NEW, Phase 1 done): a Python/FastAPI API + PostgreSQL under
server/for shared storage of SOPs, Work Packages, and comments. - Hosting target: internal, behind the company firewall. NGINX serves the
static site and proxies
/api/to the Python API. No outbound internet calls. - Next up (Phase 2, NOT started): rewire the front end to read/write the API
instead of browser
localStorage.
Architecture
browser → NGINX ──serves──> static site (index.html, work-package-suite.html, …)
└─proxy /api/─> Python API (FastAPI, uvicorn/gunicorn :8000) → PostgreSQL
Today the front end still stores data in localStorage; Phase 2 moves it to the
API. The API is already built and testable on its own.
Key decisions (and why)
| Decision | Choice | Rationale |
|---|---|---|
| Tool structure | One app, two tabs (SOP + WP Creator) | Shared state; SOP flows straight into the Creator. Standalone SOP tool was deleted as a duplicate. |
| WP Creator integration | Embedded as an <iframe> |
The real Creator is a separate, complex app; iframing avoids JS global collisions and reuses it as-is. |
| Discipline on WP types | Removed everywhere | Per request — WP types now carry Enabled / Special Rules / WO Complete Approval only. Also removed the derived Discipline field + trade-code in the Creator. |
| Hosting | Internal NGINX, behind firewall | Company requirement. Considered IIS (dropped) and SharePoint (can't host a multi-file app). |
| Database | PostgreSQL | Site admin is provisioning it; good JSON support for SOP/WP documents. Code uses SQLAlchemy, so it's portable. |
| Identity | Name field only | Trust the internal network; users type their name. No login. |
| Client data model | Server-backed (Phase 2 goal) | SOPs/WPs/comments shared across users; localStorage becomes a draft cache. |
| Comments transport | API → Postgres | Replaces an earlier Power Automate plan; comments now persist to SQL directly. |
Timeline (by commit)
| Commit | What it delivered |
|---|---|
c583daa |
Initial import of the WP Suite files. |
2af4b58 |
Consolidated to two tools (one app, two tabs); restored Rev 0 features — white header, single title, Step Comments in header, usage logs, sample data, APM in Team + Sign-offs, drag-and-drop sequencing, pre-loaded deletable sources, two-card home page, embedded WP Creator. |
d2dc6ff |
Removed discipline from WP types and the Creator; removed external Google-Fonts call (firewall hardening); added Export/Import + a central-feedback hook + DEPLOYMENT.md. |
7186d46 |
First pass wiring feedback through an IIS reverse proxy to Power Automate. |
3d4402c |
Switched that reverse proxy from IIS to NGINX. |
a33b777 |
Phase 1 backend: FastAPI + SQLAlchemy + PostgreSQL (server/); NGINX now proxies /api/ to it; comments persist to SQL; docs rewritten. |
Repo map
- index.html — home page (two tool cards; SOP card turns green/"Review" when complete).
- work-package-suite.html / work-package-suite-app.js / work-package-suite-styles.css — the SOP Configuration tool + WP tab host.
- wp-creation-index.html / wp-creation-app.js / wp-creation-styles.css — the embedded Work Package Creator.
- feedback-config.js —
FEEDBACK_ENDPOINT(/api/feedback) +postFeedback()helper. server/— FastAPI API (app.py), ORM models (models.py), DB setup (db.py),requirements.txt,.env.example, and server/README.md.- nginx-wp-suite.conf — NGINX site config (static +
/api/proxy). - DEPLOYMENT.md — end-to-end deploy guide.
How to run / deploy
- API + database: see server/README.md (Postgres setup, dev
uvicorn, prodgunicorn+ systemd). API docs at/api/docs. - Front end + proxy: see DEPLOYMENT.md and nginx-wp-suite.conf.
- Locally the API runs against a SQLite file with zero config, so it can be tried without Postgres.
What's done vs. pending
Done
- Two-tool consolidation, all Rev 0 feature restorations, discipline removal.
- Firewall hardening (no external calls).
- Export/Import feedback on every surface.
- Phase 1 backend (API + schema) + NGINX proxy + deploy docs.
- Discipline strategy, sizing, split & dashboard (branch
feat/wp-discipline-split-dashboard) — see below.
Discipline strategy, WP sizing, Split-by-Discipline & Dashboard
Discipline came back — but as a project policy set in the SOP, not a fixed field. The Governance step (SOP config, step 5) now also captures:
- Disciplines (default Mechanical / Electrical / Tech) — comma list.
- Discipline strategy (
governance.discMode):single(one discipline per WP),multi(one WP bundles disciplines, scope split per discipline), orchoice(planner decides per package — build big, split later). - Split threshold (
governance.sizeHoursMax) — the Creator warns when a package's est. hours exceed it. governance.instanceSuffix=letter(instances get A/B/C suffixes).
In the WP Creator (wp-creation-app.js):
- A Disciplines picker (hidden unless the SOP defines disciplines). Pick 2+ and the single flat work-step list becomes per-discipline scope sections, each with its own steps and its own status (e.g. Issued — Electrical while Mechanical is still In Progress). Overall WP status rolls up to the least-advanced discipline.
- ⎘ Split by Discipline turns a multi-discipline package into one instance
per discipline:
WP01-…→WP01A(Mech),WP01B(Elec),WP01C(Tech). Each instance is a single-discipline WP linked to the master viainstanceOf/parentNumber; the master is kept as a roll-up (split:true,children:[]). - New per-WP fields on the saved object:
disciplines,scope({discipline: [steps]}),discStatus,instanceOf,instanceLabel,parentNumber,split,children.
The Dashboard (📊 in the Creator header; home card Work Package Dashboard;
deep-link work-package-suite.html?view=dashboard or …?view=dashboard#…):
metrics (total / release-ready / on-hold / overdue / est vs actual hrs),
breakdowns by status & discipline, a gating panel (what's blocking each
package), and a filterable board with view / edit / issue per package.
Masters are excluded from counts so split hours aren't double-counted.
Data source today is localStorage via the WPData adapter in
wp-creation-app.js — swap list()/issue()/setStatus()
to fetch('/api/wps…') in Phase 2 and the UI is unchanged.
Backend (server/) gained the matching endpoints:
POST /api/wps/{id}/issue (refuses if constraints are open — the AWP gate),
POST /api/wps/{id}/status, and GET /api/wps/metrics. work_packages gained
parent_id and issued_at columns.
Migration caveat: tables are still auto-created on startup, so the new
parent_id/issued_atcolumns appear on a fresh DB only. Before there's real data this is fine; once there is, add Alembic (see open question #2) and migrate rather than relying oncreate_all.
Multi-project support
Note on layout: the IT admin moved all static files into
html/and added a Docker/NGINX deployment (Dockerfile,docker-compose.yml,nginx/). Front-end paths below are underhtml/.server/stayed at the repo root.
The suite is now multi-project. Projects are the top-level container; every SOP and Work Package belongs to one.
- Backend: new
projectstable + CRUD (/api/projects).sopsgainedproject_id(FK, cascade) andwork_packagesgainedproject_id; list/latest/ metrics endpoints accept aproject_idfilter. - Project layer: html/project-data.js — a shared,
API-first
ProjectDataadapter (list/get/save/removehit/api/projects) that falls back to a localStorage mirror (wp_projects) when the API is unreachable, plus active-project helpers (getActive/setActive, stored inwp_active_project/wp_active_project_obj). - Home page (html/index.html): "About This Suite" removed;
a Project picker added. With no projects it offers Create Project / Use
Sample Project; otherwise a dropdown to select. The tool cards stay hidden
until a project is active and then carry
&project=<id>; the hero shows the active project. - Suite (html/work-package-suite-app.js):
reads
?project=<id>, resolves it viaProjectData, shows it in the header, and prefills the SOP project fields (step 1) from the project record when empty. Passes&projectinto the WP-creator iframe. - WP creator: stamps
projectIdonto every saved package (for API sync).
Not yet done (the next fork): SOP/WP localStorage is still global, not
namespaced per project — selecting a different project locally still shows the
same wp_suite_sop / wp_iwp_v1 data. The intended end state is per-project
data via the API (GET /api/sops/latest?project_id=…, GET /api/wps?project_id=…).
Decide whether to (a) namespace the local keys by project id with a migration of
existing un-namespaced data into a "default" project, or (b) jump straight to the
API for SOP/WP reads. See the open question below.
Pending — Phase 2: wire the front end to the API
- SOP: on SOP Complete,
POST /api/sops; on load,GET /api/sops/latestto hydrate the Creator (currently useslocalStoragekeywp_suite_sop). - WP Creator: save packages via
POST /api/wps; list/load viaGET /api/wps(currentlylocalStorage). It reads the active SOP fromGET /api/sops/latest. - Comments already post to
/api/feedback→ the API; the list views could be switched fromlocalStoragetoGET /api/comments. - Keep
localStorageas an offline draft cache / fallback.
Open questions for whoever continues
- Projects: the DB supports many SOPs, but the UI assumes one active SOP ("latest"). Do we need a project picker?
- Migrations: tables are auto-created on startup. Before real data, decide on Alembic for future schema changes.
- Power App view: if still wanted, point it at the Postgres
comments/work_packagestables via the on-prem data gateway (no app change needed). - Backups / retention for the Postgres database (ask the site admin).
Continuing with Claude Code
Open this repo in Claude Code and start with: "Read ONBOARDING.md and DEPLOYMENT.md, then help me with Phase 2 — wiring the front end to the API." The commit messages above are a reliable trail of what changed and why.