Files
Project-SDE-WP-Suite/ONBOARDING.md
n.siegfried e5c450597a Isolate SOP/WP data per project (namespaced localStorage)
SOP and Work Package localStorage keys are now namespaced by the active
project via ProjectData.key(base) -> base+'__'+<projectId> (SK() in the
suite, wpKey() in the creator), so switching projects shows that project's
own SOP and packages. project-data.js runs a one-time discard of the legacy
un-namespaced keys (guarded by wp_ns_migrated_v1), per the chosen approach.

Active project is resolved before the store loads in both the suite and the
creator so namespaced keys resolve correctly, including standalone deep-links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:46:59 -07:00

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

How to run / deploy

  • API + database: see server/README.md (Postgres setup, dev uvicorn, prod gunicorn + 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), or choice (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 via instanceOf / 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_at columns 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 on create_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 under html/. 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 projects table + CRUD (/api/projects). sops gained project_id (FK, cascade) and work_packages gained project_id; list/latest/ metrics endpoints accept a project_id filter.
  • Project layer: html/project-data.js — a shared, API-first ProjectData adapter (list/get/save/remove hit /api/projects) that falls back to a localStorage mirror (wp_projects) when the API is unreachable, plus active-project helpers (getActive/setActive, stored in wp_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 via ProjectData, shows it in the header, and prefills the SOP project fields (step 1) from the project record when empty. Passes &project into the WP-creator iframe.
  • WP creator: stamps projectId onto every saved package (for API sync).

Per-project isolation (done, local): SOP/WP localStorage keys are now namespaced per active project via ProjectData.key(base)base + '__' + <projectId> (SK() in the suite, wpKey() in the creator). So each project keeps its own wp_suite_sop / wp_suite_state / wp_suite_sop_complete / wp_iwp_v1. On first load after this change, project-data.js runs a one-time discard of the legacy un-namespaced keys (guarded by wp_ns_migrated_v1) — chosen over migrating, since the local data was throwaway demo content.

Still ahead (true Phase 2): move SOP/WP reads+writes to the API filtered by project_id (GET /api/sops/latest?project_id=…, GET /api/wps?project_id=…) so projects are shared across users, not just isolated per browser. The endpoints already accept the project_id filter; the front end still reads localStorage.

Pending — Phase 2: wire the front end to the API

  • SOP: on SOP Complete, POST /api/sops; on load, GET /api/sops/latest to hydrate the Creator (currently uses localStorage key wp_suite_sop).
  • WP Creator: save packages via POST /api/wps; list/load via GET /api/wps (currently localStorage). It reads the active SOP from GET /api/sops/latest.
  • Comments already post to /api/feedback → the API; the list views could be switched from localStorage to GET /api/comments.
  • Keep localStorage as an offline draft cache / fallback.

Open questions for whoever continues

  1. Projects: the DB supports many SOPs, but the UI assumes one active SOP ("latest"). Do we need a project picker?
  2. Migrations: tables are auto-created on startup. Before real data, decide on Alembic for future schema changes.
  3. Power App view: if still wanted, point it at the Postgres comments / work_packages tables via the on-prem data gateway (no app change needed).
  4. 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.