From 5bc74907a37cf36ca3e560405be8d43f6fdafd76 Mon Sep 17 00:00:00 2001 From: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Date: Sat, 24 Oct 2026 21:07:44 -0700 Subject: [PATCH 1/5] fix(copy): remove proof and case-studies pages, point CTE upload copy to /ingest, label EPCIS as preview Replaces #3554 with a single Conventional Commit on current main. Removes /case-studies, /proof and /proof/transformation and their assets, links the [cte] upload copy to /ingest, and adds the removed routes to routes.json expectRedirectOr404. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --- .gitignore | 5 - README.md | 4 +- docs/PRODUCT_DESCRIPTION.md | 4 +- docs/security/PRE_PUBLIC_CHECKLIST.md | 2 +- frontend/README.md | 2 +- .../public/proof/audit-bundle-tampered.zip | Bin 2268 -> 0 bytes frontend/public/proof/audit-bundle.zip | Bin 2266 -> 0 bytes frontend/public/proof/epcis-sample.jsonld | 88 ------ frontend/public/proof/fsma-204-sample.csv | 11 - .../__tests__/pages/empty-states-3554.test.ts | 80 ++++++ .../__tests__/pages/pre-revenue-copy.test.ts | 7 - .../__tests__/pages/proof-trust-copy.test.tsx | 10 +- .../src/__tests__/public-site-fixes.test.tsx | 11 +- frontend/src/app/admin/page.tsx | 2 +- .../app/blog/fsma-204-cte-kde-guide/page.tsx | 2 +- .../page.tsx | 2 +- .../src/app/case-studies/opengraph-image.tsx | 10 - frontend/src/app/case-studies/page.tsx | 58 +--- frontend/src/app/controls/page.tsx | 2 +- .../src/app/dashboard/export-jobs/page.tsx | 25 +- .../src/app/dashboard/integrations/page.tsx | 1 + frontend/src/app/dashboard/settings/page.tsx | 2 +- .../src/app/docs/api/ApiReferenceClient.tsx | 18 +- frontend/src/app/docs/authentication/page.tsx | 2 +- frontend/src/app/fsma-204/[cte]/page.tsx | 14 +- frontend/src/app/fsma-204/page.tsx | 2 - frontend/src/app/fsma/page.tsx | 29 +- frontend/src/app/globals.css | 4 +- frontend/src/app/ingest/page.tsx | 2 +- frontend/src/app/integrations/page.tsx | 19 ++ frontend/src/app/layout.tsx | 2 +- .../src/app/onboarding/bulk-upload/page.tsx | 2 +- frontend/src/app/proof/opengraph-image.tsx | 10 - frontend/src/app/proof/page.tsx | 250 +----------------- .../proof/transformation/opengraph-image.tsx | 10 - .../src/app/proof/transformation/page.tsx | 82 +----- frontend/src/app/records/page.tsx | 4 +- frontend/src/app/requests/page.tsx | 2 +- frontend/src/app/rules/page.tsx | 2 +- frontend/src/app/security/page.tsx | 29 +- frontend/src/app/settings/support/page.tsx | 9 +- frontend/src/app/sitemap.ts | 3 - .../src/app/tools/notice-validator/page.tsx | 51 +--- frontend/src/app/trust/architecture/page.tsx | 3 +- frontend/src/app/trust/support/page.tsx | 9 +- frontend/src/app/walkthrough/page.tsx | 4 - frontend/src/app/why-regengine/page.tsx | 23 +- .../dashboard/metrics-overview-widget.tsx | 2 +- .../src/components/layout/marketing-nav.ts | 2 - .../components/mobile/FieldCaptureClient.tsx | 4 +- frontend/src/design-system/design-tokens.json | 2 +- frontend/src/lib/compliance-os.ts | 2 +- frontend/src/lib/customer-readiness.ts | 4 +- .../src/lib/marketing-analytics-routes.ts | 4 +- frontend/src/styles/DESIGN_SYSTEM.md | 2 +- frontend/tests/visual/routes.json | 2 +- scripts/build_audit_bundle.sh | 42 +-- .../tests/test_proof_verify_router.py | 3 + 58 files changed, 247 insertions(+), 735 deletions(-) delete mode 100644 frontend/public/proof/audit-bundle-tampered.zip delete mode 100644 frontend/public/proof/audit-bundle.zip delete mode 100644 frontend/public/proof/epcis-sample.jsonld delete mode 100644 frontend/public/proof/fsma-204-sample.csv create mode 100644 frontend/src/__tests__/pages/empty-states-3554.test.ts delete mode 100644 frontend/src/app/case-studies/opengraph-image.tsx delete mode 100644 frontend/src/app/proof/opengraph-image.tsx delete mode 100644 frontend/src/app/proof/transformation/opengraph-image.tsx diff --git a/.gitignore b/.gitignore index dad73ecbb..6945e923c 100644 --- a/.gitignore +++ b/.gitignore @@ -85,11 +85,6 @@ secrets.json # Artifacts that should never be tracked *.zip -# ...except the public proof bundles the frontend serves. These are -# intentionally tracked fixtures (the audit-bundle pair backs the public -# /api/v1/proof/verify demo, including the deliberately tampered one), and -# the bare *.zip rule above was silently covering them (#2873). -!frontend/public/proof/*.zip login_resp.json frontend/build_log.txt frontend/build_output.txt diff --git a/README.md b/README.md index 77e422977..e9f9fce80 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ RegEngine is an API-first traceability evidence platform for food supply chains. Inflow Workbench prepares data with preflight checks, readiness scores, a fix queue, and a commit gate. Optional Jev assistance suggests CSV column mappings and missing event types; deterministic validation and authenticated tenant boundaries remain authoritative. -[Website](https://regengine.co) · [Public proof](https://regengine.co/proof) · [Verify a bundle](https://regengine.co/verify) · [Product description](docs/PRODUCT_DESCRIPTION.md) · [Python SDK](regengine/README.md) +[Website](https://regengine.co) · [Verify a bundle](https://regengine.co/verify) · [Product description](docs/PRODUCT_DESCRIPTION.md) · [Python SDK](regengine/README.md) This is a pre-production application with deployed public proof surfaces. The capability inventory below describes checked-in implementation and release policy, not proof of customer adoption or live configuration. @@ -381,7 +381,7 @@ RegEngine is proprietary, source-visible software — all rights reserved by Reg > **Before changing this repository's visibility**, work through [docs/security/PRE_PUBLIC_CHECKLIST.md](docs/security/PRE_PUBLIC_CHECKLIST.md). Confidential business material is currently tracked in-tree, and `git rm` removes it from `HEAD` only — every prior revision stays readable. -Permissive exceptions — the public verifier tools are **Apache 2.0** (declared in [LICENSE](LICENSE)) so anyone can use, copy, and embed them: the `/proof` Merkle bundle verifier ([frontend/public/sdk/verify_chain.py](frontend/public/sdk/verify_chain.py)) and the `regengine-verify` per-record CLI ([regengine/verify_records.py](regengine/verify_records.py)). [Inflow Lab](https://github.com/regengine/inflow-lab), the integration simulator, is Apache 2.0 in its own repository for the same reason. +Permissive exceptions — the public verifier tools are **Apache 2.0** (declared in [LICENSE](LICENSE)) so anyone can use, copy, and embed them: the Merkle bundle verifier ([frontend/public/sdk/verify_chain.py](frontend/public/sdk/verify_chain.py)) and the `regengine-verify` per-record CLI ([regengine/verify_records.py](regengine/verify_records.py)). [Inflow Lab](https://github.com/regengine/inflow-lab), the integration simulator, is Apache 2.0 in its own repository for the same reason. ## Contributing diff --git a/docs/PRODUCT_DESCRIPTION.md b/docs/PRODUCT_DESCRIPTION.md index d1f7df63d..0d0920378 100644 --- a/docs/PRODUCT_DESCRIPTION.md +++ b/docs/PRODUCT_DESCRIPTION.md @@ -29,7 +29,7 @@ A browser verifier, a downloadable Python bundle verifier, and a reproducible re | Tenant-scoped records and tamper-evident chains | [Tenant authority](../services/shared/tenant_context.py), [canonical persistence](../services/shared/canonical_persistence/writer.py), [canonical chain verification](../services/shared/canonical_persistence/verification.py), [admin audit integrity](../services/admin/app/audit_integrity.py). See [persistence cutover](persistence/dual-write-cutover.md). | | FDA-oriented exports and request-response packages | Release-enabled: [FDA export](../services/ingestion/app/fda_export/router.py), [request workflow](../services/ingestion/app/request_workflow_router.py), [auditor API](../services/ingestion/app/auditor_router.py). | | Readiness assessments and recall reporting | Release-enabled: [readiness](../services/ingestion/app/readiness_router.py), [recall reports](../services/ingestion/app/recall_report.py). | -| Public proof and synthetic recall drill | [Proof page](https://regengine.co/proof), [browser verifier](https://regengine.co/verify), [recall drill](https://regengine.co/proof/recall-drill), [verifier source](../frontend/public/sdk/verify_chain.py). Synthetic demonstration data. | +| Bundle verifier and synthetic recall drill | [Browser verifier](https://regengine.co/verify), [recall drill](https://regengine.co/proof/recall-drill), [verifier source](../frontend/public/sdk/verify_chain.py). Synthetic demonstration data. | | Supplier manifest remediation and correction links | Implemented in [remediation](../services/ingestion/app/remediation/) and the [buyer dashboard](../frontend/src/app/remediation/page.tsx); excluded from the production router allowlist. Notification delivery also requires recipient/provider configuration. | | EPCIS JSON-LD export and broader recall simulations | Implemented in [EPCIS export](../services/ingestion/app/epcis_export.py) and [simulations](../services/ingestion/app/recall_simulations.py); these routers are experimental and excluded from the production allowlist. Public sample EPCIS downloads are a separate proof surface. | | Inbound email CSV ingestion | [Postmark integration](../services/ingestion/app/email_ingest/); excluded from the production allowlist, with no self-service provisioning UI. See [rollout notes](engineering/EMAIL_INGESTION.md). | @@ -48,7 +48,7 @@ A browser verifier, a downloadable Python bundle verifier, and a reproducible re ## Talking-track opener -RegEngine turns supplier and ERP records into traceability evidence for food businesses. Import records, identify missing or inconsistent CTEs and KDEs, resolve issues, and assemble FDA-oriented exports with verifiable audit evidence. Inflow Workbench prepares the data; RegEngine stores and verifies the evidence. Inspect the synthetic examples at [regengine.co/proof](https://regengine.co/proof) and check a bundle at [regengine.co/verify](https://regengine.co/verify). +RegEngine turns supplier and ERP records into traceability evidence for food businesses. Import records, identify missing or inconsistent CTEs and KDEs, resolve issues, and assemble FDA-oriented exports with verifiable audit evidence. Inflow Workbench prepares the data; RegEngine stores and verifies the evidence. Check a bundle at [regengine.co/verify](https://regengine.co/verify). ## Maintenance rule diff --git a/docs/security/PRE_PUBLIC_CHECKLIST.md b/docs/security/PRE_PUBLIC_CHECKLIST.md index 4343aae41..b85acb99d 100644 --- a/docs/security/PRE_PUBLIC_CHECKLIST.md +++ b/docs/security/PRE_PUBLIC_CHECKLIST.md @@ -72,6 +72,6 @@ it reactively at flip time.** - `REPO_PURPOSE.md` — the scope these files fall outside of. - `SECURITY.md` — reporting and disclosure posture. -- The public proof surface (`/proof`, `/verify`) already provides +- The public verifier (`/verify`) already provides transparency and audit **without** requiring source visibility. It is the reason the README's public-repository claim sounded plausible. diff --git a/frontend/README.md b/frontend/README.md index f126501d7..f5192d330 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -15,7 +15,7 @@ See the [product description](../docs/PRODUCT_DESCRIPTION.md) for capability ava | Buyer manifest remediation and correction actions | [remediation](src/app/remediation/) — requires the remediation API, which is excluded from the production allowlist | | Recall reporting and audit views | [recall report](src/app/dashboard/recall-report/), [audit](src/app/audit/) | | Email-code/passkey login, invitations, and API keys | [login](src/app/login/), [invite acceptance](src/app/accept-invite/), [API keys](src/app/api-keys/) | -| Public synthetic proof, bundle verification, and recall drill | [proof](src/app/proof/), [verify](src/app/verify/) | +| Bundle verification and recall drill | [verify](src/app/verify/), [recall drill](src/app/proof/recall-drill/) | | Product documentation and developer portal | [docs](src/app/docs/), [developer portal](src/app/developer/portal/) | Jev assistance is optional. Suggestions are visible for review; automatic application defaults off. See the [Jev guide](../docs/engineering/JEV_HEADER_MAPPING.md) for configuration, evidence, and data-sharing defaults. diff --git a/frontend/public/proof/audit-bundle-tampered.zip b/frontend/public/proof/audit-bundle-tampered.zip deleted file mode 100644 index 50e02e82f5fe6c9b744d8a0f252415bd0232fa59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2268 zcmZ{mXHXN`62}t}39@Oo16`WaYybcN1n_Juw-gxM(%Aq4 z0N-c<0M?URoVyc7S`I2N?T&GBb+nhkx_i~9lU+#Ab0OqiNxe_@X^8_&!jJriPT{k6 z>cgMjxzfy3*@bv&9CI9m6B3z*uhL{3yo+<%9r9lJ(^OFG-X&c;6*>*MZFMfd61k+` z^u)t(1%j~K()sdailO6;I;xQ)SR8*{wKG1vT$1>e^&+_PusS<2Jn%@OF+Aqw@mo2L z&#PW=Ra!$s6FbH}wDQaQCVI&qT8i`_#lN21szRZ%W;koN`$U zNW##-0fc$qKc3o@6tEc~dj(pVLFz`Qi+mPfnSRYr-J?FW_aha|dW7%@BSo z-H4v%-jwE#UC~xX;vdN#JV{~{4q@Q4P3_dC<9RhpzHltKP_?rf+}S*qPhTF?Wg@Pw z$Yam~Vg_>> z3pc51yXN3K$CuIsp1p&!b>&{PE>C%X@e=G=Mx+LXXv=*c^9G3#PN-TA4D`u_R-A(@ zK4q)83#D7xCLY$wL~guaWbC0x5yyU08Zsv+BgVYxe5!q2E2RC zwcf%O!%Z(Qs%l+IPMC}X1{^K1-kK>&2NhGgdQX!ur^))&GPz3v>5kIuL$;;zGfKp> zABzP!Xw@sHbVfkQSkRiK5K#>*shF1R&=(BRWGZHlfOSqY)W0ZzwW(wATp{8hD1v*)0DWwJzZjnq5%(|_A~^&h zSZ0}lGM7U`5wV$neV1JCR5}|{)RdfIEt7J}ee6K%|!o88?H zR{b{Wo>I$Y$0CN0W}FCYK*t zY1`YaFgjOc^Sa>RA=wC+%T|eUZPkv7fhQHPhR7qiA@bnMXF}z|j(pYj^Vn!%u0xM{ zJON>KPBCM&?Pip=^kr`E;}7T#ctO0gvzAQ>)uB7+@#Rl79BtyftK%=ryFMnQlaI90 zk2Py*4BN4v)mqw$p@LMJC6y3rs~DP7Tx5ZM_pBxB57aB`;Y)3Y&+H-=nr4@5dzUBe zCw)0+{UwaS94W9c1-iF>8hiLDq+apy`)EfSo1@o`$z}Ilk4Gk69^71Q<6T24>mU`P zEVG71-EysJT};7WIq>blHu~;m?+(p6a5+}R%%#o2Y=nvlFFNk->uIUr1Mt?~YL8oK zz1rs_L=Tq(XJsCUeZEt(t9xa8@c3;()AXx>f?qo|%{0A+?Gyk&cNzdVdoszLFwS_K zy}O6Z&n~SpM!L*F8QZAlAeAdI?N0Xng=*fj7>d0ONIXX8IlKX*)YT$$9GE&M)jN(& zS{5qc(h#qej;uYnWO{p#*OnVlpE@s^(vwJnz=_~n1SB+)&^DNhbqPO?a z5(DMuy2krxM_ScZN?d2;>#r!#u3w9Ih|8I8?q$--8h5e%vXV}rH3cI!9-}4e2#b_v zuO5g1=3=fEDle|Q+^rXH*3kd8y*Ez&ZsQ?m*+TpCk_)aSV*%OHj*%=k4oo}GA1Pt2 zHtwalhDcTOfe)Df=-i)i(jDcCyxbxcJfWUry~!(Ue=x_x!9tK+rvcLiI{C0(+ZI=T zH7c&-52m2PJ(${lZ=7v^v%(>4l%F3up6Vc~{05!V$%Xax)jQ*) znZFyp68k2hp0pizD9~^T5qxck*OrtKrB1qGoogvP!@PTB3Ae`Se)UH27+YGq>2v?t z5gG;iu(s!kQEbW@tq3Y@A6hx=fL@RA)ts4ovh4A_m9Ry=9j(Y`#kucH9Ic5A8_8_Y zISNu(V&-lPn~{!+T3`RhVW0~<#YXf0t-uLG|A~+jd+@Ku;CF%Fcf~*70e}!7{|T4> jvN?Vi_#NT@2@IatlV5-~(50pO`AlZl` diff --git a/frontend/public/proof/audit-bundle.zip b/frontend/public/proof/audit-bundle.zip deleted file mode 100644 index 062a233a63cd5305ecec8b9dd247ee857a07e63b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2266 zcmZ{mc{mh$7sqGPXfh!?*RH}P8V!YzEF-+4vW>ND7h<621sdZSI0tJpJE6-(h7P=in)TZyE&~NKHr*rxco;5p9xrpmFi`lSp(J}|P_T7qEy>R(B~oGsL9|ca zrzlcl-)b!LEi;n&%kF{m`l&R=XBg@5_?j81es}5|Qa&C7LC<4YptnC zGJ>kp#FQ}0DPmkKcs74#kxmp52s;=1$g&tqc{ptTB)ft_5bxJ)6E?uCZ`^0!G{g2y z+O35bZk5Lele@u)5UHf4KG72+FXr>IK;u@XMS)`7~Z96)(# zk)`Ov^EP7UZENnhGa*yiF%)>_#^KGl&7+1es{6ij&s=*?2uW&oaVv7}3@pk3c+J4K z#@QV$eNbEZEUJ{1I-CrQ*qIgi*H{r5T+D6h*gt{Z&$6x5%$-#vdufRExR+{=>9z8I zELM^LnUs%M^?@@mU=H#~t1(1f_jZP7XSBGvK(Santa+5L=2-!((FB8+BC3LI-ulP# z6UC&c4XV}&*MU-!E|*Z35QWYz8dQkJ(8MuX_1Oo9j^PUVpbwmG6yr1FlM9o?)qBJ# zNU9?^iyadKPs;l3oBBet9zU_DJ|pc;wU91}`Jt?OE%yjVG-6H{0J+HgIWv{fL9cE_x--joGR+o0v6bQ=hl`0`cLp7p6F zNe|Sn>abEKlbRUss^zY$MHM(7Z<;F4r*d)G(2|o^xldkdWqH2zXr-061BE35;{(qu zAc8953i`Xk?kJV7kB_JYm%zW?<5t$vwuL*B7;0H{D`Cy;&f`FvB9v^ol+#&C>Y&jpCJ~0~(^I0h|tgcuyvI|@VaNv0p{_s3~_JNp_u{)v0n|Y!jm~YSh z_CP=^gK%pA#B}5~lG)P1yB}Pd^b1nFy)E2IRz2II9-R7ACDEuVH$V7--tsY(%-XRa z@0wRtU1VZD8$WFOIl8{n#UZx_*4ty@NfB z9S)TMg{xhLNMyodb$DmO%r@{NHW!L_-z9pvx$V66%Anow-R&ECv1K{mD95oew6f7j zKvH{^{c~NbeH;J=+3SVu?Rb|6=Y?gIdL_X7a@dvlzC z_QvC|0oOHubZNz98=pz&p~h7wu+iyTOaiv+o^kLvnu~PxkZPzlhBU$@e-L@ocn)#$e*0*FJB3r}jrjY5DL$mOX z9>#Ui@3t@Tc%?jKOYoQG%`t*?zx>TpPmf0rndG3BBp_wEZu~d(m@q6_OPLJMseiXX971aXJakPJY)M66rh77Pi4b5E;-Fon9$G* z2MFf*>v3~QZ&GU}z9ere)}DYz8}!JzPuxi`nJ`7=B9D#D}62bu+dv ztJZ2KN@rG3x-NE1BOzg7;j4t5HE^FO&;K_9dkp>O+>iMYeqsy$De%u-@#l8{;C_tU o9+yAa9RDluGlc&VAOc_PnUlW(ZD$SQ{qfARcN_QO0q~FP4;NVxx&QzG diff --git a/frontend/public/proof/epcis-sample.jsonld b/frontend/public/proof/epcis-sample.jsonld deleted file mode 100644 index c107be48a..000000000 --- a/frontend/public/proof/epcis-sample.jsonld +++ /dev/null @@ -1,88 +0,0 @@ -{ - "_comment": "SYNTHETIC — Sample EPCIS 2.0 JSON-LD payload for the RegEngine /proof page. Three Critical Tracking Events covering harvesting, initial packing, and shipping for a single lot of romaine lettuce. Modeled on services/ingestion/app/epcis/. Not a real grower, lot, or shipment.", - "@context": [ - "https://ref.gs1.org/standards/epcis/2.0.0/epcis-context.jsonld", - { - "fsma": "urn:fsma:traceability:" - } - ], - "type": "EPCISDocument", - "schemaVersion": "2.0", - "creationDate": "2026-02-08T17:00:10Z", - "epcisBody": { - "eventList": [ - { - "type": "ObjectEvent", - "eventTime": "2026-02-05T13:42:00Z", - "eventTimeZoneOffset": "-08:00", - "epcList": [ - "urn:epc:id:sgtin:0614141.107346.SV-20240115-001" - ], - "action": "ADD", - "bizStep": "urn:fsma:traceability:harvesting", - "disposition": "urn:epcglobal:cbv:disp:active", - "readPoint": { - "id": "urn:epc:id:sgln:0614141.00001.0" - }, - "bizLocation": { - "id": "urn:epc:id:sgln:0614141.00001.0" - }, - "fsma:traceabilityLotCode": "SV-20240115-001", - "fsma:foodCommodity": "Romaine Lettuce, fresh-cut", - "fsma:harvestDate": "2026-02-05", - "fsma:growingArea": "Field 7B, Salinas Valley, CA" - }, - { - "type": "ObjectEvent", - "eventTime": "2026-02-06T08:15:00Z", - "eventTimeZoneOffset": "-08:00", - "epcList": [ - "urn:epc:id:sgtin:0614141.107346.SV-20240115-001" - ], - "action": "OBSERVE", - "bizStep": "urn:fsma:traceability:initial_packing", - "disposition": "urn:epcglobal:cbv:disp:in_progress", - "readPoint": { - "id": "urn:epc:id:sgln:0614141.00002.0" - }, - "bizLocation": { - "id": "urn:epc:id:sgln:0614141.00002.0" - }, - "fsma:traceabilityLotCode": "SV-20240115-001", - "fsma:packDate": "2026-02-06", - "fsma:packingFacility": "Salinas Valley Cooling & Pack, Salinas, CA", - "fsma:quantity": { - "value": 480, - "uom": "case" - } - }, - { - "type": "ObjectEvent", - "eventTime": "2026-02-07T05:30:00Z", - "eventTimeZoneOffset": "-08:00", - "epcList": [ - "urn:epc:id:sgtin:0614141.107346.SV-20240115-001" - ], - "action": "OBSERVE", - "bizStep": "urn:fsma:traceability:shipping", - "disposition": "urn:epcglobal:cbv:disp:in_transit", - "readPoint": { - "id": "urn:epc:id:sgln:0614141.00002.0" - }, - "bizLocation": { - "id": "urn:epc:id:sgln:0614141.00002.0" - }, - "destinationList": [ - { - "type": "urn:epcglobal:cbv:sdt:location", - "destination": "urn:epc:id:sgln:0614141.00099.0" - } - ], - "fsma:traceabilityLotCode": "SV-20240115-001", - "fsma:shipDate": "2026-02-07", - "fsma:shipToEntity": "DC-099, Tracy, CA", - "fsma:carrier": "Reefer Logistics Inc." - } - ] - } -} diff --git a/frontend/public/proof/fsma-204-sample.csv b/frontend/public/proof/fsma-204-sample.csv deleted file mode 100644 index daa15cd42..000000000 --- a/frontend/public/proof/fsma-204-sample.csv +++ /dev/null @@ -1,11 +0,0 @@ -Traceability Lot Code (TLC),Product Description,Quantity,Unit of Measure,Event Type,Event Date,Event Time,Facility Name,Facility GLN,Facility Address,Confidence Score -SV-20240115-001,Romaine Lettuce,500,cases,SHIPPING,2024-01-15,14:00:00,Sunny Valley Farms,1234567890128,"123 Farm Road, Salinas CA",0.98 -SV-20240115-001,Romaine Lettuce,500,cases,RECEIVING,2024-01-16,08:00:00,Fresh Cut Co,2345678901234,"456 Processing Lane, Fresno CA",0.97 -FC-20240116-A,Chopped Romaine,250,cases,TRANSFORMATION,2024-01-16,10:00:00,Fresh Cut Co,2345678901234,"456 Processing Lane, Fresno CA",0.95 -FC-20240116-B,Romaine Hearts,250,cases,TRANSFORMATION,2024-01-16,11:00:00,Fresh Cut Co,2345678901234,"456 Processing Lane, Fresno CA",0.95 -FC-20240116-A,Chopped Romaine,100,cases,SHIPPING,2024-01-17,06:00:00,Fresh Cut Co,2345678901234,"456 Processing Lane, Fresno CA",0.96 -FC-20240116-A,Chopped Romaine,100,cases,RECEIVING,2024-01-17,10:00:00,Metro Foods,3456789012340,"789 Distribution Dr, Oakland CA",0.94 -FC-20240116-B,Romaine Hearts,150,cases,SHIPPING,2024-01-17,07:00:00,Fresh Cut Co,2345678901234,"456 Processing Lane, Fresno CA",0.96 -FC-20240116-B,Romaine Hearts,150,cases,RECEIVING,2024-01-17,11:00:00,Valley Wholesale,4567890123456,"321 Wholesale Ave, Stockton CA",0.93 -FC-20240116-A,Chopped Romaine,50,cases,SHIPPING,2024-01-18,05:00:00,Metro Foods,3456789012340,"789 Distribution Dr, Oakland CA",0.95 -FC-20240116-A,Chopped Romaine,50,cases,RECEIVING,2024-01-18,08:00:00,SuperMart #42,5678901234562,"100 Retail St, San Jose CA",0.92 diff --git a/frontend/src/__tests__/pages/empty-states-3554.test.ts b/frontend/src/__tests__/pages/empty-states-3554.test.ts new file mode 100644 index 000000000..3eb08b0d2 --- /dev/null +++ b/frontend/src/__tests__/pages/empty-states-3554.test.ts @@ -0,0 +1,80 @@ +import { execFileSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { CAPABILITY_REGISTRY, STATUS_LABELS } from '@/lib/customer-readiness'; + +const read = (p: string) => readFileSync(resolve(process.cwd(), p), 'utf8'); + +describe('#3554 empty states and hidden sample artifacts', () => { + it('export-jobs and integrations show No data yet with CSV upload and JSON events API next steps', () => { + for (const page of ['src/app/dashboard/export-jobs/page.tsx', 'src/app/integrations/page.tsx']) { + const src = read(page); + expect(src).toContain('title={NO_DATA_YET}'); + expect(src).toMatch(/href="\/ingest"/); + expect(src).toMatch(/href="\/docs\/api"/); + } + }); + + it('support pages render Not set for empty channel fields', () => { + for (const page of ['src/app/settings/support/page.tsx', 'src/app/trust/support/page.tsx']) { + const src = read(page); + expect(src).toContain('channel.responseWindow || NOT_SET'); + expect(src).toContain('channel.notes || NOT_SET'); + } + }); + + it('labels the EPCIS capability as Preview', () => { + const epcis = CAPABILITY_REGISTRY.find((item) => item.id === 'epcis'); + expect(epcis?.name).toBe('GS1 EPCIS 2.0 (Preview)'); + expect(epcis && STATUS_LABELS[epcis.status]).toBe('Preview'); + }); + + it('no longer publishes the made-up proof samples', () => { + expect(existsSync(resolve(process.cwd(), 'public/proof/fsma-204-sample.csv'))).toBe(false); + expect(existsSync(resolve(process.cwd(), 'public/proof/epcis-sample.jsonld'))).toBe(false); + }); + + it('build_audit_bundle.sh requires a real export path', () => { + let status = 0; + let stderr = ''; + try { + execFileSync('bash', [resolve(process.cwd(), '../scripts/build_audit_bundle.sh')], { stdio: 'pipe' }); + } catch (err) { + const e = err as { status: number; stderr: Buffer }; + status = e.status; + stderr = e.stderr.toString(); + } + expect(status).not.toBe(0); + expect(stderr).toMatch(/^usage: /); + }); + it('hides /proof/transformation and drops every link to it', () => { + expect(read('src/app/proof/transformation/page.tsx')).toContain('notFound()'); + expect(existsSync(resolve(process.cwd(), 'src/app/proof/transformation/opengraph-image.tsx'))).toBe(false); + expect(read('src/app/sitemap.ts')).not.toContain('/proof/transformation'); + expect(read('src/app/walkthrough/page.tsx')).not.toContain('/proof/transformation'); + }); + + it('removes the public proof zips', () => { + for (const f of ['public/proof/audit-bundle.zip', 'public/proof/audit-bundle-tampered.zip']) { + expect(existsSync(resolve(process.cwd(), f))).toBe(false); + } + }); + + it('security page shows No data yet instead of illustrative verifier output', () => { + const src = read('src/app/security/page.tsx'); + expect(src).not.toContain('Sample · illustrative'); + expect(src).toContain('title={NO_DATA_YET}'); + }); + + it('CTE pages ask for the user\'s own file, not a sample', () => { + const src = read('src/app/fsma-204/[cte]/page.tsx'); + expect(src).not.toContain('Upload a sample'); + // After merging main (#3557 retired /sandbox), the closing CTA copy comes from + // sandboxCta().blurb, which asks for the user's own records and links /ingest. + expect(src).toContain('{cta.blurb}'); + expect(src).not.toContain('sandbox and get an instant'); + expect(src).not.toContain('data to the sandbox'); + expect(src).toMatch(/data to\{" "\}\s*]*>RegEngine<\/Link>/); + }); +}); diff --git a/frontend/src/__tests__/pages/pre-revenue-copy.test.ts b/frontend/src/__tests__/pages/pre-revenue-copy.test.ts index 52a2ae3a7..95eb9275c 100644 --- a/frontend/src/__tests__/pages/pre-revenue-copy.test.ts +++ b/frontend/src/__tests__/pages/pre-revenue-copy.test.ts @@ -13,7 +13,6 @@ const PUBLIC_FILES = [ 'src/app/compare/repositrak/page.tsx', 'src/app/supplier-compliance/page.tsx', 'src/app/tools/roi-calculator/components/ROICalculatorClient.tsx', - 'src/app/case-studies/page.tsx', 'src/app/developers/page.tsx', 'src/app/status/page.tsx', ]; @@ -59,10 +58,4 @@ describe('pre-revenue public copy', () => { expect(page).toContain('Pricing is temporarily unavailable'); expect(page).toContain('What does the Founding rate include?'); }); - - it('states pre-revenue status plainly on the case study disclosure', () => { - expect(read('src/app/case-studies/page.tsx')).toContain( - 'RegEngine is pre-revenue; self-serve access is in staged rollout.', - ); - }); }); diff --git a/frontend/src/__tests__/pages/proof-trust-copy.test.tsx b/frontend/src/__tests__/pages/proof-trust-copy.test.tsx index 13d875866..ae61468f1 100644 --- a/frontend/src/__tests__/pages/proof-trust-copy.test.tsx +++ b/frontend/src/__tests__/pages/proof-trust-copy.test.tsx @@ -56,14 +56,8 @@ describe('public proof-bundle trust copy', () => { } }); - it('/proof states the checks do not authenticate the issuer', () => { - const text = renderedText(); - - expect(text).toMatch(/do not authenticate the issuer/i); - - for (const claim of UNBACKED_AUTHENTICITY_CLAIMS) { - expect(text).not.toMatch(claim); - } + it('/proof is hidden until it can publish artifacts from a real export', () => { + expect(() => ProofPage()).toThrow(); }); it('verify_chain.py documents that the uploaded manifest is an untrusted commitment', () => { diff --git a/frontend/src/__tests__/public-site-fixes.test.tsx b/frontend/src/__tests__/public-site-fixes.test.tsx index c3980f73b..b3311da2a 100644 --- a/frontend/src/__tests__/public-site-fixes.test.tsx +++ b/frontend/src/__tests__/public-site-fixes.test.tsx @@ -158,14 +158,7 @@ describe('public site launch-blocker fixes', () => { }); }); - it('labels synthetic validation honestly in the footer', () => { - expect(MARKETING_FOOTER_PRODUCT_LINKS).toContainEqual({ - label: 'Product Validation', - href: '/case-studies', - }); - expect(MARKETING_FOOTER_PRODUCT_LINKS).not.toContainEqual({ - label: 'Case Studies', - href: '/case-studies', - }); + it('keeps the hidden case-studies route out of the footer', () => { + expect(MARKETING_FOOTER_PRODUCT_LINKS.map((link) => link.href)).not.toContain('/case-studies'); }); }); diff --git a/frontend/src/app/admin/page.tsx b/frontend/src/app/admin/page.tsx index 97e48dd4b..95db8c51e 100644 --- a/frontend/src/app/admin/page.tsx +++ b/frontend/src/app/admin/page.tsx @@ -311,7 +311,7 @@ export default function AdminPage() { title="API Key Created Successfully" className="mt-4" > -

