chore(docs): drop mgmt dual-delivery rollback language (#159)
Some checks failed
Deploy / deploy (push) Has been cancelled

This commit is contained in:
Adam Moussa 2026-08-04 20:24:38 -04:00 • committed by GitHub
parent d677358801
commit 5b3e20bd35
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 21 additions and 23 deletions

View file

@ -119,7 +119,7 @@ A read-only REST API (API Gateway + the `procurement-api` Lambda, `lambdas/api/`
## Architecture
**IaC:** AWS CDK (Python), three stacks in one app, region `us-east-1`. The `cdk.Environment` is deliberately **account-agnostic** (region-only, no `account=`): the stacks deploy to whichever account the deploy credentials target (`011934824531` seahaven-prod since the 2026-07 account migration; formerly mgmt `328440206208`), and every account-derived template value — bucket names, `Lambda::Permission` source account, the site-alerts SNS action ARN, the Bedrock ARN below — renders as the CloudFormation `AWS::AccountId` pseudo-parameter rather than a literal. Pinning `account=` was evaluated in Phase 4 and rejected: it would resolve those tokens to literals, and against the deployed (account-agnostic) templates CloudFormation flags the `RemovalPolicy.RETAIN` email buckets as requiring replacement — a data-loss risk — for no functional gain.
**IaC:** AWS CDK (Python), three stacks in one app, region `us-east-1`. The `cdk.Environment` is deliberately **account-agnostic** (region-only, no `account=`): the stacks deploy to whichever account the deploy credentials target. **Live target is only seahaven-prod `011934824531`** (migrated 2026-07; mgmt stacks deleted in PLAT-67 on 2026-08-05). Never deploy from `main` to mgmt `328440206208` — templates would recreate against cold-archive RETAIN leftovers. Every account-derived template value — bucket names, `Lambda::Permission` source account, the site-alerts SNS action ARN, the Bedrock ARN below — renders as the CloudFormation `AWS::AccountId` pseudo-parameter rather than a literal. Pinning `account=` was evaluated in Phase 4 and rejected: it would resolve those tokens to literals, and against the deployed (account-agnostic) templates CloudFormation flags the `RemovalPolicy.RETAIN` email buckets as requiring replacement — a data-loss risk — for no functional gain.
All Lambdas: Python 3.12, ARM64, 60-day log retention.

View file

@ -87,9 +87,9 @@ def make_function_log_group(scope, id_prefix, function_name):
RETAIN matches the repo convention for stateful resources and mirrors the
old behavior (LogRetention never deleted groups on stack delete). NOTE:
in an account where ``/aws/lambda/<fn>`` already exists out-of-band
(mgmt), deploying this CREATE would collide -- acceptable because mgmt is
frozen post-migration and never redeployed from main.
never ``cdk deploy`` this app to mgmt (328440206208) — PLAT-67 deleted the
mgmt stacks and left RETAIN cold-archive log groups; a redeploy would
collide with those leftovers.
"""
return logs.LogGroup(
scope,

View file

@ -29,10 +29,11 @@ The PO email processor (`lambdas/po/email_processor/handler.py`) sends **every**
Two phases: a read-only multi-agent (ultracode) feasibility study over a 120-email sample, then a full-bucket triage over **all 3,448** inbound emails to get real distribution numbers.
### 2.1 Data access
> **Migration note (2026-07):** this section is historical — the harvest ran
> against the management account. Post-migration the live bucket is
> **Migration note (2026-07 / PLAT-67 2026-08-05):** this section is historical — the harvest ran
> against the management account. The live bucket is
> `s3://po-ingest-emails-011934824531/inbound/` (seahaven-prod, profile
> `seahaven-prod`); the mgmt bucket persists only until decommission.
> `seahaven-prod`). The mgmt bucket `po-ingest-emails-328440206208` remains as
> PLAT-67 cold archive (tagged; wipe deferred).
- PO email bucket: `s3://po-ingest-emails-328440206208/inbound/` (AWS account **328440206208**, us-east-1).
- ~~Reached via AWS profile `amoussa-mgmt` (SSO); default CLI creds are the personal account `681986854588`~~ **Stale (corrected 2026-07-16 during the fixture harvest):** the default CLI session is now authenticated to **328440206208** directly — no `--profile` flag needed.
- ⚠️ **Corpus is aging out:** `inbound/` objects carry an S3 lifecycle expiration (~90-day rolling window; oldest object 2026-04-17 at harvest time, 3,422 objects vs 3,448 at triage). Any further harvesting should not be deferred long.

View file

@ -26,11 +26,8 @@ target. Recovery is manual, via targeted re-invoke.
| WO | `workorder-email-processor` | _CDK-generated; fill from stack resources after the first prod deploy_ | `workorder-ingest-emails-011934824531` | `workorder-email-processor-dlq-messages` |
DLQ URLs are `https://sqs.us-east-1.amazonaws.com/011934824531/<queue-name>`.
(Until mgmt decommission completes, the pre-migration mgmt-account queues
`po-ingest-EmailProcessorDlqA753DED5-az8LUZE3ubtz` and
`WorkorderIngestStack-EmailProcessorDlqA753DED5-Q8H555LrqSU1` still exist in
328440206208 with any pre-cutover dead letters.)
Both DLQs: 14-day retention, SSE, TLS-enforced, `VisibilityTimeout` 30s.
(Mgmt-account DLQs were removed with the PLAT-67 stack teardown on 2026-08-05.)
## Recovery procedure (no redrive — receive → extract key → targeted re-invoke → verify → purge)

View file

@ -1,12 +1,12 @@
# SHOC Work-Order Webhook — Delivery Contract (v1)
**Status:** DRAFT — for SHOC team (Luby) review. **Rev 2026-07-23** (supersedes the 2026-07-16 draft: producer account corrected to seahaven-prod; reconciliation backstop changed to the new read API; §4.1 `unknown` status note; §3 `write_origin` forward-compat note. Sections 2, 5, 6, 7, and 10 are unchanged from the 07-16 draft).
**Producer:** `workorder-shoc-emitter` Lambda, Sea Haven **seahaven-prod** AWS account (**011934824531**, us-east-1). The former management account (328440206208) is frozen/rollback-only and will never host this feed, its secret, or its streams.
**Producer:** `workorder-shoc-emitter` Lambda, Sea Haven **seahaven-prod** AWS account (**011934824531**, us-east-1). Mgmt-account procurement-ingest stacks were deleted in PLAT-67 (2026-08-05); this feed, its secret, and its streams exist only in seahaven-prod.
**Consumer:** SHOC backend (.NET 8), initially `https://api.dev.seahaven.com`. Endpoint path is SHOC's choice — suggested `POST /api/webhooks/work-orders`; map entities onto the work-orders domain from shoc-backend PR #10.
## 1. Overview
Every work-order mutation the procurement-ingest pipeline writes to DynamoDB is pushed to SHOC as an HTTPS POST within seconds. The feed is driven by DynamoDB Streams, so events are emitted **in the exact order the pipeline committed them**, per work order. DynamoDB remains the source of truth; this webhook is a realtime feed. The reconciliation backstop (and the initial-history load — the feed starts at activation time, not from history) is the **procurement read API** (`GET /work-orders`, `GET /work-orders/{id}/comments`, IAM SigV4 — see its OpenAPI doc). SHOC's existing SyncController DynamoDB scan is transitional: it points at the old management account and retires when those stacks are decommissioned. `SyncVendorReplies` should be dropped on the SHOC side — the `VendorReplies` table is dead (its writer was deleted), and no `vendor_reply` webhook event exists or is planned.
Every work-order mutation the procurement-ingest pipeline writes to DynamoDB is pushed to SHOC as an HTTPS POST within seconds. The feed is driven by DynamoDB Streams, so events are emitted **in the exact order the pipeline committed them**, per work order. DynamoDB remains the source of truth; this webhook is a realtime feed. The reconciliation backstop (and the initial-history load — the feed starts at activation time, not from history) is the **procurement read API** (`GET /work-orders`, `GET /work-orders/{id}/comments`, IAM SigV4 — see its OpenAPI doc). Mgmt stacks are gone (PLAT-67); SHOC should retire SyncController's DynamoDB scan of the old management account and use the read API only. `SyncVendorReplies` should be dropped on the SHOC side — the `VendorReplies` table is dead (its writer was deleted), and no `vendor_reply` webhook event exists or is planned.
## 2. Transport

View file

@ -2,8 +2,8 @@
**Branch:** `feat/shoc-wo-webhook` (re-cut on `main` 2026-07-23; all build work targets the post-refactor layout — `cdk/common.py` helpers, explicit LogGroups, decomposed handlers, consolidated test roots — not the pre-refactor style earlier drafts implied).
**Companion docs:** `docs/shoc-webhook-contract.md` (Rev 2026-07-23 — the producer/consumer contract; Luby builds the receiver against it) and the procurement read API OpenAPI spec (`lambdas/api/openapi.json`, built in the sibling `procurement-api` effort — the reconciliation/backfill path).
**Requestor:** Luby (SHOC team). Decisions captured 2026-07-16: all WO event types, seconds-latency, full-payload push, HMAC + automated rotation, in-order per WO, prod-data-in-dev accepted, dual-write retained (DynamoDB retirement deferred for scope discipline — the original blocker, seahaven-slack-bot, was decommissioned 2026-07-23; retirement is now sequenced after SHOC prod + mgmt decommission, not this PR).
**Account (2026-07-23):** everything here builds in **seahaven-prod (011934824531)**. The old management account (328440206208) is frozen/rollback-only.
**Requestor:** Luby (SHOC team). Decisions captured 2026-07-16: all WO event types, seconds-latency, full-payload push, HMAC + automated rotation, in-order per WO, prod-data-in-dev accepted, dual-write retained (DynamoDB retirement deferred for scope discipline — the original blocker, seahaven-slack-bot, was decommissioned 2026-07-23; revisit after SHOC prod).
**Account:** everything here builds in **seahaven-prod (011934824531)**. Mgmt procurement-ingest stacks were deleted in PLAT-67 (2026-08-05); RETAIN leftovers there are cold archive only.
## Architecture
@ -22,7 +22,7 @@ Rationale (vs. inline call from the processor): DynamoDB Streams gives per-parti
1. Branch re-cut on `main` (done 2026-07-23).
2. Luby signs off `docs/shoc-webhook-contract.md` Rev 2026-07-23 (endpoint path, PR #10 entity mapping, the `unknown`-status mapping, `SyncVendorReplies` retirement).
3. Confirm the SHOC receiver role ARN for the resource policies (`arn:aws:iam::396287094661:role/shoc-backend-dev`) — the role lives in SHOC's account, outside this repo's control; confirm it still exists before the CDK references it (the first cross-account `get-secret-value` test is the hard proof).
4. **⚠️ ACCOUNT GATE: every deploy in this plan targets seahaven-prod (011934824531) ONLY.** Never deploy this stack to mgmt (328440206208), and never "temporarily" enable streams on mgmt's copies of the WO tables — that is the only path by which the SES dual-delivery bake could ever double-send to SHOC. Verify `aws sts get-caller-identity` resolves to 011934824531 before `cdk deploy`.
4. **⚠️ ACCOUNT GATE: every deploy in this plan targets seahaven-prod (011934824531) ONLY.** Never deploy this app to mgmt (328440206208) — templates are account-agnostic and would recreate stacks against cold-archive RETAIN data (PLAT-67). Verify `aws sts get-caller-identity` resolves to 011934824531 before `cdk deploy`.
5. Confirm with Luby which SHOC environment should receive the feed now — "prod-data-in-dev" was accepted 2026-07-16, pre-migration; re-confirm the target URL.
## Phase 1 — Secret + rotation (CDK, `wo_stack.py`)
@ -98,11 +98,11 @@ Optional EMF `DeliveryOutcome` metric (delivered/rejected, mirroring the ParseMe
2. Luby builds the receiver against the contract + shared test vectors and loads history via the read API; receiver passes the vectors at the target env.
3. Activation: one-line `enabled=True` PR. The ESM starts at `LATEST` — no historical flood.
4. Verify end-to-end with a live APM email; watch `iterator-age` + `rejected` alarms for 48h.
5. SyncController's Dynamo scan (incl. `SyncVendorReplies`) retires with the mgmt decommission; the read API is the standing reconciliation path.
5. SyncController's Dynamo scan (incl. `SyncVendorReplies`) is **SHOC-owned** retirement work now that mgmt stacks are gone (PLAT-67); the read API is the standing reconciliation path.
## Explicit non-goals (this PR)
- No DynamoDB retirement / write-path switch (kept out for scope discipline — the former blocker, seahaven-slack-bot, is decommissioned; revisit after SHOC prod + mgmt decommission).
- No DynamoDB retirement / write-path switch (kept out for scope discipline — the former blocker, seahaven-slack-bot, is decommissioned; revisit after SHOC prod).
- No PO-pipeline webhook.
- No change to `workorder-email-processor` or its parser.
- No write-back endpoints (phase 2 of the read API, own PR + own IAM cross-review).

View file

@ -1,9 +1,9 @@
# Deploy role: githubdeploy-procurement-ingest (seahaven-prod)
OIDC deploy role for this repo's GitHub Actions pipeline in AWS account
`011934824531` (seahaven-prod), us-east-1. Created as part of the migration
from the management account (328440206208); the mgmt role of the same name
stays untouched until decommission as the emergency mgmt deploy path.
`011934824531` (seahaven-prod), us-east-1. Created during the migration from
the management account (328440206208). The former mgmt twin of this role was
deleted in PLAT-67 (2026-08-05); do not recreate it.
## Files

View file

@ -6,8 +6,8 @@
# `githubdeploy-procurement-ingest` in AWS account 011934824531 (seahaven-prod),
# us-east-1, for the Sea-Haven-Industries/procurement-ingest repo (main branch).
#
# Part of the mgmt -> seahaven-prod migration. The mgmt-account role of the
# same name is left untouched until decommission (emergency mgmt deploy path).
# Prod-only deploy role (seahaven-prod). The former mgmt twin was deleted in
# PLAT-67 (2026-08-05); do not recreate a mgmt githubdeploy-procurement-ingest.
#
# GATE - DO NOT EXECUTE until BOTH of the following have passed on these
# exact artifact files: