diff --git a/frontend/src/__tests__/lib/epcis-upload.test.ts b/frontend/src/__tests__/lib/epcis-upload.test.ts
index 799d71410..816779618 100644
--- a/frontend/src/__tests__/lib/epcis-upload.test.ts
+++ b/frontend/src/__tests__/lib/epcis-upload.test.ts
@@ -82,6 +82,6 @@ describe('epcisErrorMessage', () => {
it('falls back to the error message and then to a default', () => {
expect(epcisErrorMessage(new Error('Network Error'))).toBe('Network Error');
- expect(epcisErrorMessage(null)).toBe('EPCIS upload failed');
+ expect(epcisErrorMessage(null)).toBe('EPCIS upload (Preview) failed');
});
});
diff --git a/frontend/src/__tests__/pages/homepage.test.tsx b/frontend/src/__tests__/pages/homepage.test.tsx
index f75a5b2c4..a0044f6fa 100644
--- a/frontend/src/__tests__/pages/homepage.test.tsx
+++ b/frontend/src/__tests__/pages/homepage.test.tsx
@@ -13,7 +13,7 @@ describe("HomePage Compliance OS foundation", () => {
}),
).toBeInTheDocument();
expect(
- screen.getByText(/ingest supply-chain data through webhooks, csv, epcis, or edi/i),
+ screen.getByText(/send records by csv upload or the json events api\. epcis 2\.0 is in preview\./i),
).toBeInTheDocument();
});
diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx
index 3b8ebb178..f6cbf25ed 100644
--- a/frontend/src/app/about/page.tsx
+++ b/frontend/src/app/about/page.tsx
@@ -22,7 +22,7 @@ import { Card } from "@/components/ui/card";
export const metadata: Metadata = {
title: "About RegEngine | FSMA 204 Compliance Infrastructure",
description:
- "RegEngine was built by Christopher Sellers — AmeriCorps alum, former Senate staffer, and solo technical founder — to make FSMA 204 compliance accessible to mid-market food companies without six-figure platforms.",
+ "RegEngine was built by Christopher Sellers (AmeriCorps alum, former Senate staffer, and solo technical founder) to make FSMA 204 compliance accessible to mid-market food companies without six-figure platforms.",
openGraph: {
title: "About RegEngine | FSMA 204 Compliance Infrastructure",
description:
@@ -103,7 +103,7 @@ export default function AboutPage() {
description={
<>
RegEngine is FSMA 204 compliance infrastructure built for mid-market
- food companies—by someone who saw the gap from inside government,
+ food companies, by someone who saw the gap from inside government,
enterprise sales, and the regulatory stack itself.
>
}
@@ -135,8 +135,8 @@ export default function AboutPage() {
204 compliance tools priced for enterprises, sold through
six-month procurement cycles, and designed for teams with
dedicated compliance departments. The companies that actually
- needed help—regional distributors, mid-size processors,
- growing food brands—were stuck choosing between
+ needed help (regional distributors, mid-size processors,
+ growing food brands) were stuck choosing between
spreadsheets and software they couldn't afford.
@@ -186,7 +186,7 @@ export default function AboutPage() {
RegEngine is built by one person leveraging modern AI tooling as
- a development multiplier. This isn't a limitation—it's
+ a development multiplier. This isn't a limitation; it's
a feature.
@@ -258,7 +258,7 @@ export default function AboutPage() {
Book a free gap analysis call. We'll review your current FSMA 204
posture, identify coverage gaps, and show you exactly what RegEngine
- handles—no commitment required.
+ handles. No commitment required.
- Not every CTE applies to every business — you only record CTEs that occur at your operation. A farm typically records Harvesting and Cooling (§1.1325) and possibly Initial Packing (§1.1330). A manufacturer records Receiving (§1.1345), Transformation (§1.1350), and Shipping (§1.1340). A seafood receiver from a fishing vessel records First Land-Based Receiving (§1.1335) — non-seafood importers record Receiving instead. Use RegEngine's CTE Mapper to walk through which CTEs apply to your role.
+ Not every CTE applies to every business; you only record CTEs that occur at your operation. A farm typically records Harvesting and Cooling (§1.1325) and possibly Initial Packing (§1.1330). A manufacturer records Receiving (§1.1345), Transformation (§1.1350), and Shipping (§1.1340). A seafood receiver from a fishing vessel records First Land-Based Receiving (§1.1335). Non-seafood importers record Receiving instead. Use RegEngine's CTE Mapper to walk through which CTEs apply to your role.
diff --git a/frontend/src/app/blog/food-traceability-software/page.tsx b/frontend/src/app/blog/food-traceability-software/page.tsx
index ec1131292..bdd315334 100644
--- a/frontend/src/app/blog/food-traceability-software/page.tsx
+++ b/frontend/src/app/blog/food-traceability-software/page.tsx
@@ -53,7 +53,7 @@ export default function FoodTraceabilitySoftwarePage() {
The Selection Challenge
- Food traceability software ranges from basic Excel-based tracking to enterprise-grade platforms with automated supply chain visibility. Some vendors pitch themselves as FSMA 204 specialists. Others are generic supply chain tools retrofitted with compliance features. Many promise integration with your ERP—until you learn the integration costs $50K+ and takes six months.
+ Food traceability software ranges from basic Excel-based tracking to enterprise-grade platforms with automated supply chain visibility. Some vendors pitch themselves as FSMA 204 specialists. Others are generic supply chain tools retrofitted with compliance features. Many promise integration with your ERP, until you learn the integration costs $50K+ and takes six months.
The problem: most vendor comparison tools are written by vendors. They highlight features that make their product look best, not features that matter most for FDA's July 2028 enforcement date or your budget.
@@ -120,7 +120,7 @@ export default function FoodTraceabilitySoftwarePage() {
2. Integration: Will It Connect to Your Existing Systems?
- A food traceability platform doesn't live in isolation. It needs to pull data from your ERP, WMS, inventory system, or supplier networks—and push compliance data to FDA systems, retailers, or auditors.
+ A food traceability platform doesn't live in isolation. It needs to pull data from your ERP, WMS, inventory system, or supplier networks, and push compliance data to FDA systems, retailers, or auditors.
@@ -128,11 +128,11 @@ export default function FoodTraceabilitySoftwarePage() {
Tier 1: Native Connectors (Low Cost)
-
Pre-built integrations to popular ERPs (SAP, NetSuite, Salesforce). Usually $0–$5K setup, 2–4 weeks implementation.
+
Pre-built integrations to popular ERPs (SAP, NetSuite, Salesforce). Usually $0 to $5K setup, 2 to 4 weeks implementation.
Tier 2: API-Based Integration (Medium Cost)
-
RESTful or SOAP APIs requiring custom development. $10K–$30K, 4–8 weeks. Your IT team or a consultant builds the bridge.
+
RESTful or SOAP APIs requiring custom development. $10K to $30K, 4 to 8 weeks. Your IT team or a consultant builds the bridge.
Tier 3: Manual Data Entry or Middleware (High Cost)
@@ -159,7 +159,7 @@ export default function FoodTraceabilitySoftwarePage() {
3. Compliance Feature Depth: Does It Enforce FSMA 204 Requirements?
- Not all platforms enforce FSMA 204 compliance equally. Some are passive trackers—they record what you tell them to record. Better platforms actively enforce data validation, deviation tracking, and 24-hour response requirements.
+ Not all platforms enforce FSMA 204 compliance equally. Some are passive trackers: they record what you tell them to record. Better platforms actively enforce data validation, deviation tracking, and 24-hour response requirements.
@@ -209,11 +209,11 @@ export default function FoodTraceabilitySoftwarePage() {
Setup & Onboarding
- Configuration, data migration, user training: $5K–$50K depending on platform and complexity.
+ Configuration, data migration, user training: $5K to $50K depending on platform and complexity.
Integration Development
- Connecting to your ERP or supplier systems: $10K–$100K+ if custom development is required.
+ Connecting to your ERP or supplier systems: $10K to $100K+ if custom development is required.
Per-User or Per-Transaction Fees
@@ -221,7 +221,7 @@ export default function FoodTraceabilitySoftwarePage() {
Custom Development & Support
- Premium support tiers, custom reporting, or feature development: $100–$500/hour or flat retainers.
+ Premium support tiers, custom reporting, or feature development: $100 to $500/hour or flat retainers.
Data Storage & Archival
@@ -245,12 +245,12 @@ export default function FoodTraceabilitySoftwarePage() {
Improved supplier relationships and transparency
- If the platform saves your team 5–10 hours/week in compliance work, that alone justifies the cost.
+ If the platform saves your team 5 to 10 hours/week in compliance work, that alone justifies the cost.
- Request a detailed cost breakdown from every vendor. Ask for references—especially from companies of similar size—and ask them directly about hidden costs.
+ Request a detailed cost breakdown from every vendor. Ask for references (especially from companies of similar size) and ask them directly about hidden costs.
@@ -272,7 +272,7 @@ export default function FoodTraceabilitySoftwarePage() {
2.
-
Shortlist 3–5 Vendors
+
Shortlist 3 to 5 Vendors
Request demos focused on your specific use cases, not generic feature tours.
@@ -287,7 +287,7 @@ export default function FoodTraceabilitySoftwarePage() {
4.
Contact References
-
Speak to 2–3 companies using each platform. Ask about implementation reality vs. promises, hidden costs, and support quality.
+
Speak to 2 or 3 companies using each platform. Ask about implementation reality vs. promises, hidden costs, and support quality.
@@ -317,7 +317,7 @@ export default function FoodTraceabilitySoftwarePage() {
- Integration costs are hidden costs. Factor in setup, development, and ongoing maintenance—not just monthly SaaS fees.
+ Integration costs are hidden costs. Factor in setup, development, and ongoing maintenance, not just monthly SaaS fees.
@@ -329,7 +329,7 @@ export default function FoodTraceabilitySoftwarePage() {
- You have time, but not much. With the July 2028 deadline, start evaluation now. The right platform will take 3–6 months to implement properly.
+ You have time, but not much. With the July 2028 deadline, start evaluation now. The right platform will take 3 to 6 months to implement properly.
diff --git a/frontend/src/app/blog/fsma-204-compliance-checklist-2026/page.tsx b/frontend/src/app/blog/fsma-204-compliance-checklist-2026/page.tsx
index 17109ff19..f12753ffb 100644
--- a/frontend/src/app/blog/fsma-204-compliance-checklist-2026/page.tsx
+++ b/frontend/src/app/blog/fsma-204-compliance-checklist-2026/page.tsx
@@ -69,7 +69,7 @@ export default function FSMA204ComplianceChecklist() {
{ task: 'Document your supply chain', detail: 'List all suppliers, distributors, and customers. Where does data flow today?', done: false },
{ task: 'Assess your IT systems', detail: 'What ERP, WMS, or other systems do you use? Can they be integrated with a traceability platform?', done: false },
{ task: 'Estimate implementation cost and timeline', detail: 'Get quotes from 2-3 FSMA 204 platform vendors. Build a budget for 2026-2027', done: false },
- { task: 'Assign a compliance lead', detail: 'Designate one person to own FSMA 204 implementation. This is a significant role—don\'t dilute it', done: false },
+ { task: 'Assign a compliance lead', detail: 'Designate one person to own FSMA 204 implementation. This is a significant role, so don\'t dilute it', done: false },
].map((item, idx) => (
diff --git a/frontend/src/app/blog/fsma-204-compliance-guide/page.tsx b/frontend/src/app/blog/fsma-204-compliance-guide/page.tsx
index 2fdaca063..f2eeab7f3 100644
--- a/frontend/src/app/blog/fsma-204-compliance-guide/page.tsx
+++ b/frontend/src/app/blog/fsma-204-compliance-guide/page.tsx
@@ -60,7 +60,7 @@ export default function FSMA204ComplianceGuide() {
What Is FSMA 204?
- FSMA 204 is the FDA's Food Traceability Rule. It requires covered food businesses to keep records identifying the immediate previous sources and immediate recipients of their products. When the FDA needs your traceability data—due to a foodborne illness outbreak, contamination, or suspected intentional adulteration—you have 24 hours to provide it.
+ FSMA 204 is the FDA's Food Traceability Rule. It requires covered food businesses to keep records identifying the immediate previous sources and immediate recipients of their products. When the FDA needs your traceability data (due to a foodborne illness outbreak, contamination, or suspected intentional adulteration), you have 24 hours to provide it.
The rule applies to foods specifically listed on the FDA's Food Traceability List (FTL), including leafy greens, berry crops, bivalve mollusks, soft cheeses, and more. If your business handles an FTL product, FSMA 204 traceability obligations may apply.
@@ -119,27 +119,27 @@ export default function FSMA204ComplianceGuide() {
- Traceability Lot Code (TLC) — A unique identifier that links the product through the supply chain
+ Traceability Lot Code (TLC): A unique identifier that links the product through the supply chain
- Product Description — Name, variety, and any identifying information
+ Product Description: Name, variety, and any identifying information
- Quantity and Unit of Measure — How much product, in what units
+ Quantity and Unit of Measure: How much product, in what units
- Location Information — Farm name, facility address, or geographic location
+ Location Information: Farm name, facility address, or geographic location
- Date/Time of CTE — When the event occurred
+ Date/Time of CTE: When the event occurred
- Immediate Previous Source or Recipient — Who you received from or sent to
+ Immediate Previous Source or Recipient: Who you received from or sent to
@@ -247,7 +247,7 @@ export default function FSMA204ComplianceGuide() {
2. Standardize Your Traceability Lot Code
- Implement a consistent, unique TLC format across your supply chain. This doesn't need to be complex—it just needs to identify the product, lot, and date. Work with your suppliers and customers to ensure compatibility.
+ Implement a consistent, unique TLC format across your supply chain. This doesn't need to be complex. It just needs to identify the product, lot, and date. Work with your suppliers and customers to ensure compatibility.
@@ -296,7 +296,7 @@ export default function FSMA204ComplianceGuide() {
- Identify which Critical Tracking Events occur at your operation (§§1.1325–1.1350) and capture the KDEs the rule requires for each one — not every CTE applies to every business.
+ Identify which Critical Tracking Events occur at your operation (§§1.1325 to 1.1350) and capture the KDEs the rule requires for each one. Not every CTE applies to every business.
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 d49e134f8..03b1561de 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
@@ -219,7 +219,7 @@ export default function CTEKDEGuidePage() {
First Land-Based Receiving
- Per §1.1335, this CTE applies only to food obtained from a fishing vessel — the first land-based receiver of seafood landed by a vessel. Non-seafood imports follow the §1.1345 Receiving CTE instead, not this one.
+ Per §1.1335, this CTE applies only to food obtained from a fishing vessel: the first land-based receiver of seafood landed by a vessel. Non-seafood imports follow the §1.1345 Receiving CTE instead, not this one.
{`Required KDEs (per §1.1335):
@@ -232,7 +232,7 @@ export default function CTEKDEGuidePage() {
• Reference Document Type and Number`}
- Implementation note: If you are an importer of non-seafood Food Traceability List items, your obligation is §1.1345 Receiving (subject to the §1.1305(j) restaurant/RFE exemption when selling food directly to consumers) — not §1.1335.
+ Implementation note: If you are an importer of non-seafood Food Traceability List items, your obligation is §1.1345 Receiving (subject to the §1.1305(j) restaurant/RFE exemption when selling food directly to consumers), not §1.1335.
@@ -245,7 +245,7 @@ export default function CTEKDEGuidePage() {
Common KDEs across CTEs
- These KDEs recur across most CTEs, though the exact set required at each event differs by §§1.1325–1.1350. Confirm the specific KDE list against the section that governs the event at your operation:
+ These KDEs recur across most CTEs, though the exact set required at each event differs across §§1.1325 to 1.1350. Confirm the specific KDE list against the section that governs the event at your operation:
- Mid-market food companies—those with 10 to 500 employees—occupy a unique position in the food supply chain. You're large enough that spreadsheets and manual processes are breaking down. You're small enough that you can't afford the enterprise-scale compliance platforms built for Fortune 500 companies. And you're caught in the middle: complex enough to have multiple suppliers and customers, but lean enough that compliance can't become a full-time job for an entire team.
+ Mid-market food companies (those with 10 to 500 employees) occupy a unique position in the food supply chain. You're large enough that spreadsheets and manual processes are breaking down. You're small enough that you can't afford the enterprise-scale compliance platforms built for Fortune 500 companies. And you're caught in the middle: complex enough to have multiple suppliers and customers, but lean enough that compliance can't become a full-time job for an entire team.
- FSMA 204 compliance by July 2028 is non-negotiable. But implementing it right—efficiently, affordably, and without grinding your operations to a halt—requires a different approach than either a small startup or a multinational corporation. This guide walks mid-market food businesses through the unique challenges you face and practical strategies to meet FSMA 204 requirements.
+ FSMA 204 compliance by July 2028 is non-negotiable. But implementing it right (efficiently, affordably, and without grinding your operations to a halt) requires a different approach than either a small startup or a multinational corporation. This guide walks mid-market food businesses through the unique challenges you face and practical strategies to meet FSMA 204 requirements.
@@ -148,7 +148,7 @@ export default function FSMA204MidMarketGuide() {
Cons:
-
• Workflow compromise—features may not fit perfectly
+
• Workflow compromise: features may not fit perfectly
• Vendor lock-in (data portability concerns)
• Ongoing licensing costs ($5K-$50K annually)
@@ -281,37 +281,37 @@ export default function FSMA204MidMarketGuide() {
- ERP/WMS Integration — Does it connect to your existing systems without expensive consulting?
+ ERP/WMS Integration: Does it connect to your existing systems without expensive consulting?
- Supplier Portal — Can your suppliers easily submit traceability data without needing accounts?
+ Supplier Portal: Can your suppliers easily submit traceability data without needing accounts?
- Real-Time Response — Can you generate a 24-hour response in under 10 minutes?
+ Real-Time Response: Can you generate a 24-hour response in under 10 minutes?
- Pricing Predictability — No per-user or per-transaction fees. Fixed annual cost that scales with your business.
+ Pricing Predictability: No per-user or per-transaction fees. Fixed annual cost that scales with your business.
- Implementation Support — Does the vendor provide onboarding, training, and compliance validation?
+ Implementation Support: Does the vendor provide onboarding, training, and compliance validation?
- Data Portability — Can you export your data in open formats if you ever need to switch vendors?
+ Data Portability: Can you export your data in open formats if you ever need to switch vendors?
@@ -333,7 +333,7 @@ export default function FSMA204MidMarketGuide() {
- Plan for 18-24 months of implementation, starting now. Early assessment and pilot are critical. That timeline covers the full organization-wide compliance program — supplier onboarding, process changes, and data cleanup — while getting a traceability tool like RegEngine running takes days.
+ Plan for 18-24 months of implementation, starting now. Early assessment and pilot are critical. That timeline covers the full organization-wide compliance program (supplier onboarding, process changes, and data cleanup), while getting a traceability tool like RegEngine running takes days.
diff --git a/frontend/src/app/blog/fsma-204-traceability-lot-codes/page.tsx b/frontend/src/app/blog/fsma-204-traceability-lot-codes/page.tsx
index 7f92fe3d7..aaa303173 100644
--- a/frontend/src/app/blog/fsma-204-traceability-lot-codes/page.tsx
+++ b/frontend/src/app/blog/fsma-204-traceability-lot-codes/page.tsx
@@ -28,7 +28,7 @@ const TLC_REQUIREMENTS = [
{
cte: "Harvesting",
who: "Farms & growers",
- requirement: "Assign a TLC at harvest. The TLC must uniquely identify the lot — typically a combination of farm, field, date, and commodity.",
+ requirement: "Assign a TLC at harvest. The TLC must uniquely identify the lot, typically a combination of farm, field, date, and commodity.",
},
{
cte: "Cooling",
@@ -116,8 +116,8 @@ export default function TLCGuidePage() {
What Is a Traceability Lot Code?
- A Traceability Lot Code (TLC) is a descriptor — often a number, code,
- or combination — used to identify a traceability lot. Under 21 CFR 1.1310,
+ A Traceability Lot Code (TLC) is a descriptor (often a number, code,
+ or combination) used to identify a traceability lot. Under 21 CFR 1.1310,
every food on the Food Traceability List must carry a TLC that connects it
to the Critical Tracking Events (CTEs) in its supply chain.
diff --git a/frontend/src/app/blog/opengraph-image.tsx b/frontend/src/app/blog/opengraph-image.tsx
index ee8039b49..b0905c3d6 100644
--- a/frontend/src/app/blog/opengraph-image.tsx
+++ b/frontend/src/app/blog/opengraph-image.tsx
@@ -1,7 +1,7 @@
import { renderOgCard, OG_SIZE, OG_CONTENT_TYPE } from '@/lib/og-image';
export const runtime = 'edge';
-export const alt = 'RegEngine — FSMA 204 compliance guides';
+export const alt = 'RegEngine: FSMA 204 compliance guides';
export const size = OG_SIZE;
export const contentType = OG_CONTENT_TYPE;
diff --git a/frontend/src/app/blog/page.tsx b/frontend/src/app/blog/page.tsx
index 058029f88..451042843 100644
--- a/frontend/src/app/blog/page.tsx
+++ b/frontend/src/app/blog/page.tsx
@@ -9,12 +9,12 @@ import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
export const metadata: Metadata = {
- title: 'Blog — FSMA 204 Compliance Guides | RegEngine',
+ title: 'Blog: FSMA 204 Compliance Guides | RegEngine',
description: 'Learn about FSMA 204 compliance, food traceability software, CTEs, KDEs, and how to prepare for FDA food traceability deadlines.',
openGraph: {
url: 'https://regengine.co/blog',
siteName: 'RegEngine',
- title: 'Blog — FSMA 204 Compliance Guides | RegEngine',
+ title: 'Blog: FSMA 204 Compliance Guides | RegEngine',
description: 'Learn about FSMA 204 compliance, food traceability software, CTEs, KDEs, and how to prepare for FDA food traceability deadlines.',
type: 'website',
},
@@ -30,7 +30,7 @@ type BlogPost = {
// Slugs are typed against BLOG_POST_SLUGS so a post added here without being
// added to the shared list (and therefore the sitemap) fails typecheck.
-// Titles live in that shared list too — the social cards render from them.
+// Titles live in that shared list too - the social cards render from them.
const BLOG_POSTS: BlogPost[] = [
{
slug: 'fsma-204-compliance-guide',
diff --git a/frontend/src/app/blog/regengine-vs-spreadsheets-traceability/page.tsx b/frontend/src/app/blog/regengine-vs-spreadsheets-traceability/page.tsx
index ead54ce47..704b427d9 100644
--- a/frontend/src/app/blog/regengine-vs-spreadsheets-traceability/page.tsx
+++ b/frontend/src/app/blog/regengine-vs-spreadsheets-traceability/page.tsx
@@ -87,14 +87,14 @@ export default function RegEngineVsSpreadsheets() {
• Centralized database with single source of truth
• Complete audit trail: who entered data, when, and all changes
-
• Data integrity protection—changes are logged and cannot be undone silently
+
• Data integrity protection: changes are logged and cannot be undone silently
• Access controls: see who accessed which data and when
• Exports formatted to the §1.1455(c) sortable spreadsheet structure, with the underlying audit trail preserved
- An audit trail and chained hashing won't certify your data is correct — only your operation can do that — but they make it materially easier to show how a record was entered, by whom, and whether it changed after ingest. A standalone spreadsheet generally cannot.
+ An audit trail and chained hashing won't certify your data is correct (only your operation can do that), but they make it materially easier to show how a record was entered, by whom, and whether it changed after ingest. A standalone spreadsheet generally cannot.
@@ -114,9 +114,9 @@ export default function RegEngineVsSpreadsheets() {
• People forget to fill in fields or enter them incorrectly
-
• Date and time formats vary—is that 3/5 or 5/3?
+
• Date and time formats vary: is that 3/5 or 5/3?
• Typos in supplier or customer names break traceability links
-
• No validation—data that doesn't make sense (e.g., receiving before shipping) is accepted
+
• No validation: data that doesn't make sense (e.g., receiving before shipping) is accepted
• At 100+ transactions per day, error rates are significant
@@ -242,7 +242,7 @@ export default function RegEngineVsSpreadsheets() {
• Suppliers filling out templates incorrectly
• You manually entering supplier data into your spreadsheet
• No visibility into what's missing until you need it
-
• Supplier compliance is low—they don't see why it matters
+
• Supplier compliance is low because they don't see why it matters
@@ -253,7 +253,7 @@ export default function RegEngineVsSpreadsheets() {
• Supplier portal: suppliers log in and submit traceability data directly
-
• Simple templates: no expertise needed—suppliers fill in basic fields
+
• Simple templates: no expertise needed, since suppliers fill in basic fields
• Automatic validation: the system checks data before it's submitted
• Real-time visibility: you see what you're missing and who hasn't responded
• Fewer support emails: suppliers get instant feedback on what's needed
@@ -276,7 +276,7 @@ export default function RegEngineVsSpreadsheets() {
Spreadsheet Total Cost of Ownership
- Illustrative line items, not a forecast — model your own operation against your actual labor and recall-history baseline.
+ Illustrative line items, not a forecast. Model your own operation against your actual labor and recall-history baseline.
• Staff time to maintain spreadsheets and reconcile lot codes
@@ -292,11 +292,11 @@ export default function RegEngineVsSpreadsheets() {
RegEngine Total Cost of Ownership
-
• RegEngine subscription (priced against volume — contact us for a quote)
+
• RegEngine subscription (priced against volume; contact us for a quote)
• One-time implementation and team onboarding
• Less staff time spent on manual reconciliation once ingestion is wired in
• Pre-formatted §1.1455(c) export rather than ad-hoc preparation
-
• Audit trail and chained hashing to support — not replace — your compliance program
+
• Audit trail and chained hashing to support, not replace, your compliance program
@@ -349,7 +349,7 @@ export default function RegEngineVsSpreadsheets() {
- Stand-alone spreadsheets struggle with FSMA 204 because they lack audit trails, scale poorly, and make a 24-hour §1.1455(c) export hard to assemble — that's a workflow problem, not a verdict on the file format itself.
+ Stand-alone spreadsheets struggle with FSMA 204 because they lack audit trails, scale poorly, and make a 24-hour §1.1455(c) export hard to assemble. That's a workflow problem, not a verdict on the file format itself.
diff --git a/frontend/src/app/buyer-programs/page.tsx b/frontend/src/app/buyer-programs/page.tsx
index 2b7dda7a7..4c0b7c492 100644
--- a/frontend/src/app/buyer-programs/page.tsx
+++ b/frontend/src/app/buyer-programs/page.tsx
@@ -86,7 +86,7 @@ export default function BuyerProgramsPage() {
• FTL by that date; other products ASAP
- We do not make you Walmart-ready, transmit ASN on Retail Link, certify labels, or assess chargebacks. EDI 856 / ASN mapping is scoped work.
+ We do not make you Walmart-ready, transmit ASN on Retail Link, certify labels, or assess chargebacks.
@@ -137,7 +137,7 @@ export default function BuyerProgramsPage() {
• Advisory coverage/completeness checklist
• Append-only commit
• FDA-oriented sortable spreadsheet
-
• EPCIS/CSV where enabled
+
• CSV upload, JSON events API (EPCIS export)
• Hash manifest re-checkable at /verify
diff --git a/frontend/src/app/case-studies/page.tsx b/frontend/src/app/case-studies/page.tsx
index 890547a3e..324e1e589 100644
--- a/frontend/src/app/case-studies/page.tsx
+++ b/frontend/src/app/case-studies/page.tsx
@@ -74,7 +74,7 @@ const NORMALIZATION_EXAMPLES = [
},
];
-/** Identifiers and sample values are recorded data — mono, on the inset well. */
+/** Identifiers and sample values are recorded data - mono, on the inset well. */
const chipClass = 'rounded-sm bg-re-surface-inset px-1.5 py-0.5 font-mono text-[0.8125rem] text-re-text-primary';
const cellClass = 'px-4 py-3 align-middle text-re-text-secondary';
const headClass = 'px-4 py-3 text-left align-middle text-label text-re-text-tertiary';
@@ -103,12 +103,12 @@ export default function CaseStudy() {
Product validation · synthetic data modeled on industry patterns. Not a named-customer case study.
- These metrics are reproducible — try the same recall drill at{' '}
+ These metrics are reproducible. Try the same recall drill at{' '}
/proof/recall-drill.
- We ran a synthetic validation workflow modeled on leafy-greens supply chains — from Salinas Valley
- harvest to grocery DC receiving — and measured every step. These are not customer ROI, SLA,
+ We ran a synthetic validation workflow modeled on leafy-greens supply chains (from Salinas Valley
+ harvest to grocery DC receiving) and measured every step. These are not customer ROI, SLA,
or production-performance commitments.
@@ -314,7 +314,7 @@ export default function CaseStudy() {
OCR artifacts where O/0 or I/1 swap could change lot identity.
- System flags but doesn't auto-correct — lot codes are too critical.
+ System flags but doesn't auto-correct, because lot codes are too critical.
@@ -324,7 +324,7 @@ export default function CaseStudy() {
Records where a required field (e.g., origin location) was blank.
- Cannot be inferred — requires source data correction.
+ Cannot be inferred; requires source data correction.
@@ -347,7 +347,7 @@ export default function CaseStudy() {
We intentionally don't auto-correct lot codes. A wrong lot code
- in an FDA trace means tracing the wrong product — the cost of
+ in an FDA trace means tracing the wrong product, and the cost of
a false positive exceeds the cost of a human review.
@@ -486,7 +486,7 @@ export default function CaseStudy() {
},
{
title: 'Short Shelf Life',
- description: '7-14 day product life means recall speed is existential. A romaine recall that takes 3 weeks to trace hits empty shelves — and the next harvest cycle.',
+ description: '7-14 day product life means recall speed is existential. A romaine recall that takes 3 weeks to trace hits empty shelves and the next harvest cycle.',
metric: '< 1 second full trace latency',
},
{
@@ -534,7 +534,7 @@ export default function CaseStudy() {
Run This Validation on Your Data
Upload a sample CSV. See your auto-normalization rate, KDE completeness score,
- and time to FDA-oriented sortable export — measured on your actual supply chain data.
+ and time to FDA-oriented sortable export, measured on your actual supply chain data.
diff --git a/frontend/src/app/compare/foodlogiq/page.tsx b/frontend/src/app/compare/foodlogiq/page.tsx
index 227d6ff06..e14e897f1 100644
--- a/frontend/src/app/compare/foodlogiq/page.tsx
+++ b/frontend/src/app/compare/foodlogiq/page.tsx
@@ -8,11 +8,11 @@ import { PageHeader } from "@/components/layout/page-header";
import { CheckCircle, XCircle, Minus, ArrowRight, Shield, Clock, Code, Zap } from "lucide-react";
export const metadata: Metadata = {
- title: "RegEngine vs FoodLogiQ (Aptean) — FSMA 204 Compliance Comparison",
+ title: "RegEngine vs FoodLogiQ (Aptean): FSMA 204 Compliance Comparison",
description:
"Compare RegEngine and FoodLogiQ for FSMA 204 compliance. See how self-serve setup, rule evaluation, and transparent pricing differ from enterprise-only deployment.",
openGraph: {
- title: 'RegEngine vs FoodLogiQ (Aptean) — FSMA 204 Compliance Comparison',
+ title: 'RegEngine vs FoodLogiQ (Aptean): FSMA 204 Compliance Comparison',
description: 'Compare RegEngine and FoodLogiQ for FSMA 204 compliance. See how self-serve setup, rule evaluation, and transparent pricing differ from enterprise-only deployment.',
url: 'https://regengine.co/compare/foodlogiq',
type: 'website',
@@ -25,18 +25,18 @@ const FEATURES = [
{ feature: "Public API + OpenAPI docs", regengine: "check", competitor: "partial", note: "Full REST API with interactive docs; verify FoodLogiQ's current API surface" },
{ feature: "FSMA 204 rule engine (KDE + relational)", regengine: "check", competitor: "partial", note: "36 FSMA 204 validation rules (33 stateless + 3 cross-event); rule enforcement is configurable per workflow" },
{ feature: "Per-event compliance scoring", regengine: "check", competitor: "partial", note: "Advisory readiness signal computed per event vs batch reporting" },
- { feature: "CSV / API / webhook ingestion", regengine: "check", competitor: "partial", note: "All three; verify FoodLogiQ's current ingestion surface" },
+ { feature: "CSV upload and JSON events API", regengine: "check", competitor: "partial", note: "Both; verify FoodLogiQ's current ingestion surface" },
{ feature: "Supplier portal with compliance scoring", regengine: "check", competitor: "check", note: "Both offer supplier management" },
{ feature: "Recall simulation drills", regengine: "check", competitor: "check", note: "FoodLogiQ's original strength" },
{ feature: "Published pricing on website", regengine: "check", competitor: "x", note: "List price from $849/mo (Founding rates from $425/mo, billed annually) vs custom enterprise quotes" },
- { feature: "Developer sandbox", regengine: "partial", competitor: "x", note: "Staged rollout — request access" },
+ { feature: "Developer sandbox", regengine: "partial", competitor: "x", note: "Staged rollout; request access" },
];
const WHY_SWITCH = [
- // #2757 — this used to assert the sandbox returns results "in seconds" regardless of
+ // #2757 - this used to assert the sandbox returns results "in seconds" regardless of
// whether the sandbox surface is enabled on the deployment. Reworded to the claim the
// product makes unconditionally: rule evaluation runs on the first CSV you upload.
- { title: "Self-serve onboarding", desc: "RegEngine evaluates your first CSV against the FSMA 204 rule set on upload — no implementation project to get a readiness answer. FoodLogiQ ships through enterprise implementation; verify current onboarding timelines with their team." },
+ { title: "Self-serve onboarding", desc: "RegEngine evaluates your first CSV against the FSMA 204 rule set on upload, with no implementation project to get a readiness answer. FoodLogiQ ships through enterprise implementation; verify current onboarding timelines with their team." },
{ title: "Purpose-built for FSMA 204", desc: "FoodLogiQ originated as recall management; FSMA 204 capabilities were added later per their public roadmap. RegEngine was designed for the Food Traceability Rule from day one with KDE validation and cross-event integrity checks." },
{ title: "Open data egress", desc: "RegEngine ships a public API, OpenAPI docs, and CSV export of every record. Confirm FoodLogiQ's current data-egress options with their team before committing." },
{ title: "Published pricing", desc: "Facility-based list pricing from $849/mo, with Founding rates from $425/mo (billed annually) and no multi-year commitment required. FoodLogiQ pricing is custom-quoted, so request both to compare for your facility count." },
@@ -130,7 +130,7 @@ export default function FoodLogiQCompare() {
{/* CTA */}
See the difference yourself
- {/* #2757 — copy and destination resolve together so the blurb can never
+ {/* #2757 - copy and destination resolve together so the blurb can never
promise a CSV sandbox that this deployment does not serve. */}
{sandboxCta().blurb}
diff --git a/frontend/src/app/compare/repositrak/page.tsx b/frontend/src/app/compare/repositrak/page.tsx
index d558678d8..92d73c5a4 100644
--- a/frontend/src/app/compare/repositrak/page.tsx
+++ b/frontend/src/app/compare/repositrak/page.tsx
@@ -8,11 +8,11 @@ import { PageHeader } from "@/components/layout/page-header";
import { CheckCircle, XCircle, Minus, ArrowRight, Shield, ShoppingCart, Zap, Code } from "lucide-react";
export const metadata: Metadata = {
- title: "RegEngine vs ReposiTrak — FSMA 204 Compliance Comparison",
+ title: "RegEngine vs ReposiTrak: FSMA 204 Compliance Comparison",
description:
"Compare RegEngine and ReposiTrak for FSMA 204 compliance. Deep rule validation vs retailer compliance tracking.",
openGraph: {
- title: 'RegEngine vs ReposiTrak — FSMA 204 Compliance Comparison',
+ title: 'RegEngine vs ReposiTrak: FSMA 204 Compliance Comparison',
description: 'Compare RegEngine and ReposiTrak for FSMA 204 compliance. Deep rule validation vs retailer compliance tracking.',
url: 'https://regengine.co/compare/repositrak',
type: 'website',
@@ -25,17 +25,17 @@ const FEATURES = [
{ feature: "Public API + OpenAPI docs", regengine: "check", competitor: "partial", note: "Full REST API; verify ReposiTrak's current API surface for your stack" },
{ feature: "FSMA 204 rule engine (KDE + relational)", regengine: "check", competitor: "partial", note: "Per-event KDE validation; rule enforcement is configurable per workflow" },
{ feature: "Per-event compliance scoring", regengine: "check", competitor: "partial", note: "Advisory readiness signal computed per event vs status tracking" },
- { feature: "CSV / API / webhook ingestion", regengine: "check", competitor: "partial", note: "All three; verify ReposiTrak's current ingestion options" },
+ { feature: "CSV upload and JSON events API", regengine: "check", competitor: "partial", note: "Both; verify ReposiTrak's current ingestion options" },
{ feature: "Retailer compliance network", regengine: "partial", competitor: "check", note: "ReposiTrak's core strength" },
{ feature: "Supplier portal", regengine: "check", competitor: "check", note: "Both offer supplier management" },
{ feature: "Published pricing on website", regengine: "check", competitor: "x", note: "List price from $849/mo (Founding rates from $425/mo, billed annually) vs custom retailer quotes" },
- { feature: "Developer sandbox", regengine: "partial", competitor: "x", note: "Staged rollout — request access" },
+ { feature: "Developer sandbox", regengine: "partial", competitor: "x", note: "Staged rollout; request access" },
];
const WHY_SWITCH = [
- { title: "Validation, not just submission tracking", icon: Zap, desc: "ReposiTrak focuses on supplier-data submission tracking. RegEngine validates whether the underlying data is FSMA 204 compliant \u2014 checking KDEs, cross-event integrity, and mass balance. You can use both." },
+ { title: "Validation, not just submission tracking", icon: Zap, desc: "ReposiTrak focuses on supplier-data submission tracking. RegEngine validates whether the underlying data is FSMA 204 compliant, checking KDEs, cross-event integrity, and mass balance. You can use both." },
{ title: "Works beyond one retailer", icon: Shield, desc: "ReposiTrak is optimized for retailer-supplier compliance mandates. RegEngine works across your supply chain regardless of which retailers you serve." },
- { title: "Public API and open egress", icon: Code, desc: "RegEngine ships a public REST API, OpenAPI spec, webhook integrations, and CSV export at any time. Confirm ReposiTrak's current integration options for your stack." },
+ { title: "Public API and open egress", icon: Code, desc: "RegEngine ships a public REST API, OpenAPI spec, JSON events API, and CSV export at any time. Confirm ReposiTrak's current integration options for your stack." },
{ title: "Complement your retailer tools", icon: ShoppingCart, desc: "Use ReposiTrak for retailer compliance tracking AND RegEngine for FSMA 204 validation. They solve adjacent problems." },
];
@@ -121,7 +121,7 @@ export default function ReposiTrakCompare() {
{/* CTA */}
See the difference yourself
- {/* #2757 — copy and destination resolve together so the blurb can never
+ {/* #2757 - copy and destination resolve together so the blurb can never
promise a CSV sandbox that this deployment does not serve. */}
{sandboxCta().blurb}
diff --git a/frontend/src/app/compare/tracegains/page.tsx b/frontend/src/app/compare/tracegains/page.tsx
index 929186b57..bf5144e46 100644
--- a/frontend/src/app/compare/tracegains/page.tsx
+++ b/frontend/src/app/compare/tracegains/page.tsx
@@ -8,11 +8,11 @@ import { PageHeader } from "@/components/layout/page-header";
import { CheckCircle, XCircle, Minus, ArrowRight, Shield, FileText, Zap, Users } from "lucide-react";
export const metadata: Metadata = {
- title: "RegEngine vs TraceGains — FSMA 204 Compliance Comparison",
+ title: "RegEngine vs TraceGains: FSMA 204 Compliance Comparison",
description:
"Compare RegEngine and TraceGains for FSMA 204 compliance. Supplier document management vs CTE-level traceability rule evaluation.",
openGraph: {
- title: 'RegEngine vs TraceGains — FSMA 204 Compliance Comparison',
+ title: 'RegEngine vs TraceGains: FSMA 204 Compliance Comparison',
description: 'Compare RegEngine and TraceGains for FSMA 204 compliance. Supplier document management vs CTE-level traceability rule evaluation.',
url: 'https://regengine.co/compare/tracegains',
type: 'website',
@@ -25,18 +25,18 @@ const FEATURES = [
{ feature: "Public API + OpenAPI docs", regengine: "check", competitor: "partial", note: "Full REST API; verify TraceGains' current integration surface" },
{ feature: "FSMA 204 rule engine (KDE + relational)", regengine: "check", competitor: "partial", note: "Per-event KDE validation; confirm TraceGains' current FSMA 204 coverage" },
{ feature: "Per-event compliance scoring", regengine: "check", competitor: "partial", note: "Advisory readiness signal computed per event" },
- { feature: "CSV / API / webhook ingestion", regengine: "check", competitor: "partial", note: "All three; verify TraceGains' current ingestion options" },
+ { feature: "CSV upload and JSON events API", regengine: "check", competitor: "partial", note: "Both; verify TraceGains' current ingestion options" },
{ feature: "Supplier document management (COAs, specs)", regengine: "partial", competitor: "check", note: "TraceGains' core strength" },
{ feature: "Supplier onboarding portal", regengine: "check", competitor: "check", note: "Both offer supplier management" },
{ feature: "Published pricing on website", regengine: "check", competitor: "x", note: "List price from $849/mo (Founding rates from $425/mo, billed annually) vs custom quotes" },
- { feature: "Developer sandbox", regengine: "partial", competitor: "x", note: "Staged rollout — request access" },
+ { feature: "Developer sandbox", regengine: "partial", competitor: "x", note: "Staged rollout; request access" },
];
const WHY_SWITCH = [
- { title: "CTE-level traceability", icon: Zap, desc: "TraceGains is strong at COA and supplier-spec collection. FSMA 204 also requires CTE-level KDE traceability \u2014 confirm coverage with each vendor for your commodities." },
+ { title: "CTE-level traceability", icon: Zap, desc: "TraceGains is strong at COA and supplier-spec collection. FSMA 204 also requires CTE-level KDE traceability. Confirm coverage with each vendor for your commodities." },
{ title: "Per-event rule evaluation", icon: Shield, desc: "RegEngine evaluates events against 36 FSMA 204 validation rules (33 stateless + 3 cross-event) as part of ingestion. Verify TraceGains' current validation features directly with their team." },
{ title: "Built for FTL commodities", icon: FileText, desc: "TraceGains is positioned for CPG supplier quality management. RegEngine is built for produce, seafood, and other FTL-covered commodities." },
- { title: "Complement, don't replace", icon: Users, desc: "You can use TraceGains for supplier docs AND RegEngine for CTE traceability. They solve different problems \u2014 you may need both." },
+ { title: "Complement, don't replace", icon: Users, desc: "You can use TraceGains for supplier docs AND RegEngine for CTE traceability. They solve different problems, and you may need both." },
];
@@ -66,7 +66,7 @@ export default function TraceGainsCompare() {
description={
<>
TraceGains is a supplier document management platform. RegEngine is a FSMA 204 traceability engine.
- Different tools for different problems — here's how they compare.
+ Different tools for different problems. Here's how they compare.
>
}
/>
@@ -121,7 +121,7 @@ export default function TraceGainsCompare() {
{/* CTA */}
See the difference yourself
- {/* #2757 — copy and destination resolve together so the blurb can never
+ {/* #2757 - copy and destination resolve together so the blurb can never
promise a CSV sandbox that this deployment does not serve. */}
RegEngine monitors FDA recalls and alerts. By setting your product categories and supply regions,
- we can automatically alert you when a recall might affect your business — before it becomes a crisis.
+ we can automatically alert you when a recall might affect your business, before it becomes a crisis.
diff --git a/frontend/src/app/compliance/traceability-plan/page.tsx b/frontend/src/app/compliance/traceability-plan/page.tsx
index 2e7475e01..3bce508fb 100644
--- a/frontend/src/app/compliance/traceability-plan/page.tsx
+++ b/frontend/src/app/compliance/traceability-plan/page.tsx
@@ -195,7 +195,7 @@ export default function TraceabilityPlanPage() {
diff --git a/frontend/src/app/contact/layout.tsx b/frontend/src/app/contact/layout.tsx
index 71f27309a..79297a6ec 100644
--- a/frontend/src/app/contact/layout.tsx
+++ b/frontend/src/app/contact/layout.tsx
@@ -4,7 +4,7 @@ export const metadata: Metadata = {
title: 'Contact Us | RegEngine',
description: 'Get in touch with the RegEngine team. Ask about FSMA 204 compliance, request a demo, or discuss your traceability needs.',
openGraph: {
- title: 'Contact Us — RegEngine',
+ title: 'Contact Us | RegEngine',
description: 'Get in touch with the RegEngine team. Ask about FSMA 204 compliance, request a demo, or discuss your traceability needs.',
type: 'website',
url: 'https://regengine.co/contact',
diff --git a/frontend/src/app/contact/page.tsx b/frontend/src/app/contact/page.tsx
index 3509f41d8..d24dc54e4 100644
--- a/frontend/src/app/contact/page.tsx
+++ b/frontend/src/app/contact/page.tsx
@@ -24,8 +24,8 @@ export default function ContactPage() {
{/* Gap Analysis Offer */}
- Book a free gap-analysis call{' '}
- — 30 minutes with the founder to walk through your FSMA 204 posture and identify coverage gaps.
+ Book a free gap-analysis call:{' '}
+ 30 minutes with the founder to walk through your FSMA 204 posture and identify coverage gaps.
0 && (
<>
- {/* Summary tiles — each one also toggles its severity filter */}
+ {/* Summary tiles - each one also toggles its severity filter */}
diff --git a/frontend/src/app/dashboard/receiving/page.tsx b/frontend/src/app/dashboard/receiving/page.tsx
index 871c56713..0fcd19d80 100644
--- a/frontend/src/app/dashboard/receiving/page.tsx
+++ b/frontend/src/app/dashboard/receiving/page.tsx
@@ -30,6 +30,7 @@ import { useToast } from '@/components/ui/use-toast';
import { parseGS1 } from '@/lib/gs1-parser';
import { scanMatchesItem } from '@/lib/scan-match';
import { notifyDashboardRefresh } from '@/hooks/use-dashboard-refresh';
+import { NOT_SET } from '@/lib/empty-values';
/* ─── Types ─── */
interface LineItem {
@@ -65,9 +66,9 @@ const EXPECTED_FIELD_HINTS: readonly (readonly [string, string])[] = [
];
const RECEIPT_COLUMNS: DataTableColumn[] = [
- { key: 'productName', header: 'Product', cell: (item) => item.productName || '—' },
- { key: 'gtin', header: 'Barcode', mono: true, cell: (item) => item.gtin || '—' },
- { key: 'lotCode', header: 'Lot', mono: true, cell: (item) => item.lotCode || '—' },
+ { key: 'productName', header: 'Product', cell: (item) => item.productName || NOT_SET },
+ { key: 'gtin', header: 'Barcode', mono: true, cell: (item) => item.gtin || NOT_SET },
+ { key: 'lotCode', header: 'Lot', mono: true, cell: (item) => item.lotCode || NOT_SET },
{ key: 'quantity', header: 'Qty', align: 'right', cell: (item) => item.quantity },
];
@@ -124,7 +125,7 @@ export default function ReceivingDockPage() {
const scannedGtin = parsed.gtin || '';
const scannedLot = parsed.tlc || '';
// The input invites typing a bare GTIN or lot code, but parseGS1 only
- // extracts values from AI-encoded / Digital Link strings — so a user
+ // extracts values from AI-encoded / Digital Link strings - so a user
// typing exactly what the Expected Items list displays never matched
// and every scan landed in Unexpected Items. scanMatchesItem also
// compares the raw string directly (matrix unit-tested in
@@ -149,7 +150,7 @@ export default function ReceivingDockPage() {
}
if (!matched) {
- // Unexpected scan — add to unexpected list
+ // Unexpected scan - add to unexpected list
updated.unexpectedScans.push({
id: `unx-${Date.now()}`,
gtin: scannedGtin,
@@ -278,7 +279,7 @@ export default function ReceivingDockPage() {
/* ─── Render ─── */
return (
- {/* Back link — the shell owns the breadcrumb; this stays as the
+ {/* Back link - the shell owns the breadcrumb; this stays as the
dock's own return affordance. */}
@@ -371,7 +372,7 @@ export default function ReceivingDockPage() {
- {/* Scan input — keyboard wedge or manual */}
+ {/* Scan input - keyboard wedge or manual */}
diff --git a/frontend/src/app/dashboard/scan/page.tsx b/frontend/src/app/dashboard/scan/page.tsx
index cb93551ef..9ff06e0cc 100644
--- a/frontend/src/app/dashboard/scan/page.tsx
+++ b/frontend/src/app/dashboard/scan/page.tsx
@@ -22,7 +22,7 @@ import {
} from 'lucide-react';
/* ------------------------------------------------------------------ */
-/* QR Code generator — renders inline SVG via the `qrcode` package */
+/* QR Code generator - renders inline SVG via the `qrcode` package */
/* ------------------------------------------------------------------ */
let qrcodeLib: typeof import('qrcode') | null = null;
const SAFE_SCAN_TENANT_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
@@ -60,7 +60,7 @@ function useQRCodeSvgDataUrl(text: string) {
}
/* ------------------------------------------------------------------ */
-/* Detect viewport — show scanner on mobile, QR share on desktop */
+/* Detect viewport - show scanner on mobile, QR share on desktop */
/* ------------------------------------------------------------------ */
const MOBILE_MEDIA_QUERY = '(max-width: 768px)';
@@ -90,7 +90,7 @@ const HOW_IT_WORKS = [
{
step: '2',
title: 'Select event type & scan item',
- detail: 'Choose what you\'re doing: Receiving, Shipping, or Transforming. Point your camera at the barcode — product number, lot code, expiry, and serial are extracted automatically.',
+ detail: 'Choose what you\'re doing: Receiving, Shipping, or Transforming. Point your camera at the barcode; product number, lot code, expiry, and serial are extracted automatically.',
},
{
step: '3',
@@ -195,7 +195,7 @@ function ScanPageContent() {
- Display this QR code on a tablet or print it. Floor operators scan it with their phone to open the capture tool — no app install required.
+ Display this QR code on a tablet or print it. Floor operators scan it with their phone to open the capture tool. No app install required.
{/* QR Code */}
@@ -282,7 +282,7 @@ function ScanPageContent() {
))}
- Barcodes are parsed automatically — product number, lot code, serial, expiry, and pack date are extracted and mapped to the required data fields.
+ Barcodes are parsed automatically: product number, lot code, serial, expiry, and pack date are extracted and mapped to the required data fields.
diff --git a/frontend/src/app/dashboard/settings/page.tsx b/frontend/src/app/dashboard/settings/page.tsx
index 3faf2f70b..c653229e4 100644
--- a/frontend/src/app/dashboard/settings/page.tsx
+++ b/frontend/src/app/dashboard/settings/page.tsx
@@ -39,7 +39,7 @@ const ACCOUNT_LINKS = [
];
/* ── Available Integrations Catalog ── */
-// Integration catalog — status is updated from API if available
+// Integration catalog - status is updated from API if available
const INTEGRATIONS = [
{ id: 'sensitech', name: 'Sensitech TempTale', category: 'IoT', status: 'available', desc: 'Cold-chain temperature monitoring' },
{ id: 'tive', name: 'Tive Trackers', category: 'IoT', status: 'available', desc: 'Real-time shipment visibility' },
@@ -47,8 +47,8 @@ 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: 'available', desc: 'GS1 event format bridge' },
- { id: 'webhook', name: 'Custom Webhooks', category: 'Custom', status: 'available', desc: 'Push events to your endpoints' },
+ { id: 'epcis', name: 'EPCIS 2.0 Gateway', 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' },
];
/**
* Integration status. `state` promotes the operational statuses to the
@@ -61,6 +61,8 @@ const STATUS_CONFIG: Record
available: { label: 'Available' },
disconnected: { label: 'Available' },
coming_soon: { label: 'Coming Soon' },
+ preview: { label: 'Preview / contact us' },
+ epcis_preview: { label: 'Preview' },
};
const TABS = [
@@ -226,7 +228,7 @@ export default function SettingsPage() {
return (
{/* The dashboard shell owns the breadcrumb and the service-health
- badge — this page only renders its own header. */}
+ badge: this page only renders its own header. */}
setProfile({ ...profile, fei_number: e.target.value })}
- placeholder="Optional — used in FDA exports"
+ placeholder="Optional, used in FDA exports"
/>
diff --git a/frontend/src/app/dashboard/suppliers/page.tsx b/frontend/src/app/dashboard/suppliers/page.tsx
index e69fe7c9c..95aee7ecc 100644
--- a/frontend/src/app/dashboard/suppliers/page.tsx
+++ b/frontend/src/app/dashboard/suppliers/page.tsx
@@ -333,7 +333,7 @@ export default function SupplierDashboardPage() {
setInviteSupplierEmail('');
setInviteProfileId('');
setShowInviteForm(false);
- // Auto-copy the link — best-effort only. clipboard.writeText
+ // Auto-copy the link - best-effort only. clipboard.writeText
// rejects whenever the document is unfocused or permission is
// denied; sharing the API call's catch made a SUCCESSFUL link
// creation render as "Unable to create portal link", inviting
@@ -478,7 +478,7 @@ export default function SupplierDashboardPage() {
diff --git a/frontend/src/app/demo/layout.tsx b/frontend/src/app/demo/layout.tsx
index 947908fc9..e4835ec27 100644
--- a/frontend/src/app/demo/layout.tsx
+++ b/frontend/src/app/demo/layout.tsx
@@ -1,11 +1,11 @@
import type { Metadata } from "next";
export const metadata: Metadata = {
- title: "Interactive Demo — RegEngine FSMA 204 Compliance Platform",
+ title: "Interactive Demo: RegEngine FSMA 204 Compliance Platform",
description:
"See RegEngine in action with real compliance data. No signup required. Explore the dashboard, rule engine, supplier portal, and recall simulation.",
openGraph: {
- title: 'Interactive Demo — RegEngine FSMA 204 Compliance Platform',
+ title: 'Interactive Demo: RegEngine FSMA 204 Compliance Platform',
description: 'See RegEngine in action with real compliance data. No signup required. Explore the dashboard, rule engine, supplier portal, and recall simulation.',
url: 'https://regengine.co/demo',
type: 'website',
diff --git a/frontend/src/app/demo/opengraph-image.tsx b/frontend/src/app/demo/opengraph-image.tsx
index aa49e90b8..d3302cba8 100644
--- a/frontend/src/app/demo/opengraph-image.tsx
+++ b/frontend/src/app/demo/opengraph-image.tsx
@@ -1,10 +1,10 @@
import { renderOgCard, OG_SIZE, OG_CONTENT_TYPE } from '@/lib/og-image';
export const runtime = 'edge';
-export const alt = 'See RegEngine in action — no signup required';
+export const alt = 'See RegEngine in action. No signup required.';
export const size = OG_SIZE;
export const contentType = OG_CONTENT_TYPE;
export default function Image() {
- return renderOgCard({ eyebrow: 'Interactive Demo', title: 'See RegEngine in action — no signup required' });
+ return renderOgCard({ eyebrow: 'Interactive Demo', title: 'See RegEngine in action. No signup required.' });
}
diff --git a/frontend/src/app/demo/page.tsx b/frontend/src/app/demo/page.tsx
index 0cf13fd27..cd54b6a00 100644
--- a/frontend/src/app/demo/page.tsx
+++ b/frontend/src/app/demo/page.tsx
@@ -14,7 +14,7 @@ import { StatTile } from '@/components/ui/stat-tile';
import { StatusBadge, type ComplianceState } from '@/components/ui/status-badge';
// ---------------------------------------------------------------------------
-// Synthetic demo data — what a mid-market produce distributor would see
+// Synthetic demo data - what a mid-market produce distributor would see
// ---------------------------------------------------------------------------
const COMPLIANCE_SCORE = 94.2;
@@ -90,7 +90,7 @@ export default function DemoPage() {
title="Compliance Dashboard"
description={
<>
- DEMO WORKSPACE — FreshCo Distribution is a fictional distributor. Numbers are illustrative.
+ DEMO WORKSPACE: FreshCo Distribution is a fictional distributor. Numbers are illustrative.
>
}
/>
@@ -211,7 +211,7 @@ export default function DemoPage() {
This could be your data
- RegEngine ingests events from your ERP, validates them against 36 FSMA 204 validation rules (33 stateless + 3 cross-event), and surfaces gaps before they become audit findings during a §1.1455(c) records request. The compliance score shown is RegEngine's internal six-dimension readiness signal — not an FDA-issued rating.
+ RegEngine ingests events from your ERP, validates them against 36 FSMA 204 validation rules (33 stateless + 3 cross-event), and surfaces gaps before they become audit findings during a §1.1455(c) records request. The compliance score shown is RegEngine's internal six-dimension readiness signal, not an FDA-issued rating.
diff --git a/frontend/src/app/design-system/page.tsx b/frontend/src/app/design-system/page.tsx
index bcf352b55..fdf30a43b 100644
--- a/frontend/src/app/design-system/page.tsx
+++ b/frontend/src/app/design-system/page.tsx
@@ -29,7 +29,7 @@ import { StatusBadge, type ComplianceState } from '@/components/ui/status-badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
/**
- * /design-system — the rendered contract for the design system.
+ * /design-system - the rendered contract for the design system.
*
* Every primitive, in every state, in whichever theme is active. This is what a
* page author checks their work against, and the fastest way for a human to
@@ -264,7 +264,7 @@ export default function DesignSystemPage() {
-
Card variant inset — wells, code blocks and summary strips.
+
Card variant inset: wells, code blocks and summary strips.
diff --git a/frontend/src/app/developers/compliance-verticals/page.tsx b/frontend/src/app/developers/compliance-verticals/page.tsx
index e12957749..946b69891 100644
--- a/frontend/src/app/developers/compliance-verticals/page.tsx
+++ b/frontend/src/app/developers/compliance-verticals/page.tsx
@@ -34,7 +34,7 @@ const FRAMEWORKS = [
title: 'Seafood',
icon: Fish,
description: 'Finfish and shellfish chain-of-custody controls',
- details: 'Reference scaffold for seafood obtained from a fishing vessel — uses §1.1335 First Land-Based Receiving as the entry CTE. Final CTE/KDE coverage is configured during onboarding.',
+ details: 'Reference scaffold for seafood obtained from a fishing vessel. Uses §1.1335 First Land-Based Receiving as the entry CTE. Final CTE/KDE coverage is configured during onboarding.',
jsonSnippet: `{
"profile": "seafood",
"ftl_categories": ["Fresh Finfish", "Crustaceans"],
diff --git a/frontend/src/app/developers/page.tsx b/frontend/src/app/developers/page.tsx
index 6626deb46..e096f7a1c 100644
--- a/frontend/src/app/developers/page.tsx
+++ b/frontend/src/app/developers/page.tsx
@@ -1,7 +1,7 @@
import type { Metadata } from 'next';
import Link from 'next/link';
import {
- ArrowRight, Webhook, Cog, Users, FileSpreadsheet,
+ ArrowRight, Braces, Cog, Users, FileSpreadsheet,
BarChart3, GitBranch, Terminal, BookOpen,
} from 'lucide-react';
import { Badge } from '@/components/ui/badge';
@@ -11,13 +11,13 @@ import { Container } from '@/components/layout/container';
import { INGEST_CURL_SAMPLE, INGEST_ENV_SETUP } from '@/lib/api-samples/ingest-samples';
export const metadata: Metadata = {
- title: 'Developers | RegEngine — FSMA 204 Compliance API',
+ title: 'Developers | RegEngine FSMA 204 Compliance API',
description:
- 'Build on the RegEngine API: webhook ingestion with KDE validation, 36 FSMA 204 rules, identity resolution, FDA export, compliance scoring, and request workflow management.',
+ 'Build on the RegEngine API: a JSON events API with KDE validation, 36 FSMA 204 rules, identity resolution, FDA export, compliance scoring, and request workflow management.',
openGraph: {
- title: 'Developers | RegEngine — FSMA 204 Compliance API',
+ title: 'Developers | RegEngine FSMA 204 Compliance API',
description:
- 'Build on the RegEngine API: webhook ingestion, rules engine, identity resolution, FDA export, compliance scoring, and request workflow.',
+ 'Build on the RegEngine API: JSON events API, rules engine, identity resolution, FDA export, compliance scoring, and request workflow.',
url: 'https://regengine.co/developers',
type: 'website',
},
@@ -26,9 +26,9 @@ export const metadata: Metadata = {
/* ── API Capabilities ── */
const capabilities = [
{
- Icon: Webhook,
- title: 'Webhook Ingestion',
- description: 'POST events with KDE validation. Inbound CTEs are validated against FSMA 204 Key Data Elements; failures surface as exceptions before package assembly.',
+ Icon: Braces,
+ title: 'JSON events API',
+ description: 'POST CTE events as JSON with KDE validation. Inbound events are validated against FSMA 204 Key Data Elements; failures surface as exceptions before package assembly.',
},
{
Icon: Cog,
@@ -167,7 +167,7 @@ export default function DevelopersPage() {
SDKs Coming Soon
Official client libraries for Python, Node.js, and Go are planned.
- In the meantime, use our REST API directly — all endpoints accept and return JSON.
+ In the meantime, use our REST API directly. All endpoints accept and return JSON.
diff --git a/frontend/src/app/docs/api/ApiReferenceClient.tsx b/frontend/src/app/docs/api/ApiReferenceClient.tsx
index 874868ea8..09ddf689b 100644
--- a/frontend/src/app/docs/api/ApiReferenceClient.tsx
+++ b/frontend/src/app/docs/api/ApiReferenceClient.tsx
@@ -43,7 +43,7 @@ interface Endpoint {
// API Endpoint Documentation
const API_SERVICES: { name: string; baseUrl: string; icon: React.ComponentType<{ className?: string }>; description: string; provisioned?: boolean; endpoints: Endpoint[] }[] = [
{
- name: 'Webhook Ingestion API',
+ name: 'JSON Events API',
baseUrl: '/api/v1/webhooks',
icon: FileText,
description: 'Primary FSMA event ingestion endpoint for CTE batches',
@@ -59,12 +59,12 @@ const API_SERVICES: { name: string; baseUrl: string; icon: React.ComponentType<{
{ name: 'events', type: 'CTE[]', required: true, desc: 'Array of Critical Tracking Events (max 500 per batch)' },
{ name: 'events[].cte_type', type: 'enum', required: true, desc: '"harvesting" | "cooling" | "initial_packing" | "first_land_based_receiving" | "shipping" | "receiving" | "transformation"' },
{ name: 'events[].traceability_lot_code', type: 'string', required: true, desc: 'TLC per FSMA 204 (e.g. GTIN + lot, min 3 chars)' },
- { name: 'events[].product_description', type: 'string', required: true, desc: 'Human-readable product name (1–500 chars)' },
+ { name: 'events[].product_description', type: 'string', required: true, desc: 'Human-readable product name (1 to 500 chars)' },
{ name: 'events[].quantity', type: 'number', required: true, desc: 'Quantity (> 0)' },
- { name: 'events[].unit_of_measure', type: 'string', required: true, desc: '"cases" | "lbs" | "kg" | "pallets" | "each" — full list in docs' },
+ { name: 'events[].unit_of_measure', type: 'string', required: true, desc: '"cases" | "lbs" | "kg" | "pallets" | "each" (full list in docs)' },
{ name: 'events[].location_name', type: 'string', required: false, desc: 'Facility name. Each event needs at least one location: location_name, events[].location_gln (a 13-digit GLN), or a location KDE. harvesting, cooling, initial_packing and transformation events also require location_name itself.' },
{ name: 'events[].timestamp', type: 'ISO 8601', required: true, desc: 'Event time with timezone. An event older than 90 days comes back with status "rejected" inside a 200. An event more than 24 hours ahead fails the whole request with a 422 and nothing is stored.' },
- { name: 'events[].kdes', type: 'object', required: true, desc: 'CTE-specific Key Data Elements per 21 CFR §§1.1325–1.1350. Each cte_type has its own required keys; see Required KDEs by event type, below the endpoint list.' },
+ { name: 'events[].kdes', type: 'object', required: true, desc: 'CTE-specific Key Data Elements per 21 CFR §§1.1325 to 1.1350. Each cte_type has its own required keys; see Required KDEs by event type, below the endpoint list.' },
],
body: ingestSamplePayload('""', '""'),
response: INGEST_RESPONSE_SAMPLE,
@@ -75,12 +75,12 @@ const API_SERVICES: { name: string; baseUrl: string; icon: React.ComponentType<{
name: 'Records API',
baseUrl: '/api/v1/records',
icon: Database,
- description: 'Read canonical traceability events with full provenance — what it is, where it came from, what rules were applied, what failed, what must happen next',
+ description: 'Read canonical traceability events with full provenance: what it is, where it came from, what rules were applied, what failed, what must happen next',
endpoints: [
{
method: 'GET',
path: '',
- description: 'List and search canonical traceability events. Requires an API key with the records.read scope. Keys are tenant-bound — results are always scoped to your workspace, and no tenant_id or X-Tenant-ID is needed.',
+ description: 'List and search canonical traceability events. Requires an API key with the records.read scope. Keys are tenant-bound, so results are always scoped to your workspace, and no tenant_id or X-Tenant-ID is needed.',
auth: true,
headers: ['X-RegEngine-API-Key'],
params_table: [
@@ -125,7 +125,7 @@ const API_SERVICES: { name: string; baseUrl: string; icon: React.ComponentType<{
{
method: 'GET',
path: '/{event_id}/history',
- description: 'Amendment chain for a record — every correction with who, when, and why',
+ description: 'Amendment chain for a record: every correction with who, when, and why',
auth: true,
headers: ['X-RegEngine-API-Key'],
response: '{ "event_id": "0b9c2f4e-6d1a-4c53-9a7e-5f3d8e21c7ab", "amendment_chain": [...] }',
@@ -141,7 +141,7 @@ const API_SERVICES: { name: string; baseUrl: string; icon: React.ComponentType<{
{
method: 'GET',
path: '/trace/{tlc}',
- description: 'Trace the transformation graph for a traceability lot code — upstream sources and downstream products. Requires the fda.read scope. Unlike the Records API, FDA endpoints take tenant_id as an explicit query parameter (it must match your key’s workspace).',
+ description: 'Trace the transformation graph for a traceability lot code (upstream sources and downstream products). Requires the fda.read scope. Unlike the Records API, FDA endpoints take tenant_id as an explicit query parameter (it must match your key’s workspace).',
auth: true,
headers: ['X-RegEngine-API-Key'],
params: ['tenant_id={your-workspace-uuid}'],
@@ -168,7 +168,7 @@ const API_SERVICES: { name: string; baseUrl: string; icon: React.ComponentType<{
{
method: 'GET',
path: '/export/v2',
- description: 'Generate an FDA export from the canonical record model (v2) — includes provenance and rule-evaluation status per row. Requires the fda.export scope.',
+ description: 'Generate an FDA export from the canonical record model (v2), with provenance and rule-evaluation status per row. Requires the fda.export scope.',
auth: true,
headers: ['X-RegEngine-API-Key'],
params: ['tenant_id={your-workspace-uuid}', 'start_date=2026-01-01', 'end_date=2026-01-31'],
@@ -177,7 +177,7 @@ const API_SERVICES: { name: string; baseUrl: string; icon: React.ComponentType<{
{
method: 'GET',
path: '/export/recall',
- description: 'Recall-filtered FDA export — only events matching the product, location, lot code, or event type you filter on. Requires the fda.export scope.',
+ description: 'Recall-filtered FDA export: only events matching the product, location, lot code, or event type you filter on. Requires the fda.export scope.',
auth: true,
headers: ['X-RegEngine-API-Key'],
params: ['tenant_id={your-workspace-uuid}', 'tlc=TEST-00012345678901-LOT-001'],
@@ -227,7 +227,7 @@ const API_SERVICES: { name: string; baseUrl: string; icon: React.ComponentType<{
},
];
-/** Section label inside an endpoint panel — the only uppercase in the system (SPEC §2.2). */
+/** Section label inside an endpoint panel - the only uppercase in the system (SPEC §2.2). */
const PANEL_LABEL = 'font-mono text-label uppercase text-re-text-muted';
function CopyButton({ text }: { text: string }) {
@@ -387,7 +387,7 @@ const RATE_LIMIT_HEADERS = [
{ header: 'X-RateLimit-Reset', desc: 'When limit resets (epoch)' },
];
-/** A key/value well — one surface step below the card it sits on (SPEC §5.1). */
+/** A key/value well - one surface step below the card it sits on (SPEC §5.1). */
const WELL = 'rounded-md border border-re-border-subtle bg-re-surface-inset p-3';
export function ApiReferenceClient() {
@@ -490,7 +490,7 @@ export function ApiReferenceClient() {
Available on contracted workspaces. These endpoints are served from your
workspace's provisioned endpoint URL (supplied during onboarding), not the
- public regengine.co base URL. Webhook Ingestion, Records, and FDA
+ public regengine.co base URL. JSON Events API, Records, and FDA
Trace & Export above are the publicly reachable surface.
Keys carry scopes in a resource.action vocabulary.
The . and : separators
- are equivalent — records.read and{' '}
+ are equivalent: records.read and{' '}
records:read name the same scope. A key only needs the scopes for the
endpoints you call:
@@ -133,8 +133,8 @@ 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 EPCIS ingest, compliance snapshots, SLA request updates
- and onboarding steps, accept any valid tenant key without checking a named scope. Treat every key as
+ 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
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/docs/changelog/page.tsx b/frontend/src/app/docs/changelog/page.tsx
index 641719d14..c68568f6d 100644
--- a/frontend/src/app/docs/changelog/page.tsx
+++ b/frontend/src/app/docs/changelog/page.tsx
@@ -16,7 +16,7 @@ export const metadata: Metadata = {
},
};
-/** Inline identifier — SPEC §2.3. */
+/** Inline identifier - SPEC §2.3. */
const CODE =
'rounded-sm bg-re-surface-inset px-1.5 py-0.5 font-mono text-[0.8125rem] text-re-text-primary';
@@ -24,7 +24,7 @@ const LIST = 'mt-3 list-disc space-y-1.5 pl-5 text-ui text-re-text-secondary mar
const GROUP = 'font-mono text-label uppercase text-re-text-muted';
/**
- * One release. A hairline rule separates releases — a tinted band per version
+ * One release. A hairline rule separates releases - a tinted band per version
* would turn the page into a colour field (SPEC §3.2), and the version stamp is
* mono because it is a recorded identifier (SPEC §2.3).
*/
@@ -78,7 +78,7 @@ export default function ChangelogPage() {
/>
SHA-256 chained record hashing for tamper detection
FTL Checker tool for Food Traceability List verification
diff --git a/frontend/src/app/docs/page.tsx b/frontend/src/app/docs/page.tsx
index fba966b21..902a669fe 100644
--- a/frontend/src/app/docs/page.tsx
+++ b/frontend/src/app/docs/page.tsx
@@ -80,7 +80,7 @@ const subPages = [
{ title: 'Quickstart', href: '/docs/quickstart', icon: Zap, description: 'Your first API call in about 5 minutes' },
{ title: 'Authentication', href: '/docs/authentication', icon: Key, description: 'The key header, scopes, and what a key can reach' },
{ title: 'API Reference', href: '/docs/api', icon: Code2, description: 'Full endpoint reference with schemas' },
- { title: 'Outbound webhooks (planned)', href: '/docs/webhooks', icon: Webhook, description: 'Notifications from RegEngine. To send events in, use POST /webhooks/ingest.' },
+ { title: 'Outbound webhooks (planned)', href: '/docs/webhooks', icon: Webhook, description: 'Notifications from RegEngine. To send events in, use the JSON events API (POST /webhooks/ingest).' },
{ title: 'Inflow Lab', href: '/docs/connectors/inflow-lab', icon: Terminal, description: 'Preflight, readiness scoring, fix queue, and commit gates' },
{ title: 'Error Codes', href: '/docs/errors', icon: AlertTriangle, description: 'Error responses and troubleshooting' },
{ title: 'Rate Limits', href: '/docs/rate-limits', icon: Clock, description: 'What to do when you get a 429' },
@@ -283,7 +283,7 @@ export default function DocsHomePage() {
Ready to get started?
- Developer access is request-gated — tell us about your integration and we'll provision your API key.
+ Developer access is request-gated. Tell us about your integration and we'll provision your API key.
diff --git a/frontend/src/app/docs/sdks/page.tsx b/frontend/src/app/docs/sdks/page.tsx
index 6e2f00732..8b0bec2af 100644
--- a/frontend/src/app/docs/sdks/page.tsx
+++ b/frontend/src/app/docs/sdks/page.tsx
@@ -21,7 +21,7 @@ const SDKS = [
{ title: 'Python SDK', badge: 'Planned', planned: true },
{ title: 'Node.js SDK', badge: 'Planned', planned: true },
{ title: 'Go SDK', badge: 'Planned', planned: true },
- { title: 'REST Client', badge: 'Available now — OpenAPI 3.0 spec', planned: false },
+ { title: 'REST Client', badge: 'Available now (OpenAPI 3.0 spec)', planned: false },
];
const CHROME_LINK =
diff --git a/frontend/src/app/docs/walkthrough/page.tsx b/frontend/src/app/docs/walkthrough/page.tsx
index 186eeb59e..9e8b1aa87 100644
--- a/frontend/src/app/docs/walkthrough/page.tsx
+++ b/frontend/src/app/docs/walkthrough/page.tsx
@@ -16,16 +16,16 @@ import {
export const metadata: Metadata = {
title: 'End-to-End Walkthrough | RegEngine',
description:
- 'A 15-minute hands-on walkthrough — post a complete FSMA 204 supplier-to-shipping flow, see it on your dashboard, and produce a review-ready export.',
+ 'A 15-minute hands-on walkthrough: post a complete FSMA 204 supplier-to-shipping flow, see it on your dashboard, and produce a review-ready export.',
openGraph: {
title: 'End-to-End Walkthrough',
- description: 'A 15-minute hands-on walkthrough — post a complete FSMA 204 supplier-to-shipping flow, see it on your dashboard, and produce a review-ready export.',
+ description: 'A 15-minute hands-on walkthrough: post a complete FSMA 204 supplier-to-shipping flow, see it on your dashboard, and produce a review-ready export.',
url: 'https://regengine.co/docs/walkthrough',
type: 'website',
},
};
-/** Inline identifier — SPEC §2.3. */
+/** Inline identifier - SPEC §2.3. */
const CODE =
'rounded-sm bg-re-surface-inset px-1.5 py-0.5 font-mono text-[0.8125rem] text-re-text-primary';
@@ -79,7 +79,7 @@ export default function WalkthroughPage() {
You receive lot {WALKTHROUGH_RECEIVED_LOT} from
- Supplier A. This is a Receiving CTE — the starting anchor for the chain.
+ Supplier A. This is a Receiving CTE, the starting anchor for the chain.
@@ -94,25 +94,25 @@ export default function WalkthroughPage() {
- 503 with "Document storage is not configured" — this endpoint doesn't
+ 503 with "Document storage is not configured": this endpoint doesn't
need object storage; you shouldn't hit this. If you do, the 503 is from a different
path; ping support.
- 401 — the key is missing or not recognized, or{' '}
+ 401: the key is missing or not recognized, or{' '}
X-Webhook-Signature is missing or wrong. The key header is{' '}
X-RegEngine-API-Key (not Authorization). Sign the exact bytes
you send, which is why the sample uses --data-binary.{' '}
- 403 — the key lacks the{' '}
+ 403: the key lacks the{' '}
webhooks.ingest scope.
- 200 with status: "rejected" — read{' '}
+ 200 with status: "rejected": read{' '}
errors on that event, for example a missing KDE. Fix it and resend with a new{' '}
Idempotency-Key.
- 422 — the whole request failed validation and nothing was stored.{' '}
+ 422: the whole request failed validation and nothing was stored.{' '}
error.details names the field, for example a missing quantity or a timestamp more than 24 hours ahead.
- You wash and bag the lot — that's a Transformation CTE. The input lot is the
+ You wash and bag the lot. That's a Transformation CTE. The input lot is the
Receiving lot from Step 1; the output lot is new.
@@ -184,7 +184,7 @@ head -20 lot-export.csv`}
You should see header rows mapping to FDA's 1.1455(c) requirements, with one row per
- CTE event for the lot. Production records, not legal advice — your operation remains
+ CTE event for the lot. Production records, not legal advice. Your operation remains
responsible for compliance.
@@ -208,7 +208,7 @@ head -20 lot-export.csv`}
{filterExperimentalToolLinks([
{ title: 'Try a Recall Drill', desc: 'Simulate an FDA recall request against the data you just posted', href: '/dashboard/recall-drills' },
{ title: 'CSV Bulk Ingest', desc: 'Upload a real supplier CSV through Inflow Workbench preflight', href: '/tools/inflow-lab' },
- { title: 'Set up EPCIS or EDI', desc: 'Replace manual curl with your existing system-of-record', href: '/docs/connectors/inflow-lab' },
+ { title: 'EPCIS intake (Preview)', desc: 'Send records by CSV upload or the JSON events API. EPCIS 2.0 is in Preview on the Ingest page.', href: '/docs/connectors/inflow-lab' },
]).map(({ title, desc, href }) => (
- That is ingestion, and it is available now:{' '}
+ That is the JSON events API, and it is available now:{' '}
POST /api/v1/webhooks/ingest
@@ -125,7 +125,7 @@ export default function WebhooksDocsPage() {
Get Notified on Release
- Be the first to know when webhooks are available.
+ Be the first to know when customer-managed webhooks are available.
What this does not prove.{' '}
@@ -512,7 +512,7 @@ export default function VerifyPage() {
- verify_chain.py — bash
+ verify_chain.py (bash)
@@ -637,7 +637,7 @@ export default function VerifyPage() {
RegEngine adds hash-based consistency checks on top of standard compliance controls.
- SHA-256 hashing lets you confirm a bundle matches its own manifest independently —
+ SHA-256 hashing lets you confirm a bundle matches its own manifest independently,
whether or not you require a SOC 2 report. It is not a signature and does not
authenticate the publisher.
diff --git a/frontend/src/app/walkthrough/page.tsx b/frontend/src/app/walkthrough/page.tsx
index 9383f7d00..88e484afb 100644
--- a/frontend/src/app/walkthrough/page.tsx
+++ b/frontend/src/app/walkthrough/page.tsx
@@ -56,7 +56,7 @@ const TIMELINE = [
color: 'red',
defects: [
{ lot: 'R-2026-0312', issue: 'Missing required KDE: cooling_date', severity: 'critical' },
- { lot: 'R-2026-0312', issue: 'Identity conflict: "Valley Fresh Farms LLC" vs "Valley Fresh" — >=85% match, unresolved', severity: 'critical' },
+ { lot: 'R-2026-0312', issue: 'Identity conflict: "Valley Fresh Farms LLC" vs "Valley Fresh" (>=85% match, unresolved)', severity: 'critical' },
{ lot: 'R-2026-0314', issue: 'Invalid unit_of_measure: "bushels" not in FSMA standard units', severity: 'critical' },
],
},
@@ -65,7 +65,7 @@ const TIMELINE = [
title: 'Exception cases created',
icon: AlertTriangle,
description: 'Each blocking defect becomes an Exception Case with severity, owner assignment, and remediation guidance. The operator sees exactly what needs fixing and why.',
- detail: 'Exception cases track resolution status, signoffs, and waiver history. Nothing gets swept under the rug — every decision is recorded in the audit trail.',
+ detail: 'Exception cases track resolution status, signoffs, and waiver history. Nothing gets swept under the rug: every decision is recorded in the audit trail.',
color: 'amber',
},
{
@@ -78,7 +78,7 @@ const TIMELINE = [
},
{
time: 'T+1:30',
- title: 'All defects resolved — submission unblocked',
+ title: 'All defects resolved, submission unblocked',
icon: Shield,
description: 'The blocking defect check runs again: 0 blocking defects remain. The package can now be assembled.',
detail: 'RegEngine checks 7 blocker categories: critical rule failures, unresolved exceptions, unevaluated events, missing signoffs, identity ambiguity, stale evaluations, and deadline monitoring.',
@@ -126,7 +126,7 @@ export default function WalkthroughPage() {
Here's how you respond in 2.
>
}
- description="A step-by-step walkthrough of an illustrative FDA records-request scenario — from messy supplier data to a hash-manifested response package anyone can re-check."
+ description="A step-by-step walkthrough of an illustrative FDA records-request scenario, from messy supplier data to a hash-manifested response package anyone can re-check."
className="border-b-0 pb-0"
/>
@@ -222,7 +222,7 @@ export default function WalkthroughPage() {
You cannot submit if:
- RegEngine surfaces these checks at package assembly time. Rule enforcement is configurable per workflow — in production today, rule failures are recorded as exceptions and surfaced in the dashboard rather than blocking ingest.
+ RegEngine surfaces these checks at package assembly time. Rule enforcement is configurable per workflow. In production today, rule failures are recorded as exceptions and surfaced in the dashboard rather than blocking ingest.
{[
diff --git a/frontend/src/app/why-regengine/page.tsx b/frontend/src/app/why-regengine/page.tsx
index b7cff09ea..665ab68d5 100644
--- a/frontend/src/app/why-regengine/page.tsx
+++ b/frontend/src/app/why-regengine/page.tsx
@@ -42,7 +42,7 @@ interface ComparisonRow {
const COMPARISON_DATA: ComparisonRow[] = [
{
- feature: 'REST API + Webhooks',
+ feature: 'CSV upload + JSON events API',
regengine: true,
trustwell: false,
safetychain: false,
@@ -70,7 +70,7 @@ const COMPARISON_DATA: ComparisonRow[] = [
repositrak: 'Verify with vendor',
},
{
- feature: 'EPCIS 2.0 Native',
+ feature: 'EPCIS 2.0 export',
regengine: true,
trustwell: 'Verify with vendor',
safetychain: 'Verify with vendor',
@@ -132,7 +132,7 @@ const DIFFERENTIATORS = [
icon: Code2,
title: 'API-First Architecture',
description:
- 'REST APIs, webhooks, and CSV templates. Integrate RegEngine with your existing ERP, WMS, or custom systems. No rip-and-replace required.',
+ 'CSV upload and the JSON events API. Integrate RegEngine with your existing ERP, WMS, or custom systems through those two live paths. No rip-and-replace required.',
detail: 'Technical teams can be live in hours. Sales ops teams use our CSV templates. Everyone gets FSMA 204 compliance their way.',
},
{
@@ -146,7 +146,7 @@ const DIFFERENTIATORS = [
icon: Zap,
title: 'Self-serve, days not months',
description:
- 'Get from signup to a first FSMA 204 export in days. CSV upload or API payload → validation → export. No 3–6 month enterprise implementation.',
+ 'Get from signup to a first FSMA 204 export in days. CSV upload or API payload → validation → export. No 3 to 6 month enterprise implementation.',
detail: 'Major retailers and FDA enforcement deadlines arrive on calendar dates; teams ship sooner when the platform is self-serve.',
},
{
@@ -158,9 +158,9 @@ const DIFFERENTIATORS = [
},
{
icon: Workflow,
- title: 'EPCIS 2.0 Native',
+ title: 'EPCIS 2.0 export',
description:
- "EPCIS 2.0 support for trading-partner interoperability. Verify each enabled workflow against your retailer and buyer requirements.",
+ "EPCIS 2.0 export for trading-partner interoperability. Verify each enabled workflow against your retailer and buyer requirements.",
detail: 'We started on EPCIS 2.0; verify EPCIS coverage with each vendor for your stack.',
},
{
@@ -390,7 +390,7 @@ export default function WhyRegEnginePage() {
API-First, Not UI-Only
- Every feature in RegEngine has an API. REST endpoints. Webhooks for real-time events. CSV templates for ops teams. Your developers can integrate in hours, not months.
+ REST endpoints, CSV templates for ops teams, and outbound webhooks for FDA recall/enforcement alerts (enabled by RegEngine). API coverage for every feature is on the roadmap. Your developers can integrate in hours, not months.
RegEngine started with APIs and added the dashboard on top, so every dashboard action is reachable programmatically. Verify API surface depth with each vendor for your stack.
@@ -398,7 +398,7 @@ export default function WhyRegEnginePage() {
{[
'Full REST API for CTEs, suppliers, compliance status',
- 'Event webhooks for recall triggers and compliance alerts',
+ 'Outbound webhooks for FDA recall/enforcement alerts (enabled by RegEngine); broader compliance alerts on the roadmap',
'CSV import with smart field mapping',
'Native integrations with Shopify, WooCommerce (coming soon)',
].map((item, idx) => (
@@ -444,7 +444,7 @@ HTTP 201
{[
- 'Simple design-partner agreements — no multi-year lock-in',
+ 'Simple design-partner agreements with no multi-year lock-in',
'API access for contracted workspaces',
'Founding Design Partner rate never increases',
].map((item, idx) => (
@@ -582,7 +582,7 @@ HTTP 201
- EPCIS 2.0 Native
+ EPCIS 2.0 export
EPCIS 2.0 can support trading-partner interoperability. RegEngine treats EPCIS 2.0 as a supported schema where the enabled workflow and source data allow it.
@@ -633,7 +633,7 @@ HTTP 201
RegEngine is built for companies like:
{[
- 'Regional produce distributors ($5M–$50M annual revenue)',
+ 'Regional produce distributors ($5M to $50M annual revenue)',
'Specialty food brands and co-packers',
'Organic/specialty retailers and wholesalers',
'Small farms and grower cooperatives',
@@ -700,7 +700,7 @@ HTTP 201
},
{
title: 'We do NOT lock you in',
- detail: 'Simple design-partner agreements — no multi-year lock-in. Export all your data anytime. Your data is yours.',
+ detail: 'Simple design-partner agreements with no multi-year lock-in. Export all your data anytime. Your data is yours.',
},
].map((item, idx) => (
@@ -730,7 +730,7 @@ HTTP 201
{
Icon: Code2,
title: 'Technical Buyers',
- detail: 'Your team owns integrations. You want APIs, webhooks, and source control. RegEngine gives you that.',
+ detail: 'Your team owns integrations. You want APIs, outbound webhooks for FDA recall/enforcement alerts (enabled by RegEngine), and source control. RegEngine gives you that.',
},
{
Icon: Workflow,
@@ -767,7 +767,7 @@ HTTP 201
Ready to Get Compliant?
- Request access in minutes. Start with the onboarding path that fits your team. Simple design-partner agreements — no multi-year lock-in.
+ Request access in minutes. Start with the onboarding path that fits your team. Simple design-partner agreements with no multi-year lock-in.
diff --git a/frontend/src/components/control-plane/demo-banner.tsx b/frontend/src/components/control-plane/demo-banner.tsx
index 5551d535d..42cca6b3a 100644
--- a/frontend/src/components/control-plane/demo-banner.tsx
+++ b/frontend/src/components/control-plane/demo-banner.tsx
@@ -7,7 +7,7 @@ export function DemoBanner({ visible }: { visible: boolean }) {
return (
- Sample Data — Backend unavailable.
+ Sample Data: Backend unavailable.
Data will appear once services are connected.
diff --git a/frontend/src/components/dashboard/metrics-overview-widget.tsx b/frontend/src/components/dashboard/metrics-overview-widget.tsx
index 79011cbb6..1e3f1e394 100644
--- a/frontend/src/components/dashboard/metrics-overview-widget.tsx
+++ b/frontend/src/components/dashboard/metrics-overview-widget.tsx
@@ -28,7 +28,7 @@ function UploadCsvLink() {
* Follows the data-viz rules (SPEC §4, §7.1): exactly one hero figure per view;
* a tile is label / value / delta / trend with no icon; the value is never
* coloured; only the delta arrow carries sentiment. The readiness score appears
- * here and nowhere else on the page — it used to be rendered twice, once in
+ * here and nowhere else on the page - it used to be rendered twice, once in
* this row and again in the "Traceability Readiness" card below it.
*
* An empty tile says "No data yet" and links to CSV upload. A failed load is
diff --git a/frontend/src/components/fsma-checklist.tsx b/frontend/src/components/fsma-checklist.tsx
index 2ffdb45c2..4dccac134 100644
--- a/frontend/src/components/fsma-checklist.tsx
+++ b/frontend/src/components/fsma-checklist.tsx
@@ -43,7 +43,7 @@ const checklistItems = [
id: "kdes",
title: "All KDEs populated",
description: "Key Data Elements recorded for each Critical Tracking Event type",
- regulation: "21 CFR §§ 1.1325–1.1350",
+ regulation: "21 CFR §§ 1.1325 to 1.1350",
url: "https://www.ecfr.gov/current/title-21/chapter-I/subchapter-A/part-1/subpart-S/section-1.1325",
},
{
@@ -127,7 +127,7 @@ export default function FSMAChecklist() {
}
};
- // Band colour for the readiness meter. The number itself stays ink — the
+ // Band colour for the readiness meter. The number itself stays ink - the
// meter is a supporting mark, and "N/8" carries the meaning on its own.
const scoreBarClass =
checkedCount <= 3
@@ -162,7 +162,7 @@ export default function FSMAChecklist() {
Check off each FSMA 204 requirement your company currently meets.
- Be honest — gaps are fixable, surprises during an FDA audit aren't.
+ Be honest: gaps are fixable, surprises during an FDA audit aren't.
diff --git a/frontend/src/components/fsma/readiness-assessment.tsx b/frontend/src/components/fsma/readiness-assessment.tsx
index 955621bcf..d8adfa498 100644
--- a/frontend/src/components/fsma/readiness-assessment.tsx
+++ b/frontend/src/components/fsma/readiness-assessment.tsx
@@ -26,7 +26,7 @@ import {
import { FSMA_ENFORCEMENT_DATE, FSMA_ENFORCEMENT_FRAMING } from '@/lib/fsma-enforcement';
import { trackReadinessAssessmentSubmit, trackToolRunCompleted, trackToolRunStarted } from '@/lib/conversion-events';
-// Food Traceability List categories — synced with the final FDA FTL as implemented
+// Food Traceability List categories - synced with the final FDA FTL as implemented
// in src/app/tools/ftl-checker/components/FTLCheckerClient.tsx (source of truth)
const FTL_CATEGORIES = [
{ id: 'leafy-greens-fresh', name: 'Leafy Greens (fresh, intact)', examples: 'Whole leaf lettuce, spinach bunches, kale, arugula, chard, collard greens', icon: Leaf },
@@ -40,9 +40,9 @@ const FTL_CATEGORIES = [
{ id: 'sprouts', name: 'Sprouts', examples: 'Alfalfa, bean, broccoli sprouts', icon: Leaf },
{ id: 'fresh-cut-fruits', name: 'Fresh-Cut Fruits', examples: 'Pre-cut fruit mixes, fruit cups', icon: Package },
{ id: 'fresh-cut-vegetables', name: 'Fresh-Cut Vegetables (non-leafy)', examples: 'Veggie trays, pre-cut carrots, celery sticks, broccoli florets', icon: Package },
- { id: 'finfish-histamine', name: 'Finfish — Scombrotoxin/Histamine-Forming', examples: 'Tuna, mackerel, mahi-mahi, bluefish, amberjack', icon: Package },
- { id: 'finfish-ciguatoxin', name: 'Finfish — Ciguatoxin-Associated', examples: 'Barracuda, grouper, snapper, moray eel', icon: Package },
- { id: 'finfish-other', name: 'Finfish — Other (fresh/frozen)', examples: 'Salmon, cod, halibut, tilapia, trout', icon: Package },
+ { id: 'finfish-histamine', name: 'Finfish (Scombrotoxin/Histamine-Forming)', examples: 'Tuna, mackerel, mahi-mahi, bluefish, amberjack', icon: Package },
+ { id: 'finfish-ciguatoxin', name: 'Finfish (Ciguatoxin-Associated)', examples: 'Barracuda, grouper, snapper, moray eel', icon: Package },
+ { id: 'finfish-other', name: 'Finfish, Other (fresh/frozen)', examples: 'Salmon, cod, halibut, tilapia, trout', icon: Package },
{ id: 'finfish-smoked', name: 'Smoked Finfish', examples: 'Smoked salmon, lox, kippered herring, smoked trout', icon: Package },
{ id: 'crustaceans', name: 'Crustaceans', examples: 'Shrimp, crab, lobster, crawfish', icon: Package },
{ id: 'molluscan-shellfish', name: 'Molluscan Shellfish (bivalves)', examples: 'Oysters, clams, mussels, scallops', icon: Package },
@@ -59,7 +59,7 @@ const CTES = [
{ id: 'harvesting', name: 'Harvesting (§1.1325)', description: 'Harvesting of raw agricultural commodities' },
{ id: 'cooling', name: 'Cooling (§1.1325)', description: 'Cooling of an RAC before initial packing (combined with Harvesting in §1.1325)' },
{ id: 'initial_packing', name: 'Initial Packing (§1.1330)', description: 'Initial packing of a raw agricultural commodity (other than food obtained from a fishing vessel)' },
- { id: 'first_receiver', name: 'First Land-Based Receiving (§1.1335) — Seafood', description: 'First land-based receipt of food obtained directly from a fishing vessel. Applies only to seafood obtained from a fishing vessel — non-seafood importers perform §1.1345 Receiving, not FLBR.' },
+ { id: 'first_receiver', name: 'First Land-Based Receiving, Seafood (§1.1335)', description: 'First land-based receipt of food obtained directly from a fishing vessel. Applies only to seafood obtained from a fishing vessel. Non-seafood importers perform §1.1345 Receiving, not FLBR.' },
{ id: 'shipping', name: 'Shipping (§1.1340)', description: 'Sending food from one location to another' },
{ id: 'receiving', name: 'Receiving (§1.1345)', description: 'Receipt of food at a facility. Subject to the §1.1305(j) exemption for restaurants and retail food establishments selling food directly to consumers.' },
{ id: 'transformation', name: 'Transformation (§1.1350)', description: 'Manufacturing, processing, or changing a food into a new traceability lot' },
@@ -439,7 +439,7 @@ export function FSMA204Assessment() {
{!systemAnswers['24hr'] && (
- Gap indicated: ability to produce records within 24 hours of an FDA request — review your retrieval workflow.
+ Gap indicated: ability to produce records within 24 hours of an FDA request. Review your retrieval workflow.
)}
{!systemAnswers['testing'] && (
diff --git a/frontend/src/components/marketing/sandbox-grid/SandboxPdfReport.ts b/frontend/src/components/marketing/sandbox-grid/SandboxPdfReport.ts
index b649bc6dc..029a88c5c 100644
--- a/frontend/src/components/marketing/sandbox-grid/SandboxPdfReport.ts
+++ b/frontend/src/components/marketing/sandbox-grid/SandboxPdfReport.ts
@@ -163,7 +163,7 @@ export function generateComplianceReport(result: SandboxResult): void {
doc.setFontSize(9);
doc.setTextColor(DANGER.r, DANGER.g, DANGER.b);
doc.text(
- `IMPORT MAPPING NEEDS CORRECTION \u2014 ${result.blocking_reasons.length} blocker${result.blocking_reasons.length !== 1 ? 's' : ''}`,
+ `IMPORT MAPPING NEEDS CORRECTION: ${result.blocking_reasons.length} blocker${result.blocking_reasons.length !== 1 ? 's' : ''}`,
margin + 4, y + 6,
);
doc.setFont('helvetica', 'normal');
@@ -333,7 +333,7 @@ export function generateComplianceReport(result: SandboxResult): void {
doc.setFont('helvetica', 'normal');
doc.setFontSize(7);
doc.setTextColor(GRAY.r, GRAY.g, GRAY.b);
- doc.text('Generated by RegEngine \u2014 www.regengine.co', margin, pageH - 9);
+ doc.text('Generated by RegEngine (www.regengine.co)', margin, pageH - 9);
doc.text(`Page ${p} of ${totalPages}`, pageWidth - margin, pageH - 9, { align: 'right' });
doc.setFontSize(7);
doc.setTextColor(BRAND.r, BRAND.g, BRAND.b);
diff --git a/frontend/src/components/marketing/sandbox-grid/SandboxResultView.tsx b/frontend/src/components/marketing/sandbox-grid/SandboxResultView.tsx
index 4ef5c8ff5..9baac9b74 100644
--- a/frontend/src/components/marketing/sandbox-grid/SandboxResultView.tsx
+++ b/frontend/src/components/marketing/sandbox-grid/SandboxResultView.tsx
@@ -1,6 +1,6 @@
'use client';
-// #2757 — extracted verbatim from the shared-result page so /sandbox and
+// #2757 - extracted verbatim from the shared-result page so /sandbox and
// /sandbox/results/[id] render one diagnosis, not two that drift apart.
// Presentational only: no fetching, no analytics. Callers own those.
@@ -190,7 +190,7 @@ export function SandboxResultView({
{result.submission_blocked && (
- TEST RUN NEEDS CORRECTION — {result.blocking_reasons.length} blocker{result.blocking_reasons.length !== 1 ? 's' : ''}
+ TEST RUN NEEDS CORRECTION: {result.blocking_reasons.length} blocker{result.blocking_reasons.length !== 1 ? 's' : ''}
Supports QR (GS1 Digital Link), GS1-128, DataMatrix, EAN-13, and UPC-A.
- {continuous ? ' Keep scanning — each new code is captured automatically.' : ' Ensure good lighting and hold steady.'}
+ {continuous ? ' Keep scanning. Each new code is captured automatically.' : ' Ensure good lighting and hold steady.'}
);
diff --git a/frontend/src/components/mobile/FieldCaptureClient.tsx b/frontend/src/components/mobile/FieldCaptureClient.tsx
index 8d3e9a48c..a7fe14bf0 100644
--- a/frontend/src/components/mobile/FieldCaptureClient.tsx
+++ b/frontend/src/components/mobile/FieldCaptureClient.tsx
@@ -333,12 +333,12 @@ export function FieldCaptureClient() {
toast({
title: 'Label Analyzed & Ingested',
- description: `${productName} — ${visionData.fsma_kdes?.length || 0} KDEs extracted`,
+ description: `${productName}: ${visionData.fsma_kdes?.length || 0} KDEs extracted`,
});
} else {
- // Vision failed — fall back to raw file upload
+ // Vision failed - fall back to raw file upload
await ingestFileMutation.mutateAsync({ file, sourceSystem: 'mobile_capture_pwa' });
- toast({ title: 'Photo Uploaded', description: 'Vision analysis unavailable — raw image stored.' });
+ toast({ title: 'Photo Uploaded', description: 'Vision analysis unavailable. Raw image stored.' });
}
} catch {
await savePhoto(blob);
diff --git a/frontend/src/components/ui/data-table.tsx b/frontend/src/components/ui/data-table.tsx
index a11b1012e..cf89310b9 100644
--- a/frontend/src/components/ui/data-table.tsx
+++ b/frontend/src/components/ui/data-table.tsx
@@ -20,7 +20,7 @@ import { Skeleton } from "@/components/ui/skeleton"
import { cn } from "@/lib/utils"
/**
- * DataTable — SPEC §4.
+ * DataTable - SPEC §4.
*
* Columns are a plain descriptor array rather than TanStack `ColumnDef`
* generics: there is exactly one obvious implementation, which is what keeps
@@ -48,7 +48,7 @@ export type DataTableSort = { key: string; direction: "asc" | "desc" }
export interface DataTableProps {
columns: DataTableColumn[]
data: T[]
- /** Visually hidden by default — the table's accessible name. */
+ /** Visually hidden by default - the table's accessible name. */
caption: string
density?: "low" | "medium" | "high"
toolbar?: {
@@ -240,7 +240,7 @@ export function DataTable({
{pagination && status === "idle" && data.length > 0 && (
diff --git a/frontend/src/components/ui/stat-tile.tsx b/frontend/src/components/ui/stat-tile.tsx
index 7506e7863..529a1e686 100644
--- a/frontend/src/components/ui/stat-tile.tsx
+++ b/frontend/src/components/ui/stat-tile.tsx
@@ -4,20 +4,21 @@ import { ArrowDownRight, ArrowUpRight, Minus } from "lucide-react"
import { Card } from "@/components/ui/card"
import { Skeleton } from "@/components/ui/skeleton"
import { cn } from "@/lib/utils"
+import { NO_DATA_YET } from "@/lib/empty-values"
/**
- * StatTile — SPEC §4, following the data-viz rules.
+ * StatTile - SPEC §4, following the data-viz rules.
*
* Anatomy is fixed: label / value / optional delta / optional trend. There is
- * deliberately **no icon slot** — an icon slot is what let three competing tile
+ * deliberately **no icon slot** - an icon slot is what let three competing tile
* styles grow across the dashboard last time.
*
* Colour rules:
- * · the value is never coloured — it is always `text-re-text-primary`;
+ * · the value is never coloured - it is always `text-re-text-primary`;
* · the delta *text* stays secondary; only the arrow carries sentiment colour,
* with an sr-only word so the direction is not colour-only;
- * · the sparkline is aria-hidden and decorative — the delta carries the
- * meaning — and is `text-tertiary` on every tile except the hero, which is
+ * · the sparkline is aria-hidden and decorative - the delta carries the
+ * meaning - and is `text-tertiary` on every tile except the hero, which is
* the one place a brand stroke is allowed.
*
* Exactly one `hero` tile per view.
@@ -35,7 +36,7 @@ export interface StatTileProps {
value: React.ReactNode
unit?: string
delta?: StatTileDelta
- /** 8–30 points. Omit for no chart. */
+ /** 8-30 points. Omit for no chart. */
trend?: number[]
/** A StatusBadge, typically. */
status?: React.ReactNode
@@ -130,7 +131,7 @@ export function StatTile({
empty && "text-re-text-disabled"
)}
>
- {empty ? "—" : value}
+ {empty ? NO_DATA_YET : value}
{unit && !empty && {unit}}
{trend && trend.length > 1 && !empty && (
diff --git a/frontend/src/lib/api-client.ts b/frontend/src/lib/api-client.ts
index 5edbae9a3..01988f06a 100644
--- a/frontend/src/lib/api-client.ts
+++ b/frontend/src/lib/api-client.ts
@@ -437,7 +437,7 @@ class APIClient {
const formData = new FormData();
formData.append('file', file);
formData.append('source', sourceSystem);
- // cte_type is optional — the backend auto-detects from a cte_type/event_type column
+ // cte_type is optional - the backend auto-detects from a cte_type/event_type column
if (cteType) {
formData.append('cte_type', cteType);
}
@@ -622,7 +622,7 @@ class APIClient {
}
// validateConfig() was removed on 2026-04-17 (#1203). The compliance
- // service's POST /validate endpoint was orphaned — no component ever
+ // service's POST /validate endpoint was orphaned: no component ever
// invoked the hook, and the real compliance evaluation lives in the
// ingestion service's /api/v1/rules/evaluate route via the versioned
// RulesEngine.
@@ -668,7 +668,7 @@ class APIClient {
}
/**
- * Ingest CTE events via the webhook endpoint — persists to fsma.cte_events + hash chain.
+ * Ingest CTE events via the webhook endpoint - persists to fsma.cte_events + hash chain.
* Use this for sample data loading and programmatic event ingestion.
*/
async ingestWebhookEvents(
@@ -915,7 +915,7 @@ class APIClient {
};
}
- // Bulk upload — uses adminClient (Vercel Pro proxy with maxDuration=300s)
+ // Bulk upload - uses adminClient (Vercel Pro proxy with maxDuration=300s)
async parseSupplierBulkUpload(file: File): Promise {
const formData = new FormData();
@@ -961,7 +961,7 @@ class APIClient {
return data;
}
- // Portal Links — supplier portal link management
+ // Portal Links - supplier portal link management
async listPortalLinks(): Promise<{ links: PortalLink[]; total: number }> {
const tenantId = this.currentTenantId;
if (!tenantId) throw new Error('No tenant selected');
@@ -993,7 +993,7 @@ class APIClient {
await this.ingestionClient.patch(`/api/v1/portal/links/${encodeURIComponent(portalId)}/revoke`);
}
- // Supplier Integration Profiles — saved field mappings for portal and file feeds
+ // Supplier Integration Profiles - saved field mappings for portal and file feeds
async listIntegrationProfiles(): Promise {
const tenantId = this.currentTenantId;
if (!tenantId) throw new Error('No tenant selected');
diff --git a/frontend/src/lib/api-config.ts b/frontend/src/lib/api-config.ts
index 966a1a5d1..2fd26cede 100644
--- a/frontend/src/lib/api-config.ts
+++ b/frontend/src/lib/api-config.ts
@@ -9,7 +9,7 @@ const SERVICE_PORTS = { ingestion: 8002, graph: 8200, compliance: 8500, admin: 8
type ServiceName = keyof typeof SERVICE_PORTS;
/**
- * Server-side only — resolve backend URL from env vars with localhost fallback.
+ * Server-side only - resolve backend URL from env vars with localhost fallback.
* Use this in Next.js API routes instead of inline `|| 'http://localhost:XXXX'`.
*/
export function getServerServiceURL(service: ServiceName): string {
@@ -25,7 +25,7 @@ export function getServerServiceURL(service: ServiceName): string {
if (process.env.VERCEL) {
console.error(
- `[api-config] ${service.toUpperCase()}_SERVICE_URL not configured — local dev fallback is unreachable from Vercel`,
+ `[api-config] ${service.toUpperCase()}_SERVICE_URL not configured; local dev fallback is unreachable from Vercel`,
);
return '';
}
diff --git a/frontend/src/lib/api-proxy.ts b/frontend/src/lib/api-proxy.ts
index 55de259ce..5ffd7befd 100644
--- a/frontend/src/lib/api-proxy.ts
+++ b/frontend/src/lib/api-proxy.ts
@@ -1,9 +1,9 @@
/**
* Shared utilities for API proxy routes.
*
- * – sanitizePath: prevents path traversal and null-byte injection
- * – proxyError: returns a consistent JSON error shape across all proxies
- * – validateProxySession: validates Supabase session tokens before proxying
+ * - sanitizePath: prevents path traversal and null-byte injection
+ * - proxyError: returns a consistent JSON error shape across all proxies
+ * - validateProxySession: validates Supabase session tokens before proxying
*/
import { NextRequest, NextResponse } from 'next/server';
@@ -58,7 +58,7 @@ export function validateUuid(value: string | undefined | null): string | null {
/**
* Resolve the authoritative tenant for an opaque `__Host-regengine_session`
* by calling the backend `/auth/me`. The session cookie is httpOnly and
- * server-validated, so the returned `organization_id` is trustworthy — unlike
+ * server-validated, so the returned `organization_id` is trustworthy - unlike
* the client-writable `re_tenant_id` cookie. Returns the tenant id (lowercased)
* or `null` when no server-derived tenant is available (caller must fail closed).
*
@@ -260,7 +260,7 @@ export function requireProxyAuth(request: NextRequest): NextResponse | null {
}
return NextResponse.json(
- { error: 'Unauthorized — no valid credentials provided' },
+ { error: 'Unauthorized: no valid credentials provided' },
{ status: 401 },
);
}
@@ -334,7 +334,7 @@ export function hasSupabaseSessionCookies(request: NextRequest): boolean {
// A real Supabase session is the `sb--auth-token` cookie (and its
// chunked `.0` / `.1` variants when the payload is large). The PKCE flow
// also writes a transient `sb--auth-token-code-verifier` cookie
- // that is NOT a session — it's just the verifier for an in-flight OAuth
+ // that is NOT a session - it's just the verifier for an in-flight OAuth
// exchange and can outlive the flow if the user navigates away. Matching
// it as a session caused authenticated RegEngine sessions to 401 at the
// proxy when supabase.auth.getUser() returned no user.
@@ -355,7 +355,7 @@ export function hasSupabaseSessionCookies(request: NextRequest): boolean {
*
* Only validates when Supabase cookies are present (sb-* cookies indicate
* the user authenticated via Supabase). API-key-only requests are validated
- * by the backend — the proxy can't verify those.
+ * by the backend - the proxy can't verify those.
*
* Returns a 401 NextResponse if the session is invalid/expired,
* or null if the request should proceed.
@@ -366,7 +366,7 @@ export async function validateProxySession(
): Promise {
const regengineToken = request.cookies.get('re_access_token')?.value;
if (isUsableCallerCredential(regengineToken)) {
- // Decode-only check — the backend (services/admin) owns JWTKeyRegistry
+ // Decode-only check - the backend (services/admin) owns JWTKeyRegistry
// and is the authoritative verifier. We reject malformed/expired tokens
// and obvious iss/aud mismatches up front, then compare the JWT's
// tenant_id claim against the re_tenant_id cookie for defense-in-depth.
@@ -374,7 +374,7 @@ export async function validateProxySession(
const tokenPayload = decodeRegEngineClaims(regengineToken);
if (!tokenPayload) {
return NextResponse.json(
- { error: 'Session expired or invalid — please log in again' },
+ { error: 'Session expired or invalid. Please log in again' },
{ status: 401 },
);
}
@@ -389,7 +389,7 @@ export async function validateProxySession(
}
// The RegEngine JWT is an authoritative caller credential. Don't fall
- // through to the Supabase check — stale `sb-*` cookies (e.g. leftover
+ // through to the Supabase check - stale `sb-*` cookies (e.g. leftover
// PKCE `-code-verifier`) would otherwise fail Supabase validation and
// 401 a request that already authenticated successfully.
return null;
@@ -419,14 +419,14 @@ export async function validateProxySession(
if (!hasSupabaseCookies) {
if (options.requireSupabaseSession) {
return NextResponse.json(
- { error: 'Session expired or invalid — please log in again' },
+ { error: 'Session expired or invalid. Please log in again' },
{ status: 401 },
);
}
return null;
}
- // Also skip if the request carries an API key — backend handles that auth
+ // Also skip if the request carries an API key - backend handles that auth
const hasApiKey =
!!request.cookies.get('re_api_key')?.value ||
!!request.headers.get('x-regengine-api-key');
@@ -452,18 +452,18 @@ export async function validateProxySession(
if (error || !user) {
return NextResponse.json(
- { error: 'Session expired or invalid — please log in again' },
+ { error: 'Session expired or invalid. Please log in again' },
{ status: 401 },
);
}
} catch {
if (options.requireSupabaseSession) {
return NextResponse.json(
- { error: 'Session expired or invalid — please log in again' },
+ { error: 'Session expired or invalid. Please log in again' },
{ status: 401 },
);
}
- // Supabase SDK error — don't block requests that also carry backend-validated
+ // Supabase SDK error - don't block requests that also carry backend-validated
// credentials; let the backend make the final auth decision.
return null;
}
@@ -482,7 +482,7 @@ export async function validateProxySession(
*/
export function staticExportGuard(serviceName: string): NextResponse | null {
if (process.env.REGENGINE_DEPLOY_MODE === 'static') {
- console.error(`[proxy/${serviceName}] 503 — proxy unavailable in static export mode`);
+ console.error(`[proxy/${serviceName}] 503: proxy unavailable in static export mode`);
return proxyError(`${serviceName} proxy unavailable in static export mode`, 503);
}
return null;
diff --git a/frontend/src/lib/auth-context.tsx b/frontend/src/lib/auth-context.tsx
index ed396e6d4..f9d671e0c 100644
--- a/frontend/src/lib/auth-context.tsx
+++ b/frontend/src/lib/auth-context.tsx
@@ -36,7 +36,7 @@ type LoginOptions = {
};
/**
- * HTTP-only cookie auth — CRITICAL #2 fix complete.
+ * HTTP-only cookie auth - CRITICAL #2 fix complete.
*
* Sensitive credentials (access_token and tenant API keys) are stored
* ONLY in HTTP-only cookies via /api/session. They are never readable
@@ -50,7 +50,7 @@ type LoginOptions = {
* access is derived from the authenticated user session, never from a key.
*/
-/** Placeholder value for apiKey — indicates credentials are
+/** Placeholder value for apiKey - indicates credentials are
* managed server-side in HTTP-only cookies. NOT the real key. */
const COOKIE_MANAGED_PLACEHOLDER = 'cookie-managed';
@@ -59,13 +59,13 @@ const STORAGE_KEYS = {
ONBOARDED: 'regengine_onboarded',
DEMO_MODE: 'regengine_demo_mode',
USER: 'regengine_user',
- // DEPRECATED — migrated to HTTP-only cookies, then removed from localStorage
+ // DEPRECATED - migrated to HTTP-only cookies, then removed from localStorage
_LEGACY_API_KEY: 'regengine_api_key',
_LEGACY_ACCESS_TOKEN: 'regengine_access_token',
};
// ---------------------------------------------------------------------------
-// Cookie helpers — talk to /api/session (server-side)
+// Cookie helpers - talk to /api/session (server-side)
// ---------------------------------------------------------------------------
/** Store sensitive credentials in HTTP-only cookies via /api/session POST.
@@ -90,13 +90,13 @@ async function setSessionCookies(params: {
// double-submit check, so this POST needs a valid X-CSRF-Token header.
// GET /api/session is idempotent and sets the cookie pair on first
// call. The auth provider runs this on mount, but on cold-start /
- // SSR'd login pages the hydrate effect may not have fired yet — call
+ // SSR'd login pages the hydrate effect may not have fired yet: call
// bootstrap defensively here so a freshly-loaded login page can submit
// immediately without race-conditioning on the hydrate fetch.
if (!document.cookie.includes('re_csrf=')) {
try {
await fetch('/api/session', { method: 'GET', credentials: 'include' });
- } catch { /* fall through — POST will surface the error if cookies still missing */ }
+ } catch { /* fall through: POST will surface the error if cookies still missing */ }
}
const res = await fetchWithCsrf('/api/session', {
method: 'POST',
@@ -225,7 +225,7 @@ async function migrateLocalStorageToCookies(): Promise {
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
- console.info('[auth] Migration complete — localStorage secrets removed.');
+ console.info('[auth] Migration complete; localStorage secrets removed.');
}
}
@@ -312,7 +312,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
setAccessTokenState(COOKIE_MANAGED_PLACEHOLDER);
apiClient.setAccessToken(COOKIE_MANAGED_PLACEHOLDER);
} else if (!loginCompletedRef.current) {
- // Definitive unauthenticated answer — the cookie session expired or
+ // Definitive unauthenticated answer - the cookie session expired or
// was cleared server-side. Drop the localStorage-hydrated user (and
// ignore any session.user echoed back from a stale re_user cookie)
// so pages can't render an authenticated shell from stale state.
@@ -326,7 +326,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
setApiKeyState(COOKIE_MANAGED_PLACEHOLDER);
}
} else {
- // The session check itself failed (network error or non-2xx —
+ // The session check itself failed (network error or non-2xx -
// /api/session GET otherwise always answers 200 with an
// `authenticated` boolean). Indeterminate: keep the
// localStorage-hydrated state rather than logging the user out of
@@ -345,7 +345,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const setApiKey = useCallback(async (key: string | null) => {
if (key) {
- // MUST await — middleware checks this cookie on the next request.
+ // MUST await - middleware checks this cookie on the next request.
// Setting state before the cookie is written causes a race condition
// where the app appears authenticated but middleware rejects the request.
const cookiesOk = await setSessionCookies({ apiKey: key });
@@ -361,7 +361,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const setTenantId = useCallback(async (id: string | null) => {
if (typeof window !== 'undefined') {
if (id) {
- // Await cookie write before updating state — middleware needs the
+ // Await cookie write before updating state - middleware needs the
// tenant cookie in place before any navigation triggers a request.
await setSessionCookies({ tenantId: id });
localStorage.setItem(STORAGE_KEYS.TENANT_ID, id);
@@ -405,7 +405,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
logoutInProgressRef.current = false;
const normalizedLoginUser = normalizeUserProfile(loginUser);
- // Non-React state — these don't trigger re-renders or effects.
+ // Non-React state - these don't trigger re-renders or effects.
apiClient.setAccessToken(COOKIE_MANAGED_PLACEHOLDER);
apiClient.setUser(normalizedLoginUser);
if (loginTenantId) apiClient.setCurrentTenant(loginTenantId);
@@ -419,13 +419,13 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
//
// React state updates below can trigger navigation to /dashboard (or the
// ?next= path). The middleware runs on that navigation and checks
- // re_access_token — if the cookie isn't in the browser yet, middleware
+ // re_access_token - if the cookie isn't in the browser yet, middleware
// rejects and redirects back to /login?next=/dashboard.
//
// Awaiting setSessionCookies first lets the Set-Cookie response be
// processed by the browser before any navigation can occur.
//
- // #535: Check the return value — if the /api/session POST fails (network
+ // #535: Check the return value - if the /api/session POST fails (network
// error, server error) we must throw rather than silently continue.
// A false return means the re_access_token cookie was never written, so
// the very next middleware check would reject the user and redirect back
@@ -444,7 +444,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
loginCompletedRef.current = true;
- // React state updates — triggers re-renders and the navigation useEffect.
+ // React state updates - triggers re-renders and the navigation useEffect.
// Cookie should be stored in the browser at this point.
setAccessTokenState(COOKIE_MANAGED_PLACEHOLDER);
setAuthMethodState(null);
diff --git a/frontend/src/lib/csrf.ts b/frontend/src/lib/csrf.ts
index d185867a0..28c01a312 100644
--- a/frontend/src/lib/csrf.ts
+++ b/frontend/src/lib/csrf.ts
@@ -1,5 +1,5 @@
/**
- * CSRF Protection — Double-Submit Cookie Pattern
+ * CSRF Protection - Double-Submit Cookie Pattern
*
* Defense-in-depth on top of SameSite=Lax cookies. State-changing requests
* (POST/PUT/PATCH/DELETE) to /api/* must include an X-CSRF-Token header
@@ -7,8 +7,8 @@
*
* Token flow:
* 1. POST /api/session sets two cookies:
- * - re_csrf (httpOnly: false, JS-readable) — random token
- * - re_csrf_sig (httpOnly: true) — HMAC-SHA256(token, secret)
+ * - re_csrf (httpOnly: false, JS-readable) - random token
+ * - re_csrf_sig (httpOnly: true) - HMAC-SHA256(token, secret)
* 2. Client reads re_csrf from document.cookie and sends it as X-CSRF-Token header
* 3. Middleware verifies: HMAC(headerValue, secret) === re_csrf_sig cookie
*/
@@ -25,7 +25,7 @@ export { CSRF_COOKIE, CSRF_SIG_COOKIE, CSRF_HEADER };
/** Get the CSRF signing secret.
*
- * Production REQUIRES a dedicated CSRF_SECRET — reusing AUTH_SECRET_KEY (the
+ * Production REQUIRES a dedicated CSRF_SECRET - reusing AUTH_SECRET_KEY (the
* JWT signing key) for HMAC violates cryptographic key separation
* (NIST SP 800-57 §5.2). The AUTH_SECRET_KEY fallback is preserved only for
* dev/test convenience. RE-SEC-005.
@@ -34,7 +34,7 @@ function getSecret(): string {
const secret = process.env.CSRF_SECRET || '';
if (!secret) {
if (process.env.NODE_ENV === 'production') {
- console.error('[csrf] CSRF_SECRET not set in production — all mutating requests will be rejected');
+ console.error('[csrf] CSRF_SECRET not set in production; all mutating requests will be rejected');
return '';
}
return process.env.AUTH_SECRET_KEY || '';
@@ -79,7 +79,7 @@ export async function verifyCsrfToken(token: string, signature: string): Promise
const secret = getSecret();
if (!secret) {
if (process.env.NODE_ENV === 'production') {
- console.error('[csrf] CSRF_SECRET / AUTH_SECRET_KEY not set — rejecting mutating request');
+ console.error('[csrf] CSRF_SECRET / AUTH_SECRET_KEY not set; rejecting mutating request');
return false;
}
return true;
@@ -111,7 +111,7 @@ export const CSRF_PROTECTED_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']
/** Routes exempt from CSRF checks (pre-auth ceremonies, signed webhooks, and API-key routes).
*
* Keep this list narrow. Broad prefixes like /api/tools/ silently exempt
- * every future handler placed there — list the specific public endpoints
+ * every future handler placed there - list the specific public endpoints
* instead so adding a new /api/tools/* route is a deliberate decision.
*
* /api/session is intentionally NOT exempt. GET /api/session bootstraps the
@@ -121,17 +121,17 @@ export const CSRF_PROTECTED_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']
* like every other mutating endpoint. The Origin allowlist on POST
* /api/session (RE-SEC-003) remains as defense-in-depth.
*
- * #2885 MATCHING RULE — read this before adding an entry. isCsrfExempt() is
+ * #2885 MATCHING RULE - read this before adding an entry. isCsrfExempt() is
* called from src/proxy.ts with `request.nextUrl.pathname`, i.e. the path the
* BROWSER requested, before any rewrite or proxy runs. An entry only ever
- * matches if it is a real frontend path — either a next.config.js rewrite
+ * matches if it is a real frontend path - either a next.config.js rewrite
* source or a route under src/app/api. A backend path does NOT match the
* same-origin BFF path that proxies to it: '/api/v1/webhooks/' does not
* prefix-match '/api/ingestion/api/v1/webhooks/ingest'. That mismatch is what
* 403'd every developer-playground POST (#2885); the fix was to send the CSRF
* header via fetchWithCsrf, NOT to widen this list.
*
- * Verified 2026-08-26 — each entry against next.config.js and src/app/api:
+ * Verified 2026-08-26 - each entry against next.config.js and src/app/api:
* /api/auth/ rewrite source (next.config.js:229) ✓
* /api/admin/auth/ src/app/api/admin/[...path] ✓
* /api/admin/v1/auth/accept-invite src/app/api/admin/[...path] ✓
@@ -143,7 +143,7 @@ export const CSRF_PROTECTED_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']
* /api/inflow-lab/ src/app/api/inflow-lab/[...path] ✓
* /api/tools/check-access src/app/api/tools/check-access ✓
* /api/tools/verify src/app/api/tools/verify ✓
- * /api/webhooks/ DEAD — no rewrite source and no handler
+ * /api/webhooks/ DEAD - no rewrite source and no handler
* under src/app/api/webhooks. It matches
* nothing today, so it grants nothing;
* it is kept only because removing an
@@ -152,7 +152,7 @@ export const CSRF_PROTECTED_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']
* src/__tests__/lib/csrf-exempt-prefixes.test.ts.
* If you add a local webhook handler
* there, that handler becomes CSRF-exempt
- * silently — decide then, on purpose.
+ * silently - decide then, on purpose.
*/
export type CsrfExemptionRule = Readonly<{
methods: readonly string[];
diff --git a/frontend/src/lib/customer-readiness.ts b/frontend/src/lib/customer-readiness.ts
index d04f75b5e..9cbd77427 100644
--- a/frontend/src/lib/customer-readiness.ts
+++ b/frontend/src/lib/customer-readiness.ts
@@ -37,7 +37,7 @@ export const INTEGRATION_TYPE_LABELS: Record = {
export const INTEGRATION_TYPE_DESCRIPTIONS: Record = {
standards_based: 'Standards-oriented ingest and export support without a claim of managed two-way sync or source-system write-back.',
api_based_custom: 'Customer-managed integration via RegEngine API and implementation guidance. The customer builds and maintains the workflow unless otherwise scoped.',
- file_based_import: 'Scheduled or manual CSV/XLSX ingestion with field mapping and exception handling.',
+ file_based_import: 'Manual CSV/XLSX upload with field mapping and exception handling. Scheduled file delivery is on the roadmap.',
one_way_export: 'RegEngine prepares outbound packages. No write-back to source system.',
};
@@ -132,7 +132,7 @@ export const STATUS_LABELS: Record = {
export const DELIVERY_MODE_LABELS: Record = {
native_api: 'Native API',
webhook: 'Webhook',
- csv_sftp_import: 'CSV / SFTP Import',
+ csv_sftp_import: 'CSV Import',
export_only: 'Export Only',
custom_scoped: 'Custom Scoped',
};
@@ -190,12 +190,12 @@ export const CAPABILITY_REGISTRY: CapabilityRegistryEntry[] = [
},
{
id: 'csv-sftp',
- name: 'CSV / SFTP Import',
+ name: 'CSV Import',
category: 'erp_warehouse',
status: 'file_import_supported',
delivery_mode: 'csv_sftp_import',
integration_type: 'file_based_import',
- customer_copy: 'Use CSV or scheduled file delivery for ERP and warehouse data that is not available over API.',
+ customer_copy: 'Upload CSV files for ERP and warehouse data that is not available over API.',
evidence_url: '/tools/data-import',
},
{
@@ -215,7 +215,7 @@ export const CAPABILITY_REGISTRY: CapabilityRegistryEntry[] = [
status: 'custom_scoped',
delivery_mode: 'csv_sftp_import',
integration_type: 'file_based_import',
- customer_copy: 'NetSuite data can be onboarded via CSV or SFTP export with RegEngine mapping review.',
+ customer_copy: 'NetSuite data can be onboarded via CSV export with RegEngine mapping review.',
evidence_url: '/trust',
},
{
@@ -290,12 +290,12 @@ export const CAPABILITY_REGISTRY: CapabilityRegistryEntry[] = [
},
{
id: 'webhooks',
- name: 'Webhook ingestion (inbound)',
+ name: 'JSON events API (inbound)',
category: 'developer_api',
status: 'implemented',
- delivery_mode: 'webhook',
+ delivery_mode: 'native_api',
integration_type: 'api_based_custom',
- customer_copy: 'Webhook ingestion is available for inbound event submission and downstream normalization (one-way inbound).',
+ customer_copy: 'Submit CTE events as JSON to POST /api/v1/webhooks/ingest for validation and downstream normalization (one-way inbound).',
evidence_url: '/docs/api',
},
{
@@ -303,31 +303,21 @@ export const CAPABILITY_REGISTRY: CapabilityRegistryEntry[] = [
name: 'Inflow Lab',
category: 'developer_api',
status: 'pilot',
- delivery_mode: 'webhook',
+ delivery_mode: 'native_api',
integration_type: 'api_based_custom',
- customer_copy: 'RegEngine-owned FSMA 204 simulator for webhook demos, contract tests, and developer validation.',
+ customer_copy: 'RegEngine-owned FSMA 204 simulator that posts events to the JSON events API for demos, contract tests, and developer validation.',
evidence_url: '/docs/connectors/inflow-lab',
},
{
id: 'epcis',
name: 'GS1 EPCIS 2.0',
category: 'developer_api',
- status: 'implemented',
- delivery_mode: 'native_api',
- integration_type: 'standards_based',
- customer_copy: 'JSON-LD EPCIS ingest and export are supported. Default deployments use PostgreSQL-backed task processing; legacy Kafka-compatible signing is opt-in.',
+ status: 'export_supported',
+ delivery_mode: 'export_only',
+ integration_type: 'one_way_export',
+ customer_copy: 'JSON-LD EPCIS export is supported. EPCIS ingest is in Preview. Default deployments use PostgreSQL-backed task processing; legacy Kafka-compatible signing is opt-in.',
evidence_url: '/developers',
},
- {
- id: 'edi-856',
- name: 'EDI 856',
- category: 'developer_api',
- status: 'pilot',
- delivery_mode: 'custom_scoped',
- integration_type: 'file_based_import',
- customer_copy: 'ASN inbound processing exists, but production rollout should be treated as guided integration work.',
- evidence_url: '/trust',
- },
{
id: 'design-partner',
name: 'Design Partner Program',
@@ -402,12 +392,12 @@ export const SUPPORT_CHANNELS: SupportChannel[] = [
tier: 'Premium',
responseWindow: 'Per contract',
escalation: 'Named contacts, negotiated escalation tree, and security review support',
- notes: 'All commitments — response time, named escalations, security obligations — live in the executed agreement.',
+ notes: 'All commitments (response time, named escalations, security obligations) live in the executed agreement.',
},
];
// Sample export-job configurations rendered on the Trust Center page as
-// examples of supported configurations — NOT live production telemetry.
+// examples of supported configurations - NOT live production telemetry.
// Timestamps and manifest hashes below are placeholder strings and must
// not be presented as real run data; the Trust Center renderer hides
// timestamps for this reason. If you add a renderer that surfaces
@@ -415,7 +405,7 @@ export const SUPPORT_CHANNELS: SupportChannel[] = [
export const ARCHIVE_EXPORT_JOBS: ArchiveExportJob[] = [
{
id: 'job_fda_daily',
- name: 'Sample — Daily FDA package archive',
+ name: 'Sample: Daily FDA package archive',
format: 'FDA Package',
cadence: 'Daily',
destination: 'Object storage archive',
@@ -427,7 +417,7 @@ export const ARCHIVE_EXPORT_JOBS: ArchiveExportJob[] = [
},
{
id: 'job_epcis_weekly',
- name: 'Sample — Weekly EPCIS outbound bundle',
+ name: 'Sample: Weekly EPCIS outbound bundle',
format: 'GS1 EPCIS 2.0',
cadence: 'Weekly',
destination: 'Downloadable bundle',
@@ -439,7 +429,7 @@ export const ARCHIVE_EXPORT_JOBS: ArchiveExportJob[] = [
},
{
id: 'job_audit_monthly',
- name: 'Sample — Monthly audit evidence bundle',
+ name: 'Sample: Monthly audit evidence bundle',
format: 'Audit Bundle',
cadence: 'Monthly',
destination: 'Object storage archive',
@@ -486,7 +476,7 @@ export const MAPPING_REVIEW_ITEMS: MappingReviewItem[] = [
},
{
id: 'map_case_005',
- source: 'EDI 856 ASN',
+ source: 'JSON events API',
sourceField: 'Case Count',
mappedField: null,
status: 'needs_review',
diff --git a/frontend/src/lib/empty-values.ts b/frontend/src/lib/empty-values.ts
new file mode 100644
index 000000000..c2051af77
--- /dev/null
+++ b/frontend/src/lib/empty-values.ts
@@ -0,0 +1,13 @@
+/**
+ * Shared empty-value placeholders. Use these instead of a bare dash so empty
+ * UI states read as intentional copy:
+ *
+ * - NOT_SET: a field the user or upstream system has not provided (settings,
+ * profile fields, optional record attributes).
+ * - NO_DATA_YET: a metric or count with no underlying data yet.
+ *
+ * Real zeros stay 0. Never use these where logic compares against the
+ * placeholder (for example the recall-report grade sentinels).
+ */
+export const NOT_SET = 'Not set';
+export const NO_DATA_YET = 'No data yet';
diff --git a/frontend/src/lib/epcis-upload.ts b/frontend/src/lib/epcis-upload.ts
index 181cc9104..fda18c2d6 100644
--- a/frontend/src/lib/epcis-upload.ts
+++ b/frontend/src/lib/epcis-upload.ts
@@ -122,5 +122,5 @@ export function epcisErrorMessage(error: unknown): string {
if (fromDetail) return fromDetail;
if (error.message) return error.message;
}
- return 'EPCIS upload failed';
+ return 'EPCIS upload (Preview) failed';
}
diff --git a/frontend/src/lib/fsma-enforcement.ts b/frontend/src/lib/fsma-enforcement.ts
index 3d261a803..59f3ab472 100644
--- a/frontend/src/lib/fsma-enforcement.ts
+++ b/frontend/src/lib/fsma-enforcement.ts
@@ -2,9 +2,9 @@
// the Continuing Appropriations Act of 2026 (Nov 2025) directed FDA not to
// enforce the Food Traceability Rule before July 20, 2028, and FDA has stated
// it intends to comply. The Aug 2025 compliance-date-extension rule is still
-// only a PROPOSED rule — never state the compliance date "was extended" as
+// only a PROPOSED rule - never state the compliance date "was extended" as
// settled fact. The requirements themselves are unchanged.
export const FSMA_ENFORCEMENT_DATE = 'July 20, 2028';
export const FSMA_ENFORCEMENT_FRAMING =
- 'FDA has been directed by Congress not to enforce the Food Traceability Rule before July 20, 2028, and has stated it intends to comply — the requirements are unchanged, only enforcement moved.';
+ 'FDA has been directed by Congress not to enforce the Food Traceability Rule before July 20, 2028, and has stated it intends to comply. The requirements are unchanged; only enforcement moved.';
diff --git a/frontend/src/lib/ftl-data.ts b/frontend/src/lib/ftl-data.ts
index 1b6b697bd..5140c67e0 100644
--- a/frontend/src/lib/ftl-data.ts
+++ b/frontend/src/lib/ftl-data.ts
@@ -2,7 +2,7 @@
* FDA Food Traceability List (FTL) category data.
*
* Shared between the full FTL Checker tool and the onboarding quick-check.
- * Source: 21 CFR Part 1, Subpart S — FDA Food Traceability Rule (FSMA 204)
+ * Source: 21 CFR Part 1, Subpart S - FDA Food Traceability Rule (FSMA 204)
*/
export interface FTLCategory {
@@ -146,7 +146,7 @@ export const FTL_CATEGORIES: FTLCategory[] = [
},
{
id: 'finfish-histamine',
- name: 'Finfish — Scombrotoxin/Histamine-Forming',
+ name: 'Finfish (Scombrotoxin/Histamine-Forming)',
category: 'Seafood',
examples: 'Tuna, mackerel, mahi-mahi, bluefish, amberjack, bonito',
exclusions: 'Catfish (Siluriformes) are USDA-regulated and excluded per §1.1305(g)',
@@ -158,7 +158,7 @@ export const FTL_CATEGORIES: FTLCategory[] = [
},
{
id: 'finfish-ciguatoxin',
- name: 'Finfish — Ciguatoxin-Associated',
+ name: 'Finfish (Ciguatoxin-Associated)',
category: 'Seafood',
examples: 'Barracuda, grouper, snapper, moray eel (tropical reef species)',
exclusions: 'Catfish (Siluriformes) are USDA-regulated and excluded per §1.1305(g)',
@@ -170,7 +170,7 @@ export const FTL_CATEGORIES: FTLCategory[] = [
},
{
id: 'finfish-other',
- name: 'Finfish — Other (fresh/frozen/previously frozen)',
+ name: 'Finfish, Other (fresh/frozen/previously frozen)',
category: 'Seafood',
examples: 'Salmon, cod, halibut, tilapia, trout, bass, swordfish',
exclusions: 'Catfish (Siluriformes) are USDA-regulated and excluded per §1.1305(g)',
diff --git a/frontend/src/lib/proxy-factory.ts b/frontend/src/lib/proxy-factory.ts
index 6ab7d3c1a..db73aacdb 100644
--- a/frontend/src/lib/proxy-factory.ts
+++ b/frontend/src/lib/proxy-factory.ts
@@ -1,5 +1,5 @@
/**
- * Proxy factory — shared scaffolding for Next.js catch-all proxy route
+ * Proxy factory - shared scaffolding for Next.js catch-all proxy route
* handlers (the files under src/app/api//[...path]/route.ts).
*
* The frontend fronts several backend services (admin, ingestion, compliance,
@@ -138,7 +138,7 @@ export function createJsonProxy(config: JsonProxyConfig): MethodHandlers {
const queryString = new URL(request.url).search;
const targetUrl = config.buildTargetUrl(path, queryString, request);
if (!targetUrl) {
- console.error(`[proxy/${config.serviceName}] 503 — backend not configured`);
+ console.error(`[proxy/${config.serviceName}] 503: backend not configured`);
return proxyError(`${config.serviceName} backend not configured`, 503);
}
@@ -179,7 +179,7 @@ export function createJsonProxy(config: JsonProxyConfig): MethodHandlers {
if (!response.ok) {
const detail = extractDetail(data);
if (response.status >= 500) {
- console.error(`[proxy/${config.serviceName}] ${response.status} — upstream request failed`, {
+ console.error(`[proxy/${config.serviceName}] ${response.status}: upstream request failed`, {
targetUrl,
detail: data,
});
@@ -196,7 +196,7 @@ export function createJsonProxy(config: JsonProxyConfig): MethodHandlers {
}
return NextResponse.json(data);
} catch (error: unknown) {
- console.error(`[proxy/${config.serviceName}] 500 — proxy request failed`, { error });
+ console.error(`[proxy/${config.serviceName}] 500: proxy request failed`, { error });
return proxyError(`${config.serviceName} request failed`, 500);
}
};
@@ -369,7 +369,7 @@ export function createStreamProxy(config: StreamProxyConfig): MethodHandlers {
const queryString = new URL(request.url).search;
const bases = config.resolveTargetBases();
if (bases.length === 0) {
- console.error(`[proxy/${config.serviceName}] 503 — no upstream targets configured`);
+ console.error(`[proxy/${config.serviceName}] 503: no upstream targets configured`);
return proxyError(`${config.serviceName} backend not configured`, 503);
}
@@ -413,7 +413,7 @@ export function createStreamProxy(config: StreamProxyConfig): MethodHandlers {
}
if (response.status >= 500) {
- console.error(`[proxy/${config.serviceName}] ${response.status} — upstream request failed`, {
+ console.error(`[proxy/${config.serviceName}] ${response.status}: upstream request failed`, {
targetUrl,
upstreamError: await readUpstreamErrorForLog(response),
});
@@ -446,10 +446,10 @@ export function createStreamProxy(config: StreamProxyConfig): MethodHandlers {
}
}
- console.error(`[proxy/${config.serviceName}] 502 — all targets failed:`, attemptErrors);
+ console.error(`[proxy/${config.serviceName}] 502: all targets failed:`, attemptErrors);
return proxyError(`Unable to reach ${config.serviceName} service`, 502);
} catch (error: unknown) {
- console.error(`[proxy/${config.serviceName}] 500 — proxy request failed`, { error });
+ console.error(`[proxy/${config.serviceName}] 500: proxy request failed`, { error });
return proxyError(`${config.serviceName} request failed`, 500);
}
};
@@ -458,7 +458,7 @@ export function createStreamProxy(config: StreamProxyConfig): MethodHandlers {
}
// ---------------------------------------------------------------------------
-// Shared helpers — used by proxy `buildHeaders` implementations
+// Shared helpers - used by proxy `buildHeaders` implementations
// ---------------------------------------------------------------------------
/** Strip trailing slashes from a URL base. */
diff --git a/frontend/src/lib/public-openapi.ts b/frontend/src/lib/public-openapi.ts
index 47247631f..f3fac4de3 100644
--- a/frontend/src/lib/public-openapi.ts
+++ b/frontend/src/lib/public-openapi.ts
@@ -36,7 +36,7 @@ export const publicOpenApiSpec = {
},
],
tags: [
- { name: 'Webhook Ingestion', description: 'Create tenant-scoped CTE/KDE records.' },
+ { name: 'JSON Events API', description: 'Create tenant-scoped CTE/KDE records.' },
{ name: 'Records', description: 'Read canonical ingestion-run records.' },
{ name: 'FDA Export', description: 'Export FDA-oriented sortable records where configured data supports the request.' },
],
@@ -44,7 +44,7 @@ export const publicOpenApiSpec = {
paths: {
'/api/v1/webhooks/ingest': {
post: {
- tags: ['Webhook Ingestion'],
+ tags: ['JSON Events API'],
operationId: 'ingestWebhookEvents',
summary: 'Ingest traceability events',
description:
@@ -84,7 +84,7 @@ export const publicOpenApiSpec = {
},
'/api/v1/webhooks/recent': {
get: {
- tags: ['Webhook Ingestion'],
+ tags: ['JSON Events API'],
operationId: 'listRecentWebhookEvents',
summary: 'List recent traceability events',
description: 'Requires the webhooks.read scope.',
@@ -116,7 +116,7 @@ export const publicOpenApiSpec = {
},
'/api/v1/webhooks/chain/verify': {
get: {
- tags: ['Webhook Ingestion'],
+ tags: ['JSON Events API'],
operationId: 'verifyWebhookChain',
summary: 'Verify a tenant hash chain',
description: 'Requires the webhooks.read scope.',
diff --git a/frontend/src/lib/roi-calculator-data.ts b/frontend/src/lib/roi-calculator-data.ts
index 899442a96..829974e63 100644
--- a/frontend/src/lib/roi-calculator-data.ts
+++ b/frontend/src/lib/roi-calculator-data.ts
@@ -54,7 +54,7 @@ export const ROI_CALCULATOR_CONFIG: ToolConfig = {
],
leadGate: {
title: 'Your Illustrative Savings Estimate is Ready',
- description: 'Enter your work email to see the illustrative breakdown. The numbers are not a forecast or commitment — book a call to model against your actual record volume.',
+ description: 'Enter your work email to see the illustrative breakdown. The numbers are not a forecast or commitment. Book a call to model against your actual record volume.',
cta: 'View My Estimate'
}
}
@@ -65,7 +65,7 @@ export const calculateROI = (answers: Record) => {
const manualHours = Number(answers['manual-hours'] || 0);
const annualRevenue = Number(answers['annual-revenue'] || 0);
- // ILLUSTRATIVE COEFFICIENTS — not forecasts, not industry benchmarks. Surfaced
+ // ILLUSTRATIVE COEFFICIENTS - not forecasts, not industry benchmarks. Surfaced
// honestly in the user-facing Methodology Note. Adjust here if assumptions change.
//
// 1. Modeled labor-time avoided: ManualHours * $75/hr loaded rate * 52 weeks * 0.85 capture rate