Save this key now - it won't be shown again!

+

Save this key now. It won't be shown again!

{copiedKey} diff --git a/frontend/src/app/blog/fsma-204-cte-kde-guide/page.tsx b/frontend/src/app/blog/fsma-204-cte-kde-guide/page.tsx index 03b1561de..66128848c 100644 --- a/frontend/src/app/blog/fsma-204-cte-kde-guide/page.tsx +++ b/frontend/src/app/blog/fsma-204-cte-kde-guide/page.tsx @@ -424,7 +424,7 @@ export default function CTEKDEGuidePage() {

- Ready to implement FSMA 204 traceability in your business? See how RegEngine handles CTE/KDE capture, validation, and EPCIS-oriented export workflows. + Ready to implement FSMA 204 traceability in your business? See how RegEngine handles CTE/KDE capture, validation, and EPCIS-oriented export workflows (Preview).

- -
-
- -
- ); + notFound(); } diff --git a/frontend/src/app/controls/page.tsx b/frontend/src/app/controls/page.tsx index e4d6324a7..2db9ce1ff 100644 --- a/frontend/src/app/controls/page.tsx +++ b/frontend/src/app/controls/page.tsx @@ -282,7 +282,7 @@ export default function MyControlsPage() { > - + diff --git a/frontend/src/app/dashboard/export-jobs/page.tsx b/frontend/src/app/dashboard/export-jobs/page.tsx index 8ede51ad7..36d3e932b 100644 --- a/frontend/src/app/dashboard/export-jobs/page.tsx +++ b/frontend/src/app/dashboard/export-jobs/page.tsx @@ -11,6 +11,7 @@ import { Badge } from '@/components/ui/badge'; import { Callout } from '@/components/ui/callout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { EmptyState } from '@/components/ui/empty-state'; +import { NOT_SET, NO_DATA_YET } from '@/lib/empty-values'; import { Field } from '@/components/ui/field'; import { StatTile } from '@/components/ui/stat-tile'; import { Button } from '@/components/ui/button'; @@ -140,7 +141,7 @@ export default function ExportJobsPage() { )}
- + External archive required}>

