docs: SHOC-alignment Phase 1 — ADRs, governance files, doc corrections (#220)

- Add ADR 0002 (SHOC merge boundary: separate backend services, shared
  conventions) and ADR 0003 (adopt SHOC design system + UI/UX layout)
- Add CODEOWNERS (internal-dev) and PR template (Summary/Test plan/Jira/
  docs-current checklist + contract-table convention)
- Fix stale facts in README/CLAUDE.md: MUI v7→v9, RN 0.85→0.86,
  OpenSearch Serverless/oss-index-creator → Aurora pgvector/
  aurora-pgvector-init (ADR 0001), test counts 149→186 (123 xUnit /
  26 vitest / 37 pytest), deploy triggers are workflow_dispatch-only,
  reusable workflow refs float on @main, aws-cdk-lib version claim
  replaced with Dependabot-maintained note
This commit is contained in:
Adam Moussa 2026-07-13 17:00:45 -04:00 • committed by GitHub
parent 536d440282
commit a4b09eb0ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 167 additions and 29 deletions

2
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1,2 @@
# Default: all changes require internal-dev review
* @Sea-Haven-Industries/internal-dev

21
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,21 @@
## Summary
<!-- What changed and why. Reference finding IDs (API-C1, WEB-M5, ...) where applicable. -->
## Test plan
<!-- How this was verified: commands run, tests added, manual checks. -->
## Jira / tracking
<!-- Ticket ID(s), or "n/a". -->
## Checklist
- [ ] Docs current — README / CLAUDE.md / ADRs / Confluence updated if this changes architecture, endpoints, or behavior
- [ ] If an endpoint shape changed: contract table included below and `shared/api-contracts` updated
<!-- Contract table (only when an endpoint shape changes):
| Endpoint | Method | Request change | Response change |
|---|---|---|---|
-->

View file

@ -6,10 +6,10 @@ Proposal management platform for Sea Haven Industries. Dispatchers submit servic
## Architecture
- **api/**: .NET 8 API, EF Core, PostgreSQL, Cognito JWT auth
- **web/**: React 19 + MUI v7 SPA, Vite, CloudFront + S3
- **mobile/**: React Native 0.85 iOS app, offline-capable, Hermes
- **lambdas/**: Python 3.12 Lambdas (ARM64): pdf-extract, pdf-generate, library-ingest, suggestions, oss-index-creator
- **api/**: .NET 8 API, EF Core, PostgreSQL (Aurora Serverless v2), Cognito JWT auth
- **web/**: React 19 + MUI v9 SPA, Vite, CloudFront + S3
- **mobile/**: React Native 0.86 iOS app, offline-capable, Hermes
- **lambdas/**: Python 3.12 Lambdas (ARM64): pdf-extract, pdf-generate, library-ingest, suggestions, aurora-pgvector-init
- **infra/**: CDK TypeScript (foundation-stack, compute-stack, frontend-stack)
- **shared/**: Shared TypeScript API contracts
- **scripts/**: Local dev helpers
@ -19,6 +19,12 @@ Proposal management platform for Sea Haven Industries. Dispatchers submit servic
External: Cognito JWT via API Gateway (web + mobile client IDs, groups: dispatchers/admins/sysadmins)
Internal: Lambdas → .NET Function URL with Secrets Manager API key via custom middleware
## Key Decisions (ADRs in docs/adr/)
- **ADR 0001**: Bedrock KB vector store is Aurora PostgreSQL + pgvector (replaced OpenSearch Serverless; `oss-index-creator` Lambda replaced by `aurora-pgvector-init`)
- **ADR 0002**: SHOC merge boundary — backends stay separate services permanently (PostgreSQL + Cognito here; SQL Server + ASP.NET Identity in SHOC); consolidation converges on conventions/layers/service patterns, never on platform
- **ADR 0003**: SHOC dev's design system + UI/UX layout is canonical (Montserrat/DM Sans, primary #1c75bc, 244px sidebar, CSS-variable single-token-source consumed by MUI via getCssVar); the old Nunito/#0c4f6f canon and the interim "Sea Haven Ops" Inter/#2563EB theme are superseded
## Request Flow
1. Dispatcher submits proposal (web/mobile) → InReview (no Draft stage)
@ -29,7 +35,7 @@ Internal: Lambdas → .NET Function URL with Secrets Manager API key via custom
## Infrastructure
AWS us-east-1, RDS PostgreSQL 15, S3, SQS+DLQ, Cognito+Google OAuth, OpenSearch Serverless, Bedrock KB, GitHub Actions OIDC, CloudFront+S3 OAC
AWS us-east-1, Aurora PostgreSQL 15 Serverless v2 (RDS Data API + pgvector), S3, SQS+DLQ, Cognito+Google OAuth, Bedrock KB (Aurora pgvector store — ADR 0001), GitHub Actions OIDC, CloudFront+S3 OAC
## Agent Delegation Rules
@ -58,7 +64,7 @@ AWS us-east-1, RDS PostgreSQL 15, S3, SQS+DLQ, Cognito+Google OAuth, OpenSearch
## Audit Status
AUDIT-REPORT.md completed 2026-05-27. 5 Critical, 36 High, 75+ Medium, 60+ Low findings. Phase 1-5 complete: all Critical/High fixed, 17 Medium fixed, CI runs 108 tests.
AUDIT-REPORT.md completed 2026-05-27. 5 Critical, 36 High, 75+ Medium, 60+ Low findings. Phase 1-5 complete: all Critical/High fixed, 17 Medium fixed, CI runs 186 tests (123 xUnit, 26 vitest, 37 pytest).
### Critical Findings (fix first)

View file

@ -13,9 +13,9 @@ Internal proposal management platform for Sea Haven Industries. Dispatchers subm
Monorepo with five primary services:
- **.NET 8 API** -- Clean Architecture REST API hosted on Lambda behind API Gateway (JWT-authorized) with Function URL (AWS_IAM) for internal access
- **React 19 Web** -- MUI v7 admin/dispatcher workspace served via CloudFront + S3
- **React 19 Web** -- MUI v9 admin/dispatcher workspace served via CloudFront + S3
- **React Native Mobile** -- iOS-first field app for dispatchers (offline-capable)
- **Python Lambdas** -- PDF extraction, PDF generation, library ingestion, AI suggestions, AOSS index provisioning
- **Python Lambdas** -- PDF extraction, PDF generation, library ingestion, AI suggestions, Aurora pgvector bootstrap
- **CDK Infrastructure** -- Three TypeScript stacks managing all AWS resources
## Repository Structure
@ -23,8 +23,8 @@ Monorepo with five primary services:
```
proposal-system/
├── api/ .NET 8 Web API (Lambda-hosted, EF Core + PostgreSQL)
├── web/ React 19 + MUI v7 + Vite frontend
├── mobile/ React Native 0.85 iOS app
├── web/ React 19 + MUI v9 + Vite frontend
├── mobile/ React Native 0.86 iOS app
├── lambdas/ Python 3.12 processing functions (arm64)
├── infra/ CDK TypeScript (3 stacks)
├── shared/ TypeScript API contracts (shared between web + mobile)
@ -38,11 +38,11 @@ proposal-system/
| Component | Technologies |
|---|---|
| API | .NET 8, ASP.NET Core, EF Core + Npgsql, FluentValidation, Cognito JWT, Amazon.Lambda.AspNetCoreServer |
| Web | React 19, TypeScript, MUI v7, Vite, Redux Toolkit, TanStack Query, axios |
| Mobile | React Native CLI 0.85, React 19, React Native Paper, React Navigation, react-native-app-auth (PKCE), amazon-cognito-identity-js (SRP), Keychain, offline draft queue |
| Web | React 19, TypeScript, MUI v9, Vite, Redux Toolkit, TanStack Query, axios |
| Mobile | React Native CLI 0.86, React 19, React Native Paper, React Navigation, react-native-app-auth (PKCE), amazon-cognito-identity-js (SRP), Keychain, offline draft queue |
| Lambdas | Python 3.12, arm64, pdfplumber, reportlab, httpx, boto3 |
| Infrastructure | CDK TypeScript (aws-cdk-lib 2.253.1) |
| AI/RAG | Bedrock Knowledge Base (Titan Embeddings v2), OpenSearch Serverless (VPC-only), Claude Sonnet via Bedrock cross-region inference |
| Infrastructure | CDK TypeScript (aws-cdk-lib pinned exact, kept current by Dependabot) |
| AI/RAG | Bedrock Knowledge Base (Titan Embeddings v2), Aurora PostgreSQL + pgvector vector store (see ADR 0001), Claude Sonnet via Bedrock cross-region inference |
| Auth | Cognito User Pool + Google OAuth IdP (groups: dispatchers, admins, sysadmins) |
## AWS Resources
@ -51,8 +51,8 @@ All resources are in **us-east-1** (account 328440206208).
| CDK Stack | Key Resources |
|---|---|
| `proposal-system-foundation` | RDS PostgreSQL 15 (t4g.small), S3 buckets, SQS queue + DLQ, Cognito user pool, Secrets Manager |
| `proposal-system-compute` | API Gateway HTTP API (JWT authorizer + access logging), .NET 8 API Lambda + Function URL (AWS_IAM), Python Lambdas (pdf-extract, pdf-generate, library-ingest, suggestions, oss-index-creator), OpenSearch Serverless collection (VPC endpoint), Bedrock KB |
| `proposal-system-foundation` | Aurora PostgreSQL 15 Serverless v2 (RDS Data API, pgvector), S3 buckets, SQS queue + DLQ, Cognito user pool, Secrets Manager |
| `proposal-system-compute` | API Gateway HTTP API (JWT authorizer + access logging), .NET 8 API Lambda + Function URL (AWS_IAM), Python Lambdas (pdf-extract, pdf-generate, library-ingest, suggestions, aurora-pgvector-init bootstrap), Bedrock KB (Aurora pgvector store) |
| `proposal-system-frontend` | CloudFront distribution (S3 OAC) |
| Resource Type | Names |
@ -118,17 +118,17 @@ Seven parallel jobs calling org reusable workflows:
| Job | Workflow | What it checks |
|---|---|---|
| .NET Build & Test | `ci-dotnet.yaml` | Restore, build, test the API solution (104 xUnit tests) |
| .NET Build & Test | `ci-dotnet.yaml` | Restore, build, test the API solution (123 xUnit tests) |
| Web Frontend Check | `ci-typescript-cdk.yaml` | TypeScript typecheck for web |
| Web Tests | `ci-typescript-cdk.yaml` | vitest suite (26 tests — auth, interceptors, components) |
| Web Tests | inline job | vitest suite (26 tests — auth, interceptors, components) |
| Mobile Typecheck | `ci-typescript-cdk.yaml` | TypeScript typecheck for mobile |
| Python Lint | `ci-python-sam.yaml` | ruff check + format on lambdas/ |
| Python Tests | `ci-python-sam.yaml` | pytest suite (19 tests — pdf-generate, suggestions handlers) |
| Python Tests | inline job | pytest suite (37 tests — pdf-generate, suggestions, library-ingest, internal API signing) |
| CDK Synth | `ci-typescript-cdk.yaml` | Synthesize CDK stacks (includes .NET publish) |
### Deploy (on push to main)
### Deploy (manual)
Calls `cd-cdk.yaml` reusable workflow:
Auto-deploy on push to main is currently disabled (PR #167) — both deploy workflows run via `workflow_dispatch` from the Actions tab. `deploy.yaml` calls the `cd-cdk.yaml` reusable workflow:
1. Publishes .NET 8 API and Python Lambdas
2. Runs `cdk deploy --all`
3. Executes `scripts/post-deploy.sh` (builds web, syncs to S3, invalidates CloudFront)
@ -139,9 +139,7 @@ Deploy uses OIDC role `githubdeploy-proposal-system`. Concurrency group prevents
Workflow: `deploy-mobile.yaml` -- builds and uploads to TestFlight via `cd-mobile-ios.yaml` reusable workflow on `macos-26`.
Triggers:
- **Automatic**: push to `main` with changes in `mobile/**`
- **Manual**: `workflow_dispatch` for on-demand builds
Trigger: **manual only** (`workflow_dispatch`) while the app is pre-V1, to save macOS runner cost.
## Mobile iOS
@ -186,13 +184,13 @@ Failed SQS messages are reported via `batchItemFailures` and retried up to 3 tim
## Testing
149 tests across three stacks, all run in CI on every PR:
186 tests across three stacks, all run in CI on every PR:
| Suite | Framework | Count | Coverage |
|---|---|---|---|
| .NET API | xUnit | 104 | State machine transitions, authorization attributes, middleware, validators, ProposalNumberGenerator, LineItemService state guards |
| .NET API | xUnit | 123 | State machine transitions, authorization attributes, middleware, validators, ProposalNumberGenerator, LineItemService state guards |
| Web | vitest | 26 | ProtectedRoute, RoleGuard, API client interceptor (401 logout, token attachment) |
| Python Lambdas | pytest | 19 | pdf-generate and suggestions handler contracts |
| Python Lambdas | pytest | 37 | pdf-generate, suggestions, library-ingest handler contracts, internal API signing |
```bash
cd api && dotnet test # .NET tests
@ -207,7 +205,7 @@ Hardening applied across all layers (see AUDIT-REPORT.md for full details):
- **Auth:** Cognito JWT validation with audience check, startup fails if auth not configured, DevMode gated to `IsDevelopment()`
- **API:** FluentValidation on all DTOs, generic error responses (no stack traces or config leaks), structured audit logging with before/after diffs
- **Function URL:** AWS_IAM auth + internal API key (two-layer defense)
- **Infrastructure:** S3 `enforceSSL` + `BLOCK_ALL`, SQS managed encryption, OpenSearch VPC-only, Cognito optional TOTP MFA, API Gateway access logging
- **Infrastructure:** S3 `enforceSSL` + `BLOCK_ALL`, SQS managed encryption, Aurora in private subnets, Cognito optional TOTP MFA, API Gateway access logging
- **Lambdas:** Prompt injection sanitization, PDF size limits, numeric validation on AI suggestions, S3 key sanitization, idempotent SQS processing
- **CI/CD:** OIDC (no long-lived credentials), SHA-pinned workflow refs, `--require-approval broadening` on local deploys
- **CI/CD:** OIDC (no long-lived credentials), org reusable workflows (`@main`), `--require-approval broadening` on local deploys
- **Web:** sessionStorage for tokens (not localStorage), error boundaries, role guards on all admin routes, 401 interceptor clears auth state

View file

@ -0,0 +1,62 @@
# ADR 0002 — SHOC merge boundary: separate backend services, shared conventions
- **Status:** Accepted (2026-07-13)
- **Decision owner:** Adam Moussa
- **Scope:** `proposal-system` ↔ SHOC (`shoc-backend` / `shoc-frontend-new`) consolidation strategy
## Context
proposal-system was built as a standalone service deliberately mirroring SHOC's
architecture for a clean future merge. As of 2026-07-13 the two projects diverge on
platform fundamentals:
| Axis | proposal-system | SHOC |
|---|---|---|
| Database | Aurora PostgreSQL (pgvector) via EF Core/Npgsql | SQL Server via EF Core |
| Identity | Cognito (hosted UI, groups, web + mobile clients) | ASP.NET Identity + custom JWT issuance |
| API layout | 4-project Clean Architecture (Api/Application/Infrastructure/Domain) | Single API project, services injecting DbContext |
| Error contract | RFC 7807 ProblemDetails, string enums | Typed-exception codes per controller, numeric enums |
| Hosting | Lambda behind API Gateway (CDK) | Elastic Beanstalk (external-dev account) |
A full platform alignment (engine migration, auth migration, rehosting) would cost
weeks, carry data-migration risk, and deliver no user value.
## Decision
**The backends remain separate services permanently. Consolidation converges on
conventions, layers, and service patterns — never on platform.**
Concretely:
1. **No database engine migration** in either direction. PostgreSQL stays here;
SQL Server stays in SHOC.
2. **No identity migration now.** Cognito stays here. At consolidation time, identity
converges on Cognito (or a federation layer in front of both) — not on
ASP.NET Identity, which would regress to self-managed credentials and reintroduce
the remediated WEB-C1 token-theft class (SHOC currently stores JWTs in
localStorage).
3. **Conventions converge** (tracked by the 2026-07 SHOC-alignment plan):
- Frontend: SHOC's design system + UI/UX layout (ADR 0003), domain layering
(`src/domain/<entity>/{api,schemas,mappers,types,use-cases}`), TanStack Query,
zod, react-hook-form.
- API contracts: shared TypeScript contracts package + zod schemas; ProblemDetails
with machine-readable business `code` extensions (SHOC's error-code vocabulary,
proposal-system's envelope).
- Mutation semantics: optimistic concurrency with SHOC's 409-plus-currentState
response shape; staged audit-trail entries persisted at one SaveChanges boundary.
- Process: org reusable CI callers, CODEOWNERS, PR template, conventional commits.
4. **Layering stays Clean Architecture here.** SHOC's flatter service style is not
adopted; if SHOC restructures at consolidation, this repo's 4-project layout is the
reference shape.
## Consequences
- The eventual "merge" is a monorepo consolidation of independently deployable
services sharing a frontend architecture and wire conventions — not a single
backend.
- A merged frontend can treat both APIs identically for errors (ProblemDetails +
`code`) and conflicts (409 + currentState) once alignment phases 3/6 land.
- Anything requiring one database across both domains (cross-domain reporting,
shared entities) must go through APIs, not shared tables.
- SHOC's localStorage JWT + CORS `*` are flagged to the SHOC team as findings; they
are not constraints on this repo.

View file

@ -0,0 +1,49 @@
# ADR 0003 — Adopt the SHOC design system and UI/UX layout
- **Status:** Accepted (2026-07-13)
- **Decision owner:** Adam Moussa
- **Scope:** `web/` theming, layout shell, and all future proposal-system UI work
## Context
Three design-token sets existed across Sea Haven web apps as of 2026-07-13:
1. **Old canon** — Nunito, primary `#0c4f6f`, 220px sidebar, `#f4f6f7` background
(what SHOC's stale `docs/DESIGN_SYSTEM.md` still describes).
2. **SHOC dev's new system** (shoc-frontend-new PR #17) — Montserrat (display) /
DM Sans (body) / JetBrains Mono, primary `#1c75bc`, navy `#262262`, page background
`#f9fafb`, 244px sidebar (76px collapsed), 64px gradient topbar
(`#1b1f52 → #1c4f8f → #1c75bc`), defined in a single CSS-variable token file
(`src/styles/theme.css`) consumed by both MUI (`getCssVar` → `createTheme`) and
Tailwind v4.
3. **proposal-system's "Sea Haven Ops" theme** — Inter, accent `#2563EB`,
navy-900 AppBar, 216px drawer (`web/src/theme.ts`).
## Decision
**SHOC dev's new design system and UI/UX layout (set 2) is the canonical Sea Haven
standard.** proposal-system adopts it fully:
- **Tokens:** mirror shoc-frontend-new dev's `theme.css` values and structure
(fonts, palette, radii, spacing, shadows).
- **Mechanism:** single CSS-variable token file is the one source of truth;
`web/src/theme.ts` becomes a thin `getCssVar` → MUI `createTheme` adapter matching
SHOC's `mui-theme.ts`. Token changes are values-only edits, never theme rewrites.
- **Layout shell:** SHOC's admin shell dimensions and treatment — 244px/76px-collapse
sidebar, 64px gradient topbar, avatar initials.
- **Component library:** MUI only. Tailwind (SHOC runs a MUI + Tailwind v4 hybrid off
the same CSS variables) is **not** adopted here pre-consolidation; the shared
CSS-variable source keeps that door open.
The reference is shoc-frontend-new's **`dev` branch** at adoption time; subsequent
SHOC token changes should be ported as values-only updates.
## Consequences
- The "Sea Haven Ops" theme (Inter/#2563EB) is retired; alignment plan Phase 2
implements the port.
- Visual verification for Phase 2 compares proposal-system screens side-by-side with
SHOC dev screens, not with the old canon.
- The superseded Nunito/`#0c4f6f` tokens must not be reintroduced anywhere.
- At monorepo consolidation, both frontends already share token structure, so a
single `theme.css` can serve both.