Use scheduled exports to maintain long-term retention outside the app. @@ -215,20 +216,20 @@ export default function ExportJobsPage() { {job.status.replaceAll('_', ' ')}

- {job.format} · {job.cadence} · {job.destination} + {job.format || NOT_SET} · {job.cadence || NOT_SET} · {job.destination || NOT_SET}

@@ -249,8 +250,18 @@ export default function ExportJobsPage() {
+ Upload a CSV + + } + secondaryAction={ + + } />
{exportPrereqs.map((step) => ( diff --git a/frontend/src/app/dashboard/integrations/page.tsx b/frontend/src/app/dashboard/integrations/page.tsx index 3a3449785..82b5b538f 100644 --- a/frontend/src/app/dashboard/integrations/page.tsx +++ b/frontend/src/app/dashboard/integrations/page.tsx @@ -62,6 +62,7 @@ const STATUS_TONES: Record = { export_supported: 'info', file_import_supported: 'info', pilot: 'warning', + preview: 'warning', design_partner: 'warning', custom_scoped: 'neutral', preparation: 'neutral', diff --git a/frontend/src/app/dashboard/settings/page.tsx b/frontend/src/app/dashboard/settings/page.tsx index 503ae28d6..96474c7f1 100644 --- a/frontend/src/app/dashboard/settings/page.tsx +++ b/frontend/src/app/dashboard/settings/page.tsx @@ -48,7 +48,7 @@ const INTEGRATIONS = [ { id: 'netsuite', name: 'Oracle NetSuite', category: 'ERP', status: 'available', desc: 'Cloud ERP and financials' }, { id: 'walmart', name: 'Walmart GDSN', category: 'Retailer', status: 'coming_soon', desc: 'Global Data Synchronization' }, { id: 'kroger', name: 'Kroger 84.51\u00b0', category: 'Retailer', status: 'coming_soon', desc: 'Retailer data exchange' }, - { id: 'epcis', name: 'EPCIS 2.0 Gateway', category: 'Standards', status: 'epcis_preview', desc: 'GS1 event format bridge; ingest in Preview' }, + { id: 'epcis', name: 'EPCIS 2.0 Gateway (Preview)', category: 'Standards', status: 'epcis_preview', desc: 'GS1 event format bridge; ingest in Preview' }, { id: 'webhook', name: 'Custom Webhooks', category: 'Custom', status: 'preview', desc: 'Push events to your endpoints' }, ]; /** diff --git a/frontend/src/app/docs/api/ApiReferenceClient.tsx b/frontend/src/app/docs/api/ApiReferenceClient.tsx index 49d0a6870..89fc0b50e 100644 --- a/frontend/src/app/docs/api/ApiReferenceClient.tsx +++ b/frontend/src/app/docs/api/ApiReferenceClient.tsx @@ -370,15 +370,15 @@ function EndpointCard({ endpoint }: { endpoint: Endpoint }) { } const ERROR_CODES = [ - { code: '400', desc: 'Bad Request - Missing Idempotency-Key or invalid parameters' }, - { code: '401', desc: 'Unauthorized - Missing or invalid API key, or a missing or wrong ingest signature' }, - { code: '402', desc: 'Payment Required - No active subscription for the workspace' }, - { code: '403', desc: 'Forbidden - Key lacks the required scope, or tenant_id / X-Tenant-ID names a different workspace' }, - { code: '404', desc: "Not Found - Resource doesn't exist" }, - { code: '409', desc: 'Conflict - Idempotency-Key reused with different request bytes' }, - { code: '422', desc: 'Unprocessable - Request body failed validation' }, - { code: '429', desc: 'Too Many Requests - Rate limited' }, - { code: '500', desc: 'Internal Error - Contact support' }, + { code: '400', desc: 'Bad Request: Missing Idempotency-Key or invalid parameters' }, + { code: '401', desc: 'Unauthorized: Missing or invalid API key, or a missing or wrong ingest signature' }, + { code: '402', desc: 'Payment Required: No active subscription for the workspace' }, + { code: '403', desc: 'Forbidden: Key lacks the required scope, or tenant_id / X-Tenant-ID names a different workspace' }, + { code: '404', desc: "Not Found: Resource doesn't exist" }, + { code: '409', desc: 'Conflict: Idempotency-Key reused with different request bytes' }, + { code: '422', desc: 'Unprocessable: Request body failed validation' }, + { code: '429', desc: 'Too Many Requests: Rate limited' }, + { code: '500', desc: 'Internal Error: Contact support' }, ]; const RATE_LIMIT_HEADERS = [ diff --git a/frontend/src/app/docs/authentication/page.tsx b/frontend/src/app/docs/authentication/page.tsx index a253ba9f4..8f7746fde 100644 --- a/frontend/src/app/docs/authentication/page.tsx +++ b/frontend/src/app/docs/authentication/page.tsx @@ -134,7 +134,7 @@ export default function AuthenticationDocsPage() {

Scopes limit which of the endpoints above a key can call. They are not a read-only guarantee: some other operations on the RegEngine backend, for example compliance snapshots, SLA request updates, onboarding steps - and the EPCIS intake endpoint (in Preview), accept any valid tenant key without checking a named scope. Treat every key as + and the EPCIS intake endpoint (Preview), accept any valid tenant key without checking a named scope. Treat every key as read-write for its workspace. Keep it on your server, never in client-side code, and ask RegEngine to revoke it if it leaks.

diff --git a/frontend/src/app/fsma-204/[cte]/page.tsx b/frontend/src/app/fsma-204/[cte]/page.tsx index dfade3342..d020c66a1 100644 --- a/frontend/src/app/fsma-204/[cte]/page.tsx +++ b/frontend/src/app/fsma-204/[cte]/page.tsx @@ -425,17 +425,9 @@ export default async function CTELandingPage({ title="How RegEngine Helps" description={ <> - {cta.enabled ? ( - <> - Upload your {data.name.toLowerCase()} data to the sandbox and - RegEngine surfaces these issues before they become audit findings during a §1.1455 records request. - - ) : ( - <> - RegEngine surfaces these issues in your {data.name.toLowerCase()}{" "} - data before they become audit findings during a §1.1455 records request. - - )} + Upload your {data.name.toLowerCase()} data to{" "} + RegEngine and + RegEngine surfaces these issues before they become audit findings during a §1.1455 records request. } > diff --git a/frontend/src/app/fsma-204/page.tsx b/frontend/src/app/fsma-204/page.tsx index 3b8e9a9e8..bda6844df 100644 --- a/frontend/src/app/fsma-204/page.tsx +++ b/frontend/src/app/fsma-204/page.tsx @@ -372,8 +372,6 @@ export default function FSMA204BusinessGuidePage() {

Try it:{" "} - /proof - {" "}→{" "} /verify.

diff --git a/frontend/src/app/fsma/page.tsx b/frontend/src/app/fsma/page.tsx index dd599e907..8776c96e7 100644 --- a/frontend/src/app/fsma/page.tsx +++ b/frontend/src/app/fsma/page.tsx @@ -95,8 +95,9 @@ function FSMADashboardContent() { } }; - const handleStartDrill = async (type: RecallDrillType = 'FORWARD', fallbackTlc = 'TLC1001') => { - const tlc = activeTlc || fallbackTlc; + const handleStartDrill = async (type: RecallDrillType = 'FORWARD') => { + const tlc = activeTlc; + if (!tlc) return; try { await createDrill.mutateAsync({ type, @@ -157,8 +158,8 @@ function FSMADashboardContent() { } > - Cookie-backed session auth now powers the live FSMA dashboard. Until you sign in, - demo visualization is shown below. + Cookie-backed session auth powers the live FSMA dashboard. Sign in to see + your live facilities, lots, and recall readiness below. )} @@ -223,14 +224,6 @@ function FSMADashboardContent() { Trace
- - {/* Demo Facilities Hint */} - {!activeTlc && ( -

- Try: LOT-2025-ROMAINE-001 or{' '} - TLC-LETTUCE-BATCH-42 -

- )} @@ -272,8 +265,16 @@ function FSMADashboardContent() { bare size="sm" icon={Shield} - title="No supply chain path yet" - description="Run a traceability query to draw the chain for a lot code." + title="No supply chain data connected yet" + description="Import your facilities and lots, then run a traceability query to draw the chain for a lot code." + action={ + + } /> )} diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index d83358fa7..ca6031af9 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -391,9 +391,9 @@ body[data-inflow-lab="true"] { --re-shadow-glow: 0 0 0 3px var(--re-brand-muted); --re-shadow-glow-strong: 0 0 0 3px var(--re-brand-muted); - /* ── Type families. Marketing wears Outfit; the product wears Inter. ── */ + /* Type families. Marketing and product both wear Inter. */ --re-font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif; - --re-font-display: "Outfit", "Inter", system-ui, -apple-system, sans-serif; + --re-font-display: "Inter", system-ui, -apple-system, sans-serif; --re-font-serif: Georgia, "Times New Roman", serif; --re-font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace; diff --git a/frontend/src/app/ingest/page.tsx b/frontend/src/app/ingest/page.tsx index ce5b9d41b..cc447f2d6 100644 --- a/frontend/src/app/ingest/page.tsx +++ b/frontend/src/app/ingest/page.tsx @@ -664,7 +664,7 @@ export default function IngestPage() { htmlFor="epcis-file-upload" className="inline-flex w-fit items-center gap-1 text-ui font-medium text-re-text-primary" > - Select EPCIS file + Select EPCIS file (Preview) Preview diff --git a/frontend/src/app/integrations/page.tsx b/frontend/src/app/integrations/page.tsx index 7e677721e..52d77d7e5 100644 --- a/frontend/src/app/integrations/page.tsx +++ b/frontend/src/app/integrations/page.tsx @@ -22,6 +22,8 @@ import { type CustomerVisibleStatus, } from "@/lib/customer-readiness"; import { Badge, type BadgeTone } from "@/components/ui/badge"; +import { EmptyState } from "@/components/ui/empty-state"; +import { NO_DATA_YET } from "@/lib/empty-values"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Container } from "@/components/layout/container"; @@ -74,6 +76,7 @@ const STATUS_TONE: Record = { implemented: "success", supported: "success", pilot: "info", + preview: "info", design_partner: "info", export_supported: "neutral", file_import_supported: "neutral", @@ -126,6 +129,22 @@ export default function IntegrationsPage() {
+ {grouped.length === 0 && ( + + Upload a CSV + + } + secondaryAction={ + + } + /> + )} {grouped.map((group) => { const CategoryIcon = group.icon; diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 9d8bc43e2..b1765c4e0 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -56,7 +56,7 @@ export default async function RootLayout({ {/* PWA manifest (#568) */} diff --git a/frontend/src/app/onboarding/bulk-upload/page.tsx b/frontend/src/app/onboarding/bulk-upload/page.tsx index 89f896b1b..04d34ef6e 100644 --- a/frontend/src/app/onboarding/bulk-upload/page.tsx +++ b/frontend/src/app/onboarding/bulk-upload/page.tsx @@ -480,7 +480,7 @@ export default function BulkUploadPage() { {csvPreview.sampleRows.length > 0 && (

- Sample Data (first {csvPreview.sampleRows.length} rows) + First {csvPreview.sampleRows.length} rows of your file

diff --git a/frontend/src/app/proof/opengraph-image.tsx b/frontend/src/app/proof/opengraph-image.tsx deleted file mode 100644 index 1d60dfe75..000000000 --- a/frontend/src/app/proof/opengraph-image.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { renderOgCard, OG_SIZE, OG_CONTENT_TYPE } from '@/lib/og-image'; - -export const runtime = 'edge'; -export const alt = 'Verifiable evidence, not screenshots'; -export const size = OG_SIZE; -export const contentType = OG_CONTENT_TYPE; - -export default function Image() { - return renderOgCard({ eyebrow: 'Proof', title: 'Verifiable evidence, not screenshots' }); -} diff --git a/frontend/src/app/proof/page.tsx b/frontend/src/app/proof/page.tsx index d6ef18bcc..7c9b63613 100644 --- a/frontend/src/app/proof/page.tsx +++ b/frontend/src/app/proof/page.tsx @@ -1,250 +1,6 @@ -import type { Metadata } from "next"; -import Link from "next/link"; -import { ArrowRight, Download, FileJson, FileSpreadsheet, Hash, Terminal } from "lucide-react"; -import { Container } from "@/components/layout/container"; -import { PageHeader } from "@/components/layout/page-header"; -import { Section } from "@/components/layout/section"; -import { Button } from "@/components/ui/button"; -import { Card, CardHeader } from "@/components/ui/card"; - -export const metadata: Metadata = { - title: "Proof | RegEngine", - description: - "Download three verifiable sample artifacts generated by production code (FSMA 204 sortable spreadsheet, EPCIS 2.0 JSON-LD export (Preview), and a Merkle-rooted audit bundle) without signing up.", - openGraph: { - title: "Verify RegEngine without signing up", - description: - "Three downloadable artifacts: FSMA 204 spreadsheet, EPCIS 2.0 JSON-LD (Preview), and a Merkle-rooted audit bundle.", - url: "https://regengine.co/proof", - type: "website", - }, -}; - -type Artifact = { - name: string; - href: string; - filename: string; - sizeBytes: number; - icon: typeof FileSpreadsheet; - summary: string; - why: string; - formatLabel: string; -}; - -const ARTIFACTS: Artifact[] = [ - { - name: "FSMA 204 sortable spreadsheet", - href: "/proof/fsma-204-sample.csv", - filename: "fsma-204-sample.csv", - sizeBytes: 1524, - icon: FileSpreadsheet, - formatLabel: "CSV · sortable", - summary: - "A recall-lookback sample for one leafy-greens lot, exported in an FDA-oriented sortable layout.", - why: "FDA records requests may require records, including an electronic sortable spreadsheet under 21 CFR 1.1455(c)(3), unless an exemption or agreed alternative applies. This sample shows RegEngine's current column mapping and should be reviewed against the specific FDA request.", - }, - { - name: "EPCIS 2.0 JSON-LD export (Preview)", - href: "/proof/epcis-sample.jsonld", - filename: "epcis-sample.jsonld", - sizeBytes: 3039, - icon: FileJson, - formatLabel: "EPCIS 2.0 JSON-LD (Preview)", - summary: - "A three-event payload covering harvesting, initial packing, and shipping for one traceability lot code.", - why: "Several major buyers are standardizing on EPCIS 2.0 for supplier ingest. Confirm that what RegEngine emits validates against your retailer's onboarding tests before you sign a contract.", - }, - { - name: "Merkle-rooted audit bundle", - href: "/proof/audit-bundle.zip", - filename: "audit-bundle.zip", - sizeBytes: 2266, - icon: Hash, - formatLabel: "ZIP · SHA-256 + Merkle root", - summary: - "The CSV and EPCIS (Preview) payload plus a manifest.json with per-file SHA-256 hashes and a Merkle root computed by production code.", - why: "Compare each declared file's SHA-256 hash and the bundle's Merkle root with its included manifest. These checks establish internal consistency; they do not authenticate the issuer or establish that the source data is accurate.", - }, -]; - -function formatSize(bytes: number): string { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; -} - -/** - * A terminal transcript. SPEC §5.1: code is a well - one surface step below the - * card, bounded by a hairline rather than an inverted ink block. Result lines - * keep a status colour, but never alone: each one carries its own word ("ok", - * "matched", "FAILED"), so the meaning survives greyscale. - */ -function Transcript({ title, meta, children }: { title: string; meta?: string; children: React.ReactNode }) { - return ( - - -

{title}

- {meta &&

{meta}

} -
-
- {children} -
-
- ); -} +import { notFound } from 'next/navigation'; +// Hidden until /proof can publish artifacts built from a real export. export default function ProofPage() { - return ( -
- -
- Proof} - title="Verify RegEngine without signing up." - description="Three artifacts. No email gate, no demo call. Download the files, open them in your tools, and check the hashes yourself. If the output does not match the claim, the product fails, and you have the receipts." - className="mb-0 border-b-0 pb-0" - /> -
- - -
-
- -

$ unzip -p audit-bundle.zip manifest.json | jq .merkle.root

-

"a320428f40579ec9eaea1a640614d26649f2111a0cc6f6b379003d25278bb6d2"

-

$ python verify_chain.py audit-bundle.zip

-

manifest: ok

-

files: 2 / 2 sha256 matches

-

merkle: root recomputed and matched

-

result: bundle intact

-
-
- -
Downloads} - title="Three artifacts. Open them in your own tools." - > -
- {ARTIFACTS.map(({ name, href, filename, sizeBytes, icon: Icon, summary, why, formatLabel }) => ( -
- - -
- ))} -
-
- -
-
-
-

Run it yourself

-

- Inspect the Python verifier. -

-

- verify_chain.py reads the manifest, recomputes each declared file's SHA-256, and rebuilds the Merkle root using the same algorithm RegEngine ships in services/shared/merkle_tree.py. If those values disagree with the included manifest, the script exits non-zero. -

- -
- -

# 1. Download the bundle and the verifier

-

curl -O https://regengine.co/proof/audit-bundle.zip

-

curl -O https://regengine.co/sdk/verify_chain.py

-

# 2. Run it

-

python verify_chain.py audit-bundle.zip

-

# 3. (Optional) Recompute the leaf yourself

-

shasum -a 256 fsma-204-sample.csv

-
-
-
- -
-
-
-

Watch it fail

-

- A tampered bundle is published next to the real one. -

-

- audit-bundle-tampered.zip contains a changed CSV while the manifest's declared hash remains unchanged. Run the verifier against it and watch it exit non-zero. This demonstrates detection of changed file bytes against an unchanged manifest; replacing both the file and manifest is outside this check. -

- -
- -

$ python verify_chain.py audit-bundle-tampered.zip

-

manifest: ok

-

verify: FAILED: fsma-204-sample.csv sha256 mismatch

-

result: bundle TAMPERED

-

$ echo $?

-

1

-
-
-
- -
Changelog}> - -

2026-05-09: verifier mismatch found and fixed

-

- A reader running the recipe on this page got an argparse error instead of a verification. The verifier we'd shipped didn't match the bundle we'd shipped. The hashes were correct, but the script next to them read JSON, not the zip the page describes. -

-

- Replaced with a verifier that opens the zip, recomputes every per-file SHA-256, and rebuilds the Merkle root using the same algorithm RegEngine ships in services/shared/merkle_tree.py. Added the EPCIS payload to manifest.json, added a build block (commit, generated_at, release), and published audit-bundle-tampered.zip so anyone can watch the new verifier fail closed. -

-

- If you found the original bug before we did, thank you. That's the system working. -

-
-
- -
- - - All artifacts are synthetic. They are regenerated by production code on every release. - - - python verify_chain.py audit-bundle.zip - - -
-
- ); + notFound(); } diff --git a/frontend/src/app/proof/transformation/opengraph-image.tsx b/frontend/src/app/proof/transformation/opengraph-image.tsx deleted file mode 100644 index 78ee3ea14..000000000 --- a/frontend/src/app/proof/transformation/opengraph-image.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { renderOgCard, OG_SIZE, OG_CONTENT_TYPE } from '@/lib/og-image'; - -export const runtime = 'edge'; -export const alt = 'CSV to CTE to FDA row, verified end to end'; -export const size = OG_SIZE; -export const contentType = OG_CONTENT_TYPE; - -export default function Image() { - return renderOgCard({ eyebrow: 'Proof', title: 'CSV to CTE to FDA row, verified end to end' }); -} diff --git a/frontend/src/app/proof/transformation/page.tsx b/frontend/src/app/proof/transformation/page.tsx index 601e1286f..88330c0f3 100644 --- a/frontend/src/app/proof/transformation/page.tsx +++ b/frontend/src/app/proof/transformation/page.tsx @@ -1,80 +1,6 @@ -import type { Metadata } from "next"; -import Link from "next/link"; -import { ArrowRight } from "lucide-react"; -import { Container } from "@/components/layout/container"; -import { PageHeader } from "@/components/layout/page-header"; -import { Section } from "@/components/layout/section"; -import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; +import { notFound } from 'next/navigation'; -export const metadata: Metadata = { - title: "Proof: CSV to FDA row | RegEngine", - description: - "How a supplier CSV becomes an FDA-oriented sortable record in RegEngine. No worked example is published yet.", - openGraph: { - title: "How a supplier CSV becomes an FDA-oriented sortable record | RegEngine", - description: "No worked example is published yet.", - url: "https://regengine.co/proof/transformation", - type: "website", - }, -}; - -export default function TransformationProofPage() { - return ( -
- {/* Hero */} -
- - - ← Proof - - -

- The pipeline is: raw supplier CSV, canonical TraceabilityEvent, then - the FDA-oriented sortable row. The canonical event shape lives in{" "} - - services/shared/canonical_event.py - {" "} - and the export columns in{" "} - - services/ingestion/app/fda_export_service.py - - . -

-
-
- -
- -

Worked example

-

No data yet

-

- There is no worked example with real data to show here. Run the pipeline on your - own records and verify the resulting export bundle yourself. -

-
- - -
-
-
-
- ); +// Hidden: the walkthrough ran on synthetic CSV rows. Restore with a real export. +export default function TransformationPage() { + notFound(); } diff --git a/frontend/src/app/records/page.tsx b/frontend/src/app/records/page.tsx index 463509df2..ad8625241 100644 --- a/frontend/src/app/records/page.tsx +++ b/frontend/src/app/records/page.tsx @@ -35,8 +35,8 @@ const SOURCE_LABEL: Record = { webhook_api: 'Webhook API', csv_upload: 'CSV Upload', xlsx_upload: 'XLSX Upload', - epcis_api: 'EPCIS API', - epcis_xml: 'EPCIS XML', + epcis_api: 'EPCIS API (Preview)', + epcis_xml: 'EPCIS XML (Preview)', manual: 'Manual', legacy_v002: 'Legacy', }; diff --git a/frontend/src/app/requests/page.tsx b/frontend/src/app/requests/page.tsx index e4194bedb..a4236ed9a 100644 --- a/frontend/src/app/requests/page.tsx +++ b/frontend/src/app/requests/page.tsx @@ -94,7 +94,7 @@ export default function RequestWorkflowPage() { const pageHeader = ( {ruleList.length} rules : undefined} primaryAction={!rules.error ? ( + } + />

Bundle

diff --git a/frontend/src/app/settings/support/page.tsx b/frontend/src/app/settings/support/page.tsx index 615d1b63b..caa786d18 100644 --- a/frontend/src/app/settings/support/page.tsx +++ b/frontend/src/app/settings/support/page.tsx @@ -3,6 +3,7 @@ import Link from 'next/link'; import { ShieldAlert, FileText, Mail } from 'lucide-react'; import { SUPPORT_CHANNELS } from '@/lib/customer-readiness'; +import { NOT_SET } from '@/lib/empty-values'; import { PageContainer } from '@/components/layout/page-container'; import { PageHeader } from '@/components/layout/page-header'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; @@ -27,13 +28,13 @@ export default function SupportSettingsPage() {
- {channel.tier} - {channel.responseWindow} + {channel.tier || NOT_SET} + {channel.responseWindow || NOT_SET}
-

{channel.escalation}

-

{channel.notes}

+

{channel.escalation || NOT_SET}

+

{channel.notes || NOT_SET}

))} diff --git a/frontend/src/app/sitemap.ts b/frontend/src/app/sitemap.ts index a34ac1c5e..185a4aba2 100644 --- a/frontend/src/app/sitemap.ts +++ b/frontend/src/app/sitemap.ts @@ -94,7 +94,6 @@ export default function sitemap(): MetadataRoute.Sitemap { const contentPages = [ { url: `${baseUrl}/fsma-204`, changeFrequency: 'monthly' as const, priority: 0.9 }, { url: `${baseUrl}/walkthrough`, changeFrequency: 'monthly' as const, priority: 0.9 }, - { url: `${baseUrl}/case-studies`, changeFrequency: 'monthly' as const, priority: 0.8 }, { url: `${baseUrl}/developers`, changeFrequency: 'monthly' as const, priority: 0.8 }, { url: `${baseUrl}/blog`, changeFrequency: 'weekly' as const, priority: 0.8 }, // Every published post, from the same list the blog index renders — @@ -127,8 +126,6 @@ export default function sitemap(): MetadataRoute.Sitemap { { url: `${baseUrl}/trust/architecture`, changeFrequency: 'monthly' as const, priority: 0.5 }, { url: `${baseUrl}/trust/retention`, changeFrequency: 'monthly' as const, priority: 0.5 }, { url: `${baseUrl}/trust/support`, changeFrequency: 'monthly' as const, priority: 0.5 }, - { url: `${baseUrl}/proof`, changeFrequency: 'monthly' as const, priority: 0.5 }, - { url: `${baseUrl}/proof/transformation`, changeFrequency: 'monthly' as const, priority: 0.5 }, ]; // No blanket lastModified: stamping every URL "now" on every request diff --git a/frontend/src/app/tools/notice-validator/page.tsx b/frontend/src/app/tools/notice-validator/page.tsx index f6f872618..0d67d7b17 100644 --- a/frontend/src/app/tools/notice-validator/page.tsx +++ b/frontend/src/app/tools/notice-validator/page.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from 'react'; import Link from 'next/link'; -import { AlertTriangle, ArrowRight, CheckCircle2, ClipboardPaste, FileWarning, Info, RotateCcw, XCircle } from 'lucide-react'; +import { AlertTriangle, ArrowRight, CheckCircle2, Info, RotateCcw, XCircle } from 'lucide-react'; import { LeadGate } from '@/components/lead-gate/LeadGate'; import { EmailGate } from '@/components/tools/EmailGate'; import { Container } from '@/components/layout/container'; @@ -71,39 +71,6 @@ const REQUIREMENTS: Requirement[] = [ }, ]; -const SAMPLE_NOTICES = { - good: `FDA Request Response - -Traceability Lot Code: 00012345678901-LOT-2026-001 -Product Description: Romaine Lettuce - -Covered Critical Tracking Events: -- Receiving at FreshLeaf Distribution Center on 2026-03-03 -- Transformation into packed case lots on 2026-03-04 -- Shipping to retailer DC on 2026-03-05 - -Key Data Elements Included: -- Quantity: 500 cases -- Unit of Measure: cases -- Ship From: Valley Fresh Farms -- Ship To: FreshLeaf Distribution Center / Retailer DC-04 -- Event Dates and facility references - -Export Package: -- Sortable spreadsheet CSV attached -- Sortable spreadsheet prepared per §1.1455(c) 24 hour availability requirement -- SHA256 and chain hash references included for record verification`, - bad: `Lot response attached. - -Product moved through the system last week. - -Please review the records.`, -}; - -// Note: the "good" sample above is illustrative only — passing this self-check on a draft -// does not mean the draft satisfies §1.1455(c); the rule requires actual record content, -// not keyword presence. - export default function NoticeValidatorPage() { const [noticeText, setNoticeText] = useState(''); const [hasAnalyzed, setHasAnalyzed] = useState(false); @@ -162,22 +129,6 @@ export default function NoticeValidatorPage() {
- -
diff --git a/frontend/src/app/trust/support/page.tsx b/frontend/src/app/trust/support/page.tsx index 0c8b31f31..8c059d3cc 100644 --- a/frontend/src/app/trust/support/page.tsx +++ b/frontend/src/app/trust/support/page.tsx @@ -11,6 +11,7 @@ import { Zap, } from 'lucide-react'; import { SUPPORT_CHANNELS } from '@/lib/customer-readiness'; +import { NOT_SET } from '@/lib/empty-values'; import { Container } from '@/components/layout/container'; import { PageHeader } from '@/components/layout/page-header'; import { Section } from '@/components/layout/section'; @@ -143,13 +144,13 @@ export default function SupportPage() { {SUPPORT_CHANNELS.map((channel) => (
-
{channel.tier}
+
{channel.tier || NOT_SET}
- {channel.responseWindow} + {channel.responseWindow || NOT_SET}
-

{channel.escalation}

-

{channel.notes}

+

{channel.escalation || NOT_SET}

+

{channel.notes || NOT_SET}

))} diff --git a/frontend/src/app/walkthrough/page.tsx b/frontend/src/app/walkthrough/page.tsx index 9c9aa4939..dba6e898b 100644 --- a/frontend/src/app/walkthrough/page.tsx +++ b/frontend/src/app/walkthrough/page.tsx @@ -265,10 +265,6 @@ export default function WalkthroughPage() { See Security Details
-

- See the underlying transformation step by step at{' '} - /proof/transformation. -

diff --git a/frontend/src/app/why-regengine/page.tsx b/frontend/src/app/why-regengine/page.tsx index 7d6bd3587..e9848a492 100644 --- a/frontend/src/app/why-regengine/page.tsx +++ b/frontend/src/app/why-regengine/page.tsx @@ -588,7 +588,7 @@ HTTP 201 EPCIS 2.0 (Preview) can support trading-partner interoperability. RegEngine treats EPCIS 2.0 as a preview schema where the enabled workflow and source data allow it.

- Verify EPCIS coverage with each vendor; some platforms layer EPCIS over legacy schemas, which can introduce conversion edges. + Verify EPCIS (Preview) coverage with each vendor; some platforms layer EPCIS over legacy schemas, which can introduce conversion edges.

    {[ @@ -604,27 +604,6 @@ HTTP 201 ))}
- - - {` - - 2026-04-05T... - - ObjectEvent - - - - urn:epc:id:sgtin:... - - - OBSERVE - - urn:epcglobal:cbv:bizstep:... - - -`} - - {/* SMB Focus */} diff --git a/frontend/src/components/dashboard/metrics-overview-widget.tsx b/frontend/src/components/dashboard/metrics-overview-widget.tsx index 700b06393..da5d5b495 100644 --- a/frontend/src/components/dashboard/metrics-overview-widget.tsx +++ b/frontend/src/components/dashboard/metrics-overview-widget.tsx @@ -86,7 +86,7 @@ export function MetricsOverviewWidget({
{metricsError && (

- Refresh failed — showing the last loaded figures.{" "} + Refresh failed. Showing the last loaded figures.{" "}

{/* EPCIS */} -
+

EPCIS 2.0 export (Preview) diff --git a/scripts/build_audit_bundle.sh b/scripts/build_audit_bundle.sh index d96b42770..850e593b2 100755 --- a/scripts/build_audit_bundle.sh +++ b/scripts/build_audit_bundle.sh @@ -27,7 +27,13 @@ usage() { [[ $# -ge 1 && -n "${1:-}" ]] || usage CSV="$1" -OUT="${2:-$PWD/audit-bundle.zip}" +OUT="${2:-audit-bundle.zip}" +# Resolve a relative output path against the caller's directory now, before +# anything changes into the temp directory (whose EXIT trap deletes it). +case "$OUT" in + /*) ;; + *) OUT="$PWD/$OUT" ;; +esac NAME="$(basename "$CSV")" if [[ ! -f "$CSV" ]]; then @@ -94,5 +100,8 @@ manifest = { print(f"merkle_root={root}") PY -(cd "$WORK" && zip -q -X "$OUT" "$NAME" manifest.json) +# Build a fresh archive inside $WORK, then replace the destination only after +# zip succeeds, so entries from an earlier build never survive a rebuild. +(cd "$WORK" && zip -q -X "$WORK/bundle.zip" "$NAME" manifest.json) +mv -f "$WORK/bundle.zip" "$OUT" echo "wrote $OUT" diff --git a/services/ingestion/tests/test_proof_verify_router.py b/services/ingestion/tests/test_proof_verify_router.py index 4dee9a41b..5c2db325f 100644 --- a/services/ingestion/tests/test_proof_verify_router.py +++ b/services/ingestion/tests/test_proof_verify_router.py @@ -571,14 +571,30 @@ def test_unsupported_content_type_rejected(self, client: TestClient): assert resp.status_code == 400 +def _build_script_bundle(tmp_path: Path) -> bytes: + """Build a bundle with scripts/build_audit_bundle.sh (the tool behind the + public /proof bundle). The input CSV is a header row only, so the test + carries no record data.""" + import subprocess + + csv_path = tmp_path / "export.csv" + csv_path.write_bytes(b"Event ID,Hash\n") + out = tmp_path / "audit-bundle.zip" + subprocess.run( + ["bash", str(REPO_ROOT / "scripts/build_audit_bundle.sh"), str(csv_path), str(out)], + check=True, + capture_output=True, + ) + return out.read_bytes() + + # --------------------------------------------------------------------------- # Happy path — round-trip a real ZIP # --------------------------------------------------------------------------- class TestZipRoundTrip: - @pytest.mark.skip(reason="needs a real export bundle") - def test_checked_in_public_audit_bundle_verifies(self, client: TestClient): - """The ZIP linked from /proof must verify through the public API.""" - bundle = (REPO_ROOT / "frontend/public/proof/audit-bundle.zip").read_bytes() + def test_checked_in_public_audit_bundle_verifies(self, client: TestClient, tmp_path: Path): + """A bundle from the public bundle builder must verify through the API.""" + bundle = _build_script_bundle(tmp_path) resp = client.post( "/api/v1/proof/verify", @@ -594,14 +610,13 @@ def test_checked_in_public_audit_bundle_verifies(self, client: TestClient): assert names["merkle_root_matches_declared"]["passed"] is True assert len(data["merkle_root_recomputed"]) == 64 - @pytest.mark.skip(reason="needs a real export bundle") def test_generic_bundle_may_use_unrelated_verification_metadata( self, client: TestClient, tmp_path: Path, ): """Only FDA markers activate persisted-chain status semantics.""" - original = (REPO_ROOT / "frontend/public/proof/audit-bundle.zip").read_bytes() + original = _build_script_bundle(tmp_path) modified = io.BytesIO() with zipfile.ZipFile(io.BytesIO(original), "r") as source: with zipfile.ZipFile(modified, "w", zipfile.ZIP_DEFLATED) as target: @@ -626,10 +641,18 @@ def test_generic_bundle_may_use_unrelated_verification_metadata( bundle_path.write_bytes(bundle) assert sdk_verify_bundle(str(bundle_path)) == 0 - @pytest.mark.skip(reason="needs a real export bundle") - def test_checked_in_tampered_public_audit_bundle_fails(self, client: TestClient): - """The tampered ZIP linked from /proof must fail verification.""" - bundle = (REPO_ROOT / "frontend/public/proof/audit-bundle-tampered.zip").read_bytes() + def test_checked_in_tampered_public_audit_bundle_fails(self, client: TestClient, tmp_path: Path): + """A builder bundle whose CSV bytes changed after packaging must fail.""" + original = _build_script_bundle(tmp_path) + tampered = io.BytesIO() + with zipfile.ZipFile(io.BytesIO(original), "r") as source: + with zipfile.ZipFile(tampered, "w", zipfile.ZIP_DEFLATED) as target: + for member in source.infolist(): + data = source.read(member.filename) + if member.filename.endswith(".csv"): + data = data + b"\n" + target.writestr(member, data) + bundle = tampered.getvalue() resp = client.post( "/api/v1/proof/verify", From fb5ed4fe368cd99cff1185550b56ba5597de766f Mon Sep 17 00:00:00 2001 From: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Date: Mon, 26 Oct 2026 21:36:20 -0700 Subject: [PATCH 5/5] test(proof): expect empty state instead of removed route Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --- frontend/src/__tests__/pages/proof-trust-copy.test.tsx | 10 ++++++++-- frontend/tests/visual/routes.json | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/__tests__/pages/proof-trust-copy.test.tsx b/frontend/src/__tests__/pages/proof-trust-copy.test.tsx index ae61468f1..48b94fcd2 100644 --- a/frontend/src/__tests__/pages/proof-trust-copy.test.tsx +++ b/frontend/src/__tests__/pages/proof-trust-copy.test.tsx @@ -56,8 +56,14 @@ describe('public proof-bundle trust copy', () => { } }); - it('/proof is hidden until it can publish artifacts from a real export', () => { - expect(() => ProofPage()).toThrow(); + it('/proof shows the empty state instead of artifacts', () => { + const text = renderedText(); + + expect(text).toMatch(/no public artifacts are published yet/i); + + for (const claim of UNBACKED_AUTHENTICITY_CLAIMS) { + expect(text).not.toMatch(claim); + } }); it('verify_chain.py documents that the uploaded manifest is an untrusted commitment', () => { diff --git a/frontend/tests/visual/routes.json b/frontend/tests/visual/routes.json index 679618ebf..b51fb1c80 100644 --- a/frontend/tests/visual/routes.json +++ b/frontend/tests/visual/routes.json @@ -1 +1 @@ -{"_generated":"2026-10-23 from /tmp/re-all-routes.txt (155 non-empty lines) crosschecked against frontend/src/app/**/page.{tsx,jsx}; reconciled 2026-10-23 with the designer crawl (81 routes 200-no-redirect on production): 4 routes moved public->expectRedirectOr404 (/design-system, /tools/anomaly-simulator, /trust-center, /verticals/food-safety)","_note":"Single replaceable route data file for the visual suite. Public vs auth determined by frontend/src/proxy.ts requireAppAuth + AUTHENTICATED_APP_ROUTE_PREFIXES in frontend/src/lib/app-routes.ts + GATED_DEV_ROUTES (/developer/portal) + next.config.js redirects(). Signed-out behavior only.","authOnly":["/admin","/admin/api-console","/api-keys","/audit","/compliance/labels","/compliance/profile","/compliance/snapshots","/compliance/status","/compliance/traceability-plan","/controls","/dashboard","/dashboard/agent-reviews","/dashboard/alerts","/dashboard/audit-log","/dashboard/compliance","/dashboard/export-jobs","/dashboard/heartbeat","/dashboard/inflow-lab","/dashboard/integrations","/dashboard/issues","/dashboard/notifications","/dashboard/products","/dashboard/qa-health","/dashboard/recall-drills","/dashboard/recall-report","/dashboard/recall-response","/dashboard/receiving","/dashboard/scan","/dashboard/settings","/dashboard/suppliers","/dashboard/team","/developer/portal","/developer/portal/keys","/exceptions","/fsma","/fsma/assessment","/fsma/field-capture","/fsma/integration","/identity","/ingest","/ingest/curation","/onboarding","/onboarding/bulk-upload","/onboarding/setup","/onboarding/setup/facility","/onboarding/setup/ftl-check","/onboarding/setup/welcome","/records","/remediation","/requests","/review","/rules","/settings/notifications","/settings/profile","/settings/security","/settings/support","/sysadmin","/trace"],"baseURLEnv":"VISUAL_BASE_URL","excludedReason":{"/admin":"auth-gated: /admin prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/admin/api-console":"auth-gated: /admin prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/alpha":"redirect(): src/app/alpha/page.tsx redirects to /contact; asserted in expectRedirectOr404","/api-keys":"auth-gated transitively: bare redirect() to gated /developer/portal/keys (see GATED_DEV_ROUTES comment in proxy.ts)","/audit":"auth-gated: /audit prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/auth/sso/mock":"excluded entirely: mock SSO fixture page (CHROMELESS_EXACT_ROUTES); must never be screenshotted or visited by monitoring","/compliance":"redirect source: next.config.js 308 /compliance -> /dashboard/compliance; also auth-gated (/compliance prefix); asserted in expectRedirectOr404","/compliance/labels":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/compliance/profile":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/compliance/snapshots":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/compliance/status":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/compliance/traceability-plan":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/controls":"auth-gated: /controls prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/agent-reviews":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/alerts":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/audit-log":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/compliance":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/export-jobs":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/heartbeat":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/inflow-lab":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/integrations":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/issues":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/notifications":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/products":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/qa-health":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/recall-drills":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/recall-report":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/recall-response":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/receiving":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/scan":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/settings":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/suppliers":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/team":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/demo":"demo surface: asserted in expectRedirectOr404 (no baseline). /demo is removed by #3543: src/app/demo/page.tsx is deleted, so the expectRedirectOr404 assertion now passes","/design-system":"404 on production: src/app/design-system/page.tsx calls notFound() when NODE_ENV=production (Internal, development only); absent from the designer 200-no-redirect crawl; asserted in expectRedirectOr404","/developer/portal":"auth-gated: GATED_DEV_ROUTES in proxy.ts; signed-out proxy redirects to /login","/developer/portal/keys":"auth-gated: GATED_DEV_ROUTES in proxy.ts; signed-out proxy redirects to /login","/exceptions":"auth-gated: /exceptions prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/fsma":"auth-gated: /fsma prefix in AUTHENTICATED_APP_ROUTE_PREFIXES (exact + slash match only; does NOT cover /fsma-204); signed-out proxy redirects to /login","/fsma-204/[cte]":"dynamic [cte] param: no fixed URL to baseline; needs a param allowlist before it can be covered","/fsma/assessment":"auth-gated: /fsma prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/fsma/field-capture":"auth-gated: /fsma prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/fsma/integration":"auth-gated: /fsma prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/identity":"auth-gated: /identity prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/ingest":"auth-gated: /ingest prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/ingest/curation":"auth-gated: /ingest prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/ingest/results/[id]":"dynamic [id] param: no fixed URL to baseline; also auth-gated (/ingest prefix)","/onboarding":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/bulk-upload":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/setup":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/setup/facility":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/setup/ftl-check":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/setup/welcome":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/portal/[portalId]":"dynamic [portalId] param: no fixed URL to baseline","/proof/recall-drill":"deleted in #3551: src/app/proof/recall-drill/page.tsx no longer exists (recall drill simulation removed), so the route is expected to 404; asserted in expectRedirectOr404","/records":"auth-gated: /records prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/remediation":"auth-gated: /remediation prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/requests":"auth-gated: /requests prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/requests/[id]":"dynamic [id] param: no fixed URL to baseline; also auth-gated (/requests prefix)","/retailer-readiness":"redirect source: next.config.js 308 /retailer-readiness -> /buyer-programs; page.tsx exists but the redirect wins; asserted in expectRedirectOr404","/review":"auth-gated: /review prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/rules":"auth-gated: /rules prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/sandbox":"deleted in #3557: src/app/sandbox/ no longer exists, so the route is expected to 404; asserted in expectRedirectOr404","/settings":"redirect source: next.config.js 308 /settings -> /dashboard/settings; also auth-gated (/settings prefix); asserted in expectRedirectOr404","/settings/notifications":"auth-gated: /settings prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/settings/profile":"auth-gated: /settings prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/settings/security":"auth-gated: /settings prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/settings/support":"auth-gated: /settings prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/settings/users":"redirect source: next.config.js 308 /settings/users -> /dashboard/settings/users; has NO page.tsx (see ROUTE_CROSSCHECK.md); also auth-gated; asserted in expectRedirectOr404","/sysadmin":"auth-gated: /sysadmin prefix in AUTHENTICATED_APP_ROUTE_PREFIXES (+ sysadmin-only check); signed-out proxy redirects to /login","/tools/anomaly-simulator":"redirect(): src/app/tools/anomaly-simulator/page.tsx permanentRedirects to /tools/drill-simulator (in public); excluded from the designer 200-no-redirect crawl as redirecting; asserted in expectRedirectOr404","/tools/readiness-assessment":"redirect source: next.config.js 308 /tools/readiness-assessment -> /tools/self-reported-checklist; asserted in expectRedirectOr404","/tools/recall-readiness":"redirect source: next.config.js 308 /tools/recall-readiness -> /tools/records-request-checklist; asserted in expectRedirectOr404","/tools/retailer-readiness":"redirect source: page.tsx redirects to /retailer-readiness, which 308s to /buyer-programs; asserted in expectRedirectOr404","/trace":"auth-gated: /trace prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/trust-center":"redirect(): src/app/trust-center/page.tsx redirects to /trust (in public); excluded from the designer 200-no-redirect crawl as redirecting; asserted in expectRedirectOr404","/verticals/food-safety":"redirect(): src/app/verticals/food-safety/page.tsx redirects to /fsma-204 (in public); excluded from the designer 200-no-redirect crawl as redirecting; asserted in expectRedirectOr404"},"expectRedirectOr404":["/demo","/sandbox","/alpha","/retailer-readiness","/tools/recall-readiness","/tools/readiness-assessment","/tools/retailer-readiness","/settings","/settings/users","/compliance","/design-system","/tools/anomaly-simulator","/trust-center","/verticals/food-safety","/proof/recall-drill","/proof","/proof/transformation","/case-studies"],"priority":["/","/pricing","/integrations","/developers","/trust","/why-regengine","/tools/scan","/login","/signup"],"public":["/","/about","/accept-invite","/auth/verify","/blog","/blog/24-hour-rule","/blog/fda-food-traceability-requirements","/blog/food-traceability-software","/blog/fsma-204-compliance-checklist-2026","/blog/fsma-204-compliance-guide","/blog/fsma-204-cte-kde-guide","/blog/fsma-204-mid-market-guide","/blog/fsma-204-traceability-lot-codes","/blog/regengine-vs-spreadsheets-traceability","/buyer-programs","/compare/foodlogiq","/compare/repositrak","/compare/tracegains","/contact","/developers","/developers/compliance-verticals","/docs","/docs/api","/docs/authentication","/docs/changelog","/docs/connectors/inflow-lab","/docs/errors","/docs/fsma-204","/docs/quickstart","/docs/rate-limits","/docs/sdks","/docs/walkthrough","/docs/webhooks","/dpa","/fsma-204","/integrations","/legal/vulnerability-disclosure","/login","/pricing","/privacy","/product","/security","/signup","/status","/supplier-compliance","/terms","/tools","/tools/ask","/tools/cte-mapper","/tools/data-import","/tools/drill-simulator","/tools/export","/tools/fsma-unified","/tools/ftl-checker","/tools/inflow-lab","/tools/inflow-lab-dashboard-preview","/tools/kde-checker","/tools/knowledge-graph","/tools/label-scanner","/tools/notice-validator","/tools/obligation-scanner","/tools/records-request-checklist","/tools/roi-calculator","/tools/scan","/tools/self-reported-checklist","/tools/sop-generator","/tools/tlc-validator","/trust","/trust/architecture","/trust/retention","/trust/support","/verify","/walkthrough","/why-regengine"],"skippedDynamic":["/fsma-204/[cte]","/ingest/results/[id]","/portal/[portalId]","/requests/[id]"]} +{"_generated":"2026-10-23 from /tmp/re-all-routes.txt (155 non-empty lines) crosschecked against frontend/src/app/**/page.{tsx,jsx}; reconciled 2026-10-23 with the designer crawl (81 routes 200-no-redirect on production): 4 routes moved public->expectRedirectOr404 (/design-system, /tools/anomaly-simulator, /trust-center, /verticals/food-safety)","_note":"Single replaceable route data file for the visual suite. Public vs auth determined by frontend/src/proxy.ts requireAppAuth + AUTHENTICATED_APP_ROUTE_PREFIXES in frontend/src/lib/app-routes.ts + GATED_DEV_ROUTES (/developer/portal) + next.config.js redirects(). Signed-out behavior only.","authOnly":["/admin","/admin/api-console","/api-keys","/audit","/compliance/labels","/compliance/profile","/compliance/snapshots","/compliance/status","/compliance/traceability-plan","/controls","/dashboard","/dashboard/agent-reviews","/dashboard/alerts","/dashboard/audit-log","/dashboard/compliance","/dashboard/export-jobs","/dashboard/heartbeat","/dashboard/inflow-lab","/dashboard/integrations","/dashboard/issues","/dashboard/notifications","/dashboard/products","/dashboard/qa-health","/dashboard/recall-drills","/dashboard/recall-report","/dashboard/recall-response","/dashboard/receiving","/dashboard/scan","/dashboard/settings","/dashboard/suppliers","/dashboard/team","/developer/portal","/developer/portal/keys","/exceptions","/fsma","/fsma/assessment","/fsma/field-capture","/fsma/integration","/identity","/ingest","/ingest/curation","/onboarding","/onboarding/bulk-upload","/onboarding/setup","/onboarding/setup/facility","/onboarding/setup/ftl-check","/onboarding/setup/welcome","/records","/remediation","/requests","/review","/rules","/settings/notifications","/settings/profile","/settings/security","/settings/support","/sysadmin","/trace"],"baseURLEnv":"VISUAL_BASE_URL","excludedReason":{"/admin":"auth-gated: /admin prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/admin/api-console":"auth-gated: /admin prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/alpha":"redirect(): src/app/alpha/page.tsx redirects to /contact; asserted in expectRedirectOr404","/api-keys":"auth-gated transitively: bare redirect() to gated /developer/portal/keys (see GATED_DEV_ROUTES comment in proxy.ts)","/audit":"auth-gated: /audit prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/auth/sso/mock":"excluded entirely: mock SSO fixture page (CHROMELESS_EXACT_ROUTES); must never be screenshotted or visited by monitoring","/compliance":"redirect source: next.config.js 308 /compliance -> /dashboard/compliance; also auth-gated (/compliance prefix); asserted in expectRedirectOr404","/compliance/labels":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/compliance/profile":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/compliance/snapshots":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/compliance/status":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/compliance/traceability-plan":"auth-gated: /compliance prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/controls":"auth-gated: /controls prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/agent-reviews":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/alerts":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/audit-log":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/compliance":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/export-jobs":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/heartbeat":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/inflow-lab":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/integrations":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/issues":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/notifications":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/products":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/qa-health":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/recall-drills":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/recall-report":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/recall-response":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/receiving":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/scan":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/settings":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/suppliers":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/dashboard/team":"auth-gated: /dashboard prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/demo":"demo surface: asserted in expectRedirectOr404 (no baseline). /demo is removed by #3543: src/app/demo/page.tsx is deleted, so the expectRedirectOr404 assertion now passes","/design-system":"404 on production: src/app/design-system/page.tsx calls notFound() when NODE_ENV=production (Internal, development only); absent from the designer 200-no-redirect crawl; asserted in expectRedirectOr404","/developer/portal":"auth-gated: GATED_DEV_ROUTES in proxy.ts; signed-out proxy redirects to /login","/developer/portal/keys":"auth-gated: GATED_DEV_ROUTES in proxy.ts; signed-out proxy redirects to /login","/exceptions":"auth-gated: /exceptions prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/fsma":"auth-gated: /fsma prefix in AUTHENTICATED_APP_ROUTE_PREFIXES (exact + slash match only; does NOT cover /fsma-204); signed-out proxy redirects to /login","/fsma-204/[cte]":"dynamic [cte] param: no fixed URL to baseline; needs a param allowlist before it can be covered","/fsma/assessment":"auth-gated: /fsma prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/fsma/field-capture":"auth-gated: /fsma prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/fsma/integration":"auth-gated: /fsma prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/identity":"auth-gated: /identity prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/ingest":"auth-gated: /ingest prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/ingest/curation":"auth-gated: /ingest prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/ingest/results/[id]":"dynamic [id] param: no fixed URL to baseline; also auth-gated (/ingest prefix)","/onboarding":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/bulk-upload":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/setup":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/setup/facility":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/setup/ftl-check":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/onboarding/setup/welcome":"auth-gated: /onboarding prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/portal/[portalId]":"dynamic [portalId] param: no fixed URL to baseline","/proof/recall-drill":"deleted in #3551: src/app/proof/recall-drill/page.tsx no longer exists (recall drill simulation removed), so the route is expected to 404; asserted in expectRedirectOr404","/records":"auth-gated: /records prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/remediation":"auth-gated: /remediation prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/requests":"auth-gated: /requests prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/requests/[id]":"dynamic [id] param: no fixed URL to baseline; also auth-gated (/requests prefix)","/retailer-readiness":"redirect source: next.config.js 308 /retailer-readiness -> /buyer-programs; page.tsx exists but the redirect wins; asserted in expectRedirectOr404","/review":"auth-gated: /review prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/rules":"auth-gated: /rules prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/sandbox":"deleted in #3557: src/app/sandbox/ no longer exists, so the route is expected to 404; asserted in expectRedirectOr404","/settings":"redirect source: next.config.js 308 /settings -> /dashboard/settings; also auth-gated (/settings prefix); asserted in expectRedirectOr404","/settings/notifications":"auth-gated: /settings prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/settings/profile":"auth-gated: /settings prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/settings/security":"auth-gated: /settings prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/settings/support":"auth-gated: /settings prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/settings/users":"redirect source: next.config.js 308 /settings/users -> /dashboard/settings/users; has NO page.tsx (see ROUTE_CROSSCHECK.md); also auth-gated; asserted in expectRedirectOr404","/sysadmin":"auth-gated: /sysadmin prefix in AUTHENTICATED_APP_ROUTE_PREFIXES (+ sysadmin-only check); signed-out proxy redirects to /login","/tools/anomaly-simulator":"redirect(): src/app/tools/anomaly-simulator/page.tsx permanentRedirects to /tools/drill-simulator (in public); excluded from the designer 200-no-redirect crawl as redirecting; asserted in expectRedirectOr404","/tools/readiness-assessment":"redirect source: next.config.js 308 /tools/readiness-assessment -> /tools/self-reported-checklist; asserted in expectRedirectOr404","/tools/recall-readiness":"redirect source: next.config.js 308 /tools/recall-readiness -> /tools/records-request-checklist; asserted in expectRedirectOr404","/tools/retailer-readiness":"redirect source: page.tsx redirects to /retailer-readiness, which 308s to /buyer-programs; asserted in expectRedirectOr404","/trace":"auth-gated: /trace prefix in AUTHENTICATED_APP_ROUTE_PREFIXES; signed-out proxy redirects to /login","/trust-center":"redirect(): src/app/trust-center/page.tsx redirects to /trust (in public); excluded from the designer 200-no-redirect crawl as redirecting; asserted in expectRedirectOr404","/verticals/food-safety":"redirect(): src/app/verticals/food-safety/page.tsx redirects to /fsma-204 (in public); excluded from the designer 200-no-redirect crawl as redirecting; asserted in expectRedirectOr404"},"expectRedirectOr404":["/demo","/sandbox","/alpha","/retailer-readiness","/tools/recall-readiness","/tools/readiness-assessment","/tools/retailer-readiness","/settings","/settings/users","/compliance","/design-system","/tools/anomaly-simulator","/trust-center","/verticals/food-safety","/proof/recall-drill","/proof/transformation","/case-studies"],"priority":["/","/pricing","/integrations","/developers","/trust","/why-regengine","/proof","/tools/scan","/login","/signup"],"public":["/","/about","/accept-invite","/auth/verify","/blog","/blog/24-hour-rule","/blog/fda-food-traceability-requirements","/blog/food-traceability-software","/blog/fsma-204-compliance-checklist-2026","/blog/fsma-204-compliance-guide","/blog/fsma-204-cte-kde-guide","/blog/fsma-204-mid-market-guide","/blog/fsma-204-traceability-lot-codes","/blog/regengine-vs-spreadsheets-traceability","/buyer-programs","/compare/foodlogiq","/compare/repositrak","/compare/tracegains","/contact","/developers","/developers/compliance-verticals","/docs","/docs/api","/docs/authentication","/docs/changelog","/docs/connectors/inflow-lab","/docs/errors","/docs/fsma-204","/docs/quickstart","/docs/rate-limits","/docs/sdks","/docs/walkthrough","/docs/webhooks","/dpa","/fsma-204","/integrations","/legal/vulnerability-disclosure","/login","/pricing","/privacy","/product","/proof","/security","/signup","/status","/supplier-compliance","/terms","/tools","/tools/ask","/tools/cte-mapper","/tools/data-import","/tools/drill-simulator","/tools/export","/tools/fsma-unified","/tools/ftl-checker","/tools/inflow-lab","/tools/inflow-lab-dashboard-preview","/tools/kde-checker","/tools/knowledge-graph","/tools/label-scanner","/tools/notice-validator","/tools/obligation-scanner","/tools/records-request-checklist","/tools/roi-calculator","/tools/scan","/tools/self-reported-checklist","/tools/sop-generator","/tools/tlc-validator","/trust","/trust/architecture","/trust/retention","/trust/support","/verify","/walkthrough","/why-regengine"],"skippedDynamic":["/fsma-204/[cte]","/ingest/results/[id]","/portal/[portalId]","/requests/[id]"]}